箭头一次添加一个元素

时间:2015-05-04 11:27:42

标签: xml haskell arrows hxt monoids

这个问题是关于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>

问题:

  1. 什么是<//>

  2. 为什么有7个'bar'元素而不是3个?这是什么原因 重复?

  3. 为什么当我用bar >>> bar >>> bar替换none >>> bar >>> bar >>> bar时,我得到了:

  4.    
       <?xml version="1.0" encoding="UTF-8"?>
       <foo/>
    

    其中none为零箭头。我们在这里处理箭头上的幺半群,对吧?    none(≡zeroArrow)应该是它的身份,所以它应该是这样的:    none <+> eelem "bar"生成'bar'元素,然后生成    调用应该添加另外两个元素。但我们没有得到任何!

    1. 如何编写bar箭头的正确版本,以添加一个“条形图” 元素一次?
    2. 很抱歉提出4个问题,但我猜它们是密切相关的,所以它 应该不是问题。

1 个答案:

答案 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>"] 运算符:

+=