これを書いたときの違いは何ですか?
data Book = Book Int Int
対
newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid
newtype Book = Book Int Int無効であることに注意してください。ただし、newtype Book = Book (Int, Int)以下のドンによって示されるようにすることができます。
これを書いたときの違いは何ですか?
data Book = Book Int Int
対
newtype Book = Book (Int, Int) -- "Book Int Int" is syntactically invalid
newtype Book = Book Int Int無効であることに注意してください。ただし、newtype Book = Book (Int, Int)以下のドンによって示されるようにすることができます。
回答:
すばらしい質問です。
いくつかの重要な違いがあります。
表現
newtypeは、実行時に、ラップする型とまったく同じ表現になることを保証します。data、実行時にまったく新しいデータ構造を宣言します。したがって、ここでのポイントは、の構成がnewtypeコンパイル時に消去されることが保証されていることです。
例:
data Book = Book Int Int
newtype Book = Book (Int, Int)
コンストラクタが消去されているため(Int,Int)、とまったく同じように表現されていることに注意してくださいBook。
data Book = Book (Int, Int)
にBookは存在しない追加のコンストラクタがありnewtypeます。
data Book = Book {-# UNPACK #-}!Int {-# UNPACK #-}!Int
ポインタはありません!2つのIntフィールドは、Bookコンストラクターのボックス化されていないワードサイズのフィールドです。
代数的データ型
これはコンストラクターを消去する必要があるためnewtype、データ型を単一のコンストラクターでラップする場合にのみ機能します。「代数的」な新しいタイプの概念はありません。つまり、たとえば、次のような同等のnewtypeを作成することはできません。
data Maybe a = Nothing
| Just a
複数のコンストラクタがあるためです。あなたも書けない
newtype Book = Book Int Int
厳格
コンストラクターが消去されるという事実は、dataとの厳密性に非常に微妙な違いをもたらしnewtypeます。特に、data「リフト」されるタイプを導入します。これは、本質的に、ボトム値に評価する追加の方法があることを意味します。を使用して実行時に追加のコンストラクタnewtypeはないため、このプロパティは保持されません。
Bookto (,)コンストラクターの追加のポインターにより、底値を入れることができます。
結果として、newtypeそしてdataように、わずかに異なる厳密性を有するHaskellのウィキの記事で説明。
開梱
コンストラクタがないため、のコンポーネントのボックス化を解除しても意味がありませんnewtype。書くことは完全に合理的ですが:
data T = T {-# UNPACK #-}!Int
TコンストラクターとInt#コンポーネントを含むランタイムオブジェクトを生成します。あなただけで裸にIntなりnewtypeます。
参照:
newtypeコンパイル後に消去され、ランタイムが古いタイプと新しいタイプに同じ表現を使用する場合、どうすれば古いタイプと新しいタイプの両方のインスタンスを定義できるでしょうか?ランタイムはどのインスタンスを使用するかをどのように理解できますか?
newtypeは明らかにまだ消去されていません。