p.23
型は、互いに関連する値の集合です。
たとえば、Bool型にはFalseとTrueという二つの真理値が含まれます。
また、「Bool -> Bool」という型には、Bool型をBool型へ変換する否定演算子 notのような関数がすべて含まれます。vが型 Tの値であるという意味で、「v :: T」という表記を使います。
この形式を型注釈と呼びます。
「v :: T」は、「vの型は Tである」と読みます。
以下に例を示します。
#code(haskell){{
False :: Bool
True :: Bool
not :: Bool -> Boo
}}
p.24
GHCi では、:typeコマンドを式の前に付けると式の型が表示されます。
p.25
Char ――― 文字
Unicode のすべての単一文字が値として含まれる型です。
'a'、'A'、'3'、'_'といった通常の英語キーボードにあるすべての図形文字だけでなく、'\n'(改行文字)や '\t'(タブ文字)といった制御文字も含まれます。
他の一般的なプログラミング言語と同様に、単一文字はシングルクォート(' ')で囲みます。
String ――― 文字列
"abc"、"1+2=3"、空文字列 ""のような文字の並びが値として含まれる型です。
他の一般的なプログラミング言語と同様に、文字列はダブルクォート(" ")で囲みます。
Haskellにおけるシングルクォートとダブルクォートの使い分け。
p.26
リストとは、同じ型の要素の並びであり、それらの要素を角括弧で囲みカンマで区切ったものです。
T型の要素を持つリストの型を [T]と書きます。
以下に例を示します。
#code(haskell){{
[False,True,False] :: [Bool]
['a','b','c','d'] :: [Char]
["One","Two","Three"] :: [String]
}}
試しに確かめてみたら、String型にならなかった。Windows10だと挙動が違うとか?
*Main> :t ["One","Two","Three"] ["One","Two","Three"] :: [[Char]]
リストの要素の個数を、そのリストの長さと呼びます。
リスト[]は、長さが0 であり、空リストと呼ばれます。
[False]、['a']、[[]]は、要素が一つのリストです。[[]]と []は異なるリスト型の値であることに注意してください。
前者は空リストのみを含む長さ 1 のリストであり、後者は要素を持たない単なる空リストです。
p.27
タプルとは、有限個の要素の組です。
各要素の型が異なってもかまいません。
要素は括弧で囲み、カンマで区切ります。
タプルの型は、i番めの要素が型 Tiを持つとき、(T1,T2,...,Tn)と表記します(iは1からnまでの値を取り、nは2 以上です)。
#code(haskell){{
(False,True) :: (Bool,Bool)
(False,'a',True) :: (Bool,Char,Bool)
("Yes",True,'a') :: (String,Bool,Char)
}}
試しに確かめてみたら、String型にならなかった。Windows10だと挙動が違うとか?
*Main> :t (False,True) (False,True) :: (Bool, Bool)
*Main> :t (False,'a',True) (False,'a',True) :: (Bool, Char, Bool)
*Main> :t ("Yes",True,'a') ("Yes",True,'a') :: ([Char], Bool, Char)
気になったので検索してみたら、String型は[Char]型の別名、ということらしい。
5. 文字列型
文字列型の型名は String で,リテラルは "hello" のようにダブルクォートを用いて書きます.
実は,文字列の実体は文字のリストであり,String は [Char] の別名に過ぎません.
文字列リテラル "hello" は文字のリスト ['h', 'e', 'l', 'l', 'o'] を表します.
それを先に言え!ってかんじですねw
Haskellのタプルは、注意点が多々あるので、何度もよく読んでおくこと。
関数は、ある型の引数を他の型の結果に変換します。
型 T1の引数を型 T2の返り値に変換する関数の型を「T1 -> T2」と書きます。
p.28
関数には、引数の型と結果の型に制限がありません。
そこで、複数の値をリストかタプルで表現すれば、一つの引数を取り一つの結果を返す関数で、複数の引数を取ったり、複数の結果を返したりできます。
たとえば、整数の組を取り足し合わせた値を返す関数 addと、0から与えられた上限までの整数のリストを返す関数 zerotoは、以下のように定義できます。
#code(haskell){{
add :: (Int,Int) -> Int
add (x,y) = x+y
zeroto :: Int -> [Int]
zeroto n = [0..n]
}}
この例では、Haskell の慣習に従って、関数定義の前で関数の型を(型注釈を使って)宣言しています。
関数の型は、関数についての有益な説明になっています。
このような、プログラマーにより指定された型の情報は、型推論を使って自動的に決定された型と照合されます。
p.29
一度に一つの引数を取る関数を、カリー化されていると表現します。
カリー化された関数を扱うときに括弧が過剰になるのを避けるため、二つの規則が採用されています。
一つは、型の中の ->は右結合であるという規則です。
たとえば、以下の型を考えましょう。Int -> Int -> Int -> Intこれは、以下を意味します。
Int -> (Int -> (Int -> Int))
もう一つは、空白文字を用いて表す関数適用は、必然的に左結合であるという規則です。
たとえば、以下の関数適用を考えます。mult x y zこれは、以下を意味します。
((mult x) y) z
Haskell では、明示的に括弧付けをしなくても、複数の引数を取る関数はカリー化された関数として定義されます。
また、二つの規則のおかげで、必要な括弧の個数を少なくできます。
p.30
型に型変数を含めることで正確に表現できます。
型変数の先頭は小文字でなければなりません。
通常は、単にa、b、cという名前を使います。
たとえば、lengthの型は次のようになります。length :: [a] -> Int
「型変数」という用語の意味の定義が不十分な気がしたので、調べてみた。
言葉 説明 例 型変数 関数の明示的な型宣言に使われる どんな型も取り得る事を意味する head :: [a] -> a この「a」の事を型変数とよぶ
型変数を使って、型をパラメータ化することもできます。
data Maybe a = Nothing | Just a型変数には、当然ですが具体的な型が入ります。
型変数、型クラス
add2 :: a -> a -> a add2 x y = x + y上記コードのaが 型変数 です。
サンプルでは型変数名にaを使っていますが、b, cや複数文字を使っても問題ありません。
この型変数は任意の型が入ることを示しています。
ただし同じ名前の型変数の型は、全て同じであることに注意してください。*Main> :t (+) (+) :: Num a => a -> a -> aNum a => は初めて見る書き方ですが、これは型変数aに対して型クラス制約を行っています。
型変数をそのまま使うと任意の型を許容してしまうため、型クラス制約を型変数にかける必要があるということです。
とりあえず、今の段階での理解を整理しておく。
(参考)型変数の意味とは直接関係ないかもしれないけど、型変数の活用方法=「型依存プログラミング」「型レベルプログラミング」という考え方が紹介されていた。