4

実世界の Haskellの章(>>)では、次のような正当化を行います。

特定の順序でアクションを実行したいが、1 つの結果がどうなるかは気にしない場合に、この関数を使用します。

次に、それを示す良い例を示します。

ghci > print "foo" >> print "bar"
"foo"
"bar"

この章の後半では、State モナドでランダムな値を生成するために同じ概念を使用しています。

getRandom :: Random a => RandomState a
getRandom =
  get >>= \gen ->
  let (val, gen') = random gen in
  put gen' >>
  return val

put gen' >>しかし、この場合、最終的に出力値を無視しているのに、なぜステートメントを使用しているのでしょうか。上記の関数を次のようにできないのはなぜですか。

getRandom :: Random a => RandomState a
getRandom =
  get >>= \gen ->
  let (val, gen') = random gen in
  return val

この質問を完了するために、上記のコンテキストに関連する型定義と関数を追加します。

newtype State s a = State {
  runState :: s -> (a, s)
  }

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ \_ -> ((), s)

type RandomState a = State StdGen a
4

1 に答える 1

9

put副作用があるからです。後でアクセスして変更できる状態モナドに何かを貼り付けます。

戻り値は()退屈なので気にしませんが、新しい乱数発生器を確実に状態にする必要があります。

このように考えてください。状態モナドは実際には関数s -> (a, s)です。そしてput_

put s = \oldState -> (() , s)

したがって、これには副作用があり、古い状態を捨てて置き換えることになります。この例を考えてみましょう

  test = put 1 >> get >>= \x -> put 2 >> get >>= \y -> return (x, y)

  -- or with do notation
  test' = do
    put 1
    x <- get
    put 2
    y <- get
    return (x, y)

ここx1yがあり2ます。明らかに、単なる戻り値を超えた興味深い効果があります。

于 2013-11-30T17:16:30.497 に答える