こわくないHaskell入門(初級その2)

# こわくないHaskell入門(初級その2)

手続き型に慣れた人にもやさしい、こわくないHaskell入門記事の第2弾です。
初級のつぎは中級だと思った？ 残念。

## まえがき

[前回](http://qiita.com/arowM/items/9ebfb7cafecd99290663)のやつのViewsが1000を超えた記念に続編を書いてみます。

前回は「Haskellって関数型（この言葉を見るだけで、うっ頭が...）とか呼ばれるけど、ふつうに手続き型でこわくないよ」を示しました。
今回はHaskellの型についてです。[^type]
Haskellは強い静的な型付言語と呼ばれていてこわそうですが、実際にはこわくないです。
むしろ僕みたいにプログラム書くのがうまくない人がよろしくやれるようになります。

型を使えるようになって、いままで以上にコンパイラちゃんと仲良くおはなししましょう。

## 対象者

* なんかもっと生産性の上がる言語を習得したい人
* なんかHaskell入門しようとして挫折しかけた人
* なんかマサカリ投げたい怖いお兄さんたち
* なんか前回のを読んで楽しみにしてくれた殊勝な人

## 結論

やっぱりHaskellこわくないよ。
とっても便利だよ。
とっても学習障壁ひくいよ。

なのに、Haskellをお勉強すると他の言語も美しく書けるようになるよ。

## Haskellの型

さて、[前回](http://qiita.com/arowM/items/9ebfb7cafecd99290663)の記事では、まるでスクリプト言語のようにサクサクとコードを書いていました。
でも実際に[遊べる環境](http://runnable.com/VKgDYJy1EnlDfVOR/haskell-sample)で遊んでみた人はあれに出会ってしまったのではいでしょうか。
「型エラー」に。

```
No instance for (Num [Char]) arising from a use of `+'
```

こういうやつです。
なんかいきなりよくわからないこと言われてびっくりしましたか？
大丈夫、数週間後のあなたは「型エラー？！ヒャッハー！！うんこーどは消毒だぜー！」と言っています。
その数週間後のあなたをこの現実世界に召喚するために、Haskellの型について一緒にお勉強しましょう。

### ともかく型を書いてみる

いままでは「型」を明記せずにスクリプト言語みたいにコードを書いてきました。

``` haskell
main = do
  let message = howOldAreYou "田村ゆかり" 17
  putStrLn message

howOldAreYou name age = nameSan ++ ageSai
 where
  nameSan = name ++ "さん"
  ageSai  = show age ++ "歳"
```

Haskellにはどんな型があるのかとか、そういう小難しい話は後回しにして、まずはコードの中に型を明記した例を見てみましょう。

``` withTypes.hs
main :: IO ()
main = do
  let message = howOldAreYou "田村ゆかり" 17
  putStrLn message

howOldAreYou :: String -> Int -> String
howOldAreYou name age = nameSan ++ ageSai
 where
  nameSan = name ++ "さん"
  ageSai  = show age ++ "歳"
```

このコードはちゃんとコンパイルも通って実行できます。[^日本語環境]

``` bash
$ runghc withTypes.hs
田村ゆかりさん17歳
```

もっと詳しく親の仇のようにしつこく型を書くこともできます。

``` haskell
main :: IO ()
main = do
  let 
    message :: String
    message = howOldAreYou "田村ゆかり" 17 :: String
  putStrLn (message :: String) :: IO ()

howOldAreYou :: String -> Int -> String
howOldAreYou name age = nameSan ++ ageSai :: String
 where
  nameSan :: String
  nameSan = name ++ ("さん" :: String) :: String
  ageSai :: String
  ageSai  = show age ++ ("歳" :: String) :: String
```

なにか血の気が引くコードですね。ここまでがっつり明記しても実用性はあまりありません。病院に行きましょう。

Haskellでは、このように型を明記することもできますが、
型を書かなくてもコンパイラちゃんが頑張ってあなたの気持ちを読み取ってくれます。
裏側で型を推論する「型推論」をしています。

### なぜ型を書くのか

さて、型を明記できるのはわかりました。
でも、ここで疑問が浮かびます。
「なぜなくてもいいものをわざわざ書くの？ばかなの？死ぬの？」
ばかじゃないです。死なないです。

型をコード中に明記することで大きく2つのメリットが得られます。

1. コンパイルエラーの理由がわかりやすくなる
2. お前の下手なコメントより型の方が上等なドキュメントだ

まずは前者について考えます。
先ほどのコードをすこし変更したこちらのコードをコンパイル（あるいは`runghc`で実行）してみてください。

``` haskell
main = do
  let message = howOldAreYou "田村ゆかり" 17
  putStrLn message

howOldAreYou name age = nameSan ++ ageSai
 where
  nameSan = name ++ "さん"
  ageSai  = age ++ "歳"
```

エラーがでます。

``` 
    No instance for (Num [Char]) arising from the literal `17'
    Possible fix: add an instance declaration for (Num [Char])
```

日本語に訳すと

> はうぅ〜、お兄ちゃん、文字列は数字じゃないよ〜
> `17'ってところ見てみて☆

と言っているみたいです。
とてもあほかわいいですね。文字列が数字じゃないのは当たり前じゃないですか。
`17`ってところを見てみても、もともと意図している通り数字で年齢を渡しているだけです。

では、今度は型を明記してみましょう。

``` haskell
main = do
  let message = howOldAreYou "田村ゆかり" 17
  putStrLn message

howOldAreYou :: String -> Int -> String
howOldAreYou name age = nameSan ++ ageSai
 where
  nameSan = name ++ "さん"
  ageSai  = age ++ "歳"
```

細かいことは後まわしにしますが、

> お兄ちゃんはね、`howOldAreYou`関数に文字列と整数をあげて、その結果として文字列がほしいんだよ

とコンパイラに手取り足取り教えています。
なんだか性的倒錯していますね。

ではこの型を加えたコードをコンパイルするとどうなるでしょうか。
もちろん、型を明記しただけなのでエラーが出るのは変わりません。
でも、エラーメッセージはさっきと違います。

``` 
    Couldn't match expected type `[Char]' with actual type `Int'
    In the first argument of `(++)', namely `age'
```

> はうぅ〜、お兄ちゃん、\`++'は文字列がほしいのに\`age'は整数だよ？

今度はコンパイラちゃんが何言っているかわかります。
そう、文字列を連結する関数（2項演算子）の`++`に対して、整数を与えてしまっています。

> ごめんね、お兄ちゃん、整数を文字列に変換する`show`を忘れてたよ...

これでこの兄妹はその後も幸せに暮らしていくことができます。
幸せとはひとそれぞれなので、みんなで暖かく見守っていきましょう。

このように、コンパイラちゃんに「この関数はなにをしようとしているのか」を教えてあげることで、コンパイラちゃんもそれにあったお返事をくれます。

> 「あいつマジでつかえねぇ」って愚痴ってるけど、それお前のコミュニケーション不足だようんこ

みたいなことってありますよね？
日頃からHaskellを使ってコンパイラちゃん相手に意思疎通の練習をしていれば、そういううんこにならない効果もあるんです！
ぜんぜんこわくないどころか、まっとうな人間になれる効果があるなんてやっぱりHaskellはすごいですね。
~~あの会社の人事の人たちもHaskell勉強すればいいのに。~~

もちろん、型を明記するメリットの2つ目にあったように、数日後の自分や初めてこのコードを読む他の人にとっても、下手くそなコメントを書くよりずっと上等なメッセージになります。

このように、なぜ型を書いた方がいいのかわかると

> よし、お兄ちゃん型のお勉強しちゃうぞ〜

とやる気になりますね！

### 型の書き方

ではお待ちかね、型の書き方を見てみましょう。

``` haskell
-- [関数の型の書き方]
-- 引数を好きなだけ並べて最後に返値の型
-- 全部矢印でつなげる
関数名 :: 最初の引数の型 -> 次の引数の型 -> 返値の型
関数の定義

-- 値に型を明記する
-- :: をつけて型を明記する
(値 :: 値の型)
-- = 以降の部分の型を示す
なんちゃらかんちゃら = 式 :: 式の部分の型
```

親の仇のように型を明記したコードの中の実例と比べてみてください。

``` haskell
-- howOldAreYou の型はStringとIntをもらってStringを返す
howOldAreYou :: String -> Int -> String
-- 以下howOldAreYouの定義
howOldAreYou name age = nameSan ++ ageSai :: String
 where
  nameSan :: String   -- nameSanの型はString
  -- "さん"の型はString
  -- カッコを省略すると`=`以降の型を表す
  -- name ++ "さん" の型はString
  nameSan = name ++ ("さん" :: String) :: String
  ageSai :: String
  ageSai  = show age ++ ("歳" :: String) :: String
```

ね、簡単でしょ？

### 基本の型

Haskellの型の中からよく使うものをピックアップしました。

#### `Char`

文字です。
`a`とか`あ`とかみたいな1文字のCharacterです。
新しく`Char`型の値を作るときはシングルクォート`'`で囲みます。

``` haskell
main = do
  -- もちろん `:: Char`は書かなくても動く。
  print ('a' :: Char)
```

#### `String`

文字列です。
実態は文字のリストです。

``` haskell
main = do
  -- abcd と表示される
  -- Char型の値のリストがString
  putStrLn (['a'..'d'] :: String)
```

新しく文字列の値を作るときにはダブルクォートで囲みます。

``` haskell
main = do
  -- abcd と表示される
  putStrLn ("abcd" :: String)
```

### `Int`

整数です。
世の中にありがちなように、32bitとか64bitとかで表現できる上限までしか表現できません。

ghci（対話環境）で試してみましょう。

``` 
Prelude> (1000000000000000000 :: Int)
1000000000000000000
Prelude> (10000000000000000000 :: Int)
-8446744073709551616
```

環境によって結果は変わりますが、下の例は`Int`の上限値を超えてしまったため、正しく値が評価されていません。

### `Integer`

先ほどのサンプルプログラムの田村ゆかり[^tamura]さんの年齢のように、`Int`の範囲でおさまる整数の場合は問題ありませんが、もっと大きな整数をあつかうにはどうしたらいいでしょうか。

なんとHaskellには、（メモリとかリソースが許す限り）無限に大きい整数をあつかうことができる型`Integer`が用意されています。

`Int`のときと同じようにghciでためしてみましょう。

``` 
Prelude> (1000000000000000000 :: Integer)
1000000000000000000
Prelude> (10000000000000000000 :: Integer)
10000000000000000000
```

Haskellまじかっけー。

### `Float`, `Double`

浮動小数点型です。
`Float`が単精度、`Double`が倍精度とか呼ばれるあれです。

もちろん、丸め誤差とかの影響を受けない厳密な数値をあつかいたいこともありますよね？
そうしないと古代言語COBOLからHaskellに移行できないですもんね。
その実現方法の1つである`Ratio`についてはちょっと特殊なのであとでお勉強します。

### `Bool`

真偽値です。
`if`とかの条件に使えるあれですね。

ちなみに世の中の一部の言語で可能な、

> `if`の条件に整数型とかを与えてもちゃんと動く

みたいなことはありえません。
それが原因でバグを引き起こす人が多いですから。

新しく`Bool`型の値を使うときは`True`か`False`を使います。

``` haskell
main = do
  -- もちろん、ふつうはifを使う
  case (3 > 4) of
    True -> do
      putStrLn "そうだよ"
    False -> do
      putStrLn "ちがうよ"
```

### リストふたたび

これまで配列みたいに使ってきたリストにももちろん型は存在します。
たとえば`Int`のリストの型は`[Int]`ですし、`Char`のリストの型は`[Char]`です。

文字列の実態が`Char`のリストだったことも思い出せば、こういうコードを書くこともできます。

``` haskell
main = do
  print ([23, 45, 56] :: [Int])
  print ("hi"         :: String)
  -- String と [Char]は同じもの
  print ("hi"         :: [Char])
```

でもリストの型は`Bool`とか`Double`とかとは少し様子が異なります。
他の型（リストの中身の型）を元にして作られています。
厳密には「代数的データ型(Algebraic data type)」と呼ばれるものですが、深追いすると長くなるのでまた今度です。

### タプル

リストのように特殊な型に**タプル**があります。

リストで遊んでいて、こんなコードを書いてコンパイラちゃんに怒られませんでしたか？

``` haskell
-- コンパイラちゃんエラー
main = do
  print [23, "foo", True]
```

CとかJavaとかみたいな言語を使っている人はまずやらないと思いますが、ふだんJSをおさわりしているいやらしい人はやっちゃうかもしれません。

リストは中身の要素がすべて同じ型でないといけません。
もしも型が異なる値の集合みたいなものをあつかいたいならタプル(Tuple)を使いましょう。

使い方は簡単、リストの`[]`が`()`になっただけです。

``` haskell
main = do
  print (23, "foo", True)
```

型は中身の要素の型に応じて決まります。

``` haskell
main = do
  print ((23, "foo", True) :: (Int, String, Bool))
```

このタプルのおかげで、複数の値を返す関数が気軽に書けるようになりました。

``` haskell
main = do
  strA <- getLine
  strB <- getLine
  let
    a = read strA :: Int
    b = read strB :: Int
    (d, m) = intDivMod a b
  putStrLn ("商は"     ++ show d)
  putStrLn ("あまりは" ++ show m)

-- | 商とあまりを返す関数
intDivMod :: Int -> Int -> (Int, Int)
intDivMod a b = (div a b, mod a b)
```

### `IO`

最後に`IO なんちゃら`という特殊な型を紹介すれば終わりです。
入出力が関わっているときにでてくる型です。

ghciで`:t getLine`を実行してみてください。
ghciには`:t`のあとに型を調べたい関数名を与えると、その型を教えてくれる便利な機能があります。

```
Prelude> :t getLine 
getLine :: IO String
```

> 文字列を返す関数だけど、入出力が関わっているのでそのまま`String`にするのは悔しい！ビクンビクン！

という声が聞こえてきますね。
逆に、`IO`がくっついているときは入出力が関わっていることの目印になるので、

> この関数は同じ引数を与えても気分で返値がかわるめんどくさいやつだよ

と判断することができて便利です。
型に`IO`がくっついている関数は`定数 <- 関数`で値を取り出しましょう。

ところで、`main`関数の型は`IO ()`でした。
`main`関数は、特に返値を返さないので、空のタプル`()`に`IO`をくっつけています。
`()`が他の言語でありがちな`void`と同じようなものだと思っておきましょう。

そのほかの入出力に関わる関数の型も例示しておきます。

``` haskell
-- FilePath は Stringの別名 （意味がわかりやすいでしょ？）
-- ファイルのパスをもらって、入出力が関わったString型の値を返す
readFile :: FilePath -> IO String
-- ファイルのパスと書き出す内容をもらって、入出力が関わった処理を行う
writeFile :: FilePath -> String -> IO ()
putStrLn :: String -> IO ()
```

### 型に別名をつける

`readFile`や`writeFile`の型を調べると、`FilePath`という、実態は`String`の謎の型がありました。
これはドキュメントとしての効果を高めるために、型に別名をつける機能を活用したものです。

もちろん自分で型に別名をつけることもできます。

``` haskell
type Age     = Int
type Name    = String
type Message = String

main = do
  let message = howOldAreYou "田村ゆかり" 17
  putStrLn message

howOldAreYou :: Name -> Age -> Message
howOldAreYou name age = nameSan ++ ageSai
 where
  nameSan = name ++ "さん"
  ageSai  = show age ++ "歳"
```

型を見てその関数が何をしようとしているのかわかりやすくなりました。
特に`Name`も`Message`も実態は同じ`String`でまぎらわしかったのがすっきりしたことに注目してください。

実際に型に別名をつけるには

```
type 別名 = 実態の型
```

をコードのどこか好きなところに書いておくだけです。

実は`String`も`FilePath`も

```
type String = [Char]
type FilePath = String
```

と定義されています。

### 型変数

あなたが実験大好き人間なら、ここを読む前に`print`関数の型を調べたはずです。

``` haskell
print :: Show a => a -> IO ()
```

見たことない太い矢印`=>`と、謎の小文字`a`があります。
この`a`は「なんか適切な型」を意味していて、**型変数**と呼ばれます。

`print`は`String`でも`Int`でも`[Bool]`でも、引数をとにかく標準出力に表示してくれる融通のきく便利なやつでした。
この「型はなんでもいいよ」というのを示すのに使うのが型変数です。
`a`以外にも任意の小文字のアルファベットの文字列が使われます。
`b`でも`unko`でもOKです。

さて、`print`はなんでも表示してくれると言いましたが、正しくは

> なんでもは表示しないわよ。表示できるものだけ。

です。

その「表示できるものだけ」という制約を`a`に課すのが
`Show a =>`です。

つまり、

```
print :: Show a => a -> IO ()
```

は

> printは表示できる型の値をなんか引数にして、入出力の処理をする

という意味になります。

その他よくある制約は以下のとおりです。

* `Show a`: 表示できるものだけ
* `Read a`: 文字列から変換できるものだけ
* `Eq a`: 等しいか比較できるものだけ
* `Ord a`: 大きさの順序を比べられるものだけ
* `Num a`: 数字っぽいものだけ
* `Integral a`: 整数っぽいものだけ

ghciで
`:t read`, `:t div`, `:t (/)`, `:t (>)`, `:t (==)`
あたりを実行して型を見てみると面白いです。
（2項演算子は`()`で囲まないとエラーになります)

### Hoogle

いままで、何か標準入力から値を読み取って、`String`以外の型の値として使うとき、

``` haskell
main = do
  strA <- getLine
  let a = read strA :: Int
  print (a + 3)
```

みたいに`getLine`の結果を仮の定数にいれたあとに`read`で型を変換していました。
ちょっと面倒ですよね？
直接`Int`型とか`Bool`型の値として読み取れたら便利なのに。

そんなあなたのために、それを可能にする関数を探してみましょう。
まず、ほしい関数の型を考えます。
`getLine`と`read`の型は

``` haskell
getLine :: IO String
read :: Read a => String -> a
```

です。
すると、今ほしい関数は

``` haskell
Read a => IO a
```

文字列から変換できる任意の型で、しかも入出力が関わっている型です。

ほしい関数の型を考えたら、[hoogle](https://www.haskell.org/hoogle/)の検索窓にその型を入力しましょう。
"Search"をクリックすると一番最初に

``` haskell
readLn :: Read a => IO a
```

がでてきました。
説明を読んでみると、どうやら我々が求めていたもののようです。

なんと、Haskellでは「こんな型の関数がほしいなぁ」と思ったら、その関数を検索して簡単に見つけることができるのです。
こわいどころか甘々ですね。ちょろい。

### `intDivMod`の改良

最後に「タプル」の項で提示した`intDivMod`のコードを改善して終わりにします。

改善ポイントは2つ。

1. 標準入力を任意の型として読める`readLn :: Read a => IO a`を使う
2. `intDivMod`を`Int`以外に`Integer`にも使えるようにする

``` haskell
main = do
  -- 1行標準入力から読み取って任意の型（今回はInteger）に変換する
  a <- readLn :: IO Integer
  b <- readLn :: IO Integer
  let (d, m) = intDivMod a b
  putStrLn ("商は"     ++ show d)
  putStrLn ("あまりは" ++ show m)

-- | 商とあまりを返す関数
intDivMod :: Integral n => n -> n -> (n, n)
intDivMod a b = (div a b, mod a b)
```

コンパイラちゃんにも人にもやさしいコードが簡単に書けました。

## 宣伝

ぼくと契約してHaskellを書こうよ。
[ARoW](http://www.arow.info)
完全週休4日、標準労働時間5時間(残業禁止)副業OKです。
正社員でもアルバイトでも好きにしてください。

## 次回

相変わらず需要がどれだけあるのかわからないですが、次回があるとしたら以下の内容をお届けします。

### もっと便利な関数をつくろう

* 高階関数
* 代数的データ型

### もっと書きやすく

* `$`
* 中置記法
* ラムダ式
* カリー化
* パターンマッチング
* ガード

### forのその先へ

* map
* fold
* unfold

### コードをラクして改善する

* hlint
* doctest, quickcheck


### さらに進んで
* 例外処理
* 型クラス


[^type]: 前回ほどアプローチの仕方に新規性がないですが、次回以降のために型は通らなくてはならないものなので、できるだけ肩肘はらずに学べるように心がけました。
[^日本語環境]: 残念ながら前回紹介した[遊べる環境](http://runnable.com/VKgDYJy1EnlDfVOR/haskell-sample)は端末内で日本語を表示することができないのでエラーが吐かれてしまいます。 日本語部分をローマ字に変えてみてください。 どうしても日本語で田村ゆかりさんを見たければ、GHCをご自分のパソコンにインストールしましょう。 
[^tamura]: この記事中の田村ゆかりさんはフィクションです。実在の田村ゆかりさんとは関係ありません。