HXT: левый фактор-фактор недетерминированных стрелок? - PullRequest
7 голосов
/ 19 ноября 2010

Я пытаюсь смириться с XML Toolbox на Haskell ( HXT ) и где-то бью стену, потому что мне кажется, что я не совсем понимаю стрелки как вычислительный инструмент.

Вот моя проблема, которую я надеялся проиллюстрировать немного лучше, используя сеанс GHCi:

> let parse p = runLA (xread >>> p) "<root><a>foo</a><b>bar</b><c>baz</c></root>"
> :t parse
parse :: LA XmlTree b -> [b]

Итак, Parse - это маленькая вспомогательная функция, которая применяет любую стрелку, которую я даю, к тривиальному XML-документу

<root>
  <a>foo</a>
  <b>bar</b>
  <c>baz</c>
</root>

Я определяю другую вспомогательную функцию, на этот раз для извлечения текста под узлом с заданным именем:

> let extract s = getChildren >>> isElem >>> hasName s >>> getChildren >>> getText 
> :t extract
extract :: (ArrowXml cat) =>
   String -> cat (Data.Tree.NTree.TypeDefs.NTree XNode) String
> parse (extract "a" &&& extract "b") -- extract two nodes' content.
[("foo","bar")]

С помощью этой функции легко использовать комбинатор &&& для объединения текста двух разных узлов, а затем, скажем, передать его конструктору, например так:

> parse (extract "a" &&& extract "b" >>^ arr (\(a,b) -> (b,a))) 
[("bar","foo")]

Теперь приходит часть, которую я не понимаю: я хочу уйти! extract дважды вызывает getChildren на корневом узле. Вместо этого я бы хотел, чтобы он назывался только один раз! Поэтому я сначала получаю дочерний узел корневого узла

> let extract' s = hasName s >>> getChildren >>> getText
> :t extract'
extract' :: (ArrowXml cat) => String -> cat XmlTree String
> parse (getChildren >>> isElem >>> (extract' "a" &&& extract' "b"))
[]

Обратите внимание, что я попытался изменить порядок вызовов, скажем, isElem и т. Д., Чтобы выяснить, в этом ли проблема. Но в настоящее время я просто не знаю, почему это не работает. На вики Haskell есть стрелка 'tutorial' , и, как я понял, должно иметь возможность делать то, что я хочу, - а именно использовать &&& в Для того чтобы объединить результаты двух вычислений.

Это тоже работает - но только в начале цепочки стрелок, а не в середине желоба, когда у меня уже есть какие-то результаты, которые я хочу оставить «общими». У меня такое ощущение, что я просто не в состоянии обернуть голову в разницу между идеями о нормальной композиции функций и обозначениях стрелок. Я был бы очень благодарен за любые указатели! (Даже если это просто какое-то общее руководство по стрелкам, которое идет немного глубже, чем в вики на Haskell.)

Спасибо!

1 Ответ

2 голосов
/ 19 ноября 2010

Если вы преобразуете стрелку в (а затем из) в детерминированную версию, это будет работать, как и ожидалось:

> let extract' s = unlistA >>> hasName s >>> getChildren >>> getText
> parse (listA (getChildren >>> isElem) >>> (extract' "a" &&& extract' "b"))
[("foo","bar")]

Хотя это не совсем удовлетворительно, и я не могу вспомнить, почему (&&&) ведет себя так с недетерминированной стрелкой (я бы лично использовал обозначение proc/do за что-то гораздо более сложное, чем это).


ОБНОВЛЕНИЕ: Кажется, здесь что-то странное происходит с runLA и xread. Если вы используете runX и readString, все работает как положено:

> let xml = "<root><a>foo</a><b>bar</b><c>baz</c></root>"
> let parse p = runX (readString [] xml >>> p)
> let extract' s = getChildren >>> hasName s >>> getChildren >>> getText
> parse (getChildren >>> isElem >>> (extract' "a" &&& extract' "b"))
[("foo","bar")]

Это означает, что вы должны запускать анализатор в монаде IO, но есть преимущества использования runX в любом случае (улучшенные сообщения об ошибках и т. Д.).

...