Saya memiliki tipe Id a
dan saya mencoba untuk mencegah paksaan tanpa sengaja, misalnya, Id Double
ke Id Int
.
Jika saya memahami mengetikkan peran dengan benar, yang berikut ini tidak boleh dikompilasi.
{-# 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)
Sayangnya, itu tidak:
Prelude> :load Id.hs
[1 of 1] Compiling Main ( Id.hs, interpreted )
Ok, one module loaded.
*Main> :type badKey
badKey :: Id Int
Apa yang saya lewatkan tentang peran tipe?
a
inId
adalah variabel phantom dan tidak berdampak pada nilai aktual di dalamnya. Jika Anda melakukannyanewtype Id a = Id a
, maka paksaan akan gagal.type role
adalah untuk membuat itu tidak terjadi. Pertanyaan ini menanyakan mengapa itu tidak berhasil.Jawaban:
Coercible
memiliki tiga "jenis" yang mungkin dari instance (yang secara otomatis dihasilkan oleh kompiler, tidak ditentukan oleh pengguna). Hanya satu dari mereka yang benar-benar dipengaruhi oleh peran .representational
atauphantom
. Misalnya, Anda dapat memaksaMap Char Int
menjadiMap Char (Data.Monoid.Sum Int)
karena untukMap
kita milikitype role Map nominal representational
.Dalam contoh Anda, aturan ketiga berlaku. Jika tipe baru telah didefinisikan dalam modul lain dan konstruktor tidak diimpor, paksaan akan gagal (untuk membuatnya bekerja lagi, Anda harus beralih peran ke
phantom
).Perilaku khusus yang agak mengejutkan untuk tipe baru dijelaskan dalam masalah GHC ini .
sumber