14

モナドについて読んでいると、「Xyz モナドの計算」のようなフレーズをよく見かけます。計算が特定のモナドの「中に」あるとはどういう意味ですか?

私はモナドが何であるかについてかなり理解していると思います: 計算により、通常は期待されるタイプの出力を生成できるようになりますが、代わりに、または追加で、エラーステータス、ログ情報、状態などの他の情報を伝えることができます。そのような計算を連鎖できるようにします。

しかし、計算がモナドの「中に」あると言われる方法がわかりません。これは単項結果を生成する関数を参照しているだけですか?

例: (「computation in」で検索)

4

5 に答える 5

12

一般に、「モナドでの計算」とは、モナドの結果を返す関数だけでなく、doブロック内で、またはの2番目の引数の一部として、(>>=)またはそれらと同等のものとして使用される関数を意味します。この区別は、コメントであなたが言ったことに関連しています。

「計算」は、入力モナドからvalが抽出された後、結果がモナドとしてラップされる前に、funcfで発生します。計算自体がモナドの「中に」あるのかわかりません。モナドの「外」が目立つようです。

これはそれについて考えるのに悪いdo方法ではありません-実際、それは物事を見るのに便利な方法であるため、表記法はそれを奨励します-しかしそれは少し誤解を招く直感をもたらします。モナドから「抽出」されているものはどこにもありません。理由を理解するために、忘れてください。これは、表記法(>>=)をサポートするために存在する複合操作です。doモナドのより基本的な定義は、3つの直交関数です。

fmap :: (a -> b) -> (m a -> m b)
return :: a -> m a
join :: m (m a) -> m a

...mモナドはどこにありますか。

次に、これらを実装する方法について考えてみましょう。型と(>>=)の引数から始めて、唯一のオプションは、型の何かを取得するために使用することです。その後、ネストされた「レイヤー」をフラット化して、を取得することができます。m aa -> m bfmapm (m b)joinm b

言い換えると、モナドから「取り出される」ものは何もありません。代わりに、計算をモナドの奥深くまで進め、連続するステップをモナドの単一のレイヤーに折りたたむと考えてください。

モナドの法則もこの観点からははるかに単純であることに注意してください。基本的にjoin、ネストの順序が保持されている限り(結合の形式)、いつ適用されるかは重要ではなく、によって導入されたモナド層returnは何もしない(のID値join

于 2013-02-22T15:04:18.383 に答える
12

これは単項結果を生成する関数を参照しているだけですか?

はい、要するに。


要するに、( を介して) に値を注入Monadできるためですが、一度内部に入るとスタックしてしまいます。値を「出力」するよりも厳密に具体的なorのような関数を使用する必要があります。returnMonadevalWriterrunContMonad

それ以上に、Monad(実際にはそのパートナーであるApplicative) は、「コンテナー」を持ち、その中で計算を実行できるようにすることの本質です。それ(>>=)が、 の「内部」で興味深い計算を行う機能を提供するものですMonad

したがって、次のような関数はMonad m => m a -> (a -> m b) -> m b、 a の周囲および内部で計算できますMonadMonad m => a -> m aに注入できるような関数Monad。のような関数を使用すると、一般に (特定の場合にのみ) 存在しないことを除いてm a -> a、「エスケープ」できます。ですから、会話のために、「モナドの内部」のMonadような結果型を持つ関数について話したいと思います。Monad m => m a

于 2013-02-22T04:52:17.643 に答える
4

通常、例として「コレクションのような」モナドから始めると、モナドの内容を把握しやすくなります。2 点の距離を計算するとします。

data Point = Point Double Double

distance :: Point -> Point -> Double
distance p1 p2 = undefined

今、あなたは特定の文脈を持っているかもしれません。たとえば、ポイントの 1 つが、いくつかの境界 (画面上など) の外にあるため、「不正」である可能性があります。Maybeしたがって、既存の計算をモナドでラップします。

distance :: Maybe Point -> Maybe Point -> Maybe Double
distance p1 p2 = undefined

まったく同じ計算を行いますが、「結果がない」可能性があるという追加機能があります (としてエンコードされますNothing)。

または、「可能な」点の 2 つのグループがあり、相互の距離が必要な場合 (たとえば、後で最短の接続を使用する場合)。リストモナドはあなたの「文脈」です:

distance :: [Point] -> [Point] -> [Double]
distance p1 p2 = undefined

または、ポイントがユーザーによって入力され、計算が「非決定論的」になります (変化する可能性のある外界のものに依存するという意味で) 場合、IOモナドはあなたの友達です:

distance :: IO Point -> IO Point -> IO Double
distance p1 p2 = undefined

計算は常に同じままですが、たまたま特定の「コンテキスト」で行われるため、いくつかの有用な側面 (失敗、多値、非決定論) が追加されます。これらのコンテキストを組み合わせることもできます (モナド変換子)。

上記の定義を統一し、どのモナドでも機能する定義を書くことができます:

 distance :: Monad m => m Point -> m Point -> m Double
 distance p1 p2 = do
     Point x1 y1 <- p1
     Point x2 y2 <- p2
     return $ sqrt ((x1-x2)^2 + (y1-y2)^2)  

これは、計算が実際のモナドから本当に独立していることを証明し、「x は (-side) y モナドで計算される」という定式化につながります。

于 2013-02-22T07:46:40.737 に答える
2

あなたが提供したリンクを見ると、「計算」の一般的な使用法は、単一のモナド値に関するものであるようです。抜粋:

穏やかな導入 - ここでは SM モナドで計算を実行しますが、計算はモナド値です:

-- run a computation in the SM monad
runSM                   :: S -> SM a -> (a,S)

モナドに関するすべて -前の計算は、シーケンス内のモナド値を参照します。

>> 関数は、シーケンス内の前の計算からの入力を必要としないモナド計算をバインドするために使用される便利な演算子です。

モナドを理解する - ここで最初の計算getLineは、例えばモナド値を参照することができます:

(バインディング) は、計算を実行するという概念を必要とせずに、別の計算で計算の結果を使用するという本質的なアイデアを提供します。

類推として、私が言うとi = 4 + 2iは値です6が、それは同様に計算、つまり計算4 + 2です。リンクされたページでは、この意味での計算(モナド値としての計算) を使用しているようです。

于 2013-02-22T11:51:42.643 に答える
1

IOモナドを考えてみましょう。タイプの値はIO a、動作が一連のIOイベント(読み取り、書き込みなど)である、多数の(多くの場合無限の)動作の記述です。このような値は「計算」と呼ばれます。この場合、それはIOモナドでの計算です。

于 2013-02-22T03:50:59.830 に答える