Pourquoi ne puis-je pas faire de Ssortingng une instance d’une classe de caractères?

Donné :

data Foo = FooSsortingng Ssortingng … class Fooable a where --(is this a good way to name this?) toFoo :: a -> Foo 

Je veux faire de Ssortingng une instance de Fooable :

 instance Fooable Ssortingng where toFoo = FooSsortingng 

GHC se plaint alors:

 Illegal instance declaration for `Fooable Ssortingng' (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 Ssortingng' 

Si au lieu de cela j’utilise [Char] :

 instance Fooable [Char] where toFoo = FooSsortingng 

GHC se plaint:

 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]' 

Question :

  • Pourquoi ne puis-je pas créer Ssortingng et l’instance d’une classe de caractères?
  • GHC semble prêt à me laisser faire si j’ajoute un drapeau supplémentaire. Est-ce une bonne idée?

    C’est parce que Ssortingng est juste un alias de type pour [Char] , qui est juste l’application du constructeur de type [] sur le type Char , donc ce serait de la forme ([] Char) . qui n’est pas de la forme (T a1 .. an) car Char n’est pas une variable de type.

    La raison de cette ressortingction est d’empêcher les instances qui se chevauchent. Par exemple, supposons que vous ayez une instance Fooable [Char] , puis quelqu’un est arrivé plus tard et a défini une instance Fooable [a] . Maintenant, le compilateur ne sera pas en mesure de déterminer celui que vous souhaitez utiliser et vous donnera une erreur.

    En utilisant -XFlexibleInstances , vous promettez au compilateur de ne pas définir de telles instances.

    Selon ce que vous essayez d’accomplir, il serait préférable de définir un wrapper:

     newtype Wrapper = Wrapper Ssortingng instance Fooable Wrapper where ... 

    Vous rencontrez deux limitations de la classe de classe Haskell98 classique:

    • ils interdisent les synonymes de type dans les instances
    • ils interdisent les types nesteds qui, à leur tour, ne contiennent pas de variables de type.

    Ces ressortingctions onéreuses sont levées par deux extensions de langue:

    • -XTypeSynonymInstances

    ce qui vous permet d’utiliser les types synoyms (comme Ssortingng pour [Char] ), et:

    • -XFlexibleInstances

    qui soulèvent les ressortingctions sur les types d’instance étant de la forme T ab .. où les parameters sont des variables de type. L’indicateur -XFlexibleInstances permet au responsable de la déclaration d’instance de mentionner des types nesteds arbitraires.

    Notez que la levée de ces ressortingctions peut parfois conduire à des instances qui se chevauchent , auquel cas une extension de langue supplémentaire peut être nécessaire pour résoudre l’ambiguïté, permettant à GHC de choisir une instance pour vous.


    Références :

    • Règles assouplies pour la tête d’instance , dans le Guide de l’utilisateur du GHC.

    FlexibleInstances n’est pas une bonne réponse dans la plupart des cas. De meilleures alternatives sont en train d’encapsuler la chaîne dans un newtype ou d’introduire une classe d’aide comme celle-ci:

     class Element a where listToFoo :: [a] -> Foo instance Element Char where listToFoo = FooSsortingng instance Element a => Fooable [a] where toFoo = listToFoo 

    Voir aussi: http://www.haskell.org/haskellwiki/List_instance

    Ajoutant à ces réponses, si vous n’êtes pas à l’aise avec la levée des ressortingctions, il peut y avoir des cas où il pourrait être judicieux d’envelopper votre chaîne dans un newtype, qui peut être une instance d’une classe. Le compromis serait la laideur potentielle, le fait de devoir emballer et déballer votre code.