すごいHaskell二章
型
型があるとか関数定義の時は型の定義も書くとか。
型変数
どんな型も取りうることを示す変数。
同じ名前の型変数は、同じ型が入る。
多分コンパイルする時に特定の型に決定されるんだろうな。
中置関数
1 + 2
の" + "みたいな置き方をする関数。つまり演算子も関数。
特殊文字のみで構成される関数は、デフォルトで中置関数になる。
前置関数として呼び出したい場合、他の関数に渡したい場合、型を調べたい場合などは括弧で囲む必要がある。
ghci> :t (+) (+) :: Num a => a -> a -> a ghci> (+) 1 2 3
前置関数を中置したいとき、バッククォートで囲むのと逆になってるっぽい。
ghci> :t div div :: Integral a => a -> a -> a ghci> div 4 2 2 ghci> 4 `div` 2 2
型クラス
分からん。継承できるってことなのかな。
型が型クラスのインスタンスっていうのはどういうことなんだろう。
とりあえず、オブジェクト指向のクラスとは同じではない。
Eq型クラスは、等値性をテストするためのインターフェイスを提供します。
ある型の2つの値の等値性を比較することに意味があるなら、その型はEq型クラスのインスタンスにできます。Haskellのすべての標準型(I/O型と関数を除く)はEqのインスタンスです。(28p)
型クラスはインターフェイスを提供するもの?
Eqのインスタンスが実装すべき関数は== と/=です。(28p)
確かにインターフェイスっぽい。オブジェクト指向の知識は助けになるのか邪魔になるのか微妙だな……
ghci> :t (==) (==) :: Eq a => a -> a -> Bool
これは、
等値性関数は、同じ型の任意の2つの引数を取り、Boolを返す。引数の2つの値の型はEqクラスのインスタンスでなければならない。(28p)
と読める。=>シンボルよりも前にあるものは型クラス制約と呼ぶ。
ちょっと分かるのは、型レベルで色々制限がかかるということ。いわゆる強い型付になってるということ。それはコンパイル時に解決されるということ。
何が制約かというと、関数の引数に取れる値の型。その型を、型クラスのインスタンスであるかどうか、という点で制約できる。さらに、ある型クラスのインスタンスであれば、その型クラスで指定されている関数を実装する必要がある。指定されている関数は、引数の型に対して、型クラスのインスタンスであることを制約している…… グルグル回ってる気もするがなんとなくわかった。
(略)関数の型変数にEqクラスの制約が付いていたら、その関数の定義のどこかで==か/=が使われているということです。型が関数を実装しているとは、その関数がその特定の型に対して使われた時に、どういう振る舞いをするか定義するということです。(28p)
関数の型変数に、型クラスの制約がつく。このとき、関数の定義のどこかで、型クラスが指定する関数が使われている。(==)の定義のどこかで==か/=が使われているというのはどういうことだ。
"型が関数を実装している"って何か違和感あるな。その型に対する関数が実装されているということだと思うけど。
例えば、
(==) :: Integer -> Integer -> Bool (==) a b = (わからないので略)
とかみたいに。
"関数は除く"というのは、型クラスのインスタンスの型は指定された関数を実装するから、関数の型に対して関数を実装すること制約すると、永遠に入れ子になっちゃうってことだと思う。
例えば、関数funcが型クラスEqのインスタンスだとすると、func型に対して==や/=を実装する必要がある。
func :: a -> a (==) :: func -> func -> Bool
あれ、いいのかな?多分なにかおかしいと思うけど……
ところで、型は型を持たないっぽい
ghci> :t Bool <interactive>:1:1: Not in scope: data constructor `Bool'
関係ないけど、
ghci> 3.0 == 3 True ghci> :t 3.0 3.0 :: Fractional a => a gchi> :t 3 3 :: Num a => a
Numクラスで==と/=が定義されてるっぽい。見てないので多分。
型クラスが指定する関数を実装してない型は、その型クラスのインスタンスではない。型クラスに制約された型を引数に取る関数を使えない(型があわない)
このへんも、型変数を広く扱うためのやり方なのかな。プログラミング科学での「インターフェイス」って結構特別な意味な気がする。
ある型クラスのインスタンスであるならば、型クラスの指定する関数が使えることが保証されている。逆に、型クラスの指定する関数が使えない型は、型クラスのインスタンスではない。
Ord型クラス
OrderingはGT、LTまたはEQのいずれかの値を取る型で、それぞれ「より大きい」、「より小さい」、「等しい」を意味します。(29p)
面白い。Cとかだと1、-1、0で表現されるところだと思う。
同じように、BoolはTrueまたはFalseのいずれかの値を取る型なんだろう。
数字はどうなってるのかな。
Show型クラスとRead型クラス
showは値を文字列に、readは文字列を値にする関数。
Stringは[Char]の別名。
readをリストの中に入れると、周囲の要素に合わせて型を解決してくれる。
ghci> [read "1", 2, 3] [1,2,3] ghci> [read "1", 2, read "3"] [1,2,3] ghci> [read "1", read "2", read "3"] <interactive>:1:22: Ambiguous type variable `t0' in the constraint: (Read t0) arising from a use of `read' Probable fix: add a type signature that fixes these type variable(s) In the expression: read "3" In the expression: [read "1", read "2", read "3"] In an equation for `it': it = [read "1", read "2", read "3"]
おーん。
ところで、これは何がダメなんだろう
ghci> [(read "1"), "2.2", "3.3"] ["*** Exception: Prelude.read: no parse
型注釈がいるのかなと思って下のようにしてもダメ
ghci> [(read "1" :: Float), "2.2", "3.3"] <interactive>:1:23: Couldn't match expected type `Float' with actual type `[Char]' In the expression: "2.2" In the expression: [(read "1" :: Float), "2.2", "3.3"] In an equation for `it': it = [(read "1" :: Float), "2.2", "3.3"]
よく考えたらFloatじゃなくて[Char]だったと思って下のようにしたけどダメ。
ghci> [(read "1" :: [Char]), "2.2", "3.3"] ["*** Exception: Prelude.read: no parse
readする必要がないから?
型注釈
式が取るべき型を明示的に与える。
ところで31pの上から5行目、3番目の例、エラーになるんじゃね?と思ったけどなった。
ghci> read ("5" :: Float) * 4 <interactive>:1:7: Couldn't match expected type `Float' with actual type `[Char]' In the first argument of `read', namely `("5" :: Float)' In the first argument of `(*)', namely `read ("5" :: Float)' In the expression: read ("5" :: Float) * 4
と思ったら、自分の入力が間違ってた(括弧の位置)
ghci> (read "5" :: Float) * 4 20.0
ほかにもこんな感じになる。
ghci> (read "8.2") + 3 *** Exception: Prelude.read: no parse ghci> read "8.2" + 3.2 11.399999999999999 ghci> (read "8.2") + 3.2 11.399999999999999
Enum型クラス
Enumのインスタンスは要素の値を列挙できる(順番もある)型。
後者関数succと前者関数predが定義されてる。値はレンジの中で扱える。
succ、predは端をこえようとするとExceptionになるっぽい
ghci> succ False True ghci> pred True False ghci> pred False *** Exception: Prelude.Enum.Bool.pred: bad argument ghci> succ True *** Exception: Prelude.Enum.Bool.succ: bad argument
レンジの中で扱えるって .. が使えるってことか。
ghci> ['a' .. 'f'] "abcdef" ghci> [LT .. GT] [LT,EQ,GT]
おもしろい。
Bounded型クラス
インスタンスは上限と下限を持ち、minBound関数、maxBound関数で調べられる。
ghci> :t maxBound maxBound :: Bounded a => a ghci> :t minBound minBound :: Bounded a => a ghci> minBound 1 <interactive>:1:1: No instance for (Bounded (a0 -> arising from a use of `minBou Possible fix: add an instance d In the expression: minBound 1 In an equation for `it': it = m ghci> minBound :: Int -2147483648 ghci> maxBound :: (Bool, Int, Char) (True,2147483647,'\1114111') ghci> minBound :: [Int] <interactive>:1:1: No instance for (Bounded [Int]) arising from a use of `minBound' Possible fix: add an instance declaration for (Bounded [Int]) In the expression: minBound :: [Int] In an equation for `it': it = minBound :: [Int]
面白い。minBouded関数とかは、引数取らず、型注釈だけを受け取る関数。minBoundとかの型を多層定数とかいうらしい。型によって値が変わる定数(最小値、最大値)だからか。
リストは受け取れないけど、タプルは受け取れる。
Num型クラス
数を表す。あらゆる数は多相定数らしい。
ghci> :t 0 0 :: Num a => a ghci> 0 :: Int 0 ghci> 0 :: Integer 0 ghci> 0 :: Float 0.0 ghci> 0 :: Double 0.0 ghci> 234244444444444444444444444444444444444444444444444444444 :: Int 477218588
おもしろい。ここの定数はどこに定義されてるんだろう…浮動小数点数とかキリがない気がするんだけど。
Floating型クラスはFloatとDouble、Integral型クラスはIntとIntegerが含まれる。
fromIntegralはIntegralの値を周囲を見て最も一般的な値に変換する。
ghci> fromIntegral (length [1,2,3,4]) + 34.1 38.1 ghci> length [1,2,3,4] + 34.1 <interactive>:1:20: No instance for (Fractional Int) arising from the literal `34.1' Possible fix: add an instance declaration for (Fractional Int) In the second argument of `(+)', namely `34.1' In the expression: length [1, 2, 3, 4] + 34.1 In an equation for `it': it = length [1, 2, 3, ....] + 34.1
IntがFractionalに変換されている。
というかFractionalの説明が出てこなかったな……