17

Arrowsのコンセプトで遊ぶためのおもちゃのコードをいくつか書きました。ステートフル関数の概念をエンコードするArrowを記述できるかどうかを確認したかったのですが、呼び出しごとに異なる値を与えます。

{-# LANGUAGE Arrows#-}
module StatefulFunc where

import Control.Category
import Control.Arrow

newtype StatefulFunc a b = SF { unSF :: a -> (StatefulFunc a b, b) }

idSF :: StatefulFunc a a
idSF = SF $ \a -> (idSF, a)

dotSF :: StatefulFunc b c -> StatefulFunc a b -> StatefulFunc a c
dotSF f g = SF $ \a -> 
    let (g', b) = unSF g a
        (f', c) = unSF f b
    in (dotSF f' g', c)

instance Category StatefulFunc where
  id = idSF
  (.) = dotSF

arrSF :: (a -> b) -> StatefulFunc a b
arrSF f = ret
  where ret = SF fun
        fun a = (ret, f a)

bothSF :: StatefulFunc a b -> StatefulFunc a' b' -> StatefulFunc (a, a') (b, b')
bothSF f g = SF $ \(a,a') ->
    let (f', b) = unSF f a
        (g', b') = unSF g a'
    in (bothSF f' g', (b, b'))

splitSF :: StatefulFunc a b -> StatefulFunc a b' -> StatefulFunc a (b, b')
splitSF f g = SF $ \a ->
    let (f', b) = unSF f a
        (g', b') = unSF g a
    in (splitSF f' g', (b, b'))

instance Arrow StatefulFunc where
  arr  = arrSF
  first = flip bothSF idSF
  second = bothSF idSF
  (***) = bothSF
  (&&&) = splitSF

eitherSF :: StatefulFunc a b -> StatefulFunc a' b' -> StatefulFunc (Either a a') (Either b b')
eitherSF f g = SF $ \e -> case e of
      Left a -> let (f', b) = unSF f a in (eitherSF f' g, Left b)
      Right a' -> let (g', b') = unSF g a' in (eitherSF f g', Right b')

mergeSF :: StatefulFunc a b -> StatefulFunc a' b -> StatefulFunc (Either a a') b
mergeSF f g = SF $ \e -> case e of 
      Left a -> let (f', b) = unSF f a in (mergeSF f' g, b)
      Right a' -> let (g', b) = unSF g a' in (mergeSF f g', b)

instance ArrowChoice StatefulFunc where
  left = flip eitherSF idSF
  right = eitherSF idSF
  (+++) = eitherSF
  (|||) = mergeSF

そのため、さまざまな型クラスの定義を確認した後(ArrowZeroがこれで機能するかどうか、またはどのように機能するかわからないため、スキップしました)、いくつかのヘルパー関数を定義しました。

evalSF :: (StatefulFunc a b) -> a -> b
evalSF f a = snd (unSF f a)

givenState :: s -> (s -> a -> (s, b)) -> StatefulFunc a b
givenState s f = SF $ \a -> let (s', b) = f s a in (givenState s' f, b)

そして、使用例を考え出しました

count :: StatefulFunc a Integer
count = givenState 1 $ \c _ -> (c+1, c)

countExample :: StatefulFunc a Integer
countExample = proc _ -> do
                  (count', one) <- count -< ()
                  (count'', two) <- count' -< ()
                  (count''', three) <- count'' -< ()
                  returnA -< three

ただし、コンパイルしようとするとcountExample、との「範囲外」エラーが発生します。これは、チュートリアルに戻って、いつ使用できるかを確認する必要があることを意味していると思います。とにかく本当に欲しいのはもっとcount'count''

countExample :: Integer
countExample =
  let (count', one) = unSF count ()
      (count'', two) = unSF count' ()
      (count''', three) = unSF count'' ()
  in three

しかし、それはちょっと厄介で、もう少し自然なことを望んでいました。

Arrowsがどのように機能するのか、そしてそれらがどのように使用されるのか、私がどのように誤解しているのか誰かが説明できますか?私が見逃しているArrowsの基本的な哲学はありますか?

4

1 に答える 1

33

矢印がどのように機能するか、およびそれらがどのように使用されるかについて、私がどのように誤解しているのかを誰か説明できますか? 私が見逃している Arrows の基本的な哲学はありますか?

Arrowあなたはこれを のように扱っているような印象を受けますMonad。これが「基本的な哲学」と見なされるかどうかはわかりませんが、重複する頻度が高いにもかかわらず、両者には大きな違いがあります。ある意味で、a を定義する鍵となるのMonadjoin関数です。ネストされた構造を 1 つのレイヤーに折りたたむ方法。これらは、再帰関数で新しいモナド層を作成したり、その内容に基づいて構造をjoin変更したりできるため、便利です。Functorしかし、これはMonads に関するものではないので、そのままにしておきます。

Arrow一方、の本質は、 関数 の一般化されたバージョンですCategory型クラスは、関数合成と恒等関数の一般化されたバージョンを定義しますが、型Arrowクラスは、通常の関数を に持ち上げる方法と、Arrow複数Arrowの引数をとる s を処理する方法を定義します (タプルの形式で-Arrows必ずしもそうであるとは限りません)。カレー!)。

Arrow最初の関数のように、基本的な方法で s を組み合わせる場合countExample、実際に行っていることは、複雑な関数合成のようなものです。--の定義を振り返ってください(.)。2 つのステートフル関数を取り、それらを 1 つのステートフル関数に接続し、状態変更の動作が自動的に処理されます。

したがって、あなたの主な問題countExampleは、それが言及 count'していることなどです。モナドdoで記法を使用するときに状態パラメータを明示的に渡す必要がないのと同じように、これはすべて舞台裏で行われます。State

さて、このproc表記法では大きな複合Arrowsを構築するだけなので、ステートフルな関数を実際に使用するには、モナドで実際に計算を実行するためにArrow必要な場合と同様に、構文の外で作業する必要があります。あなたの2番目はこれらの線に沿っていますが、専門的すぎます。一般的なケースでは、ステートフル関数は入力のストリームを出力のストリームにマップし、有限状態トランスデューサーにするため、おそらく入力値の遅延リストを取り、右折畳みを使用してそれらを出力値の遅延リストに変換しますを使用して、それぞれをトランスデューサに順番に供給します。runStateStatecountExamplerunStatefulFunctionunSF

例を見たい場合、arrowsパッケージには、使用した単純な関数の代わりに任意の in を除いて、 とほぼ同じものを定義するトランスフォーマーが含まれています。ArrowAutomatonStatefulFunctionArrow


Arrowああ、 s とMonads の関係を簡単に再検討します。

プレーンArrowsは「一次」関数のようなものだけです。前に言ったように、常にカリー化できるとは限らず、同様に、($)関数が関数を適用するのと同じ意味で常に「適用」できるとは限りません。実際に高次が必要な場合はArrows、型クラスArrowApplyで application を定義しますArrow。これにより、 に大きな力が追加されArrow、とりわけ、 が提供するのと同じ「入れ子構造を折りたたむ」機能が可能になり、任意Monadのインスタンスに対して一般的にインスタンスを定義できるようになります。MonadArrowApply

反対に、Monads は新しいモナド構造を作成する関数を結合できるためMonad m、 型の関数である「クライスリの矢」について話すことができますa -> m b。a の Kleisli 矢印は、非常に明白な方法でインスタンスをMonad与えることができます。Arrow

とクライスリの矢印以外ArrowApplyに、型クラス間に特に興味深い関係はありません。

于 2010-09-10T18:02:20.833 に答える