Mam typ Id a
i staram się zapobiec przypadkowemu Zmuszanie, np Id Double
do Id Int
.
Jeśli dobrze rozumiem role typu, poniższe nie powinny się kompilować.
{-# 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)
Niestety:
Prelude> :load Id.hs
[1 of 1] Compiling Main ( Id.hs, interpreted )
Ok, one module loaded.
*Main> :type badKey
badKey :: Id Int
Czego mi brakuje w typach ról?
a
inId
jest zmienną fantomową i nie ma wpływu na rzeczywistą wartość wewnątrz. Gdyby tak byłonewtype Id a = Id a
, przymus by się nie udał.type role
to, aby tak nie było. To pytanie nasuwa pytanie, dlaczego to nie zadziałało.Odpowiedzi:
Coercible
ma trzy możliwe „typy” instancji (które są generowane automatycznie przez kompilator, nie zdefiniowane przez użytkownika). Tylko jedna z nich ma wpływ na role .representational
lubphantom
. Na przykład możesz przymusićMap Char Int
do a,Map Char (Data.Monoid.Sum Int)
ponieważMap
mamytype role Map nominal representational
.W twoim przykładzie obowiązuje trzecia reguła. Gdyby nowy typ został zdefiniowany w innym module, a konstruktor nie został zaimportowany, przymus nie powiódłby się (aby ponownie działać, trzeba by zmienić rolę
phantom
).Nieco zaskakujące specjalne zachowanie nowych typów wyjaśniono w tym numerze GHC.
źródło