很难对这个问题给出好的标题......我坚持使用HXT 再次。我明白我想做什么,但我不确定如何让它发挥 很好地用箭头。在这里,我简要介绍了问题。
功能foo
需要Int
并返回箭头:
foo :: ArrowXml a => Int -> a XmlTree XmlTree
函数bar
提取某些属性的值:
bar :: ArrowXml a => a XmlTree String
现在,我需要编写baz
,其中包含从String
到Int
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
不仅编译器不喜欢它,而且也很难推理。
答案 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
的查找(假设这已在一般环境中给出)。然后,当且仅当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
迫使其实施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。