6

私は Haskell、関数型言語、およびモナドの初心者です。
私はこれを約1か月いじっています。Learn you a haskellを読み 、スナップをいじって、私の Haskell Web サイトを作成しようとしていました。

しかし、私を悩ませているものがあります: モナドの抽象化です。私の理解が正しければ、モナドは順序付け可能な「データのコンテナ」です。たとえば ">>=" で「アンパック」でき、「舞台裏」でさらに多くの作業が行われるため、モナド定義がない場合は、どのようにアンパックされるかを推測する必要があります。

例えば:

アンパックするとその要素がシーケンスされるリストモナドがあります

[1,2,3] >>= return . (+1) -- gives back [2,3,4]

または、これらの例のライターのようなより複雑なモナド: ログ ライター モナド

または、値を「アンパック」するたびに、リモートサーバーにリクエストを送信する webWriter モナドがあるかもしれません (これについてはよくわかりませんが、極端なケースを挙げようとしています)。

私の質問は次のとおりです: 適用関数 ('>>=' 、 'applyLog') が裏で何をしているのか、モナドのユーザー インターフェイス (型定義だと思います) を見るだけでわかりますか?

私が自分自身をよく説明したことを願っています。

ありがとう、オレン。

4

7 に答える 7

8

インターフェースを見ただけでは特定のモナドに何をするかはわかりませんが(>>=)、「適切な」モナドを構成するためにすべてのモナドが尊重しなければならない法則があります。returnそして、それはとの可能な実装を制限します(>>=)モナド則は次のとおりです。

  • 左の同一性: return a >>= f等しいf a
  • 権利の同一性: m >>= return等しいm
  • 結合性: (m >>= f) >>= g等しいm >>= (\x -> f x >>= g)

例えば、もしList モナドが代わりにreturnとして定義されていたら、それは左恒等法を破るでしょう。とは異なります。\x -> [x,x]\x -> [x]return 5 >>= \x -> [x+1](\x -> [x+1]) 5

また、すべてのモナドがある種の「コンテナ」として直感的に理解できるわけではありません。コンテナーの類推は List と Maybe で機能しますが、たとえば Reader はどうでしょうか? Reader の値には、実際には何も含まれていません。代わりに、それは外部の不変の環境に依存する計算の記述です。

モナドとは、モナドインターフェースを実装し、モナドの法則を尊重するものです。

編集:特定の型に対してモナド インスタンスが何をするかを直感的に理解する方法の例として、 streamsパッケージのData.Stream.Infinite.Streamを考えてみましょう。ストリームはリストのようなものですが、常に無限です。

Stream には Monad インスタンスがあります。この場合、何をしreturn、何をしますか?(>>=)

returnタイプはa -> Stream aです。この型で唯一可能な関数は、パラメータとして渡された値の無限の繰り返しを返す関数です。

(>>=)はもっとトリッキーです。タイプがありStream a -> (a -> Stream b) -> Stream bます。このタイプの可能な関数の 1 つは、最初の引数の先頭を取り、それを 2 番目の引数に適用して、結果のストリームを返す関数です。s >>= f = f $ head s.

の別の可能な実装は(>>=)、 type の関数を元のストリームのすべての要素に適用し、 typea -> Stream b中間結果を取得してStream (Stream b)から、何らかの方法でストリームのストリームを単一のStream b値に折りたたむことです。どうやってするか?無限の正方形の対角線を取ることができます!

のどのバージョンが(>>=)モナド則と互換性がありますか? 最初のものは、正しいアイデンティティを壊すため、確かにそうではありません。の結果は1,2,3,4... >>= returnになります1,1,1,1...。2 番目の実装は、正しい ID を尊重しており (理由がわかりますか?)、これにより(>>=)、ストリームの正しい実装方法である可能性がより高くなります。もちろん、すべての法律の実際の証拠が必要です。

于 2013-09-10T06:13:10.997 に答える
4

>>=特定のモナドに対するの動作について学ぶために使用できる 4 つの情報源について説明します。

のタイプ>>=

の型>>=は常に同じです。Monad型クラスで指定されます。ドキュメントを参照してください。タイプは次のとおりです。

(>>=) :: forall a b. m a -> (a -> m b) -> m b

mは、関心のある特定のモナドのプレースホルダーです。たとえば、リスト モナドの場合、型は>>=次のとおりです。

(>>=) :: forall a b. [a] -> (a -> [b]) -> [b]

に置き換えm ...ただけであることに注意してください[...]

モナドの法則

の実装は>>=モナドごとに異なりますが、すべてのモナドはモナドの法則に従う必要があります。これらの法則は、Monad型クラスのドキュメントで指定されています。ドキュメントをもう一度参照してください。法律は次のとおりです。

  1. return a >>= k == k a
  2. m >>= return == m
  3. m >>= (\x -> k x >>= h) == (m >>= k) >>= h

