だんだんそれっぽくなってきた。
多相型
型の中に型変数を含めることで、任意の型に適用可能な関数を定義できる。これジェネリクスってことかなあ。
教科書に書いてあるlength関数の例は、
length :: [a] -> Int
なんだけど、C#で言えば、
static int Length<T>(IEnumerable<T> arg);
ってことのようだ。(C#では関数じゃなくてメソッドにするのが普通なので、そのように書いた。)
型変数には小文字を使うそうだ。
多重定義型
新しい言葉が出てきた。クラスとインスタンス。Haskellでいうクラスはもちろんオブジェクト指向言語のクラスとは違う。クラスのインスタンスは型なのだ。複数の型に共通する何かがクラスということらしい。
その後のところを読むと、どうもJavaやC#でいうところのインターフェースが、Haskellのクラスにちょっと似ている気がする。(でもきっと間違っているのだろう。間違いに気づくのはいつだろうか。)
クラスの話は後回しにして、多重定義型。最初の例が加算演算子+
だったので、オーバーロードみたいなこと?と思ったがよく読むと違う。どうも、多層型がジェネリクスだとすれば、多重定義型は型制約のあるジェネリクスのように思える。
Haskellの例は
(+) :: Num a => a -> a -> a
となっているから、C#だと
static T Add<T>(T x, T y) where T : ICalculatable // ICalculatableインターフェースは今適当にでっち上げた
みたいな。(でもきっと間違っているのだろう。間違いに気づくのはいつだろうか。)
そして、数値も多重定義型っていうのはちょっと驚いたけど納得。だからInt
のコンテキストでもFloat
のコンテキストでも3というリテラル(?)を使えるのだ。C#の場合は数値リテラルの型が決まっていて、暗黙の型変換またはキャストができるという発想。違うもんだね。
基本クラス
上に書いたように、インターフェースだと思うと何となく納得。
Eq
クラスIEquatable<T>
。Ord
クラスIComparable<T>
。Show
クラスIFormattable
。いやちょっと違うか。C#の場合はobject型にToString()メソッドがあるし。Read
クラスIConvertible
。これもちょっと違うけど。Num
クラス- C#にはこれに当たる型はない。組み込みの数値型には最初から演算子が定義されている。
Integral
クラス- C#にはこれに当たる型はない。組み込みの数値型には最初から演算子が定義されている。
Fractional
クラス- C#にはこれに当たる型はない。組み込みの数値型には最初から演算子が定義されている。
div
と (/)
の使い分けに注意かな。
練習問題
練習問題1
['a', 'b', 'c'] :: [Char]
('a', 'b', 'c') :: (Char, Char, Char)
[(False, 'o'), (True, '1')] :: [(Bool, Char)]
([False, True], ['0', '1']) :: ([Bool], [Char])
[tail, init, reverse] :: [ [a] ]
かな?init関数は教科書に出てきてないぞ。- Hugsで見てみた。おっと、関数だったのは分かってたはずなのに書きそこねた。
[tail, init, reverse] :: [ [a] -> [a] ]
だね。
- Hugsで見てみた。おっと、関数だったのは分かってたはずなのに書きそこねた。
練習問題2
second xs = head (tail xs) :: [a] -> a
swap(x, y) = (y, x) :: (a, b) -> (b, a)
pair x y = (x, y) :: a -> b -> (a, b)
double x = x * 2 :: Num a => a -> a
palindrome xs = reverse xs == xs :: Eq a => [a] -> Bool
- リストはそもそもEqクラスなのかな?と思ってよく読み返してみたら、要素がEqクラスならリストもEqクラスということのようだ。
twice f x = f (f x) :: (a -> a) -> a -> a
- これ少し難しいな。
練習問題3
Hugsで見てみた。演習問題1の最後の以外は合ってた!
練習問題4
一般的に関数の型をEqクラスのインスタンスにするのが実現不可能な理由?それって停止問題みたいなことかなあ。わからんからまた明日考える。