generated at
Haskellのnewtype
Haskellのnewtype
代数的データ型を用いて新たな型を定義する
元の型とは全くの別物になる
型システム上はHaskellのdataと同じように扱われる
ランタイム上ではHaskellのtypeと同じように動作する
パフォーマンスに影響を与えない


Haskellのdataより高速
コンストラクタに包んだり出したりするオーバヘッドがかからない
実行時に影響を及ぼさないゼロコスト抽象化
fieldはdefaultで弱頭部正規形まで評価される
ghci(hs)
ghci> newtype N = N Int ghci> let n = N (1+1) ghci> :sprint n n = _ ghci> seq n () () ghci> :sprint n n = N 2
そのため ! を使った定義はできない
(元々なっているので)
再帰的定義もできる
例.hs
newtype Fix f = Fix (f (Fix f))


制限がある
値コンストラクタは1つだけ
hs
newtype Volume = Volume Double -- ok newtype A = B String | C Int -- ng
その値コンストラクタが持てるフィールドも1つだけ
hs
newtype Size = Size { unSize :: Int } -- ok newtype Point = Point -- ng { pointX :: Int , pointY :: Int }
一方、Haskellのdataでは、上記のNGのような型を定義できる



パターンマッチ時にどこまで評価するかがHaskellのdataと異なる
newtype は1つしか値コンストラクタを取らない
そのため、パターンマッチにおけるパターンは1つしかない
このルールは、ランタイムでtypeと同じ挙動をさせるために必要
このルールによって、dataと少し異なる挙動をすることがある
例えば以下の様なコード
data newtype を使って同じ様な構造の型と関数を定義する
hs
data D a = D a f (D _) = "hello" newtype N a = N a g (N _) = "hello"
これらの関数を undefined に適用する
hs
f undefined -- error g undefined -- "hello"
newtype の定義は、ランタイム上で見れば、以下の様に定義しているのと同じ
hs
type N a = a g _ = "hello"
だからerrorにならない




derivingで型クラスのインスタンスにするときは、中身の既存の型がその型クラスのインスタンスである必要がある
hs
newtype ZipList a = ZipList { getZipList :: [a] } -- こう使える getZipList $ ZipList "hoge"


DerivingViaでインスタンス定義を再利用できる
GeneralizedNewtypeDerivingをすることでnewtypeに対する型クラスのderivingを簡略化できる ref

newtypeと遅延評価について
すごいH本 p.261


参考
typeより、エラーメッセージがわかりやすくなる ref