59

矢印はほとんどのFRP実装の基礎であるため、矢印を把握しようとしています。私は基本的な考え方を理解していると思います-それらはモナドに関連していますが、各バインド演算子に静的情報を保存するため、矢印のチェーンをたどって、矢印全体を評価することなく静的情報を見ることができます。

しかし、1 番目、2 番目、およびスワップについて話し始めた時点で、私は迷子になります。2-タプルは矢印と何の関係がありますか? チュートリアルでは、タプルをあたかも明らかな次のステップであるかのように提示していますが、実際には関連性がわかりません。

さらに言えば、矢印構文は直感的に何を意味するのでしょうか?

4

1 に答える 1

51

http://www.cs.yale.edu/homes/hudak/CS429F04/AFPLectureNotes.pdfをご覧ください。FRP で矢印がどのように機能するかが説明されています。

2 タプルは、2 つの引数を取る矢印化された関数を表す必要があるため、矢印の定義に使用されます。

FRP では、定数と変数は多くの場合、その「入力」を無視する矢印として表されます。

twelve, eleven :: Arrow f => f p Int
twelve = arr (const 12)
eleven = arr (const 11)

次に、関数の適用はコンポジション ( >>>)に変換されます。

# (6-) 12

arr (6-) <<< twelve

では、引数が 2 つの関数をどのように矢印に変えるのでしょうか? 例えば

(+) :: Num a => a -> a -> a

カリー化により、これを関数を返す関数として扱う場合があります。そう

arr (+) :: (Arrow f, Num a) => f a (a -> a)

これを定数に適用してみましょう

arr (+)             -- # f     a (a -> a)
  <<< twelve        -- # f b Int
                      :: f b     (Int -> Int)

+----------+      +-----+      +--------------+
| const 12 |----> | (+) |  ==  | const (+ 12) |
+----------+      +-----+      +--------------+

ちょっと待って、うまくいきません。結果は依然として関数を返す矢印ですが、 に似たものを期待していf Int Intます。Arrow では合成のみが許可されているため、カリー化が失敗していることに気付きました。したがって、最初に関数をアンカリー化する必要があります

uncurry :: (a -> b -> c) -> ((a, b) -> c)

uncurry (+) :: Num a => (a, a) -> a

次に、矢印があります

(arr.uncurry) (+) :: (Num a, Arrow f) => f (a, a) a

このため、2タプルが発生します。&&&次に、これらの 2 タプルを処理するために、次のような束関数が必要です。

(&&&) :: f a b -> f a d -> f a (b, d)

その後、追加を正しく実行できます。

(arr.uncurry) (+)        -- # f   (a,    a) a
  <<<     twelve         -- # f b  Int
      &&& eleven         -- # f b      Int
                           :: f b           a

+--------+
|const 12|-----.
+--------+     |       +-----+      +----------+
              &&&====> | (+) |  ==  | const 23 |
+--------+     |       +-----+      +----------+
|const 11|-----'
+--------+

&&&&(さて、 3 つの引数を持つ関数の 3 タプルのようなものが必要ではないのはなぜですか?((a,b),c)代わりに a を使用できるためです。)


編集: John Hughes の元の論文Generalising Monads to Arrowsから、理由を次のように述べています。

4.1 アローとペア

しかし、モナドの場合、有用なコードを書き始めるために必要なのは演算子returnとだけですが、アローの場合、類似の演算子とでは十分ではありません。先ほど見た単純なモナド加算関数でさえ>>=arr>>>

   add :: Monad m => m Int -> m Int -> m Int
   add x y = x >>= \u -> (y >>= \v -> return (u + v))

まだ矢印で表現できません。入力への依存を明示的にすると、類似の定義は次の形式を取る必要があることがわかります。

   add :: Arrow a => a b Int -> a b Int -> a b Int
   add f g = ...

順番にf組み合わせる必要がある場所。g使用可能な唯一の順序付け演算子は>>>, であり、構成する適切な型がfありgません。実際、add関数は、同じ入力を に供給できるように、の計算全体で typeの入力を保存する必要があります。同様に、 の結果はの計算全体で保存する必要があります。これにより、最終的に 2 つの結果を加算して返すことができます。これまでに紹介したアロー コンビネータでは、別の計算で値を保存する方法がありません。そのため、別のコンビネータを導入する以外に方法はありません。bfgfg

于 2010-07-01T08:01:49.830 に答える