Quel est l'effet du type synonymes sur les instances de classes de type? Qu'est-ce que le TypeSynonymInstances pragma à GHC faire?
Question
Je lis Real World Haskell Pg 151, et je l'ai regardé fixement au passage suivant pour plus d'une heure:
Rappelons que chaîne est synonyme de [Char], qui à son tour est du type [a] où Char est remplacé par le type un paramètre. Selon Haskell 98 de règles, nous ne sommes pas autorisés à fournir une le type à la place d'un paramètre de type lorsque nous écrivons une instance. En d'autres termes, il serait légal pour nous d'écrire un par exemple pour [a], mais pas pour [Char]. 16 commentaires 5335
Il est tout simplement pas en train de sombrer dans. Fixant le la copie papier (gratuite non piraté) du chapitre 6 RWH Je vois d'autres personnes beaucoup sont vraiment souffrance avec cela. Je ne comprends toujours pas des commentaires ...
Tout d'abord, tout ce qui concerne ce qui me confond, alors s'il vous plaît si vous sentez que vous pouvez expliquer quoi que ce soit au sujet de ce passage, ou TypeSynonymInstances
s'il vous plaît faire.
Voici mon problème:
-
Int
est constructeur de données -
String
est constructeur de données de type synonyme
Maintenant, je ne peux pas répondre à ces questions:
- Pourquoi un synonyme de type empêche la réalisation du type d'un membre d'une classe de type (je suis à la recherche pour une raison quelconque qui se rapporte sans doute à la compilation ou d'un synonyme implimentation de type)?
- Pourquoi les concepteurs de la langue, veulent pas cette syntaxe (je demande raisonnement pas une théorie extensive ou des symboles mathématiques unicode).
- Je vois cette ligne « du type [a] où Char est remplacé par le paramètre de type a » , et je veux savoir pourquoi je ne peux pas le remplacer pour cette "la taper une où Int est remplacé par le paramètre de type d'un ".
Merci!
La solution
Je pense qu'une partie de la question est que deux, en grande partie sans rapport, les restrictions sont en jeu:
- Aucun cas de synonymes de type signifie que les instances ne peuvent être déclarés choses avec
data
ounewtype
, nontype
. Cela interditString
, mais pas[Char]
. - Aucun cas flexibles signifie que les instances ne peuvent mentionner un type qui ne sont pas une variable, et seulement ce type peut être utilisé comme un constructeur de type. Cela interdit
Maybe Int
etf Int
, mais pasMaybe a
.
Voici ce que dit à propos GHCi Int
, Char
et String
:
data Char = GHC.Types.C# GHC.Prim.Char#
data Int = GHC.Types.I# GHC.Prim.Int#
type String = [Char]
Int
et Char
sont les deux types simples sans paramètres variables de type; il n'y a pas de constructeur de type impliqué, vous pouvez donc faire avec eux des cas à peu près librement.
String, cependant, échoue sur les deux cas . Il est synonyme de type, ce qui est interdit, et il est aussi un constructeur de type appliqué à un non variable , à savoir le constructeur de type liste appliquée à Char.
A titre de comparaison, notez que [a]
, Maybe a
et Either a b
sont tous valides dans les cas, mais [Int]
, Maybe [a]
et Either String a
sont interdits; nous espérons que vous pouvez voir maintenant pourquoi.
En ce qui concerne vos questions directes, je ne sais pas ce que les motivations initiales étaient pour la conception de la langue de cette façon, et je ne suis nullement qualifié pour faire des déclarations faisant autorité sur les « meilleures pratiques », mais pour mon propre codage personnel Je ne hésite pas vraiment utiliser ces pragmas:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
Vous pouvez toujours aller voir forfaits rel="noreferrer"> . cas flexibles, il semble, obtiennent une bonne quantité d'utilisation, et des paquets « respectables » (il y a un succès couple dans la source de parsecs, par exemple).
Autres conseils
En fait, ni Int
ni String
sont des constructeurs de données. Autrement dit, vous ne pouvez pas les utiliser pour créer une valeur de
> (Int 42, String "bob")
<interactive>:1:1: Not in scope: data constructor `Int'
<interactive>:1:9: Not in scope: data constructor `String'
Noms de Int
un nouveau type de données distinctes, algébrique. String
est un « type synonyme », ou alias, pour le type déjà existant: [Char]
. Le problème est que Haskell 98 dit que vous ne pouvez pas utiliser un synonyme de type dans une déclaration d'instance.
Je ne peux pas dire pourquoi les auteurs du rapport Haskell 98 choisissent de restreindre les synonymes de type dans ce cas. Il y a un certain nombre de restrictions sur eux. Par exemple, ils ne peuvent pas être appliquées partiellement (si elles prennent des arguments de type). Je pense que la moindre idée vient à la fin de §4.2.2:
Les synonymes de type sont une pratique, mais strictement syntaxique, le mécanisme pour faire Type signatures plus lisible. UNE synonymes et sa définition sont complètement interchangeables, sauf dans le type d'une instance de l'instance Déclaration (Section 4.3.2).
On peut supposer que, il y avait une approche de compilation du programme, pour lequel cette interchangeabilité syntaxique aurait causé des problèmes pour les instances. Peut-être qu'il a à voir avec aspect notable des cas qu'ils fuite de paquets ...
Quant à votre dernière question, je crois que l'explication est l'amalgame entre deux choses: 1) String
est synonyme de type pour [Char]
, qui est à son tour une spécialisation du type plus général [a]
et 2) que, même sans le synonyme, [Char]
ne peut pas être utilisé dans la tête d'une instance.
Ce second problème n'a rien à voir avec des synonymes de type, mais que les chefs d'instance doit avoir tous les paramètres de type au constructeur de type des variables, pas les types de béton. Autrement dit, vous ne pouvez pas définir des instances distinctes pour [Int]
et [Char]
pour une classe, vous ne pouvez défini une [a]
instances. (Rappelez-vous que, malgré la syntaxe pratique, []
est un constructeur de type, et la chose est à l'intérieur du paramètre de type.)
Encore une fois, je ne sais pas pourquoi le rapport limite ceux-ci, mais je soupçonne que cela a aussi à voir avec la stratégie de compilation. Depuis la stratégie de compilation de GHC pour les instances peut gérer cela, vous pourrez vous détendre dans cette contrainte GHC via -XFlexibleInstances
.
Enfin, j'ai vu les deux extensions activées dans beaucoup de code, mais peut-être quelqu'un avec plus d'expérience Haskell peut peser à si elles sont « bonnes pratiques » ou pas.
Haskell 98
- une tête d'instance doit avoir la forme C (T u1 ... uk), où T est un constructeur de type défini par une déclaration de données ou newtype (voir TypeSynonymInstances ) et l'interface sont des variables de type distinctes, et
- chaque assertion dans le contexte doit avoir la forme C » v, où v est l'un des ui.
Il est donc valable d'utiliser instance ClassName TypeConstructor where
et TypeConstructor MUST être tels que Int
, Double
ou [a]
, assurez-vous que seul un constructeur de type peut être impliqué !!
BTW, []
est constructeur de type, de sorte [TypeConstructor]
ne peut pas être utilisé, mais [NewType]
et [TypeVariable]
sont autorisés.
Cette restriction est Haskell, et nous pouvons l'éviter en permettant FlexibleInstances .
Int et chaîne sont des types, non données construtors. Chaîne se trouve être un alias pour qui peut également être écrite Liste Char [Char]. Un constructeur de données est quelque chose comme juste, si seulement 3 est une valeur de type Int Peut-être. Type cas de synonymes sont expliqués ici:
http://hackage.haskell.org/trac/haskell-prime / wiki / TypeSynonymInstances