Ich habe eine Art Id a
und ich versuche , aus Versehen Nötigung zu verhindern, beispielsweise ein Id Double
zu ein Id Int
.
Wenn ich Typrollen richtig verstehe, sollte Folgendes nicht kompiliert werden.
{-# 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)
Leider tut es:
Prelude> :load Id.hs
[1 of 1] Compiling Main ( Id.hs, interpreted )
Ok, one module loaded.
*Main> :type badKey
badKey :: Id Int
Was fehlt mir an Typrollen?
a
inId
ist eine Phantomvariable und hat keinen Einfluss auf den tatsächlichen Wert im Inneren. Wenn Sie esnewtype Id a = Id a
getan hätten, wäre der Zwang gescheitert.type role
ging darum, dass dies nicht der Fall ist. Diese Frage fragt, warum das nicht funktioniert hat.Antworten:
Coercible
hat drei mögliche "Arten" von Instanzen (die vom Compiler automatisch generiert und nicht vom Benutzer definiert werden). Nur einer von ihnen ist tatsächlich von Rollen betroffen .representational
oderphantom
. Zum Beispiel können Sie einMap Char Int
in einMap Char (Data.Monoid.Sum Int)
weil zwingen, weilMap
wir habentype role Map nominal representational
.In Ihrem Beispiel gilt die dritte Regel. Wäre der neue Typ in einem anderen Modul definiert und der Konstruktor nicht importiert worden, wäre der Zwang fehlgeschlagen (damit er wieder funktioniert, müssten Sie die Rolle auf wechseln
phantom
).Das etwas überraschende spezielle Verhalten für Newtypes wird in dieser GHC-Ausgabe erläutert .
quelle