私はメタデータが1つのタイプパラメータとして表され、もう1つのレコードフィールドとしてメタデータが表現される2つのライブラリをインターフェースしなければなりませんでした。私はGADTを使ってアダプタを書いた。蒸留バージョンは次のとおりです。タイプファミリーと部分的な新しいタイプの違いは? (部分データ?)
{-# LANGUAGE GADTs #-}
newtype TFId a = MkId a
data TFDup a = MkDup !a !a
data GADT tf where
ConstructorId :: GADT TFId
ConstructorDup :: GADT TFDup
main = do
f ConstructorId
f ConstructorDup
f :: GADT tf -> IO()
f = _
これは機能します。 (完璧ではないかもしれませんが、歓迎ですが、それは問題ではありません)
この作業状態になるまでにはしばらく時間がかかりました。私の最初の直感は、TFId
のタイプファミリーを使用することでした。 "GADT
は種類が(* -> *) -> *
です。 ConstructorDup
TFDup
には種類* -> *
があります。 「
{-# LANGUAGE TypeFamilies #-}
type family TFId a where TFId a = a
型コンストラクタは、同じ種類* -> *
を持っていないが、GHCは明らかに同じ場所でそれを持っています:
error: …
- The type family ‘TFId’ should have 1 argument, but has been given none
- In the definition of data constructor ‘ConstructorId’ In the data type declaration for ‘GADT’
のでConstructorId
のために、私は次のよう* -> *
型ファミリを使用することができますさて、もしそうなら...
私はそれがなぜこのような違いをもたらすのか分かりません。それを適用せずにタイプのファミリーの茎を使用していない?どうしたの?他の(より良い)方法は何ですか?
はい、タイプファミリは_saturated_を使用する必要があります。つまり、宣言されたすべての引数にタイプファミリを適用する必要があります。型ファミリはコンパイル時に展開されるので、コンパイラは型のファミリの引数をすべて知っていなければならない。同じことが型同義語にも当てはまります。 –
コンパイル時にもnewtypesが(よく、縮小されて)拡張されていると主張したいと思うでしょう。だから、おそらく "コンパイル時"は少し広いです。何が違うの?つまり、なぜですか? –
確かに、「コンパイル時間」という言葉を少しゆるやかに使っていました。 newtypeはコード生成中に消去されます - newtypeの実行時表現はラップされた型と同じであり、どちらの方向にも自由に使えますが、 'type'sのような型検査中は展開されません。 'newtype'は他の型から離れています。タイプ同義語/ファミリーはそうではありません。 –