そして数のいろんな型もお目見えします.
ちょっと長い目.
en.wikibooks の Haskell に基づいたものです. CC-BY-SA で配布.
polymorphic types
さて,[True, False, True] ['a', 'b'] ["hey", "may"]それぞれ型が違います.最初のは
[Bool]
, 続いて [Char]
, さらに [[Char]]
.ほんで,これまでやってきた通り,関数が受け取れる型っていうのは予め決まってるわけ.
と,いうことは.例えば list の長さが欲しい時には,
lengthBools :: [Bool] -> Int
とか lengthChars :: [Char] -> Int
とか色々必要,というのはあまりに面倒というか理不尽.実際にはひとつの関数
length
が対応してくれるのでこれの型を見てみましょう.
Prelude> :t length length :: [a] -> Intこの
a
っていうのは型じゃない(そもそも型は大文字から始まる).これが type variable, 型変数と呼ばれるもので,ここにはどんな型でも入ってよい.
つまるところこの
[a]
という型表示は,Bool
[[Char]]
その他,何でもいいからそれのリスト,ってこと.
こういうふうに複数の型を(同時にではないが)許容するのは type theory の用語で polymorphism というらしい.形容詞系は polymorphic, 対義語は monomorphic.
ここで気をつけるべきは,ひとつの type signature の中では同じ型変数には同じ型が入るということ.
ふつうの変数と同じですね.例えば,
f :: a -> aという時にはなんの型でもいいから受け取って同じ型で返す関数を表わしてて,
f :: a -> bという時には
a
と b
は同じかもしれないし違うかもしれない.
というわけで早速具体例を見てみよう.そろそろ何を見ても「型はどうなってるかな」と気になる頃. さっきやった
fst
と snd
はどうなるだろうか(もちろんまず自分で考えて).
Prelude> :t fst fst :: (a, b) -> a Prelude> :t snd snd :: (a, b) -> bすっきり.
head
と tail
とかも各自.
練習問題
- 前の
( ("Hello", 4), True)
から 4 を取り出してみよう.- tuple で list の 頭と残りを返す関数書いてみよう.
h x y z = chr (x-2)
の型は? (試すときは:m
かimport
忘れずに)
練習問題のあれ
Prelude> :m Data.Char Prelude Data.Char> let h x y z = chr (x-2) Prelude Data.Char> :t h h :: Int -> t -> t1 -> Charこの
t
とか t1
とかでどのアルファベットを選ぶかってなんか決まりあるんだろうか.
Type Basics II
Typeclass
さてさて,これまでぼんやりとしか喋ってこなかったけれども,例えば(+)
の type はどうあるべきか.アレやん,
Int
同士の足し算ならいいけどさ, Float
とかも自在に扱って欲しいわけ.かといって
Char
と Int
を勝手に足されても困る.たとえば
Number
みたいな型があって,それで数全部を扱ってしまえれば嬉しい:
(+) :: Number -> Number -> Numberせやけど,例えば浮動小数点型と整数型は区別せな色々困るわけで,これはやっぱり望み薄.
先程の type variable の概念を援用し,たいところだが,そのままだと数以外のものの足し算も許容してしまう.
というわけで… clever にも,「数を表す型ならなんでもおk」な型変数みたいな指定ができるのです!
(+) :: (Num a) => a -> a -> aこの
Num
っていうのが typeclass と言われるもので,Num a =>
の部分で制約をかけているわけ.かっこ良くいうと
a
を Num
の instance に制限する,ということらしい.
Numeric Types
というわけで数を表す型にどんなものがあるか.Int
. 精度の固定されてるよくある整数型.少なくとも[-2^29 .. 2^29-1]
を扱えるらしい.Integer
. 任意精度の整数(メモリのある限り数え続けてくれる).python でいう Long Integer.Float
. 単精度浮動小数点型.特段事情がなければDouble
使いましょう.Double
. 倍精度浮動小数点型.小数というか実数扱うならだいたいこれのお世話になる.
この光のもとで足し算とかそのへんを見なおしてみる.
先ほど申し上げた通り,
(+) :: (Num a) => a -> a -> aというわけですが,では例えば
3 + 2.5これ整数 (
Int
とか Integer
とか) と
非整数 (Float
or Double
) の足し算にみえるけど.でも2つの型が違うと困る.上の
a -> a -> a
が困る.実は:
Prelude> :t 3 3 :: Num a => a3 自体はまだ
Int
でも Integer
でも Double
でも無いわけですねー.
じゃあ 2.5 はっと
Prelude> :t 2.5 2.5 :: Fractional a => aほみほみ.なんか
Real
とか Eq
とか色々あるみたいやけど,今は深入りはやめとこう.少なくとも
Fractional
は Num
で,
Int
や Integral
は Fractional
じゃないです. Haskell さんの解釈としては,まず制約のきつい
2.5
の方を考えて,特に他に情報はないのでデフォルトの
Double
として処理し,その後全体によきにはからう.結局(この場合)計算全体は
Double
で進行して Double
で帰ってくる.これ,型キャストとも雰囲気が似ているが,Haskell では明示的に polymorphic なものにしか行われない.
さてさて.
Prelude> :t (/) (/) :: Fractional a => a -> a -> a
Fractional
への制限は,python2.x みたいに 3/2
が 1
にはなってほしくないので必要だけど,たとえばはっきり
Int
やでって宣言してるような奴
Prelude> :t length length :: [a] -> Intとかでやろうとしてみると
Prelude> 4 / length [1,2] <interactive>:2:3: No instance for (Fractional Int) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Int) In the expression: 4 / length [1, 2] In an equation for `it': it = 4 / length [1, 2]polymorphism が型キャストと違うというのはこういうところ.
なおこの場合は便利な関数があって,
fromIntegral :: (Integral a, Num b) => a -> b見ての通り.
Prelude> 4 / fromIntegral(length [1,2]) 2.0 Prelude> 4 / fromIntegral(length [1,2,3]) 1.3333333333333333めでたし.かな.
ついでに補足.
(==) :: (Eq a) => a -> a -> Boolこの
Eq
は正味比較して等しいかどうかを判断できるもの,みたいなアレ.non-functional なのはだいたいこれらしい.typeclass のもっとすごい力とか,カスタマイズの仕方とかは,後々出てくるのでお待ちください.
No comments:
Post a Comment