HXT:如何使用箭头的输出作为函数参数?

时间:2015-04-22 17:44:32

标签: xml haskell hxt

很难对这个问题给出好的标题......我坚持使用HXT 再次。我明白我想做什么,但我不确定如何让它发挥 很好地用箭头。在这里,我简要介绍了问题。

功能foo需要Int并返回箭头:

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

函数bar提取某些属性的值:

bar :: ArrowXml a => a XmlTree String

现在,我需要编写baz,其中包含从StringInt s的地图 返回一个箭头:

import qualified Data.Map.Lazy as M

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

baz的逻辑:使用bar提取属性的值并查找 地图。如果M.lookup返回Just x,请调用foo x,否则请勿执行此操作 任何东西(箭头的输入都没有变化)。

AFAIK每个这样的箭头都可以作为过滤器使用,所以实际上ArrowXml a => a XmlTree String类型意味着需要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

不仅编译器不喜欢它,而且也很难推理。

2 个答案:

答案 0 :(得分: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,它应该期待来自XmlTree的{​​{1}}和String输入,因此它的箭头类型需要为{{} 1}},在这里我发现最简单的实现它

bar

所有这一箭头正在做的是将a (String, XmlTree) something转换为传入baz :: ArrowXml a => M.Map String Int -> a (String, XmlTree) (Maybe Int, XmlTree) baz m = first $ arr $ flip M.lookup m 的查找(假设这已在一般环境中给出)。然后,当且仅当StringM.Map时,我们需要一个包装器将(Maybe Int, XmlTree)提供给(Int, XmlTree)。这里是箭头语法真正派上用场的地方。由于我们在此处做出决定,因此我们的箭头也需要Maybe Int,所以

Just something

现在我们可以将所有内容组合成一个整体应用程序,只需内置组合器(我也发现ArrowChoice,所以你可以使用它,我只是认为它更容易了解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) 。虽然这不是太糟糕,但我并不能完全称之为可读性,并且有太多与实际业务逻辑无关的事情。一旦你转向更复杂的情况,这也会在复杂性方面爆炸,而|||符号应该保持相对简单。

答案 1 :(得分: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 Transform external references into absolute references