9

次の「do」表記の「bind」関数は、 getLine >>= \line -> putStrLn

do line <- getLine
   putStrLn line

しかし、次の表記はバインド関数とどのように同等ですか?

do line1 <- getLine
   putStrLn "enter second line"
   line2 <- getLine
   return (line1,line2)
4

4 に答える 4

19

「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」)を介してバインドされている変数は、残りの句全体のスコープ内にあります。

于 2010-09-14T16:56:13.987 に答える
7

この特定の例では、実際には両方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")

最初は少し変に見えますが、慣れるととても自然な感じになります。

于 2010-09-14T18:04:27.723 に答える
6

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います、これはもっとうまくいくでしょうが、これは単なる例です)

于 2010-09-15T04:54:03.717 に答える
5
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)

一般的にfoo <- barはなりbar >>= \foo ->bazなりますbaz >>(do-blockの最後の行でない限り、その場合はそのままになりますbaz)。

于 2010-09-14T16:56:03.537 に答える