120

アプリカティブは構成しますが、モナドは構成しません。

上記のステートメントはどういう意味ですか? そして、どちらが他のものよりも好ましいのはいつですか?

4

7 に答える 7

122

種類を比較してみると

(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m =>       m s -> (s -> m t) -> m t

2 つの概念を分けるものの手がかりが得られます。(s -> m t)のタイプのこと(>>=)は、 の値がsの計算の動作を決定できることを示していますm t。モナドは、値レイヤーと計算レイヤーの間の干渉を可能にします。演算子はその(<*>)ような干渉を許しません: 関数と引数の計算は値に依存しません。これは本当に刺さります。比較

miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
  b <- mb
  if b then mt else mf

これは、何らかの効果の結果を使用して 2 つの計算(ミサイルの発射と休戦への署名など​​)を決定しますが、

iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
  cond b t f = if b then t else f

これは の値を使用してと の2 つの計算の値abを選択し、両方を実行すると、おそらく悲劇的な結果になります。ataf

(>>=)モナド バージョンは、本質的に、値から計算を選択する追加の力に依存しており、これは重要な場合があります。ただし、その力をサポートすると、モナドを構成するのが難しくなります。「ダブルバインド」を構築しようとすると

(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???

ここまでたどり着きましたが、レイヤーがすべてごちゃごちゃになっています。があるn (m (n t))ので、外側の を取り除く必要がありnます。Alexandre C が言うように、適切な

swap :: n (m t) -> m (n t)

n内側に並べ替え、joinそれをもう一方に並べ替えnます。

より弱い「二重適用」は、定義がはるかに簡単です

(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs

レイヤー間の干渉がないためです。

これに対応して、s の特別な機能が本当に必要な場合Monadと、サポートする厳密な計算構造を使用できる場合を認識することをお勧めしApplicativeます。

ところで、モナドを構成するのは難しいですが、必要以上かもしれないことに注意してください。このタイプm (n v)は、 -effects を使用してm計算し、次にn-effects を使用して -value に計算することを示しますv。ここで、m-effects は -effects が開始する前に終了しnます (したがって が必要ですswap)。m-effects と-effectsをインターリーブしたいだけならn、合成はおそらく多すぎるでしょう!

于 2011-08-12T15:44:52.033 に答える
86

アプリカティブは構成しますが、モナドは構成しません。

モナド構成しますが、結果はモナドではないかもしれません。対照的に、2 つのアプリカティブの合成は、必然的にアプリカティブです。元のステートメントの意図は、「適用性は構成するが、モナド性は構成しない」ということだったのではないかと思います。言い換えれば、「Applicative構成の下で閉じられMonadていない」。

于 2011-08-15T20:20:13.407 に答える
42

applicativeA1A2がある場合、型data A3 a = A3 (A1 (A2 a))も applicative です (そのようなインスタンスを一般的な方法で記述できます)。

一方、モナドがM1ありM2、型data M3 a = M3 (M1 (M2 a))がモナドであるとは限らない場合 (コンポジションのための賢明なジェネリック実装はありません>>=) join

1 つの例は型です[Int -> a](ここでは型コンストラクター[]をで構成しますが(->) Int、どちらもモナドです)。簡単に書けます

app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x

そして、それはあらゆるアプリケーションに一般化されます:

app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)

しかし、合理的な定義はありません

join :: [Int -> [Int -> a]] -> [Int -> a]

これに納得がいかない場合は、次の表現を検討してください。

join [\x -> replicate x (const ())]

返されるリストの長さは、整数が提供される前に石で設定する必要がありますが、正しい長さは提供される整数によって異なります。したがって、この型には正しいjoin関数は存在しません。

于 2011-08-12T13:48:46.093 に答える
18

残念ながら、私たちの真の目標であるモナドの合成は、かなり難しいものです。.. 実際、ある意味で、2 つのモナドの演算のみを使用して上記の型の結合関数を構築する方法がないことを実際に証明できます (証明の概要については、付録を参照してください)。したがって、合成を形成する唯一の方法は、2 つのコンポーネントをリンクするいくつかの追加の構造がある場合です。

モナドの作成、http: //web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf

于 2011-08-12T13:50:05.367 に答える