Applicativesは作成しますが、モナドは作成しません。
上記の文はどういう意味ですか?そして、いつ他の方が好ましいですか?
タイプを比較すると
_(<*>) :: 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
_
ab
の値を使用して、の値 2つの計算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
を取り除く必要があります。アレクサンドル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
_
レイヤー間の干渉がないためです。
それに応じて、Monad
sの追加のパワーが本当に必要なとき、およびApplicative
がサポートする厳密な計算構造を使用して逃げることができるときを認識するのは良いことです。
ちなみに、モナドの作成は難しいものの、必要以上のものになるかもしれないことに注意してください。タイプm (n v)
は、m
- effectsで計算し、次にn
- effectsでv
- valuesに計算し、m
- effectsが終了することを示します。 n
-エフェクトが開始する前(したがって、swap
が必要です)。 m
- effectsをn
- effectsとインターリーブしたいだけの場合、構成はおそらく多すぎます!
応用者は作曲しますが、モナドは作曲しません。
モナドdo composeですが、結果はモナドではないかもしれません。対照的に、2人のアプリカティブの構成は必然的にアプリカティブです。元の声明の意図は、「適用性は構成するが、モナドネスは構成しない」ことだったと思う。言い換えると、「Applicative
は合成中に閉じられ、Monad
は閉じられません。」
アプリカティブ_A1
_および_A2
_がある場合、タイプdata A3 a = A3 (A1 (A2 a))
もアプリカティブです(そのようなインスタンスを一般的な方法で記述できます)。
一方、モナド_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つのコンポーネントをリンクする追加の構造が存在する場合にのみ、コンポジションを作成することができます。
分布法則解l:MN-> NMで十分です
nMの単一性を保証します。これを見るには、ユニットとマルチが必要です。マルチに焦点を当てます(単位はunit_N unitMです)
NMNM - l -> NNMM - mult_N mult_M -> NM
これは、MNがモナドであることをnot保証しません。
しかし、決定的な観察は、分配法の解決策があるときに作用します
l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN
したがって、LM、LN、およびMNはモナドです。 LMNがモナドか(
(MN)L-> L(MN)またはby N(LM)->(LM)N
これらのマップを作成するのに十分な構造があります。ただし、 Eugenia Chengが観察する のように、いずれかの構造の単進性を保証するために、六角形の条件(Yang-Baxter方程式の表示に相当)が必要です。実際、六角形の条件では、2つの異なるモナドが一致します。