2016-10-08 4 views
3

Je suis confus sur l'utilisation des types de fantômes:Phantom type confusion?

type Words = String 
type Numbers = Int 

data NonPhantom = NP1 Words | NP2 Numbers deriving (Show) 

data Phantom a = P1 Words | P2 Numbers deriving (Show) 

nonPhantomFunction :: NonPhantom -> Int 
nonPhantomFunction r = 100 


phantomFunction :: Phantom Numbers -> Int 
phantomFunction a = 2001 


main = do 
    print $ nonPhantomFunction (NP1 "sdsdds") --can also pass NP2 here! 
    print $ phantomFunction (P1 "sdsdsd") --This shouldn't work!? 

Je me attends ce code PAS pour compiler, comme phantomFunction indique explicitement son type de données attendant Phantom de Numbers.

Cependant, cela compile bien? Qu'est-ce que je fais mal?

+2

Peut-être que vous voulez un GADT à la place? – augustss

Répondre

4
data Phantom a = P1 Words | P2 Numbers deriving (Show) 

Cela rend P1 "aa" de tout type de forme Phantom a, pour tout a, y compris Numbers.

1

Il n'y a pas de connexion implicite entre l'argument d'un constructeur et l'argument type du type auquel appartient le constructeur. Si vous voulez que le type indiqué par l'argument type apparaisse n'importe où dans les arguments d'un constructeur, vous devez l'indiquer explicitement.

Vous pouvez le voir aussi dans les expressions suivantes:

Nothing 
[] 

Le premier peut créer un Maybe a pour tout a et la seconde une liste [a] pour tout a.

De même

P1 "xyz" 

de votre exemple peut faire une Phantom a pour tout a

0

Les autres réponses ont déjà expliqué que le type de votre constructeur

P1 :: Words -> Phantom a 

signifie qu'il est capable de Construire une valeur de type P1 a pour tout choix de a; en particulier, pour le choix de a ~ Numbers. C'est pourquoi votre appel de fonction

phantomFunction (P1 "sdsdsd") 

typechecks.

Maintenant, comment résolvez-vous cela? Je suppose que vous souhaitez P1 :: Words -> Phantom Words? GADTs vous permettent de contraindre les variables de type se produisant dans le type de la valeur construite, en vous permettant d'écrire

{-# LANGUAGE GADTs #-} 
data Phantom a where 
    P1 :: Words -> Phantom Words 
    P2 :: Numbers -> Phantom Numbers 

Cela

  • Assurez P1 _ est de type Phantom Words vous ne pouvez donc pas construire un Phantom Numbers avec elle
  • permettre aux fonctions qui consomment un Phantom Words pour faire la recherche de motifs exhaustive par seulement correspondant à P1
  • Al les fonctions basses qui sont polymorphes sur Phantom a pour affiner leur type en fonction de l'appariement de motifs (c'est le grand), donc vous pouvez écrire par ex.

    dup :: Phantom a -> a 
    dup (P1 ws) = ws ++ ws -- Here, we have to return a Words, and ws is a Words, so ++ will work 
    dup (P2 n) = n + n -- Here, we have to return a Numbers, and x is a Numbers, so + will work