J'ai un type Id a
et j'essaie d'empêcher toute contrainte accidentelle, par exemple, un Id Double
à un Id Int
.
Si je comprends bien les rôles de type, les éléments suivants ne doivent pas être compilés.
{-# LANGUAGE RoleAnnotations #-}
import Data.Coerce (coerce)
type role Id nominal
newtype Id a = Id String
badKey :: Id Int
badKey = coerce (Id "I point to a Double" :: Id Double)
Malheureusement, cela:
Prelude> :load Id.hs
[1 of 1] Compiling Main ( Id.hs, interpreted )
Ok, one module loaded.
*Main> :type badKey
badKey :: Id Int
Qu'est-ce que je manque sur les rôles de type?
a
inId
est une variable fantôme et n'a aucun impact sur la valeur réelle à l'intérieur. Si vous l'aviez faitnewtype Id a = Id a
, la contrainte aurait échoué.type role
était de faire en sorte que ce ne soit pas le cas. Cette question demande pourquoi cela n'a pas fonctionné.Réponses:
Coercible
a trois "types" d'instances possibles (qui sont générés automatiquement par le compilateur, non définis par l'utilisateur). Un seul d'entre eux est réellement affecté par les rôles .representational
ouphantom
. Par exemple, vous pouvez contraindre unMap Char Int
en unMap Char (Data.Monoid.Sum Int)
car pourMap
noustype role Map nominal representational
.Dans votre exemple, la troisième règle s'applique. Si le nouveau type avait été défini dans un autre module et que le constructeur n'avait pas été importé, la contrainte aurait échoué (pour le faire fonctionner à nouveau, vous devriez basculer le rôle sur
phantom
).Le comportement spécial quelque peu surprenant des nouveaux types est expliqué dans ce numéro de GHC.
la source