9

Haskell のモナドの背後にある正確な代数と理論を理解していません。しかし、一般的に関数型プログラミングについて考えると、初期状態を取得し、次の状態を表すためにそのコピーを生成することによって状態がモデル化されるという印象を受けます。これは、あるリストが別のリストに追加される場合に似ています。どちらのリストも変更されませんが、3 番目のリストが作成されて返されます。

したがって、モナド操作を暗黙的に初期状態オブジェクトをパラメーターとして取り、暗黙的に最終状態オブジェクトを返すと考えるのは有効ですか? これらの状態オブジェクトは、プログラマーがそれらについて心配したり、アクセス方法を制御したりする必要がないように隠されます。したがって、プログラマーは 10 分前の IO ストリームを表すオブジェクトをコピーしようとはしません。

つまり、次のコードがある場合:

main = do
    putStrLn "Enter your name:"
    name <- getLine
    putStrLn ( "Hello " ++ name )

... IO モナドと "do" 構文を、このスタイルのコードを表すものと考えてよろしいですか?

putStrLn :: IOState -> String -> IOState
getLine :: IOState -> (IOState, String)
main :: IOState -> IOState

-- main returns an IOState we can call "state3"
main state0 = putStrLn state2 ("Hello " ++ name)
    where (state2, name) = getLine state1
        state1 = putStrLn state0 "Enter your name:"
4

5 に答える 5

18

いいえ、それはモナドが一般的に行うことではありません。ただし、あなたの類推は実際には、たまたまモナドであるdata type に関して正確に正しいです。は次のように定義されます。State s aState

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

...ここで、型変数sは状態値でaあり、使用する「通常の」値です。したがって、「State モナド」の値は、初期状態から戻り値、最終状態までの単なる関数です。に適用されるモナド スタイルはState、一連の関数を通じて状態値を自動的にスレッド化するだけです。

STモナドは表面的には似ていますが、魔法を使用して実際の副作用を伴う計算を可能にしますが、その副作用は外部の特定の用途から観察できないようにするだけですST

IOモナドは本質的に「より魔法」に設定されたモナドSTであり、外部の世界に触れる副作用と、IO計算が実行される単一のポイント、つまりプログラム全体のエントリポイントのみを備えています。それでも、概念レベルでは、通常の方法で関数を介して「状態」値をスレッド化するものと考えることができますState

しかし、他のモナドは必ずしもスレッド状態やシーケンス関数などとは何の関係もありません。何かをモナドにするために必要な操作は、信じられないほど一般的で抽象的です。たとえば、Maybeorをモナドとして使用すると、エラーを返す可能性のある関数を使用できます。モナド スタイルの処理では、状態値をスレッド化するEitherのと同じ方法でエラーが発生したときに計算から逃れます。Stateリストをモナドとして使用すると非決定性が得られ、関数を複数の入力に同時に適用し、可能なすべての出力を表示できます。モナド スタイルでは、関数が各引数に自動的に適用され、すべての出力が収集されます。

于 2010-05-12T00:41:39.750 に答える
8

したがって、モナド操作を暗黙的に初期状態オブジェクトをパラメーターとして取り、暗黙的に最終状態オブジェクトを返すと考えるのは有効ですか?

これは、モナドを学習するための一般的な障害点のようです。つまり、単一の魔法のモナドの原始的なスープが、ステートフルな計算、失敗する可能性のある計算、非決定論的な計算、例外、継続、シーケンスの副作用、およびすぐ。

一連のステートフルな計算による状態のスレッド化は、モナドの法則を満たす操作の1 つの例です。

Stateモナドとモナドが近い従兄弟であることを観察するのは正しいですがIO、たとえばリストモナドを挿入しようとすると、アナロジーは崩壊します。

于 2010-05-12T00:09:11.053 に答える
4

一般的にはモナドではありませんが、IO モナドの場合はそうです — 実際、型IO aはしばしば関数 type として定義されますRealWorld -> (RealWorld, a)。したがって、このビューでは、putStrLnisString -> RealWorld -> (RealWorld, ())getCharisの desugared 型がRealWorld -> (RealWorld, Char)— そして、それを完全に評価して RealWorld を渡すように処理するモナド バインドを使用して、部分的にのみ適用します。(GHC の ST ライブラリには、実際には非常にリアルなRealWorld型が含まれていますが、これは「非常に魔法的」であり、実際に使用するものではないと説明されています。)

ただし、このプロパティを持たないモナドは他にもたくさんあります。たとえば、モナド[1,2,3,4]Just "Hello".

于 2010-05-12T00:00:31.207 に答える
3

絶対違う。これは一般的なモナドとは異なります。モナドを使用して暗黙的にデータを渡すことができますが、それは 1 つのアプリケーションにすぎません。このモデルのモナドを使用すると、モナドができる本当にクールなことの多くを逃すことになります。

代わりに、モナドを計算を表すデータの断片と考えてください。たとえば、純粋な言語はすべての引数と戻り値の型について明示的であると主張するため、暗黙的にデータを渡すことは純粋ではないという感覚があります。したがって、暗黙的にデータを渡したい場合は、これを行うことができます。何か不純なことを行うことを表す新しいデータ型を定義し、それを操作するコードを記述します。

極端な例 (単なる理論上の例であり、これを実行する可能性は低いです) は次のとおりです。C では不純な計算が許可されているため、C コードの一部を表す型を定義できます。次に、これらの C 構造体の 1 つを取得して解釈するインタープリターを作成できます。すべてのモナドはこのようなものですが、通常は C インタープリターよりもはるかに単純です。しかし、このビューは、隠された状態を通過することではないモナドについても説明しているため、より強力です。

おそらく、IO が隠された世界の状態を通過していると見なす誘惑に抵抗する必要があります。IO の内部実装は、IO についてどのように考えるべきかとは何の関係もありません。IO モナドは、実行したい I/O の表現を構築するためのものです。これらの表現の 1 つを Haskell ランタイム システムに返します。その表現をどのように実装するかは、そのシステム次第です。

何かをしたいときはいつでも、純粋な方法でそれを直接実装する方法はわかりませんが、欲しいものを記述する純粋なデータ構造を構築する方法はわかります。モナドのアプリケーションがあるかもしれません。特に、構造が自然に何らかのツリーである場合。

于 2010-05-12T18:00:32.780 に答える
1

私は、モナドを遅延アクション (runXXX、main) を表すオブジェクトとして考え、その結果に従って結合できる結果を生成することを好みます。

return "somthing"
actionA >>= \x -> makeActionB x

そして、これらのアクションはステートフルである必要はありません。つまり、関数構築のモナドは次のように考えることができます。

instance Monad ((->) a) where
    m >>= fm = \x -> fm (m x) x
    return = const

sqr = \x -> x*x
cube = \x -> x*x*x

weird = do
    a <- sqr
    b <- cube
    return (a+b)
于 2010-05-12T05:41:45.297 に答える