web-dev-qa-db-ja.com

モナド対矢

関数型プログラミングで使用される モナド および 矢印 の概念に広く精通しています。また、同様の問題を解決するために使用できることも理解しています。

ただし、特定の状況で使用するものを選択する方法については、まだ少し混乱しています。

モナドはいつ使用する必要があり、矢印はいつ使用する必要がありますか?

70
mikera

Lindley、Wadler&Yallopによる2つの優れた論文があります(LTUで議論されています ここ )。

理解しておくべき最も重要なことは、モナドであるものよりも矢印であるものが多いあるということです。逆に、モナドは矢印よりも厳密に強力です( 上記の2番目の論文 は正確にどの方法で指定します)。

特に、モナドはタイプ(a ~> b, a) ~> bの適用関数を備えた矢印です。ここで、(~>)は特定の矢印のコンストラクターです。 Lindley etal。これにより、用語とコマンド(または、必要に応じてオブジェクトと射)の間で維持される細心の区別が破壊されることを指摘します。

適用可能なファンクターには、特にストリームでの操作として最もよく考えられているものに対して、さまざまなアプリケーションがあります。実際、矢印は、ストリーム上のトランスフォーマーの概念を一般化することから生じると考えることができます(つまり、特定のアプリケーションファンクターによって構築されたオブジェクトの射の新しい言語を導入します)。

私の経験では、モナドはオブジェクトと射の区別を曖昧にするため(つまり、単語を正しく使用している場合は、閉じたデカルト閉圏が生じます)、モナドの用語は一般に、の用語よりもはるかに不透明です。矢印または適用可能なファンクター(ただし、どちらも arr および pure メソッドによってそれぞれ任意の関数を挿入できることに注意してください)。

したがって、モナドの特性が与えられた場合(概念的にはモナドを形成していても)、何かがnotでない場合、より詳細な検査と最適化が可能になります。また、シリアル化が容易になる可能性もあります。したがって、パーサーと回路モデリングでのApplicativeと矢印の使用。


上記は、一般的で説明的なものにしようとしました。以下は、私の経験則の一部です。

状態のように見えるものをモデル化する必要がある場合は、モナドから始めます。グローバル制御フローのように見えるもの(つまり、例外、継続)をモデル化する必要がある場合は、モナドから始めます。モナドのパワーと一般性と矛盾する要件が発生した場合(つまり、結合(join :: m (m a) -> m a)が強力すぎる場合)、使用しているもののパワーを削ることを検討してください。

ストリーム、およびストリーム、特に特定の特性(特に過去と未来の無制限のビュー)が不透明である必要があるストリームの変換をモデル化する必要がある場合は、適用可能なファンクターから始めます。ストリームの変換のプロパティについてより強力な推論が必要な場合は、矢印に手を伸ばすことを検討してください。

または、非常に大雑把に言えば、Applicativeは回路の動作用、矢印は回路の構造用、モナドは汎用の計算効果用です。

もちろん、話にはまだまだたくさんのことがあります。 Applicativeについては、特に Conal ElliottのFRPに関する作業 を参照してください。矢印については、 HXT XMLパーサーライブラリYampa FRPプロジェクトHaskell on a Horse Webフレームワーク 、Hudak andLiuのクラシック-を参照してください。 "スペースリークを矢印でつなぐ" 紙など。モナドについては、どこでも参照してください。そしてもちろん、何かがモナドであるという理由だけで、それは適用可能な表記法がより明確でより表現力がないかもしれないという意味ではないことに注意してください。

72
sclv

簡単に言うと、矢印はモナドよりも一般的であり、使用するのも面倒です。したがって、モナドが適用されない場合には矢印の使用を残して、可能な限りモナドを使用する必要があります。

「風光明媚なルート」の答えは次のとおりです。

Arrowsを紹介したJohnHughesが、私がお勧めする2つのすばらしい論文を発表しました: 「モナドを矢印に一般化する」「矢印を使用したプログラミング」 。これらの2つの記事は読みやすく、質問に対する答えを提供します。これらの2つの記事のすべての詳細やコードを理解していない人もいますが、モナドとアローに関する多くの情報と非常に役立つ説明を確実に見つけることができます。

ここで、あなたの質問に関連するこれらの記事の要点を強調します

モナドが導入されたとき、人々は彼らが全能であると思いました。確かに、モナドは多くの力を詰め込んでいます。しかし、ある時点で、モナドを適用できない場合があることがわかりました。これらのケースは、特に一部の入力が静的で一部の入力が動的である場合に、複数の入力に関係しています。そこで、ジョン・ヒューズがステップアップしてアローズを紹介しました。

矢印はモナドよりも一般的です。矢印はモナドのスーパーセットです。彼らはモナドがすることすべてをすることができます。しかし、それらは使用するのもより困難です。 John Hughesは、可能な限りモナドを使用し、モナドを使用できない場合はArrowsを使用することを推奨しています。

ジョン・ヒューズに同意します。また、アインシュタインの言葉「すべてをできるだけシンプルにする必要がありますが、シンプルにするべきではありません」という言葉を思い出します。

もちろん、それはすべて目前の特定の状況に依存します。説明させてください。あなたがHaskellを学んでいるとしましょう。次に、モナドアプローチを使用して各プログラムを実行し、矢印ベースのアプローチを使用してそれをやり直すことは素晴らしいタスクです。あなたが学ぶとき、あなたはすべての可能性を探求し、あらゆる種類のアプローチを実行するよう努めるべきです。このようにして、優れた洞察を得ることができ、さまざまなソリューションを直接比較することができます。

ここで、コミュニティにライブラリを提供したいとします。さて、あなたはあなたのコードを読む人々にあなたが最も理解しやすくそしてそれでも仕事を成し遂げるアプローチを使うべきであるという義務があります。また、ソリューションに不必要な複雑さが欠けているのは、コードを使用する人々のおかげです。このようにして、ソリューションはより簡単に保守でき、エラーやバグが発生しにくくなります。

しかし、あなたが境界的なケースにいる場合はどうなりますか? Arrowsの追加のパワーが必要かどうかわからないとしましょう。それならあなたは何をすべきですか?モナドアプローチから始めて、必要に応じて後で矢印ベースのアプローチに切り替える必要がありますか?それとも、プロジェクトの途中でコストのかかる切り替えを避けて、最初からArrowsから始める必要がありますか?

繰り返しますが、私の答えは最初のアプローチを試すことです。可能であれば、モナドを使用してみてください。後でMonadsを使用できないことがわかった場合は、コストのかかる切り替えに耐える必要があります。Arrowsを使用するには、プロジェクトを再起動してやり直す必要があります。このアプローチは確かにあなたの側からより多くの時間と他のリソースを必要とします。しかし、あなたはあなたが正しいことをしたことを知っているでしょう、それは可能な限り最も単純で、最も明確で、より複雑でない解決策を提供しようとすることでした。

不必要な複雑さを回避することが最も重要です。信じられないかもしれませんが、これが圏論の概念(関数の合成、モナド、矢印など)がコンピュータサイエンスに導入された理由です。皮肉な?

15