这个问题是关于HXT的,但我想它适用于概念
ArrowPlus
一般而言。请考虑以下程序:
module Main (main) where
import Text.XML.HXT.Core
import Control.Monad (void)
main :: IO ()
main = void $ runX $ root [] [foo]
>>> writeDocument [withIndent yes] "test.xml"
foo :: ArrowXml a => a XmlTree XmlTree
foo = selem "foo" [bar >>> bar >>> bar]
bar :: ArrowXml a => a XmlTree XmlTree
bar = this <+> eelem "bar"
你能告诉我test.xml
会保存什么吗?我的期望:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar/>
<bar/>
<bar/>
</foo>
我的逻辑:箭头bar
复制其所有输入并添加一个“bar”元素
(this
是标识箭头的别名):
| |
this eelem "bar"
| |
\ /
\ /
<+>
|
因此,bar >>> bar >>> bar
的结果应该是三个'bar'元素(注意
那个eelem "bar" >>> eelem "bar"
只会产生一个'bar'元素,
因为mkelem
家族的箭头忽略了他们的输入(尽管它仍然可以
用于生成其内容)并仅输出新创建的元素。)
说了这么多,我在执行后提出了test.xml
的内容
该计划:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<//>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
</foo>
问题:
什么是<//>
?
为什么有7个'bar'元素而不是3个?这是什么原因 重复?
为什么当我用bar >>> bar >>> bar
替换none >>> bar >>> bar >>>
bar
时,我得到了:
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
其中none
为零箭头。我们在这里处理箭头上的幺半群,对吧?
none
(≡zeroArrow
)应该是它的身份,所以它应该是这样的:
none <+> eelem "bar"
生成'bar'元素,然后生成
调用应该添加另外两个元素。但我们没有得到任何!
bar
箭头的正确版本,以添加一个“条形图”
元素一次?很抱歉提出4个问题,但我猜它们是密切相关的,所以它 应该不是问题。
答案 0 :(得分:2)
您似乎对>>>
和<+>
运算符的工作原理感到困惑。为了建立直觉,让我们首先定义两个不同的bar
:
bar1 :: ArrowXml a => a XmlTree XmlTree
bar1 = this <+> eelem "bar"
bar2 :: ArrowXml a => a n XmlTree
bar2 = eelem "bar"
我们注意到的第一件事是类型签名。 bar1
的输入类型为XmlTree
,这意味着它以某种方式修改现有树,而bar2
则丢弃其参数。这是因为在this
中使用bar1
来复制其元素。现在,让我们在ghci
中加载这些内容,以确定>>>
和<+>
如何协同工作:
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2
["<bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2 >>> bar2 >>> bar2
["<bar/>"]
嗯,这很奇怪,无论我们用>>>
组合多少次,它都会不断创建相同的结构。这是因为对于bar2
,我们每次转换时都会丢弃树:请记住它的类型签名是a n XmlTree
而不是a XmlTree XmlTree
。我们将其与bar1
进行比较:
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1
["<//><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> bar1
["<//><bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> bar1 >>> bar1
["<//><bar/><bar/><bar/><bar/><bar/><bar/><bar/>"]
哇,它成倍增长!为什么?好吧,每次你使用>>>
撰写时,你都会使用之前的树,以及应用函数应用程序this <+> eelem "bar"
的每个元素。 bar1
的第一次调用没有先前的树,因此this
成为根节点,您只需向其添加<bar/>
元素。但是,对于bar1 >>> bar1
,第一个bar1
将创建<//><bar/>
,第二个将<//><bar/>
的每个节点再次与bar1
组成,从而导致:
bar1 === <//><bar/>
bar1 >>> bar1 === <//><bar/><bar/><bar/>
|--------||----------|
First Second
现在你继续这样做了,你可以看到bar1 >>> bar1 >>> bar1
将如何生成七个<bar/>
s前面的<//>
。
好的,现在我们对>>>
的{{1}}有直觉,我们可以看到ArrowXml
的行为:
<+>
哦,那非常简单......他们只是一个接一个追加。我们可以看到Prelude Text.XML.HXT.Core> runX $ xshow $ bar2 <+> bar2 <+> bar2
["<bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 <+> bar1 <+> bar1
["<//><bar/><//><bar/><//><bar/>"]
的类型仍然是转换类型bar1 <+> bar1
的值的类型,因此如果将其与a XmlTree XmlTree
结合使用,您将获得一些疯狂的行为。看看你是否可以围绕这个输出思考:
>>>
要回答有关Prelude Text.XML.HXT.Core> runX $ xshow $ (bar1 <+> bar1) >>> bar1
["<//><bar/><bar/><bar/><//><bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> (bar1 <+> bar1)
["<//><bar/><//><bar/><bar/><bar/><bar/><bar/>"]
的问题,请检查其类型签名:
none
对我说,它需要Prelude Text.XML.HXT.Core> :t none
none :: ArrowList a => a b c
类型的值并返回类型b
的值。由于我们不知道c
是什么(我们没有将其作为c
的参数提供),我们可以假设它将成为空集。这与我们之前对none
和>>>
:
<+>
在第一种情况下,附加了另一个文档的空文档基本上是标识操作。在第二个中,Prelude Text.XML.HXT.Core> runX $ xshow $ none <+> bar1
["<//><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> bar1
[""]
不生成任何元素,因此当您使用none
撰写时,没有要操作的元素,因此结果是空文档。事实上,由于Haskell很懒惰,我们对bar1
组成的内容更加自负,因为我们知道它永远不会被评估:
none
鉴于这些知识,你可能想要做的是这样的事情:
Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> undefined
[""]
类似的解决方案是使用Prelude Text.XML.HXT.Core> let bar = eelem "bar"
Prelude Text.XML.HXT.Core> runX $ xshow $ selem "foo" [bar <+> bar <+> bar]
["<foo><bar/><bar/><bar/></foo>"]
运算符:
+=