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

标签 xml haskell hxt

很难给这个问题起一个好的标题……我被 HXT 困住了 再次。我明白我想做什么,但我不知道如何让它发挥作用 很好地带有箭头。这里我对问题进行简单描述。

函数 foo 接受一个 Int 并返回一个箭头:

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

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

bar :: ArrowXml a => a XmlTree String

现在,我需要编写 baz 来获取从 StringInt 的映射,并且 返回一个箭头:

import qualified Data.Map.Lazy as M

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

baz的逻辑:用bar提取属性值并在其中查找 map 。如果 M.lookup 返回 Just x,则调用 foo x,否则不执行 任何内容(箭头的输入保持不变)。

据我所知,每个这样的箭头都可以用作过滤器,因此实际上 ArrowXml a => a XmlTree String 类型意味着它需要一个 XmlTree 并返回(可能是 空)String 列表。这让我重新表述了 baz 的逻辑。为了 给定输入 XmlTree 可能有很多字符串,应该使用每个字符串 查找一个整数,第一个找到的整数应该传递给foo。如果 所有这些都会导致什么也没有,不要做任何事情。

这是我想出的:

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

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

最佳答案

如果你稍微重新表述你的类型签名,你可以让它很好地排列起来。因为您的值来自 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 ,它应该期待 XmlTreeString输入来自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

这个箭头所做的就是转换 String对传入的 M.Map 进行查找(假设这已经在一般环境中给出)。然后我们需要一个包装器来提供 (Maybe Int, XmlTree)进入(Int, XmlTree)当且仅当Maybe IntJust 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约束,所有ArrowXmlText.XML.HXT.Core 引入范围的实例还实现ArrowChoice .

如果你好奇如果没有 proc 会是什么样子符号,即使是这个简单的 case 语句也会变成(我认为)

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表示法应该保持相对简单。

关于xml - HXT:如何使用箭头的输出作为函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29805202/

相关文章:

c# - 使用 ConfigurationManager 加载 System.ServiceModel 配置部分

python - 文本小部件 Tkinter 中的行号间距

c# - 如何实现具有注入(inject)功能的通用结构层次结构

parsing - 左右递归解析器

haskell - 如何正确收集Hxt程序的命令行选项?

performance - 调试HXT性能问题

haskell - 避免在 haskell 中使用长元组定义

xml - 构建需要元素或属性但不能两者同时存在的 XML 模式

jquery - 如何使用ajax从另一个div(如iFrame)填充div中的xml文档?

haskell - 在可存储和拆箱之间转换