Biorąc pod uwagę :
data Foo =
FooString String
…
class Fooable a where --(is this a good way to name this?)
toFoo :: a -> Foo
Chcę utworzyć String
przykład Fooable
:
instance Fooable String where
toFoo = FooString
GHC następnie narzeka:
Illegal instance declaration for `Fooable String'
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'
Jeśli zamiast tego użyję [Char]
:
instance Fooable [Char] where
toFoo = FooString
GHC narzeka:
Illegal instance declaration for `Fooable [Char]'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are type *variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Fooable [Char]'
Pytanie 30 :
- Dlaczego nie mogę utworzyć ciągu znaków i wystąpienia typeklasy?
- Wydaje się, że GHC chce pozwolić mi ujść na sucho, jeśli dodam dodatkową flagę. Czy to dobry pomysł?
haskell
ghc
typeclass
type-systems
John F. Miller
źródło
źródło
{-# LANGUAGE FlexibleInstances #-}
(lub dowolną inną pragmę) na początku swojego pliku .hs.Odpowiedzi:
Dzieje się tak, ponieważ
String
jest to tylko alias typu dla[Char]
, który jest po prostu zastosowaniem konstruktora[]
typu w typieChar
, więc będzie to formularz([] Char)
. która nie ma postaci,(T a1 .. an)
ponieważChar
nie jest zmienną typu.Powodem tego ograniczenia jest zapobieganie nakładaniu się wystąpień. Na przykład, powiedzmy, że masz plik
instance Fooable [Char]
, a potem ktoś przyszedł i zdefiniował plikinstance Fooable [a]
. Teraz kompilator nie będzie w stanie dowiedzieć się, którego chcesz użyć, i wyświetli błąd.Używając
-XFlexibleInstances
, zasadniczo obiecujesz kompilatorowi, że nie zdefiniujesz żadnych takich instancji.W zależności od tego, co próbujesz osiągnąć, lepiej zdefiniować opakowanie:
newtype Wrapper = Wrapper String instance Fooable Wrapper where ...
źródło
instance Fooable [a]
. Czy istnieje sposób, abytoFoo
funkcja działała inaczej, jeślia
jest Char?-XOverlappingInstances
które na to pozwala i wybiera najbardziej konkretną instancję. Szczegółowe informacje można znaleźć w podręczniku użytkownika GHC .Napotykasz dwa ograniczenia klasycznych typeklas Haskell98:
Te uciążliwe ograniczenia są znoszone przez dwa rozszerzenia językowe:
-XTypeSynonymInstances
co pozwala na użycie synoymów typu (takich jak
String
dla[Char]
) oraz:-XFlexibleInstances
które znoszą ograniczenia dotyczące typów instancji, które mają postać, w
T a b ..
której parametry są zmiennymi typu.-XFlexibleInstances
Flaga pozwala szef deklaracji instancji wspomnieć arbitralnych zagnieżdżone typy.Należy pamiętać, że zniesienie tych ograniczeń może czasami prowadzić do nakładania się wystąpień , w którym to momencie może być potrzebne dodatkowe rozszerzenie języka, aby rozwiązać tę niejednoznaczność, umożliwiając GHC wybranie wystąpienia za Ciebie.
Odniesienia ::
źródło
W większości przypadków elastyczne warunki nie są dobrą odpowiedzią. Lepsze alternatywy to zawijanie String w nowy typ lub wprowadzenie klasy pomocniczej, jak poniżej:
class Element a where listToFoo :: [a] -> Foo instance Element Char where listToFoo = FooString instance Element a => Fooable [a] where toFoo = listToFoo
Zobacz także: http://www.haskell.org/haskellwiki/List_instance
źródło
Dodając do tych odpowiedzi, jeśli nie czujesz się komfortowo z zniesieniem ograniczeń, mogą zaistnieć przypadki, w których sensowne może być umieszczenie ciągu znaków w nowym typie, który może być instancją klasy. Kompromisem byłaby potencjalna brzydota, konieczność zawijania i rozpakowywania kodu.
źródło