Pisz role i mylące zachowanie przez „przymus”

11

Mam typ Id ai staram się zapobiec przypadkowemu Zmuszanie, np Id Doubledo 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?

Smażony Brice
źródło
ain Idjest zmienną fantomową i nie ma wpływu na rzeczywistą wartość wewnątrz. Gdyby tak było newtype Id a = Id a, przymus by się nie udał.
lehins
@lehins Chodziło o type roleto, aby tak nie było. To pytanie nasuwa pytanie, dlaczego to nie zadziałało.
Joseph Sible-Reinstate Monica

Odpowiedzi:

12

Coerciblema 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 .

  • Każdy typ jest przymusowy dla siebie.
  • Możesz wymusić „pod” konstruktorem typu, pod warunkiem, że zmiennymi typu, których dotyczy problem, są representationallub phantom. Na przykład możesz przymusić Map Char Intdo a, Map Char (Data.Monoid.Sum Int)ponieważ Mapmamy type role Map nominal representational.
  • Można zawsze zmusić do newtype do bazowego typu i odwrotnie, pod warunkiem, że konstruktor newtype jest w zasięgu. To ignoruje wszystkie role! Uzasadnieniem jest to, że biorąc pod uwagę, że konstruktor jest dostępny, zawsze można ręcznie owijać i rozpakowywać ręcznie, więc rola i tak nie zapewnia żadnego bezpieczeństwa.

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.

danidiaz
źródło