2011-05-09 21 views
80

Biorąc:Dlaczego nie mogę uczynić String instancją egzemplarza typograficznego?

data Foo = 
    FooString String 
    … 

class Fooable a where --(is this a good way to name this?) 
    toFoo :: a -> Foo 

Chcę, aby String wystąpienie Fooable:

instance Fooable String where 
    toFoo = FooString 

GHC potem 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' 

Gdybym zamiast używać [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:

  • Dlaczego nie mogę zrobić String i wystąpienie typeclass?
  • GHC wydaje się skłonny pozwolić mi uciec, jeśli dodam dodatkową flagę. Czy to dobry pomysł?
+6

Jest to rodzaj pytań, upvote i znak jako ulubione, ponieważ w przeciwnym razie wiem, w sposób w niedalekiej przyszłości będę o to pytać;) –

+3

Odnośnie dodatkowej flagi: to chyba dobry pomysł, o ile ufasz GHC i rozumiesz, co robi flaga. [Yesod] (http://www.yesodweb.com/) przychodzi na myśl: zachęca do korzystania zawsze z pragmy OverloadedStrings podczas pisania aplikacji Yesod, a QuasiQuotes to konieczność dla reguł routingu Yesod. Zauważ, że zamiast flagi podczas kompilacji możesz także umieścić '{- # LANGUAGE FlexibleInstances # -}' (lub dowolną inną pragmę) na górze pliku .hs. –

Odpowiedz

57

To dlatego String jest po prostu alias dla [Char] typ, który jest po prostu stosowanie konstruktora typu [] o rodzaju Char, więc to byłoby formularza ([] Char). który nie jest w postaci (T a1 .. an), ponieważ Char nie jest zmienną typu.

Powodem tego ograniczenia jest zapobieganie nakładaniu się instancji. Na przykład, powiedzmy, że masz instance Fooable [Char], a następnie ktoś później pojawił się i zdefiniował instance Fooable [a]. Teraz kompilator nie będzie w stanie określić, który z nich chciałbyś użyć, i da ci błąd.

Korzystając z numeru -XFlexibleInstances, obiecujesz kompilatorowi, że nie zdefiniujesz takich instancji.

W zależności od tego, co starasz się osiągnąć, to może być lepiej zdefiniować Wrapper:

newtype Wrapper = Wrapper String 
instance Fooable Wrapper where 
    ... 
+4

Powiedzmy ze względu na argument, że rzeczywiście chciałem również 'instance Fooable [a]'. Czy istnieje sposób, aby funkcja 'toFoo' zachowywała się inaczej, jeśli' a' jest Char? –

+7

@John: Istnieje rozszerzenie '-XOverlappingInstances' które pozwoli na to i wybierz najbardziej konkretną instancję. [Aby uzyskać szczegółowe informacje, patrz podręcznik użytkownika GHC] (http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html#instance-overlap). – hammar

17

używasz do dwóch ograniczeń klasycznych typeclasses Haskell98:

  • one uniemożliwić typu synonimy w przypadkach
  • uniemożliwiałyby one zagnieżdżone typy, które nie dzieje z kolei zawierają zmienne typu.

These uciążliwych ograniczeń są podnoszone przez dwóch rozszerzeń językowych:

  • -XTypeSynonymInstances

który pozwala na użycie synoyms typu (jak String dla [Char]), oraz:

  • -XFlexibleInstances

które znoszą ograniczenia dotyczące typów instancji w postaci T a b .., gdzie parametrami są zmienne typu. Flaga -XFlexibleInstances pozwala szefowi deklaracji instancji na wymienienie dowolnie zagnieżdżonych typów.

Należy pamiętać, że zniesienie tych ograniczeń może czasami prowadzić do overlapping instances, w którym to momencie może być konieczne dodatkowe rozszerzenie języka w celu rozwiązania niejasności, pozwalając GHC wybrać instancję dla ciebie.


Referencje::

2

Dodając do tych odpowiedzi, jeśli nie są wygodne ze zniesieniem ograniczeń, może się zdarzyć, może być użyteczne zawinięcie łańcucha w nowy typ, który może być instancją klasy. Kompromitacja byłaby potencjalną brzydotą, którą musiałby owinąć i rozwinąć w swoim kodzie.

4

FlexibleInstances nie są dobrą odpowiedzią w większości przypadków. lepsze alternatywy są zawijania ciąg w newtype lub wprowadzić klasę pomocniczą tak:

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

Powiązane problemy