関数プログラミングの特徴:高いエラー検出率
ここで高いエラー検出率について説明しましょう。そのためには,
関数の型を書く方法
Haskellでは,
isDigit :: Char -> Bool
isDigit c = c >= '0' && c <= '9'
isDigitは引数に文字を取り,::
は->
は
このコードをたとえばchar.
% ghci char.hs
> isDigit '0'
True
> isDigit 'a'
False
Haskellでは真がTrue,
複数の引数を取る関数の型
isDigitは一引数の関数でした。次に二引数の関数の例として,
makeString :: Char -> Int -> [Char]
makeString c 0 = []
makeString c n = c : makeString c (n - 1)
makeStringは第一引数に文字
> makeString 'w' 5
"wwwww"
文字列が返ってきました。このようにHaskellでは,
型変数
たとえば,
length :: [a] -> Int
aが型変数です。このようにHaskellでは型変数を小文字で書きます
> length "Hello"
5
> length [1..10]
10
- 注2)
- 具体的な型は大文字から始めるのと対照的です。
高階関数の型
高階関数mapの型は次の通りです。
map :: (a -> b) -> [a] -> [b]
第一引数に
> map isDigit "H2O"
[False,True,False]
isDigitの型は
型推論と型検査
先ほどHaskellで実装したcalcに型を書いてみましょう。
calc :: [Int] -> Int
calc = foldl (+) 0 . map mul . zip [0..]
そして,
たとえば,
foldl :: (a -> b -> a) -> a -> [b] -> a
foldlの結果がcalcの結果になるわけですから,
foldl :: (Int -> Int -> Int) -> Int -> [Int] -> Int
ここで,
foldl (+) 0 :: [Int] -> Int
同様に,
map mul :: [(Int,Int)] -> [Int]
zip [0..] :: [Int] -> [(Int,Int)]
中にカンマのある丸括弧は,
(.) :: (b -> c) -> (a -> b) -> a -> c
この型を字面から理解するのは難しいので,
エラー検出の例
具体的に,
calcErr.
mul (i,x) = x * i
calc :: [Int] -> Int
calc xs = foldl (+) 0 . zip [0..]
GHCiに読み込ませてみます
図2 エラー検出の例
% ghci calcErr.hs 略 calcErr.hs:4:11: Couldn't match expected type `Int' with actual type `a0 -> c0' In the expression: foldl (+) 0 . zip [0 .. ] In an equation for `calc': calc xs = foldl (+) 0 . zip [0 .. ] 略
エラーの意味はわからなくてかまいません。エラーを発見できたことがわかれば十分です。
zip [0..]の出力の型は[(Int,Int)]であり,
このように関数型言語では,