2

この質問に適切なタイトルを付けるのは非常に難しいです... 私は再び HXT に行き詰まっています。やりたいことは理解できますが、矢印でうまく機能させる方法がわかりません。ここでは、問題を簡単に説明します。

関数fooは を受け取り、Int矢印を返します。

foo :: ArrowXml a => Int -> a XmlTree XmlTree

関数barはいくつかの属性の値を抽出します:

bar :: ArrowXml a => a XmlTree String

ここで、 s からs へのbazマップを取り、矢印を返すように記述する必要があります。StringInt

import qualified Data.Map.Lazy as M

baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree

ロジックbaz: で属性の値を抽出barし、マップで検索します。return の場合M.lookupJust xinvoke foo x、それ以外の場合は何もしません (矢印の入力はそのまま通過します)。

私の知る限り、そのような矢印はすべてフィルターとして機能するため、実際にはArrowXml a => a XmlTree Stringtype は を取り、XmlTree(おそらく空の)Stringのリストを返すことを意味します。これにより、 のロジックを再定式化できますbaz。与えられた入力に対してXmlTree多くの文字列が存在する可能性があり、すべての文字列を使用して整数を検索し、最初に見つかった整数を に渡す必要がありますfoo。それらの結果がすべて の場合はNothing、何もしないでください。

ここで私が思いついたもの:

baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
baz m = this &&& (bar >>> arr (`M.lookup` m)) >>> arr (uncurry f)
    where f xml Nothing  = xml
          f xml (Just x) = foo x xml
-- compiler says:          ^^^ not so fast, boy

Could not deduce (ArrowXml (->)) arising from a use of ‘foo’
from the context (ArrowXml a)
  bound by the type signature for
             baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree

コンパイラが気に入らないだけでなく、推論も困難です。

4

2 に答える 2

2

型シグネチャを少し再定式化すると、これをうまく並べることができます。bazの動作に影響を与える矢印の結果から得られる値があるため、これらの値は、通常の引数としてではなく、矢印fooを使用して供給する必要があります。fooこれは実際には物事を大幅に簡素化しますが、 を作成し、決定自体を処理する を作成することをお勧めしfooますfooWrapper。あなたが持っている正しいタイプで

{-# LANGUAGE Arrows, NoMonomorphismRestriction #-}

import qualified Data.Map as M
import Control.Arrow.ArrowTree
import Text.XML.HXT.Core

foo :: ArrowXml a => a (Int, XmlTree) XmlTree
foo = undefined

bar :: ArrowXml a => a XmlTree String
bar = undefined

の場合、と からの入力のbaz両方を期待する必要があるため、矢印のタイプは である必要があります。ここでは、次のように実装するのが最も簡単であることがわかりました。XmlTreeStringbara (String, XmlTree) something

baz :: ArrowXml a => M.Map String Int -> a (String, XmlTree) (Maybe Int, XmlTree)
baz m = first $ arr $ flip M.lookup m

この矢印が行っているのはString、渡された をルックアップに変換することだけですM.Map(これが一般的な環境で既に与えられていると仮定します)。次に、が である場合にのみフィードするラッパーが必要(Maybe Int, XmlTree)です。ここで、アロー構文が本当に役に立ちます。ここで決定を下しているので、矢印が である必要もあります。(Int, XmlTree)Maybe IntJust somethingArrowChoice

fooWrapper :: (ArrowXml a, ArrowChoice a) => a (Maybe Int, XmlTree) XmlTree
fooWrapper = proc (lkup, tree) -> do
    case lkup of
        Nothing -> returnA -< tree
        Just v  -> foo -< (v, tree)

これで、組み込みのコンビネータだけですべてを全体的なアプリケーションに結び付けることができます ( も発見したreturnA = arr idので、代わりにそれを使用できます。 を使用した方が理解しやすいと思いますarr id)

program :: (ArrowXml a, ArrowChoice a) => M.Map String Int -> a XmlTree XmlTree
program m =
    bar &&& arr id >>> -- First split the input between bar and arr id
    baz m          >>> -- Feed this into baz m
    fooWrapper         -- Feed the lookup into fooWrapper so it can make the
                       -- decision on how to route the XmlTree

ArrowChoice制約について心配する必要はありません。ArrowXmlスコープ内に持ち込まれたすべてのインスタンスText.XML.HXT.Coreも実装しArrowChoiceます。

これが表記なしでどのように見えるかについて興味がある場合はproc、この単純なケースステートメントでさえ (私は思う)

fooWrapper :: (ArrowXml a, ArrowChoice a) => a (Maybe Int, XmlTree) XmlTree
fooWrapper =
    arr (\(lkup, tree) -> case lkup of
        Nothing -> Left tree
        Just v  -> Right (v, tree)) >>>
    (returnA ||| foo)

の使用は|||、実装を強制するものArrowChoiceです。これはそれほど悪くはありませんが、読みやすいとは言えず、実際のビジネス ロジックとはまったく関係のない処理が多すぎます。より複雑な状況に移ると、これも複雑さが爆発的に増加しますが、proc記法は比較的単純なままにする必要があります。

于 2015-04-22T19:42:26.050 に答える
1

これを達成する方法を理解するのに少し時間がかかりました。矢印のタイプがa (b, XmlTree) XmlTreeの場合、API の他の部分とタイプが競合するため、実際に使用できるからです。

より慣用的と思われる別の解決策を次に示します。

baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
baz m = maybe this foo $< (bar >>> arr (`M.lookup` m))

すべての魔法は ($<) 機能のために起こります。ドキュメントから:

入力から追加のパラメーターを使用して矢印のパラメーターを計算し、すべてのパラメーター値の矢印を入力に適用します

HXT Wiki のこのセクションも参照してください: 8.2 外部参照を絶対参照に変換します

于 2015-05-02T08:19:01.990 に答える