`coerce`によるタイプロールと紛らわしい動作


11

タイプがId aあり、誤って強制的に変換しないようにしようとしId DoubleていId Intます。

タイプの役割を正しく理解していれば、以下はコンパイルできません。

{-# 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)

残念ながら、それはします:

Prelude> :load Id.hs
[1 of 1] Compiling Main             ( Id.hs, interpreted )
Ok, one module loaded.
*Main> :type badKey
badKey :: Id Int

タイプロールについて何が不足していますか?


ain Idはファントム変数であり、内部の実際の値には影響しません。もし持っていたらnewtype Id a = Id a、強制は失敗したでしょう。
lehins

@lehinsのポイントは、そうでtype roleはないことです。この質問はそれがうまくいかなかった理由を尋ねています。
ジョセフ・サイブル復活モニカ

回答:


12

Coercibleインスタンスの3つの可能な「タイプ」があります(これらはユーザーによって定義されず、コンパイラーによって自動的に生成されます)。実際に役割の影響を受けるのはそのうちの1つだけです。

  • すべてのタイプはそれ自体に強制可能です。
  • 影響を受ける型変数がrepresentationalまたはであれば、型コンストラクタの「下」に強制できますphantom。たとえば、あなたが強制することができますMap Char IntMap Char (Data.Monoid.Sum Int)のためにあるためMap、我々は持っていますtype role Map nominal representational
  • newtypeコンストラクターがスコープ内にある場合は、常に newtypeを基になる型に強制変換でき、その逆も可能です。これはすべての役割を無視します!理論的根拠は、コンストラクターが使用可能であれば、常に手動でラップおよびアンラップすることができるため、このロールはとにかく安全性を提供しないということです。

あなたの例では、3番目のルールが適用されます。newtypeが別のモジュールで定義されていて、コンストラクターがインポートされていなかった場合、強制は失敗します(再度機能させるには、役割をに切り替える必要がありますphantom)。

newtypeのやや意外な特別な動作は、この GHCの問題で説明されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.