成らぬは人の為さぬなりけり

エンジニアライフをエンジョイする為のブログ

すごいHaskellたのしく学んでみる-その9

今回のテーマは、、、

以上のテーマで、参ります。

newtypeとdata

「1つの型を取り、何かにくるんで別の型にみせかける」
というような事がしたい場合、
dataではなく、newtypeキーワードを使う事ができます。


例えば、このような場合

Prelude> data MyList a = MyList { getList::[a] }

これは以下のように書き換えられます。

Prelude> newtype MyList a = MyList { getList::[a] }


こう書くと、何が嬉しいんでしょうか??
dataとnewtypeの違いを見ていきたいと思います。

newtypeの方が高速

型をくるむのにdataキーワードを使うと、
コンストラクタに包んだり、ほどいたりする度にオーバーヘッドがかかります。
しかし、newtypeはそもそも、それ用のキーワードなので、
「内部処理は同じままに、違う型にしたいだけ」
という事をHaskellは、知っている為、
型推論を済ませた後、包んだりほどいたりの処理を最適化してくれるそうです。

じゃあ全部newtypeにすればよいのでは??

それはできません。
newtypeには
「コンストラクタは1種類、持てる値は1つだけ」
という制約があります。
(何かをラッピングする為だけに使うのですから、当然ですね)

newtypeで作った型のインスタンスの自動導出

newtypeでもderivingキーワードが使えます。
ただし、newtypeで包もうとしている中身の型が、
自動導出したい型クラスのインスタンスである必要があります。

Prelude> newtype MyList a = MyList { getList::[a] } deriving(Show)

newtypeと遅延評価

これは興味深い項です。
Haskellは、デフォルトで遅延評価な言語ですが、
dataとnewtypeでパターンマッチ時の評価が違います。

例えば、このような場合。

data Hoge = Hoge { getInt::Int }
hoge :: Hoge -> String
hoge (Hoge _) = "hoge"

Hogeの値をもらって、問答無用で「hoge」を返す関数を書いてみました。


このhoge関数にundefined(ぶっ壊れた計算を表すもの)を渡すと、エラーになります。
やってみましょう。

Main> hoge $ Hoge 1
"hoge"
Main> hoge $ Hoge 10
"hoge"
Main> hoge undefined 
"*** Exception: Prelude.undefined ← ※エラー

これは、dataキーワードで定義された型である為です。
dataキーワードで定義された型には、コンストラクタが何個かある可能性がある為、
引数が「(Hoge _)」に一致するかどうかを検証する為に、
どのコンストラクタが使われたのか、という事がわかる所まで、
引数を評価する必要があります。
ここで、undefinedが評価され、ブッブーとなるわけです。


では、newtypeはどうでしょうか。
newtypeはコンストラクタは絶対に一つしかありません。


先ほどの型をnewtypeに書き換えて試してみます。

newtype Hoge = Hoge { getInt::Int }
*Main> hoge undefined
"hoge"

「hoge」が取得できました。
undefinedが評価されなかったということですね。

これはとても重要な違いだと思います。

  • data  : オリジナルな型を1から作り出す
  • newtype : 既存の型を元に、別の新し型を作り出す


ここまでやってきて思いましたが。
newtypeを使う場面が、今のところ想像できません。

newtypeは、もっぱら型クラスのインスタンスを作りやすくするためのものです。

と書かれていますが。
その後の例を見ても、いまいちピンと来ない。
うーん、なんか良い例が無いものか、、、


単純に
「コンストラクタ1種類しかなくて、値1つしか持ってないdataがあったら、newtypeの方がはえーよ」
ってことなんでしょうか??


次回はMonoidとMonadをやって、最終回にしたいと思います。