Types and Typeclasses
とりあえずTypeとTypeclassの違いを分かりたい。Typeはその名の通り型で、Typeclassは型の振る舞い(特性)を決めるひな形っぽい役割を持っている気がする。typeclassは型制約として
:t
コマンドで表示したときの=>
シンボルの前に表れる。:t コマンド
:t
コマンドで型を調べることができる。*Main> :t "y"
"y" :: [Char]
*Main> :t "yukarin"
"yukarin" :: [Char]
*Main> :t 'y'
'y' :: Char
*Main> :t "y"
"y" :: [Char]
*Main> :t "yukarin"
"yukarin" :: [Char]
*Main> :t 10
10 :: Num a => a
*Main> :t True
True :: Bool
*Main> :t doubleMe
doubleMe :: Num a => a -> a
*Main> :t [1,2,3]
[1,2,3] :: Num t => [t]
*Main> :t (1,2)
(1,2) :: (Num t, Num t1) => (t, t1)
*Main> :t ("yukarin", "makky")
("yukarin", "makky") :: ([Char], [Char])
型というか定義だな。これ。::
はhas a type of
と読み替えてね、とのこと。part1で出てきたこの関数。
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
- [Char]はつまるところString
- このケースでは、別に型の情報を与えなくてもコンパイラが勝手に
String -> String
を解釈
:t
は関数を作ったものの、型がよく分からない時に使うといいよ、って書いてある。--addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
型を指定する場合と指定しない場合では:t
したときの結果が違う。指定した場合。
*Main> :t addThree
addThree :: Int -> Int -> Int -> Int
指定しない場合。*Main> :t addThree
addThree :: Num a => a -> a -> a -> a
指定した場合はInt以外を与えると落ちそうだな…*Main> addThree 1.1 2.2 3.3
<interactive>:266:10:
No instance for (Fractional Int) arising from the literal `1.1'
Possible fix: add an instance declaration for (Fractional Int)
In the first argument of `addThree', namely `1.1'
In the expression: addThree 1.1 2.2 3.3
In an equation for `it': it = addThree 1.1 2.2 3.3
落ちた。型を指定しない場合はNumなので小数点も捌いてくれる。Type
型 | 補足 |
---|---|
Int | -2147483648..2147483647 |
Integer | 制限なし(not bounded) |
Float | 単精度 |
Double | 倍精度 |
Bool | True, False |
Char | キャラクタ(文字)、シングルクォートで括って表現 |
Tuple | タプルもいわゆる型だが、タプル長ごとに異なる型と言える。タプルの各要素も型であるため、理論的には無限個の型が存在する。ちょっと難しいのでこれ以上は書かない。ただ() もまた、単一の() を値として持つ型だということを覚えておいてくれ..ともう何言ってるか分からないorz |
Typeclasses 101
==
の定義。
Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool
シンボル=>
の前の記述は全てclass constrait
になる。この定義の意味は、
==
は2つの型が同じ値を取り、返り値としてBool
を返すになる。この2つの型aはEqクラスに属しているとなり、これがclass constraint
。Eq typeclassは等価性を確認するためのインターフェースを提供。IOを除く全てのHaskellの標準型はEq typeclassに属している。
Prelude> 5 == 5
True
Prelude> 5 /= 5
False
Prelude> 'a' == 'a'
True
Prelude> "yukarin" == "yukarin"
True
Prelude> 3.1234 == 3.1234
True
Prelude> [1, 2] == [1, 2]
True
Prelude> (1, 2) == (1, 2)
True
これを踏まえた上でelem
関数。Prelude> :t elem
elem :: Eq a => a -> [a] -> Bool
リストの中に同じ値があるかをチェックするのでclass constraint
としてEq a
が設定されている。Eqは等価性のテストをサポートする
==
と/=
を実装している。関数の型変数にEq
class constraintがかかている場合,その定義のどこかで==
または/=
が使われている。Ord
Ord
は順序を持つ型。Prelude> :t (>)
(>) :: Ord a => a -> a -> Bool
Prelude> :t compare
compare :: Ord a => a -> a -> Ordering
ここまでで言及した関数以外の型はOrd
型にも属している。Ord
は’>’, <
,>=
, <=
といった標準的な比較関数を実装している。比較関数は2つの同じ型のメンバーを取り、Bool値を返す。 Ordering
は型の一種であり、GT
,LT
,EQ
のいずれかの値を持つ。それぞれgreater than
, lesser than
, equal
の意味。Ord
のメンバーになるためには、まずEq
の一員でなければならない。Prelude> "Yukarin" > "Maki"
True
Prelude> "Yukarin" `compare` "Maki"
GT
Prelude> 5 >= 2
True
Prelude> compare 5 2
GT
Show
Show
のメンバーはStringで表現できる。Prelude> :t show
show :: Show a => a -> String
Prelude> show 2
"2"
Prelude> show 1.2345
"1.2345"
Prelude> show True
"True"
Prelude> show "yukarin"
"\"yukarin\""
関数以外の全ての型はこれまで同様Show
クラブの会員様。Show
クラブの中でも最も人気があるのは上記例のshow
で、受け取った会員の値をstringで表現する。Oposite
Read
はShowとは対照的なtypeclassになっている。Stringで受け取ったデータを、対応するRead
クラブに所属する型に変換する。Prelude> :t read
read :: Read a => String -> a
Prelude> read "True" || False
True
Prelude> read "8.2" + 3.8
12.0
Prelude> read "5" - 2
3
Prelude> read "[1,2,3,4]" ++ [3]
[1,2,3,4,3]
Prelude> read "8.2"
<interactive>:49:1:
No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Read () -- Defined in `GHC.Read'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Read a, Read b, Read c) => Read (a, b, c)
-- Defined in `GHC.Read'
...plus 25 others
In the expression: read "8.2"
In an equation for `it': it = read "8.2"
右側の取ってつけたようなRead
会員様はもしかして、型を確定(推定)させるために付与してるのん…? 続く文面でそんなことが書いてるからきっとそうなんだな…。こんな悲しい事態を回避するために
type annotation
さんがいる。Prelude> read "5" :: Int
5
Prelude> read "5" :: Float
5.0
Prelude> read "[1,2,3,4]" :: [Int]
[1,2,3,4]
Prelude> read "(\"yukarisan\", 18)" :: (String, Int)
アノテーションでRead
クラブの会員様を指定すれば変換が許可されるみたいだ。Enum
Enum
クラブはシーケンシャルな順番を有するかが参加資格になる。この手のtypeclassの利点はレンジ指定ができるところにある。あらかじめ次の値、1つ前の値が確定している。succ
, pred
でこの値を参照できる。Enum
クラブの会員typeclass。(), Bool, Char, Ordering, Int, Integer, Float, Double
Prelude> :t succ
succ :: Enum a => a -> a
Prelude> :t pred
pred :: Enum a => a -> a
Prelude> ['a'..'e']
"abcde"
Prelude> [LT .. GT]
[LT,EQ,GT]
Prelude> [3 .. 5]
[3,4,5]
Prelude> succ 'B'
'C'
Bounded
上限・下限、自分の限界をわきまえた大人のためのクラブ。Prelude> :t minBound
minBound :: Bounded a => a
Prelude> :t maxBound
maxBound :: Bounded a => a
Prelude> minBound :: Int
-2147483648
Prelude> maxBound :: Char
'\1114111'
Prelude> maxBound :: Bool
True
Prelude> maxBound :: (Bool, Int, Char)
(True,2147483647,'\1114111')
minBound, maxBoundは意味敵的には多様な定数と捉えることができる。最後のサンプルのようにタプルは、その要素の型がBounded会員の場合は、自身もBounded会員になる。Num
数値に関するtypeclass。このメンバーは数値のように振る舞う特性を持っている。Prelude> :t 10
10 :: Num a => a
Prelude> :t 0.1
0.1 :: Fractional a => a
aはNum型に何かだよ、という制約が付いている。FractionalというtypeclassもNumと同じレベルであるんだな。Prelude> :t (*)
(*) :: Num a => a -> a -> a
積算の定義、typeclassがNumの2つの値を取り、同a型を返す。Integral
Numと異なり、整数のみを含むtypeclass。IntとIntegerを包含している。Floating
数値を扱うとても便利な関数にfromIntegral
がある。こいつの定義。
Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
Integral会員に属す型の値を与えると、それをNum会員の値に変換して返す。Prelude> :t length
length :: [a] -> Int
Prelude> length [1,2,3,4] + 3.2
<interactive>:99:20:
No instance for (Fractional Int) arising from the literal `3.2'
Possible fix: add an instance declaration for (Fractional Int)
In the second argument of `(+)', namely `3.2'
In the expression: length [1, 2, 3, 4] + 3.2
In an equation for `it': it = length [1, 2, 3, ....] + 3.2
この例でlength関数はInt型の値を返すので、その結果と3.2
という浮動小数点で計算ができない。ここでfromIntegrsal先生を使うと、これはNum b
を返すので3.2との足し算が可能になる。Prelude> fromIntegral (length [1,2,3,4]) + 3.2
7.2
Written with StackEdit.
No comments:
Post a Comment