の場合タイプm :: * -> *
はMonad
インスタンスを持ちますが、型がa -> m b
のTuring-completeの構成が得られます。これは非常に便利なプロパティです。特定の意味から離れたさまざまなTuring-completeコントロールフローを抽象化することができます。これは、それをサポートするタイプを操作するための制御フローを抽象化することをサポートする最小のコンポジションパターンです。
これを例えばApplicative
と比較してください。そこで、プッシュダウンオートマトンと同等の計算能力を持つ合成パターンのみを取得します。もちろん、より多くのタイプがより限定されたパワーを持つ合成をサポートするのは事実です。利用可能な電力を制限すると、追加の最適化を行うことができます。これらの2つの理由は、Applicative
クラスが存在し、有用である理由です。しかし、通常はMonad
のインスタンスになる可能性のあるものがあり、そのタイプのユーザーはそのタイプで可能な最も一般的な操作を実行できます。
編集:ここで好評 は、Monad
クラスを使用して、いくつかの機能です:
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM c x y = c >>= \z -> if z then x else y
whileM :: Monad m => (a -> m Bool) -> (a -> m a) -> a -> m a
whileM p step x = ifM (p x) (step x >>= whileM p step) (return x)
(*&&) :: Monad m => m Bool -> m Bool -> m Bool
x *&& y = ifM x y (return False)
(*||) :: Monad m => m Bool -> m Bool -> m Bool
x *|| y = ifM x (return True) y
notM :: Monad m => m Bool -> m Bool
notM x = x >>= return . not
は、構文(または生>>=
オペレータ)を行うとともに、それらを組み合わせることでは、結合、無期限ループに名前を与え、ブール論理を完全なものにする。それはTuringの完全性を与えるのに十分なプリミティブのよく知られたセットです。単純な値ではなく、すべての関数がどのようにしてモナドの値に作用するのかに注目してください。すべてのモナディ効果は必要な場合にのみバインドされます。ifM
の選択されたブランチからの効果のみが最終値にバインドされます。可能であれば、*&&
と*||
は両方の引数を無視します。など。
ここで、これらの型シグネチャは、すべてのモナドのオペランドの関数を伴わなくてもかまいませんが、これは単なる認知の簡略化です。すべての非関数の引数と結果がに変更された場合、意味の違いはなく、底を無視します。そのコグニティブオーバーヘッドを最適化することは、ユーザーにとっては面白いことです。
ここでは、Applicative
インターフェイスのこれらの機能に何が起こるかを見てみましょう。
ifA :: Applicative f => f Bool -> f a -> f a -> f a
ifA c x y = (\c' x' y' -> if c' then x' else y') <$> c <*> x <*> y
まあ、ああ。それは同じタイプの署名を持っています。しかし、ここには本当に大きな問題があります。 xとyの両方の効果は、どちらの値が選択されているかにかかわらず、合成された構造にバインドされます。
whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <$> step x) (pure x)
まあ、それはそれもその近くではありません除きifA
は常に...両方の分岐が実行されますので、それは無限ループだという事実を除いて、大丈夫だろうと同じように、[OK]を、それはそうです。 pure x
のタイプはf a
です。 whileA p step <$> step x
のタイプはf (f a)
です。これは無限ループではありません。これはコンパイルエラーです。もう一度お試しください。
whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <*> step x) (pure x)
よく撮影してください。それは遠くにしないでください。 whileA p step
のタイプはa -> f a
です。 <*>
の最初の引数として使用しようとすると、の先頭の型コンストラクタのApplicative
インスタンスが取得されます。(->)
ではなく、f
です。うん、これはどちらもうまくいかない。
実際にApplicative
インターフェイスで動作する私のMonad
の例の唯一の機能はnotM
です。その特定の関数は実際にはFunctor
インターフェイスでうまく動作します。残り?彼らは失敗する。
Applicative
インターフェイスでは使用できないMonad
インターフェイスを使用してコードを書くことができます。結局のところ、厳密にはより強力です。しかし興味深いのはあなたが失うものです。入力に基づいてどのような影響を与えるかを変更する関数を作成する能力は失われます。つまり、タイプa -> f b
の関数を構成する特定の制御フローパターンを書く能力がなくなります。
チューリング完了組成は、正確にはです。Monad
インターフェイスを興味深いものにします。チューリング完了コンポジションが許可されていない場合、プログラマーであるあなたには、あらかじめ適切にパッケージ化されていない特定のコントロールフローで、IO
アクションを一緒に構成することは不可能です。 Monad
プリミティブを使用して、の制御フローを表現し、IO
タイプをHaskellのIO問題を管理するための実現可能な方法にすることができました。
IO
よりも多くの種類が、意味的に有効なMonad
インターフェイスを持っています。そして、Haskellには、インタフェース全体を抽象化するための言語機能があります。これらの要因により、Monad
は可能な場合にインスタンスを提供する貴重なクラスです。そうすることで、具体的な型が何であるかにかかわらず、モナド型を扱うために用意されている既存のすべての抽象的な機能にアクセスできます。
したがって、Haskellのプログラマが、タイプに対して常にMonad
インスタンスを気にしているように見えるのは、それが最も一般的に有用なインスタンスであるからです。
あなたの前提についてはわかりません。私が知っている高度なハスケラーの大半は、ある程度の賛成を得ています。私は確かにしています。人々はまた、まったく無関係な仕事のために、常にモノイドを使用します。カテゴリはまた、しばらくの間、それ自身で出てくる。どんな理由でも、モナスはハスケルではない回路で多くの報道陣を手に入れます。 –
@TikhonJelvis:私は、「私が知っているより高度なハスケラーの大半は、ある程度アプリケーションを支持している」とは思わない。私が最も注目を集めていることは、特に応用に関連するものではありません。例として、私が働いているもの(http://hackage.haskell.org/package/free-operational)を使うために、フリーのモナドは無料のアプリよりももっと興味を持っていると私は判断します。しかし最近では無料申請への関心が高まっている。 –
私が思い出す限り、モンドはWadlerによって一般化されました。その時、退屈なCPSや明示的な状態渡しなしの解析をせずにIOを行うというアイデアは大きなセールスポイントでした。それは非常にエキサイティングな時でした。 A.F.A.I.R.、Haskellはコンストラクタークラスを行っていませんでしたが、Gofer(Hugsの父)が行いました。 Wadlerは、モナドのリスト理解をオーバーロードすることを提案したので、後で表記が来た。 IOがモナドになった後、モナドは初心者のために大きな役割を果たしました。できるだけ多くの方が申し込みがあり、より一般的な矢印はありますが、後で来て、IOはモナドを売るのが大変です。 – AndrewC