とりあえず型を合わせるHaskell入門

初心者による初心者のためのHaskell*1
そこそこ需要がある内容ではある気がしてる。

このエントリを読むのに最低限な知識

ghciを起動。

Prelude> :t 'c'
'c' :: Char

式の型をを調べるには、
:t 式
と入力します。

Prelude> :t 1
1 :: Num a => a

型制約です。
「1 はaという型を持つ、ただしaはNumという型クラスのインスタンス
と読む。

Prelude> :t \x -> x `div` 2
\x -> x `div` 2 :: Integral a => a -> a

関数型は a -> b という風に書きます。

型を合わせるテクニック その1 「普通に書く」

普通に書けば普通は型が合います。さもなくば、普通に型があってない。

型を合わせるテクニック その2 「IOをマスター(?)」

初心者が苦戦する壁「IO」。

main :: IO ()
main = putStrLn "hello, world"

基本中の基本、hello, worldです。
ところで、真似する場合、ここからはファイルに書いてghcコンパイルしてください。

ちょっと変えてみよう。

main :: IO ()                                                                   
main = do                                                                       
  putStrLn "h = llo"

doをつけても通ります。
doを付けると、型が変わると思っていませんでしたか?そうでもないみたいです。
端的に言うとdoは、複数の命令をまとめて、IO hogeにする。(この場合、hogeは()型)

これも通ります。

main :: IO ()                                                                   
main = do                                                                       
  putStrLn "hello"                                                              
  putStrLn "moja"                                                               
  putStrLn "huga"

doにはもうひとつ役割がある。
doの中では、次のように、「IOを剥がす」ことができる。

main :: IO ()                                                                   
main = do                                                                       
  line <- getLine                                                               
  putStrLn line

「IOを剥がす」とは...
getLine :: IO String
line :: String
こういうことです。

次はif文の例。

main :: IO ()                                                                   
main = do                                                                       
  line <- getLine                                                               
  if read line == 0                                                             
  then
    do                                                            
      putStrLn "hello"
      putStrLn "hello"                                                            
  else                                                                          
    putStrLn "f*ck" 

then のあとのdoは必要ですよ。2つのputStrLnをまとめる。
else のあとには要らないのは、1つだから。

else節を空にしたい。どうやりますか?

main :: IO ()                                                                   
main = do                                                                       
  line <- getLine                                                               
  if read line == 0                                                             
  then                                                                          
    putStrLn "hello"                                                            
  else                                                                          
    return ()

returnを使う。
hoge :: a の時、
return hoge :: IO a
になるみたいです。
in other words,
return :: a -> IO a *2

型を合わせるテクニックその3 「型注釈」

こんなのを書いてみる。

Prelude> :t do { line <- getLine; return $ read line }
do { line <- getLine; return $ read line } :: Read b => IO b

(ここからはまたghciです)
そういえば、doの書き方、こういうふうにも書けるみたい。
Read bってなんやねん。型クラスムズイ。僕はreadIntを作ったんだよ。
そういうときは、型注釈を使う。

Prelude> :t do { line <- getLine; return $ (read line :: Int) }
do { line <- getLine; return $ (read line :: Int) } :: IO Int

OK.
不要な多相を消し去ると、型エラーが読みやすくなるので良いです。 *3
ところでこの型注釈演算子(演算子ではない?)結合の強さが謎いので注意

*1:こわい方へ: 間違ってるところは、優しく教えてほしいです。

*2:正しくは、Monad m => a -> m a

*3:というか、型注釈を付けないとエラーが出るコードもあるらしい。