Haskellのnewtype
Haskellのnewtype
代数的データ型を用いて新たな型を定義する
元の型とは全くの別物になる
型システム上はHaskellのdataと同じように扱われる
ランタイム上ではHaskellのtypeと同じように動作する
パフォーマンスに影響を与えない
Haskellのdataより高速
コンストラクタに包んだり出したりするオーバヘッドがかからない
実行時に影響を及ぼさないゼロコスト抽象化
fieldはdefaultで弱頭部正規形まで評価される
code: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
そのため!を使った定義はできない
(元々なっているので)
再帰的定義もできる
code:例.hs
newtype Fix f = Fix (f (Fix f))
制限がある
値コンストラクタは1つだけ
code:hs
newtype Volume = Volume Double -- ok
newtype A = B String | C Int -- ng
その値コンストラクタが持てるフィールドも1つだけ
code: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を使って同じ様な構造の型と関数を定義する
code:hs
data D a = D a
f (D _) = "hello"
newtype N a = N a
g (N _) = "hello"
これらの関数をundefinedに適用する
code:hs
f undefined -- error
g undefined -- "hello"
newtypeの定義は、ランタイム上で見れば、以下の様に定義しているのと同じ
code:hs
type N a = a
g _ = "hello"
だからerrorにならない
derivingで型クラスのインスタンスにするときは、中身の既存の型がその型クラスのインスタンスである必要がある
code:hs
newtype ZipList a = ZipList { getZipList :: a }
-- こう使える
getZipList $ ZipList "hoge"
DerivingViaでインスタンス定義を再利用できる
#??
GeneralizedNewtypeDerivingをすることでnewtypeに対する型クラスのderivingを簡略化できる ref
newtypeと遅延評価について
すごいH本.icon p.261
/mrsekut-book-4774183903/252
参考
data / newtype / type の使い方 - Haskell-jp
https://speakerdeck.com/konn/ben-dang-hasugoi-newtype?slide=3
typeより、エラーメッセージがわかりやすくなる ref
https://qiita.com/HirotoShioi/items/70cd235e93dee99bf8e8
すごいH本 12章
https://qiita.com/Izawa_/items/9e641d7145fb9d4c4155