2016-05-12 3 views
0

Je suis conscient que Haskell a paramaterized types de données:Limiter les types que paramater Type peut prendre dans les données Déclarations

data Maybe a = Nothing | Just a 

Mais est-il un moyen de limiter le genre de types qui a peuvent désigner? En particulier, je voudrais créer un type

data Tag a = Tag a 

tels que a peut prendre soit le type TagPrimitiveou le type TagComplex (cependant, je ne veux pas qu'il soit possible que a est de type, disons, Integer ou String ou quelque chose qui n'a aucun sens dans mon programme).

Est-ce possible?

+0

Je suppose que je suis un peu confus: pourquoi avoir le type 'Tag' du tout? Cherchez-vous 'type Tag = Soit TagPrimitive TagComplex' (ou éventuellement' data Tag = Primitive TagPrimitive | Complex TagComplex') - c'est-à-dire, un type * single * qui peut contenir des valeurs de 'TagPrimitive' ou' TagComplex' types - à la place? –

+0

Oui, j'étais confus, et vous avez raison. Est-ce que l'une de ces deux alternatives que vous avez mentionnées est plus idiomatique? – George

+0

Cela dépend un peu de l'utilisation. Voir aussi [cette question] (http://stackoverflow.com/q/19072930/791604) pour une discussion sur les raisons pour lesquelles vous pourriez préférer la déclaration 'data' personnalisée. –

Répondre

4

Votre Tag proposé est un peu bizarre: il est un type paramétrés qui peut être spécialisé dans un type peut seulement contenir TagPrimitive s ou peut être spécialisé à un type qui peut seulement contenir TagComplex s. Mais cela semble un peu inutile: nous avons déjà les deux types spécialisés TagPrimitive et TagComplex pour servir ces deux rôles. Au lieu de cela, je propose que ce que vous vouliez réellement était un seul type qui peut contenir des valeurs de l'un ou l'autre type. Pour cela, je recommande la cuisson d'un nouveau type de somme:

data Tag = Primitive TagPrimitive | Complex TagComplex 
    deriving (Eq, Ord, Read, Show) 

Pour les premiers prototypes, vous pourriez sortir avec l'aide du type de somme canonique, Either, comme dans

type Tag = Either TagPrimitive TagComplex 

mais je soupçonne que votre le programme se développe, cela deviendra un increasingly bad choice.

+0

Mais ce type ne vous indique pas, statiquement, lequel de 'TagPrimitive' ou' TagComplex' il enveloppe, ce qui pourrait être requis par OP. – Cactus

+1

@Cactus Correct. C'est pourquoi j'ai écrit un commentaire sur la question posée à ce sujet avant d'écrire cette réponse. Mais comme je le signale dans le premier paragraphe: les types 'TagPrimitive' et' TagComplex' eux-mêmes * font * cela * garantissent statiquement ce qu'ils sont! –

7

Vous pouvez utiliser l'approche habituelle, singleton-y:

{-# language GADTs #-} 

data Taggable a where 
    Primitive : Taggable TagPrimitive 
    Complex : Taggable TagComplex 

définir ensuite Tag comme

data Tag a where 
    Tag : Taggable a -> a -> Tag a 

puis quand vous décomposez le a d'une valeur Tag donnée, vous pouvez simplement correspondre sur le singleton:

f :: Tag a -> T 
f (Tag Primitive x) = ... - here, you know x :: TagPrimitive 
f (Tag Complex x) = ... -- here, you know x :: TagComplex 

Ou, comme @rampion mentionné dans un commentaire, vous pouvez plier Taggable en type Tag, vous laissant avec

data Tag a where 
    Primitive :: TagPrimitive -> Tag TagPrimitive 
    Complex :: TagComplex -> Tag TagComplex