HXT:如何使用箭头的输出作为函数参数?
HXT: How to use output of an arrow as function argument?
很难给这个问题起个好标题……我被 HXT 困住了
再次。我明白我想做什么,但我不确定如何让它发挥作用
很好用箭头。这里我简单的描述一下问题。
函数 foo
接受一个 Int
和 returns 一个箭头:
foo :: ArrowXml a => Int -> a XmlTree XmlTree
函数bar
提取某些属性的值:
bar :: ArrowXml a => a XmlTree String
现在,我需要编写 baz
获取从 String
s 到 Int
s 的映射,并且
returns一个箭头:
import qualified Data.Map.Lazy as M
baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
baz
的逻辑:提取bar
属性的值并在
地图。如果M.lookup
returns Just x
,调用foo x
,否则不做
任何东西(箭头的输入不变)。
AFAIK 每个这样的箭头都用作过滤器,所以实际上 ArrowXml a => a
XmlTree String
类型意味着它需要 XmlTree
和 returns(可能
空)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
不仅编译器不喜欢它,而且也很难推理。
如果你稍微重新制定你的类型签名,你可以让它排列得很好。由于您的值来自 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
,它应该期待来自 bar
的 XmlTree
和 String
输入,所以它的箭头类型需要是 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
是 Just something
时,我们需要一个包装器将 (Maybe Int, XmlTree)
送入 (Int, XmlTree)
。这是箭头语法真正派上用场的地方。由于我们在这里做出决定,因此还要求我们的箭头是 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
约束,所有从 Text.XML.HXT.Core
引入范围的 ArrowXml
实例也实现了 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
符号应该保持相对简单。
我花了一些时间才明白如何做到这一点,因为当你
箭头的类型类似于 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))
所有的奇迹都是因为
($<)
功能。来自文档:
compute the parameter for an arrow with extra parameters from the input
and apply the arrow for all parameter values to the input
另请参阅 HXT Wiki 的这一部分:8.2 将外部引用转换为
绝对
参考文献.
很难给这个问题起个好标题……我被 HXT 困住了 再次。我明白我想做什么,但我不确定如何让它发挥作用 很好用箭头。这里我简单的描述一下问题。
函数 foo
接受一个 Int
和 returns 一个箭头:
foo :: ArrowXml a => Int -> a XmlTree XmlTree
函数bar
提取某些属性的值:
bar :: ArrowXml a => a XmlTree String
现在,我需要编写 baz
获取从 String
s 到 Int
s 的映射,并且
returns一个箭头:
import qualified Data.Map.Lazy as M
baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
baz
的逻辑:提取bar
属性的值并在
地图。如果M.lookup
returns Just x
,调用foo x
,否则不做
任何东西(箭头的输入不变)。
AFAIK 每个这样的箭头都用作过滤器,所以实际上 ArrowXml a => a
XmlTree String
类型意味着它需要 XmlTree
和 returns(可能
空)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
不仅编译器不喜欢它,而且也很难推理。
如果你稍微重新制定你的类型签名,你可以让它排列得很好。由于您的值来自 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
,它应该期待来自 bar
的 XmlTree
和 String
输入,所以它的箭头类型需要是 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
是 Just something
时,我们需要一个包装器将 (Maybe Int, XmlTree)
送入 (Int, XmlTree)
。这是箭头语法真正派上用场的地方。由于我们在这里做出决定,因此还要求我们的箭头是 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
约束,所有从 Text.XML.HXT.Core
引入范围的 ArrowXml
实例也实现了 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
符号应该保持相对简单。
我花了一些时间才明白如何做到这一点,因为当你
箭头的类型类似于 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))
所有的奇迹都是因为
($<)
功能。来自文档:
compute the parameter for an arrow with extra parameters from the input and apply the arrow for all parameter values to the input
另请参阅 HXT Wiki 的这一部分:8.2 将外部引用转换为 绝对 参考文献.