Запуск Haskell HXT вне IO?

Все примеры, которые я видел до сих пор с использованием инструментария Haskell XML, HXT, используют runX для выполнения синтаксического анализатора. runX работает внутри монады IO. Есть ли способ использовать этот синтаксический анализатор XML вне IO? Мне кажется, это чистая операция, не понимаю, почему я вынужден быть внутри IO.


person Muchin    schedule 10.10.2010    source источник
comment
С первого взгляда мне кажется, что runX читает файл XML и, таким образом, является нечистым вводом-выводом.   -  person alternative    schedule 10.10.2010
comment
Я думаю, что синтаксический анализатор HXT является «онлайн» синтаксическим анализатором, т. е. ему не нужно читать весь ввод, чтобы начать производить вывод. Положительным моментом этого является то, что (по крайней мере, в принципе) он может работать с постоянным объемом памяти, недостатком является то, что он должен считывать фрагменты ввода по запросу, поэтому он должен быть в IO.   -  person Stephen Tetley    schedule 10.10.2010
comment
Но нельзя ли это легко решить с помощью ленивого ввода-вывода? Например, для высокопроизводительного синтаксического анализа XML прямо сейчас я использую Hexpat (для ByteString и ленивого синтаксического анализа SAX) и polyparse (Poly.Lazy). Я получаю постоянный объем памяти, быструю обработку и весь анализ выполняется в чистых функциях!   -  person Aleksandar Dimitrov    schedule 12.11.2010


Ответы (2)


Вы можете использовать ссылку HXT xread вместе с runLA для анализа строки XML за пределами IO.

xread имеет следующий тип:

xread :: ArrowXml a => a String XmlTree

Это означает, что вы можете скомпоновать его с любой стрелкой типа (ArrowXml a) => a XmlTree Whatever, чтобы получить a String Whatever.

runLA похож на runX, но для вещей типа LA:

runLA :: LA a b -> a -> [b]

LA является экземпляром ArrowXml.

Чтобы собрать все это вместе, следующая версия моего ответа на ваш предыдущий вопрос использует HXT для анализа строки, содержащей правильно сформированный XML, без участия IO:

{-# LANGUAGE Arrows #-}
module Main where

import qualified Data.Map as M
import Text.XML.HXT.Arrow

classes :: (ArrowXml a) => a XmlTree (M.Map String String)
classes = listA (divs >>> pairs) >>> arr M.fromList
  where
    divs = getChildren >>> hasName "div"
    pairs = proc div -> do
      cls <- getAttrValue "class" -< div
      val <- deep getText         -< div
      returnA -< (cls, val)

getValues :: (ArrowXml a) => [String] -> a XmlTree (String, Maybe String)
getValues cs = classes >>> arr (zip cs . lookupValues cs) >>> unlistA
  where lookupValues cs m = map (flip M.lookup m) cs

xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\
      \<div class='c3'>123</div><div class='c4'>234</div></div>"

values :: [(String, Maybe String)]
values = runLA (xread >>> getValues ["c1", "c2", "c3", "c4"]) xml

main = print values

classes и getValues аналогичны предыдущей версии с небольшими изменениями, чтобы соответствовать ожидаемому вводу и выводу. Основное отличие состоит в том, что здесь мы используем xread и runLA вместо readString и runX.

Было бы неплохо иметь возможность читать что-то вроде ленивого ByteString подобным образом, но, насколько я знаю, в настоящее время это невозможно с HXT.


Еще пара вещей: вы можете анализировать строки таким образом без IO, но, вероятно, лучше использовать runX всякий раз, когда вы можете: это дает вам больше контроля над конфигурацией анализатора, сообщениями об ошибках и т. д. .

Кроме того: я пытался сделать код в примере простым и легко расширяемым, но комбинаторы в Control.Arrow и Control.Arrow.ArrowList позволяют работать со стрелками гораздо более лаконично, если хотите. Ниже приводится эквивалентное определение classes, например:

classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
  where pairs = getAttrValue "class" &&& deep getText
person Travis Brown    schedule 10.10.2010
comment
Привет Трэвис. Замечательно. Ваша помощь очень полезна в моей попытке разобраться с HXT. - person Muchin; 11.10.2010

Ответ Трэвиса Брауна был очень полезен. Я просто хочу добавить сюда свое собственное решение, которое, на мой взгляд, является более общим (используя те же функции, просто игнорируя проблемы, связанные с конкретной проблемой).

Ранее я распаковывал с помощью:

upIO      :: XmlPickler a => String -> IO [a]
upIO str   = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)

который я смог изменить на это:

upPure    :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str

Я полностью согласен с ним в том, что это дает вам меньше контроля над конфигурацией парсера и т. д., что очень жаль.

person John J. Camilleri    schedule 21.10.2014