Es gibt ein paar Ansätze, die Sie ergreifen können; Ich glaube nicht, dass Sie genügend Kontext zur Verfügung gestellt haben, um zu bestimmen, welches das am besten geeignete wäre. Wenn Sie GHC-7.4 verwenden, sollten Sie die Erweiterung DefaultSignatures
ausprobieren.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
-- A generic class with a generic function.
class Foo a where
foo :: a -> a
default foo :: Bar a => a -> a
foo = bar . baz
-- A specific class with specific functions.
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Foo String
main :: IO()
main =
putStrLn (foo "bar")
Sie müssen noch erklären, dass ein Typ eine Instanz von Foo
ist, aber Sie brauchen nicht auf die Methode Erklärung zu wiederholen, da die Standardimplementierung verwendet wird.
Ein anderer ziemlich leichter Ansatz ist die Verwendung eines neuen Typs. Wenn Sie Funktionen haben, die eine Foo
Instanz benötigen, können Sie eine Bar
Instanz in den newtype umbrechen.
newtype FooBar a = FooBar { unFooBar :: a }
instance Bar a => Foo (FooBar a) where
foo = FooBar . bar . baz . unFooBar
-- imported from a library or something...
needsFoo :: Foo a => a -> b
myFunc = needsFoo (FooBar someBar)
Alternativ können Sie in der Lage sein, mit zu erhalten, indem foo
mit einer normalen Funktion zu ersetzen, oder machen eine spezielle Version für Bar
Instanzen:
-- if every `Foo` is also a `Bar`, you can just do this. No need for `Foo` at all!
foo :: Bar a => a -> a
foo = bar . baz
-- if most `Foo`s aren't `Bar`s, you may be able to use this function when you have a `Bar`
fooBar :: Bar a => a -> a
foo = bar . baz
Dies sind wahrscheinlich die besten Lösungen, wenn sie arbeiten für deine Situation.
Eine andere Möglichkeit besteht darin, jede Foo
Instanz manuell zu deklarieren. Obwohl es viele verschiedene vorstellbare Instanzen gibt, ist es ziemlich häufig für Codebasen, nur eine Handvoll Instanzen zu haben, die tatsächlich verwendet werden. Wenn das hier wahr ist, ist es wahrscheinlich weniger Arbeit, nur die 3 oder 4 Instanzen zu schreiben, die Sie brauchen, anstatt zu versuchen, eine allgemeinere Lösung zu implementieren.
Als allerletzte Mittel, können Sie so etwas wie Ihre Original-Code verwenden, aber Sie müssen auch OverlappingInstances
, damit es funktioniert (wenn Sie OverlappingInstances
nicht brauchen, dann brauchen Sie keine Foo
Klasse). Mit dieser Erweiterung kann GHC die "spezifischste Instanz" auswählen, wenn mehrere Übereinstimmungen vorhanden sind. Dies wird mehr oder weniger funktionieren, obwohl Sie möglicherweise nicht bekommen, was Sie erwarten.
class Foo a where
foo :: a -> a
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Bar a => Foo a where
foo = bar . baz
instance Foo [a] where
foo _ = []
main :: IO()
main =
print (foo "foo")
Jetzt main
druckt eine leere Zeichenfolge. Es gibt zwei Foo
Instanzen für a
und [a]
. Letzteres ist spezifischer, so dass es für foo "foo"
gewählt wird, da ein String den Typ [Char]
hat, obwohl Sie wahrscheinlich ersteres wollten. So, jetzt würden Sie auch
instance Foo String where
foo = bar . baz
bei denen schreiben müssen zeigen Sie auch die ganz Bar a => Foo a
Instanz auslassen kann.
Durch die Deklaration der 'Instanz Bar a => Foo a' Sie eine Instanz eingeführt haben, für jeden' a' - Kontext 'Bar a' nicht in Instanz Auswahl verwendet - obwohl, wenn Sie überlappende Instanzen haben GHC die meisten finden spezifisch für jedes Modul. –
Das ist ... Zähler intuitiv :-) Gibt es einen Weg um es herum? Ich nehme an, dass "das Finden der spezifischsten Instanz" das ist, was "UndecidableInstances" versuchen wird, aber in meinem Code versagt es kläglich. –
Verwandte: http://stackoverflow.com/questions/3213490 – sdcvvc