5

この非常に単純なコードが意味をなさないことに気付くまで、私は Haskell モナドをうまく扱えると思っていました (これはState モナドに関する haskell wikiからのものです)。

playGame :: String -> State GameState GameValue
playGame []     = do
  (_, score) <- get
  return score

私を混乱させているのは、提供された唯一の引数が文字列であるのに、コードが「get」を呼び出すことが許可されているのはなぜですか? 何もないところから値を引き出しているようです。

私が質問するより良い方法は>>=、do 表記の代わりに and ラムダを使用してこの関数をどのように書き直すかということかもしれません。私はそれを自分で理解することができません。

4

2 に答える 2

7

doこれを表記法に脱糖すると、次のようになります

 playGame [] =
   get >>= \ (_, score) ->
   return score

これを次のように書くこともできますfmap

 playGame [] = fmap (\(_, score) -> score) get
 playGame [] = fmap snd get

ここでの秘訣は、getそれが型を持つ他の値と同じであることを認識することです

 State s s

何が返されるかは、状態の明示的な開始値を提供する場所または同様の場所にget計算をフィードするまで決定されません。runState

これをさらに単純化し、状態モナドを取り除くと、

playGame :: String -> (GameState -> (GameState, GameValue))
playGame [] = \gamestate -> (gamestate, snd gamestate)

状態モナドは、この手動で渡されたすべてをラップしているだけですが、「関数」が渡された値にアクセスしているGameStateと考えることができます。get

于 2014-04-03T15:20:27.383 に答える
0

モナドとは、コンテキスト (m と呼びます) を取り、モナドの法則を尊重しながら値を「生成」する「もの」です。これは、モナドの「内側」と「外側」という観点から考えることができます。モナドの法則は、「往復」に対処する方法を教えてくれます。特に、法則は、m (ma) が本質的に (ma) と同じ型であることを示しています。

ポイントは、モナドはこのラウンドトリップの一般化であるということです。join は (m (ma)) を (ma) に押しつぶし、(>>=) はモナドから値を引き出し、関数をモナドに適用します。別の言い方をすれば、関数 (f :: a -> mb) を (ma) の a に適用し、(m (mb)) を生成し、join を介してそれを押しつぶして (mb) を取得します。

では、これは「get」とオブジェクトとどのような関係があるのでしょうか?

do 記法は、計算の結果がモナドにあるように設定します。そして (<-)はモナドから値を引き出すことを可能にするので、名目上はモナドの中にいながら関数にバインドすることができます。たとえば、次のようになります。

doStuff = do
   a <- get
   b <- get
   return $ (a + b)

a と b は純粋であることに注意してください。get の「外側」にあるのは、実際に get の内側を覗いたからです。しかし、モナドの外に値があるので、それを使って (+) 何かをしてからモナドに戻す必要があります。

これはほんの少し示唆に富む表記法ですが、次のようにするとよいでしょう。

doStuff = do
  a       <- get
  b       <- get
  (a + b) -> (\x -> return x) 

その前後を本当に強調します。モナド アクションを終了すると、そのテーブルの右側の列にいる必要があります。これは、アクションが完了すると、レイヤーを平坦化するために「結合」が呼び出されるためです。(少なくとも、概念的には)

ああ、オブジェクト。ええ、明らかに、オブジェクト指向言語は基本的に、ある種の IO モナドの中で生活し、呼吸しています。しかし、実際にはもう少し分解することができます。次の行に沿って何かを実行すると:

 x = foo.bar.baz.bin()

あなたは基本的にモナドトランスフォーマースタックを実行しています。これはIOコンテキストを取り、fooコンテキストを生成し、barコンテキストを生成し、bazコンテキストを生成し、binコンテキストを生成します。そして、ランタイム システムは、必要に応じて何度でも join を「呼び出し」ます。このアイデアが「コール スタック」とうまくかみ合っていることに注目してください。実際、これが Haskell 側で「モナド変換スタック」と呼ばれる理由です。これは、モナド コンテキストのスタックです。

于 2014-04-10T06:15:23.870 に答える