249
コメント:
|
10545
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 2: | 行 2: |
== Haskellにおける型 == Haskellでは型という概念は関数と同じぐらい重要なポジションを占めています。<<BR>> Haskellでは実行する前にすべての値や変数の型がわかっています。このことによって多くのプログラムのバグを実行する前に発見することができるという性質があります。<<BR>> Haskellを書いていると、型が一致していればだいたい動くのではないか、という錯覚に陥るぐらいにHaskellの型システムは多くのバグを事前に取り除いてくれます。 ghciをつかえばあるものがHaskellでどのような型を持っているのかを調べることができます。:tコマンドに続けて式を入力すれば、それがどんな型なのかを教えてくれます。 {{{#!highlight haskell :t "hoge" "hoge" :: [Char] }}} ::という記号は前にあるものがうしろにある型を持っていることを表す記号です。上の例の場合、"hoge"は型[Char]を持つのだということがわかります。<<BR>> 角括弧はリストを表しているので、文字のリストの型を持っているのだということがわかります。 |
|
行 4: | 行 18: |
Haskellには型推論があるので、通常型をプログラム中で示す必要はありません。しかしプログラムの中で型を明示的に示すこともできます。<<BR>> 型を明示的に示すには、先ほど示した::をつかいます。型を示したい対象の後ろに::を書いて、型名を書けばよいのです。 {{{#!highlight haskell c :: Char c = 'a' }}} 実は'''関数も型を持ちます'''。このことは非常に重要なことなので絶対に覚えて帰ってください。関数の型というのは引数の型と戻り値の型で決まります。<<BR>> たとえば次のような関数を考えましょう。 {{{#!highlight haskell f x = if x `mod` 2 == 0 then "even" else "odd" }}} この関数fは、xを引数にとってそれが偶数なら"even"を返し、奇数なら"odd"を返します。この関数の型は次のように書けます。 (Haskellをちょっと知っている人なら疑問が出てくるかもしれませんが、ここはそういったことは考えないことにします) {{{#!highlight haskell f :: Int -> [Char] }}} 気持ちで読んでみましょう。fという関数はIntという型のなにかをとって、[Char]型の何かを返すのだ、という風に読めますね。このように引数の型を書いて、それに続いて->を書き、最後に戻り値の型を書いたものが関数の型になるわけです。 複数の引数がある場合はどうなるでしょうか。2つの整数を足し合わせる関数を考えます。 {{{#!highlight haskell addTwo x y = x + y }}} この型は次のようになります。 {{{#!highlight haskell addTwo :: Int -> Int -> Int }}} 引数が何個になろうが、->で区切って型を並べるという点は変わらないのですね。なぜ引数と返り値を区別していないのか疑問に思った人もいるかもしれません。<<BR>> それは後にカリー化という概念をやった時にわかります。 一般にHaskellでは簡単な関数でない限り、関数に明示的に型を与えることが良い習慣だと考えられています。 |
|
行 6: | 行 54: |
型の名前は必ず大文字から始まります。 === Int === Intは整数を表す型です。コンパイラによりますが、Intには上限と下限があります。 === Integer === Integerも整数に使いますが、IntegerにはIntのような上限と下限がありません。ただしIntegerの値を処理するのはIntに比べると遅いです。 === Float === Floatは小数を表す型です。 === Double === Doubleも小数を表す型ですが、Floatに比べて倍の精度を持ちます。 === Bool === Boolは真理値型です。TrueとFalseのどちらかの値を持ちます。 === Char === Charは文字を表す型です。Charはシングルクォーテーションで囲んで表します。Charのリストは文字列になります。 === タプル === タプルも型の一種です。但し前に述べたとおりその要素の型と数によってそれぞれ別の型になります。<<BR>> 空のタプル()はなにか特別な結果を返す必要がない関数の戻り値として使われることがあります。これをUnitと呼びます。 |
|
行 8: | 行 79: |
Haskellの関数を少し調べてみると、いろいろな型に対して動作する関数があることに気づきます。たとえばheadという関数を思い出しましょう。<<BR>> headはリストの要素が数だろうと、文字だろうと、はたまたリストであろうと動きます。この関数はあらゆる型のリストに対して動作しないといけません。 head関数の型は次のようになっています。 {{{#!highlight haskell head :: [a] -> a }}} これはaという名前の型があるという意味ではありません。このaを型変数と言って、どんな型でもとりえますよ、という意味を持ちます。<<BR>> 型変数を使うことで、型の利点を殺さぬまま、関数を色々な型のデータに対して使うことができるようになります。 型変数を用いた関数を多相型関数と言います。 型変数にはどんな名前を使っても良いですが、一般にはaやbといった1文字の名前をつけることが多いです。 ペアの前の要素を返す関数fstの型を見てみましょう。 {{{#!highlight haskell fst :: (a, b) -> a }}} fstは大きさ2のタプルを引数にとって、その1つ目と同じ型の値を返すことが明らかにわかります。 |
|
行 10: | 行 101: |
型クラスは、型に特定の性質を与えたいときに使うシステムです。 具体的な例を見ながら型クラスというものを考えて行きましょう。型クラスの中で最もよく知られているものの一つがEq型クラスというものです。<<BR>> Eq型クラスは型に対して「等価性」を定義したいときに使う型クラスです。 たとえばInt型を考えてみましょう。Int型は整数なので明らかに等しいか等しくないかを判定できるはずです。<<BR>> Char型も考えてみましょう。Char型は文字なのでやっぱり等しいか等しくないかを判定できるはずです。 このようにある型が表しているものが「等価性」というものを定義できる対象であるとわかったなら、その型をEq型クラスのインスタンスにします。<<BR>> ある型をEq型クラスのインスタンスというものにすることで、その型が表すものは「等価性」というものを判定できる性質を持っているのだということを定義できます。<<BR>> CharもIntもHaskellの初期状態で、Eq型クラスのインスタンスになっています。 「等価性」というものを判定できる性質を持っていることを定義できると何が嬉しいのでしょうか?<<BR>> 実は「等価性」を判定できる性質を持っていることを定義したら、==演算子をその型に対して必ず定義しなければいけません。<<BR>> つまりEq型クラスのインスタンスになっていることで {{{#!highlight haskell 1 == 2 'a' == 'b' }}} のような比較ができるということが完全に保証されます。逆に言うと、Eq型クラスのインスタンスになっていない型のデータはこのように==を使って等価かどうかを判定できる保証はないのです。<<BR>> ある型がプログラムで登場した時、それがEq型クラスのインスタンスになっていれば、なんのためらいもなく==を使っても問題ない、ということが保証されます。<<BR>> インスタンスになっていなければ、==を使うと「そんな関数は使えないよ!」と怒られるかもしれない可能性があるというわけです。 言い換えれば型クラスは、ある型を型クラスのインスタンスにすることで、関数を定義することを強制することができ、それによって型が特定の性質を持つように保証するためのシステムだと言えます。<<BR>> 今の例だと、Int型やChar型はEq型クラスのインスタンスになっているので、Int型やChar型の値を引数にとる==という関数を定義することが強制されています。<<BR>> それによってIntやCharの値を==を使って等しいかどうか判定できることができる、ということを保証しているわけです。 今説明したことを実際に==の型を見ながら確認しましょう。 {{{#!highlight haskell (==) :: (Eq a) => a -> a -> Bool }}} (Eq a) => という見たことない表現が出て来ました。これを型クラス制約と言います。これは後ろに出てくるaという型変数はEq型クラスのインスタンスであるような型でないといけないということを示しています。 前に出てきた型変数はなんの制約もなく、どんな型が来てもおkというものでした。<<BR>> しかし今回は前に(Eq a) =>というものがついています。これによって'''Eq型クラスのインスタンスであれば'''どんなものでもおkという風に若干条件がきつくなっているのです。 以下に代表的な型クラスを挙げます。 |
|
行 11: | 行 139: |
Eqは等価性を調べることができる型にたいして使われる型クラスです。Haskellの標準で存在する型はほぼすべてEq型クラスのインスタンスです。<<BR>> Eq型クラスのインスタンスにした場合、==と/=という関数を定義しなければいけません。しかし普通は/=は==の逆なので、==だけ定義すれば十分なことが多いです。 |
|
行 12: | 行 143: |
Ordは何らかの順序を付けられる型のための型クラスです。 |
目次
Haskellにおける型
Haskellでは型という概念は関数と同じぐらい重要なポジションを占めています。
Haskellでは実行する前にすべての値や変数の型がわかっています。このことによって多くのプログラムのバグを実行する前に発見することができるという性質があります。
Haskellを書いていると、型が一致していればだいたい動くのではないか、という錯覚に陥るぐらいにHaskellの型システムは多くのバグを事前に取り除いてくれます。
ghciをつかえばあるものがHaskellでどのような型を持っているのかを調べることができます。:tコマンドに続けて式を入力すれば、それがどんな型なのかを教えてくれます。
::という記号は前にあるものがうしろにある型を持っていることを表す記号です。上の例の場合、"hoge"は型[Char]を持つのだということがわかります。
角括弧はリストを表しているので、文字のリストの型を持っているのだということがわかります。
型を明示的に示す
Haskellには型推論があるので、通常型をプログラム中で示す必要はありません。しかしプログラムの中で型を明示的に示すこともできます。
型を明示的に示すには、先ほど示した::をつかいます。型を示したい対象の後ろに::を書いて、型名を書けばよいのです。
実は関数も型を持ちます。このことは非常に重要なことなので絶対に覚えて帰ってください。関数の型というのは引数の型と戻り値の型で決まります。
たとえば次のような関数を考えましょう。
1 f x = if x `mod` 2 == 0 then "even" else "odd"
この関数fは、xを引数にとってそれが偶数なら"even"を返し、奇数なら"odd"を返します。この関数の型は次のように書けます。 (Haskellをちょっと知っている人なら疑問が出てくるかもしれませんが、ここはそういったことは考えないことにします)
1 f :: Int -> [Char]
気持ちで読んでみましょう。fという関数はIntという型のなにかをとって、[Char]型の何かを返すのだ、という風に読めますね。このように引数の型を書いて、それに続いて->を書き、最後に戻り値の型を書いたものが関数の型になるわけです。
複数の引数がある場合はどうなるでしょうか。2つの整数を足し合わせる関数を考えます。
1 addTwo x y = x + y
この型は次のようになります。
1 addTwo :: Int -> Int -> Int
引数が何個になろうが、->で区切って型を並べるという点は変わらないのですね。なぜ引数と返り値を区別していないのか疑問に思った人もいるかもしれません。
それは後にカリー化という概念をやった時にわかります。
一般にHaskellでは簡単な関数でない限り、関数に明示的に型を与えることが良い習慣だと考えられています。
Haskellの基本的な型
型の名前は必ず大文字から始まります。
Int
Intは整数を表す型です。コンパイラによりますが、Intには上限と下限があります。
Integer
Integerも整数に使いますが、IntegerにはIntのような上限と下限がありません。ただしIntegerの値を処理するのはIntに比べると遅いです。
Float
Floatは小数を表す型です。
Double
Doubleも小数を表す型ですが、Floatに比べて倍の精度を持ちます。
Bool
Boolは真理値型です。TrueとFalseのどちらかの値を持ちます。
Char
Charは文字を表す型です。Charはシングルクォーテーションで囲んで表します。Charのリストは文字列になります。
タプル
タプルも型の一種です。但し前に述べたとおりその要素の型と数によってそれぞれ別の型になります。
空のタプル()はなにか特別な結果を返す必要がない関数の戻り値として使われることがあります。これをUnitと呼びます。
型変数
Haskellの関数を少し調べてみると、いろいろな型に対して動作する関数があることに気づきます。たとえばheadという関数を思い出しましょう。
headはリストの要素が数だろうと、文字だろうと、はたまたリストであろうと動きます。この関数はあらゆる型のリストに対して動作しないといけません。
head関数の型は次のようになっています。
1 head :: [a] -> a
これはaという名前の型があるという意味ではありません。このaを型変数と言って、どんな型でもとりえますよ、という意味を持ちます。
型変数を使うことで、型の利点を殺さぬまま、関数を色々な型のデータに対して使うことができるようになります。
型変数を用いた関数を多相型関数と言います。
型変数にはどんな名前を使っても良いですが、一般にはaやbといった1文字の名前をつけることが多いです。
ペアの前の要素を返す関数fstの型を見てみましょう。
1 fst :: (a, b) -> a
fstは大きさ2のタプルを引数にとって、その1つ目と同じ型の値を返すことが明らかにわかります。
型クラス
型クラスは、型に特定の性質を与えたいときに使うシステムです。
具体的な例を見ながら型クラスというものを考えて行きましょう。型クラスの中で最もよく知られているものの一つがEq型クラスというものです。
Eq型クラスは型に対して「等価性」を定義したいときに使う型クラスです。
たとえばInt型を考えてみましょう。Int型は整数なので明らかに等しいか等しくないかを判定できるはずです。
Char型も考えてみましょう。Char型は文字なのでやっぱり等しいか等しくないかを判定できるはずです。
このようにある型が表しているものが「等価性」というものを定義できる対象であるとわかったなら、その型をEq型クラスのインスタンスにします。
ある型をEq型クラスのインスタンスというものにすることで、その型が表すものは「等価性」というものを判定できる性質を持っているのだということを定義できます。
CharもIntもHaskellの初期状態で、Eq型クラスのインスタンスになっています。
「等価性」というものを判定できる性質を持っていることを定義できると何が嬉しいのでしょうか?
実は「等価性」を判定できる性質を持っていることを定義したら、==演算子をその型に対して必ず定義しなければいけません。
つまりEq型クラスのインスタンスになっていることで
のような比較ができるということが完全に保証されます。逆に言うと、Eq型クラスのインスタンスになっていない型のデータはこのように==を使って等価かどうかを判定できる保証はないのです。
ある型がプログラムで登場した時、それがEq型クラスのインスタンスになっていれば、なんのためらいもなく==を使っても問題ない、ということが保証されます。
インスタンスになっていなければ、==を使うと「そんな関数は使えないよ!」と怒られるかもしれない可能性があるというわけです。
言い換えれば型クラスは、ある型を型クラスのインスタンスにすることで、関数を定義することを強制することができ、それによって型が特定の性質を持つように保証するためのシステムだと言えます。
今の例だと、Int型やChar型はEq型クラスのインスタンスになっているので、Int型やChar型の値を引数にとる==という関数を定義することが強制されています。
それによってIntやCharの値を==を使って等しいかどうか判定できることができる、ということを保証しているわけです。
今説明したことを実際に==の型を見ながら確認しましょう。
1 (==) :: (Eq a) => a -> a -> Bool
(Eq a) => という見たことない表現が出て来ました。これを型クラス制約と言います。これは後ろに出てくるaという型変数はEq型クラスのインスタンスであるような型でないといけないということを示しています。
前に出てきた型変数はなんの制約もなく、どんな型が来てもおkというものでした。
しかし今回は前に(Eq a) =>というものがついています。これによってEq型クラスのインスタンスであればどんなものでもおkという風に若干条件がきつくなっているのです。
以下に代表的な型クラスを挙げます。
Eq
Eqは等価性を調べることができる型にたいして使われる型クラスです。Haskellの標準で存在する型はほぼすべてEq型クラスのインスタンスです。
Eq型クラスのインスタンスにした場合、==と/=という関数を定義しなければいけません。しかし普通は/=は==の逆なので、==だけ定義すれば十分なことが多いです。
Ord
Ordは何らかの順序を付けられる型のための型クラスです。
Show
Read
Enum
Num
Floating