HXT: лево-факторинг недетерминистые стрелки?
Вопрос
Я пытаюсь прийти к срокам с 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]
Таким образом, анализ - это небольшая функция помощника, которая применяет любую стрелкой, которую я даю в тривиальном документе 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 Wiki И так, как я понял это, это должен быть возможным делать то, что я хочу сделать это - а именно использовать &&&
Для того, чтобы соединить результаты двух вычислений.
Это тоже работает - но только в начале цепочки стрелки, а не в середине рупота, когда у меня уже есть некоторые результаты, которые я хочу сохранить «общий». У меня есть ощущение, что я просто не смогу обернуть голову вокруг разницы в идеях между нормальной функциональной композицией и ноуткой стрелкой. Я был бы очень признателен любым указателям! (Даже если это только для некоторой универсальной стрелкой, который идет немного более глубоко, чем на Haskell-Wiki.)
Спасибо!
Решение
Если вы преобразуете стрелку в (а затем из) детерминированной версии, это работает как ожидалось:
> 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
В любом случае (лучшие сообщения об ошибках и т. Д.).