17

私は Haskell で計算がどのようにモデル化されているかにかなり興味を持っています。いくつかのリソースでは、モナドを「構成可能な計算」、アローを「計算の抽象ビュー」と説明しています。このように記述されたモノイド、ファンクター、アプリカティブ ファンクターは見たことがありません。必要な構造が欠けているようです。

私はそのアイデアが面白いと思っており、似たようなことをする他の構成要素があるかどうか疑問に思っています. もしそうなら、それらを知るために使用できるリソースは何ですか? 便利な Hackage のパッケージはありますか?

注:この質問は Monads vs. Arrowsおよびhttps://stackoverflow.com/questions/2395715/resources-for-learning-monads-functors-monoids-arrows-etcに似ていますが、ファンターを超えた構造、適用可能なものを探していますファンクター、モナド、アロー。

編集: Applicative Functor は「計算構造」と見なされるべきであることは認めますが、まだ出会っていないものを本当に探しています。これには、アプリカティブ ファンクター、モナド、アローが含まれます。

4

3 に答える 3

25

Arrowsカテゴリによって一般化され、型クラスによって一般化されCategoryます。

 class Category f where
     (.) :: f a b -> f b c -> f a c
     id :: f a a

Arrow型クラス定義はスーパーCategoryクラスとして持っています。カテゴリ (haskell の意味で) は関数を一般化します (作成することはできますが、適用することはできません)。したがって、カテゴリは間違いなく「計算のモデル」です。 タプルを操作するための追加の構造をArrow提供します。Categoryつまり、CategoryHaskell の関数空間に関するものをミラー化しながら、Arrowそれをプロダクト タイプに関するものに拡張します。

EveryMonadは "Kleisli カテゴリ" と呼ばれるものを生成し、この構造により のインスタンスが得られますArrowApply。完全に一周しても行動が変わらないようにMonad、任意のものから を構築できます。したがって、深い意味では、 と は同じものです。ArrowApplyMonadArrowApply

 newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

 instance Monad m => Category (Kleisli m) where
     id = Kleisli return
     (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)

 instance Monad m => Arrow (Kleisli m) where
     arr f = Kleisli (return . f)
     first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
     second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))

実際には、すべてArrowがスーパークラスApplicativeに加えて(種類を正しくするために普遍的に定量化された)を生成し、適切なとCategoryの組み合わせがあなたのを再構築するのに十分であると信じています. CategoryApplicativeArrow

ですから、これらの構造は深く結びついています。

警告: 先に気まぐれな解説があります。Functor/ Applicative/Monad考え方とCategory/考え方の中心的な違いの 1 つArrowは、 whileとその同類がオブジェクト(Haskell の型)Functorのレベルでの一般化であり、 /はの概念の一般化 ( Haskell の関数) であるということです。一般化された射のレベルでの思考は、一般化されたオブジェクトのレベルでの思考よりも高いレベルの抽象化を伴うと私は信じています。それが良いこともあれば、そうでないこともあります。一方で、カテゴリカルな根拠があり、数学の誰も考えていないという事実にもかかわらずCategoryArrowArrowsApplicative興味深いです。Applicative一般的に よりもよく理解されているのは私の理解ですArrow

基本的には「Category < Arrow < ArrowApply」「Functor < Applicative < Monad」「Category ~ Functor」「Arrow ~ Applicative」「ArrowApply ~ Monad」と考えることができます。

より具体的な以下: 計算をモデル化するための他の構造について: カテゴリ構造の「矢印」(ここでは射を意味するだけ) の方向を逆にして、「双対」または「共同構造」を得ることができます。したがって、モナドが次のように定義されている場合

class Functor m => Monad m where
   return :: a -> m a
   join :: m (m a) -> m a

(まあ、それは Haskell が物事を定義する方法ではないことはわかっていますがma >>= f = join $ fmap f majoin x = x >>= idそうである可能性もあります) その場合、コモナドは次のようになります。

class Functor m => Comonad m where
   extract :: m a -> a -- this is co-return
   duplicate :: m a -> m (m a) -- this is co-join

これもかなり一般的であることが判明しました。それがセルオートマトンComonadの基本的な構造であることがわかりました。完全を期すために、エドワード・クメットがファンクターと「拡張可能なファンクター」の間のクラスに入れていることを指摘する必要があります。Control.ComonadduplicateComonad

   extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
   extend f = fmap f . duplicate
   --this is enough
   duplicate = extend id

Monadすべての s も「拡張可能」であることが判明しました

   monadDuplicate :: Monad m => m a -> m (m a)
   monadDuplicate = return

すべてComonadsが「参加可能」ですが

   comonadJoin :: Comonad m => m (m a) -> m a
   comonadJoin = extract

したがって、これらの構造は非常に接近しています。

于 2012-03-09T23:43:03.183 に答える
9

すべてのモナドはアローです (モナドは ArrowApply と同形です)。別の方法では、すべてのモナドはApplicativeのインスタンスであり、<*>isControl.Monad.ap*>is>>です。Applicative は動作を保証しないため弱いです>>=。したがって、Applicative は、以前の結果を調べず、値で分岐する計算をキャプチャします。振り返ってみると、多くのモナド コードは実際には適用可能であり、きれいに書き直せば、これが発生します。

モナドの拡張、GHC 7.4.1 の最近の Constraint の種類により、制限されたモナドのより良い設計ができるようになりました。また、パラメータ化されたモナドを見ている人もいます。もちろん、私はOlegによる何かへのリンクを含めています。

于 2012-03-09T17:07:06.440 に答える
5

ライブラリでは、これらの構造によってさまざまな種類の計算が行われます。

たとえば、Applicatives を使用して静的効果を実装できます。つまり、フォアハンドで定義されるエフェクトを意味します。たとえば、ステート マシンを実装する場合、入力状態を拒否または受け入れます。入力に関して内部構造を操作するために使用することはできません。

タイプはそれをすべて言います:

 <*> :: f (a -> b) -> f a -> f b

f の構造が a の入力に依存することはありません。a は型レベルで f に到達できないためです。

モナドは動的効果に使用できます。これは、型シグネチャからも推論できます。

 >>= :: m a -> (a -> m b) -> m b

どうやってこれを見ることができますか?a は m と同じ「レベル」にあるためです。数学的には、2 段階のプロセスです。Bind は、fmap と join の 2 つの関数で構成されています。最初に fmap をモナド アクションと一緒に使用して、古い構造体に埋め込まれた新しい構造体を作成します。

fmap :: (a -> b) -> m a -> m b
f :: (a -> m b)
m :: m a
fmap f :: m a -> m (m b)
fmap f m :: m (m b)

Fmap は、入力値に基づいて新しい構造を作成できます。次に、join で構造を折りたたむので、入力に依存する方法でモナド計算内から構造を操作できます。

join :: m (m a) -> m a
join (fmap f m) :: m b

多くのモナドは join を使って実装する方が簡単です:

(>>=) = join . fmap 

これはモナドで可能です:

addCounter :: Int -> m Int () 

しかし、Applicative ではできませんが、Applicative (および任意のモナド) は次のようなことができます:

addOne :: m Int ()

矢印を使用すると、入力と出力のタイプをより詳細に制御できますが、私にとっては、実際にはアプリケーションに似ていると感じています。多分私はそれについて間違っています。

于 2012-03-10T14:43:51.890 に答える