很难给这个问题起一个好的标题……我被 HXT 困住了 再次。我明白我想做什么,但我不知道如何让它发挥作用 很好地带有箭头。这里我对问题进行简单描述。
函数 foo
接受一个 Int
并返回一个箭头:
foo :: ArrowXml a => Int -> a XmlTree XmlTree
函数bar
提取某些属性的值:
bar :: ArrowXml a => a XmlTree String
现在,我需要编写 baz
来获取从 String
到 Int
的映射,并且
返回一个箭头:
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
,它应该期待 XmlTree
和 String
输入来自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 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
会是什么样子符号,即使是这个简单的 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/