アプリカティブは構成しますが、モナドは構成しません。
上記のステートメントはどういう意味ですか? そして、どちらが他のものよりも好ましいのはいつですか?
アプリカティブは構成しますが、モナドは構成しません。
上記のステートメントはどういう意味ですか? そして、どちらが他のものよりも好ましいのはいつですか?
種類を比較してみると
(<*>) :: 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
を選択し、両方を実行すると、おそらく悲劇的な結果になります。at
af
(>>=)
モナド バージョンは、本質的に、値から計算を選択する追加の力に依存しており、これは重要な場合があります。ただし、その力をサポートすると、モナドを構成するのが難しくなります。「ダブルバインド」を構築しようとすると
(>>>>==) :: (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
、合成はおそらく多すぎるでしょう!
アプリカティブは構成しますが、モナドは構成しません。
モナドは構成しますが、結果はモナドではないかもしれません。対照的に、2 つのアプリカティブの合成は、必然的にアプリカティブです。元のステートメントの意図は、「適用性は構成するが、モナド性は構成しない」ということだったのではないかと思います。言い換えれば、「Applicative
構成の下で閉じられMonad
ていない」。
applicativeA1
とA2
がある場合、型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
関数は存在しません。
残念ながら、私たちの真の目標であるモナドの合成は、かなり難しいものです。.. 実際、ある意味で、2 つのモナドの演算のみを使用して上記の型の結合関数を構築する方法がないことを実際に証明できます (証明の概要については、付録を参照してください)。したがって、合成を形成する唯一の方法は、2 つのコンポーネントをリンクするいくつかの追加の構造がある場合です。