視点 A は少し奇妙です。一般的に言えば、抽象化は他の抽象化よりも強力で一般的ではありません。二人は対立している。「より多くの力」を持つということは、あなたが扱っているものの構造についてより多くのことを知ることを意味し、それはより多くの制限を意味します。極端な場合、作業しているタイプが正確にわかります。これは非常に強力です。任意の有効な関数を適用できます。一方で、これは一般的でもありません。この仮定で書かれたコードは、その型にのみ適用されます! 反対に、自分の型について何も知ることができません(たとえば、型変数を持っているa
)。これは非常に一般的で、すべてに適用されます。タイプしますが、何もするのに十分な情報がないため、まったく強力ではありません!
Functor
実際のコードに根ざした例は、との違いApplicative
です。ここでは、Functor
はより一般的です。厳密には、すべての型も aですが、その逆はないため、 Functor
s よりも多くの型が s です。ただし、より多くの構造を運ぶため、厳密にはより強力です。では、引数が 1 つの関数のみを型にマップできます。を使用すると、任意の数の引数の関数をマップできます。繰り返しますが、一方はより一般的で、もう一方はより強力です。Applicative
Applicative
Functor
Applicative
Functor
Applicative
それで、それはどれですか?アローはモナドよりも強力ですか、それともより一般的ですか? これは、ファンクタ、アプリケーション ファンクタ、モナドを比較するよりも難しい問題です。なぜなら、矢印は非常に異なる獣だからです。それらには別の種類さえあります: モナドなどには種類* -> *
があり、矢印には種類があり* -> * -> *
ます。幸いなことに、アローをアプリケーション ファンクター/モナドと識別できることが判明したため、実際にこの質問に有意義に答えることができます。アローはモナドよりも一般的であり、その結果、強力ではありません。モナドが与えられた場合、それから矢印を作成できますが、すべての矢印に対してモナドを作成することはできません。
基本的な考え方は次のとおりです。
instance Monad m => Category (a -> m b) where
id = return
(f . g) x = g x >>= f
instance Monad m => Arrow (a -> m b) where
arr f = return . f
first f (x, y) = f x >>= \ x' -> return (x', y)
ただし、 の矢印インスタンスがあるため、実際のコードa -> b
にラップa -> m b
する必要があります。newtype
これnewtype
はKlesli
( Klesli カテゴリのため) と呼ばれます。
ただし、その逆はできません。任意の から a を取得するための構造はありMonad
ません Arrow
。これはArrow
、モナドができるのに対し、計算はそれを流れる値に基づいてその構造を変更できないために発生します。これを回避する唯一の方法は、追加のプリミティブ関数を使用して矢印の抽象化に力を加えることです。これはまさに何をするかArrowApply
です。
矢印の>>>
演算子は.
関数の一般化であるため、同じ一般的な制限があります。>>=
一方、 は関数適用の一般化に似ています。タイプに注意してください。 の場合>>>
、両側が矢印です。の>>=
場合、最初の引数は値 ( m a
) で、2 番目の引数は関数です。さらに、 の結果は、 の結果が値>>>
である別の矢印>>=
です。アローには>>>
に相当する概念しかないため>>=
、一般にアローを引数に「適用」することはできません。アロー パイプラインを構築することしかできません。実際の適用/実行機能は、特定の矢印に固有である必要があります。一方、モナドは次の観点から定義されます。>>=
したがって、デフォルトでいくつかのアプリケーションの概念が付属しています。
ArrowApply
app
アプリケーションの一般的な概念である で矢印を拡張するだけです。通常の関数の適用を想像してみましょう:
apply :: (b -> c) -> b -> c
apply f x = f x
これをアンカリー化して取得できます:
apply :: ((b -> c), b) -> c
矢印が関数を一般化する方法は、基本的に->
は変数 ( a
) に置き換えることです。の両方のオカレンスをinfixapply
に置き換えて、これを行いましょう:->
a
apply :: (b `a` c, b) `a` c
apply
の最初のバージョンと同じ構造をまだ見ることができ`a`
ます->
。ここで、バッククォートを削除してa
プレフィックスを作成すると、次の署名が得られますapp
。
app :: a (a b c, b) c
ArrowApply
これで、矢印にアプリケーションの概念を追加する方法がわかります。これは、>>=
モナド (または、特に形状の関数a -> m b
) への適用の概念である への応用です。これは、アローからモナドを構築するのに十分な追加構造であるため、ArrowApply
に同型Monad
です。
なぜこれらを使用したいのでしょうか? 正直なところ、そうは思わないでしょう。アローはかなり過大評価されているので、モナドとアプリカティブ ファンクターに固執してください。