したがって、特定のモナドの実装が何であれ、これらの法則を使用してコードを推論できます。たとえば、コードに法則の左側のようなコードが含まれている場合、そのコードを対応する法則の右側に置き換えることができ、動作は変更されません。

以下はモナド則の使い方の例です。次のコードを書いたとします。

foo = do
  x <- bar
  return x

ここでどのモナドが使われているかさえわかりませんが、do 記法が見られるので、いくつかのモナドがあることはわかります。モナドの法則を適用するには、次の呼び出しの do 表記を desugar する必要があります>>=

foo = bar >>= (\x -> return x)

\x -> return xは just ( returnby η-reduction.

foo = bar >>= return

モナドの第 2 法則により、このコードは単に bar を呼び出すこととまったく同じことを意味します。

foo = bar

>>=そのため、元の関数の は何も興味深いことをまったくできないように見えますfoo。なぜなら、モナドの法則により、それを除外することが許されているからです。>>=ここでは、どの特定のモナドが演算子を提供しているのかさえ知らずに、それを理解しました。

特定のモナドのドキュメント

特定のモナドに対するの動作について詳しく知る必要がある場合は>>=、特定のモナドのドキュメントを参照してください。hoogleを使用してドキュメントを検索できます。たとえば、のドキュメントにStateTは次のように記載されています。

return関数は状態を変更せずに残しますが>>=、最初の計算の最終状態を 2 番目の初期状態として使用します。

特定のモナドの実装

特定のモナドの実装についてさらに詳しく知りたい場合は、おそらく実際の実装を見なければなりません。instance Monad ...宣言を検索します。たとえば、 の実装をStateT見てください。リストモナドの実装は、このファイルのどこかにあります。検索するinstance Monad []か、これを除いて見てください:

instance  Monad []  where
    m >>= k             = foldr ((++) . k) [] m
    m >> k              = foldr ((++) . (\ _ -> k)) [] m
    return x            = [x]
    fail _              = []

>>=おそらく最も明白な定義ではないかもしれませんが、リストモナドを呼び出すとまさにそれが起こります。

概要

>>=すべてのモナドは、とreturnおよび モナド法則の型シグネチャを共有します。これらの制約とは別に、すべてのモナドは>>=andの異なる実装を提供しreturnます。すべての詳細を知りたい場合は、instance Monad ...宣言のソース コードを調べる必要があります。特定のモナドの使い方を学びたいだけなら、それに関するドキュメントを探してみてください。

于 2013-09-10T06:35:58.343 に答える
2

モナド API の型シグネチャだけを見ることは、関数の型シグネチャを見ることと同じa -> b -> cです。関数がこれを行う方法は、関数の実装の詳細です。

同様にbindreturnおよび他のモナド固有の関数 (状態モナドの のような) はput、これらの関数を使用して、あるものから別のものへのすべてのマッピングを行うことができるというget考えを与えるだけです。モナドが実際にどのように機能するかの根底にあるロジックを理解する必要がある場合は、ドキュメント (提供されている場合) またはソースコードを調べます。

于 2013-09-10T04:19:51.160 に答える
2

apply 関数が裏で何をしているのか教えてもらえますか

あなたが尋ねていない質問に対する完全に正確な答えがたくさんあります。モナドの使用状況からモナドが何をしているのか分かりますか? いいえ、一般的ではありません。

モナドの名前とそのドキュメント (多くの場合非常にまばらです) が役に立ち、周囲のコード (多くの場合非常に簡潔です) がコンテキストを提供してくれることを期待する必要があります。

もちろん、それが自分のコードまたは使い慣れたコードベースであれば、簡単です。ただし、ほとんどの Haskell コードは、「スキミング可能」になるように最適化されていないようです。

于 2013-09-10T06:51:54.983 に答える
1

与えられたモナドに対して型シグネチャだけを見ても、そのモナドに対して何をするのか、何をしようとしているのreturnかを正確に知ることはできません。>>=これは、それらの型シグネチャが常に

return :: Monad m => a -> m a
(>>=)  :: Monad m => m a -> (a -> m b) -> m b

トレスジェネリック。関数がそこで行うことの正確な仕様であるため、これは型レベルで優れています。

より細かい解決策として、モナドのインスタンス宣言を見る必要があります。

于 2013-09-10T04:21:38.713 に答える
1

それは興味深い質問です。あなたはモナドができることをきちんと理解していると思います。ドキュメントを読まずに、関数の特定の動作を知ることは不可能だと思います。各モナドの bind と return は、型の構造と目的のために特別に実装されています。

于 2013-09-10T04:20:18.083 に答える