top page > computer > haskell > web_lecture > for_programmer > newtype.html
更新日:
文責: 重城良国

構文: newtype

(工事中 80%)

組み合わせる

2つの値を組み合わせるという「性質」について考えてみよう。以下のような型クラスを考える。

class Composable a where
compose :: a -> a -> a

整数を組み合わせる

2つの整数を組み合わせる方法にまずは加算が思いつく。以下のようなインスタンス宣言となるだろう。

instance Composable Int where
compose x y = x + y

整数を組み合わせるのには乗算も考えられる。

instance Composable Int where
compose x y = x * y

この2つはもちろん両立させることができない。

新しい型を

このような場合には新しい型を作ってやればいい。加算用の型Addを定義してComposableにする。

data Add = Add Int deriving Show

instance Composable Add where
compose (Add x) (Add y) = Add $ x + y

乗算用の型Mulを定義してComposableにする。

data Mul = Mul Int deriving Show

instance Composable Mul where
compose (Mul x) (Mul y) = Mul $ x * y

これで2通りの組み合わせ方法で整数値をComposableのインスタンスにできた。

単なるラッパー

型Addや型Mulは型Intに対する単なるラッパーである。中身は型Intそのものであるのに処理系はいちいちラップ・アンラップの作業を行う。効率が悪い。

新しい型が古い型の単なるラッパーであるような場合に特別な書きかたがある。

newtype Add = Add Int deriving Show

newtype Mul = Mul Int deriving Show

予約語dataの代わりにnewtypeを使う。このようにすると処理系は型AddやMulが単なるラッパーであることを理解して、裸のままのIntを使うようになる。

newtype構文が必要な理由

newtypeDiff.hs

dataによって作られた型が単なるラッパーであることを機械的に判断することは簡単だ。それならばわざわざnewtype構文など作る必要はなく、機械的な最適化を行えばいいはずだ。

これが問題となるのはdataによって定義された型とnewtypeによって定義された型で動作が異なってくる場合があるからだ。以下のコードを読みこんでみよう。

data DT = DT Int deriving Show

newtype NT = NT Int deriving Show

checkDT :: DT -> String
checkDT (DT _) = "OK!"

checkNT :: NT -> String
checkNT (NT _) = "OK!"

% ghci newtypeDiff.hs
*Main> checkDT undefined
"*** Exception: Prelude.undefined
*Main> checkNT undefined
"OK!"

関数checkDTではまずは値と(DT _)とのマッチが試される。値はundefined値であるためエラーが発生する。NT型は型チェックのあとはInt型と同じように扱われる。よって関数checkNTでは(NT _)というパターンマッチは実質的には(_)と等しくエラーは発生しない。

まとめ

ひとつの型に対して2つ以上のやりかたでインスタンス化を行いたいときはnewtype構文によってラッパーとなる型を作ってやればいい。

「複合的な型を型クラスBoolLikeのインスタンスにする」へもどる 「仕様としての型クラス」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics