次の「do」表記の「bind」関数は、 getLine >>= \line -> putStrLn
do line <- getLine
putStrLn line
しかし、次の表記はバインド関数とどのように同等ですか?
do line1 <- getLine
putStrLn "enter second line"
line2 <- getLine
return (line1,line2)
「putStrLn」の結果をバインドする方法を確認しようとしていると思います。答えはputStrLnのタイプにあります:
putStrLn :: String -> IO ()
「()」は単一の値を持つユニットタイプであることに注意してください(「()」とも表記されます)。したがって、これをまったく同じ方法でバインドできます。ただし、使用しないため、「ドントケア」値にバインドします。
getLine >>= \line1 ->
putStrLn "enter second line" >>= \_ ->
getline >>= \line2 ->
return (line1, line2)
たまたま、戻り値「>>」を無視するためにすでに定義されている演算子があります。したがって、これを次のように書き直すことができます
getLine >>= \line1 ->
putStrLn "enter second line" >>
getline >>= \line2 ->
return (line1, line2)
バインド演算子がどのようにデイジーチェーン接続されているかも理解しようとしているかどうかはわかりません。これを確認するために、上記の例に暗黙の角かっこと追加のインデントを入れましょう。
getLine >>= (\line1 ->
putStrLn "enter second line" >> (
getline >>= (\line2 ->
return (line1, line2))))
各バインド演算子は、値を左側にリンクし、関数を右側にリンクします。この関数は、「do」句の残りのすべての行で構成されます。したがって、ラムダ(最初の行の「line1」)を介してバインドされている変数は、残りの句全体のスコープ内にあります。
この特定の例では、実際には両方do
を回避でき、次の>>=
コンビネータを使用できControl.Applicative
ます。
module Main where
import Control.Applicative ((<$>), (<*>), (<*))
getInput :: IO (String, String)
getInput = (,) <$> getLine <* putStrLn "enter second line" <*> getLine
main = print =<< getInput
期待どおりに機能します:
travis@sidmouth% ./Main
hello
enter second line
world
("hello","world")
最初は少し変に見えますが、慣れるととても自然な感じになります。
Real-Worldhaskellという本の「Do-blockの脱糖」の章を読むことを強くお勧めします。それはあなたに、あなた方全員が間違っていることを告げます。プログラマーにとって、ラムダを使用するのは自然な方法ですが、do-blockは、パターンマシンの失敗が発生した場合に対応するfail
モナドの実装を呼び出す関数を使用して実装されます。
たとえば、あなたのケースは次のようになります。
let f x =
putStrLn "enter second line" >>
let g y = return (x,y)
g _ = fail "Pattern mismatched"
in getLine >>= g
f _ = fail "Pattern mismatched"
in getLine >>= f
このような場合、これはまったく関係がない可能性があります。ただし、パターンマッチングを含む式について考えてみます。また、この効果をいくつかの特別なものに使用できます。たとえば、次のようなことができます。
oddFunction :: Integral a => [a] -> [a]
oddFunctiond list = do
(True,y) <- zip (map odd list) list
return y
この関数は何をしますか?リストの要素を操作するためのルールとして、このステートメントを読むことができます。最初のステートメントは、リストの要素を変数yにバインドしますが、yが奇数の場合に限ります。yが偶数の場合、パターンマッチングの失敗が発生し、fail
呼び出されます。リストのモナドインスタンスでは、fail
は単純に[]
です。したがって、この関数はリストからすべての偶数要素を削除します。
(私は知ってoddFunction = filter odd
います、これはもっとうまくいくでしょうが、これは単なる例です)
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)
一般的にfoo <- bar
はなりbar >>= \foo ->
、baz
なりますbaz >>
(do-blockの最後の行でない限り、その場合はそのままになりますbaz
)。