みどりねこ日記

よくわからないけど、頑張りますよ。

Haskellのdo記法(3)

ユーザに氏名を尋ねるプログラムを考えましょう。

nameDo :: IO ()
nameDo = do putStr "What is your first name? "
	first <- getLine
	putStr "And your last name? "
	last <- getLine
	let full = first ++ " " ++ last
	putStrLn ("Pleased to meet you, " ++ full ++ "!")

do記法のところはさっくり読めますし、何が起こっているかがわかりやすいです。<-記法は、firstやlastといった変数をあたかも純粋であるかのように扱うことができるようにします。実際にはそうではありません。関数getLineは不純です。というのは、これは実行するたびに違う結果を返す関数だからです。むしろ、そうでなくては困ります。

もしこれをモナディックなコードに書き直すと、以下のようになります。

name :: IO ()
name = putStr "What is your first name? " >>
	getLine >>= f
	where
	f first = putStr "And your last name? " >>
		getLine >>= g
		where
		g last = putStrLn ("Pleased to meet you, " ++ full ++ "!")
			where
			full = first ++ " " ++ last

そろそろdo記法の良さについて触れます。nameDoの方が明らかに読みやすいし、画面の端にまでコードが広がったりしません。

nameの深すぎるインデントは主に>>=演算子のために使われるwhereのせいで、IOモナドから値を引っ張ってくるときに、直接では無理なので関数を新しく定義する必要があることがさらにそれを助長しています。このため、モナドの中から、直接では無理な値を引っ張ってくるという処理の多いIOモナドを扱うときはdo記法が多用されます。

インデントを減らし、do記法に近づけようとするなら、ラムダ式を使えばよいです。

nameLambda :: IO ()
nameLambda = putStr "What is your first name? " >>
	getLine >>=
	\first -> putStr "And your last name? " >>
	getLine >>=
	\last -> let full = first ++ " " ++ last
		in putStrLn ("Pleased to meet you, " ++ full ++ "!")