21

Haskell コミュニティではアローの人気が高まっているようですが、モナドの方が強力なように思えます。矢を使うと何が得られますか? 代わりにモナドを使用できないのはなぜですか?

4

5 に答える 5

11

この問題を、矢を使うことで何が得られるかという言葉で考えるのは難しいといつも思っていました。他のコメンテーターが言及したように、すべてのモナドは自明に矢印に変えることができます。したがって、モナドはすべての矢印のようなことを行うことができます。しかし、モナドではない Arrow を作ることができます。つまり、モナド結合をサポートしなくても、これらの矢印のようなことを実行できる型を作成できます。そうではないように思えるかもしれませんが、モナドのバインド関数は実際にはかなり制限的な (したがって強力な) 操作であり、多くの型を不適格とします。

bind をサポートするには、入力の型に関係なく、出力されるものがモナドにラップされることをアサートできなければなりません。

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

しかし、data Foo a = F Bool a確かに Foo の a と別の Foo を組み合わせることができますが、Bools をどのように組み合わせますか。Bool が、たとえば、他のパラメーターの値が変更されたかどうかをマークしたと想像してください。私が持っていてa = Foo False whateverそれを関数にバインドすると、その関数が変更されるかどうかわかりませんwhatever。Bool を正しく設定するバインドを記述できません。これは、しばしば静的メタ情報の問題と呼ばれます。バインドされている関数を調べて、変更されるかどうかを判断することはできませんwhatever

このようなケースは他にもいくつかあります: 変化する関数を表す型、早期に終了できるパーサーなどです。しかし基本的な考え方は次のとおりです: モナドは、すべての型がクリアできるわけではない高い基準を設定します。アローを使用すると、バインドを満たさなくても強力な方法で型 (この高度なバインド標準をサポートできる場合とサポートできない場合があります) を構成できます。もちろん、モナドの力の一部は失われます。

話の教訓: モナドはいつでも矢にすることができるので、矢にできてモナドにできないことは何もない。ただし、型をモナドにすることはできなくても、構成上の柔軟性とモナドのパワーのほとんどを持たせたい場合があります。

これらのアイデアの多くは、優れたHaskell Arrows の理解(バックアップ)に触発されました。

于 2013-04-22T22:37:53.910 に答える
3

Arrowさて、質問を からに変更して、ここで少しごまかすつもりですApplicative。多くの同じ動機が当てはまり、私は矢よりも応用をよく知っています。(実際、everyArrowApplicativeですが、その逆ではありません。そのため、勾配を少し下って にしFunctorます。)

everyMonadが であるようにArrow、everyMonadApplicativeです。Applicativessでないもの(MonadなどZipList) があるので、それが 1 つの可能な答えです。

Monadしかし、インスタンスと.を受け入れる型を扱っていると仮定しますApplicativeApplicativeの代わりにインスタンスを使用することがあるのはなぜMonadですか? はそれほど強力ではないためApplicative、次の利点があります。

  1. MonadできることとできないことがわかっていることがApplicativeあります。たとえば、 のApplicativeインスタンスを使用しIOて単純なアクションから複合アクションを組み立てる場合、構成するアクションのいずれも、他のアクションの結果を使用することはできません。applicativeIOができることは、コンポーネント アクションを実行し、その結果を純粋な関数と結合することだけです。
  2. Applicativeアクションを実行する前にアクションの強力な静的分析を行うことができるように、型を記述することができます。したがって、Applicativeアクションを実行する前にそのアクションを検査し、そのアクションが何をするのかを把握し、それを使用してパフォーマンスを向上させたり、ユーザーに何を実行するかを伝えたりするプログラムを作成できます。

最初の例として、s を使用した一種のOLAP計算言語の設計に取り組んでいApplicativeます。型はMonadインスタンスを許可しますが、意図的にそれを避けるようにしました。これは、クエリが許可されるよりも強力ではないことを望んでいるためMonadです。 Applicative各計算が予測可能な数のクエリに底をつくことを意味します。

後者の例として、まだ開発中の運用Applicativeライブラリのおもちゃの例を使用します。Reader代わりにモナドを操作Applicativeプログラムとして書くと、結果の s を調べて、操作Readerを使用した回数を数えることができます。ask

{-# LANGUAGE GADTs, RankNTypes, ScopedTypeVariables #-}

import Control.Applicative.Operational

-- | A 'Reader' is an 'Applicative' program that uses the 'ReaderI' 
-- instruction set.
type Reader r a = ProgramAp (ReaderI r) a

-- | The only 'Reader' instruction is 'Ask', which requires both the
-- environment and result type to be @r@.
data ReaderI r a where
    Ask :: ReaderI r r

ask :: Reader r r
ask = singleton Ask

-- | We run a 'Reader' by translating each instruction in the instruction set
-- into an @r -> a@ function.  In the case of 'Ask' the translation is 'id'.
runReader :: forall r a. Reader r a -> r -> a
runReader = interpretAp evalI
    where evalI :: forall x. ReaderI r x -> r -> x
          evalI Ask = id

-- | Count how many times a 'Reader' uses the 'Ask' instruction.  The 'viewAp'
-- function translates a 'ProgramAp' into a syntax tree that we can inspect.
countAsk :: forall r a. Reader r a -> Int
countAsk = count . viewAp
    where count :: forall x. ProgramViewAp (ReaderI r) x -> Int
          -- Pure :: a -> ProgamViewAp instruction a
          count (Pure _) = 0
          -- (:<**>) :: instruction a 
          --         -> ProgramViewAp instruction (a -> b)
          --         -> ProgramViewAp instruction b
          count (Ask :<**> k) = succ (count k)

私が理解している限り、モナドとしてcountAsk実装すると書くことはできません。Reader(私の理解は、ここ Stack Overflow で質問することから得られます。追加します。)

これと同じ動機が、実際にはArrows の背後にあるアイデアの 1 つです。の大きな動機付けの例の 1 つは、Arrow"静的情報" を使用してモナド パーサーよりも優れたパフォーマンスを得るパーサー コンビネーターの設計です。「静的情報」が意味することは、私の例とほぼ同じです。私の例と同じように、パーサーを検査できるインスタンスReaderを作成することができます。次に、解析ライブラリは、パーサーを実行する前に、パーサーを検査して、失敗することを事前に予測できるかどうかを確認し、その場合はスキップします。ArrowReader

あなたの質問への直接のコメントの 1 つで、jberryman は、矢印が実際には人気を失っている可能性があると述べています。私が見ているように、それApplicativeは矢が人気を失っているものです.


参考文献:

于 2013-04-23T01:05:50.113 に答える