Все примеры, которые я видел до сих пор с использованием инструментария Haskell XML, HXT, используют runX
для выполнения синтаксического анализатора. runX
работает внутри монады IO. Есть ли способ использовать этот синтаксический анализатор XML вне IO? Мне кажется, это чистая операция, не понимаю, почему я вынужден быть внутри IO.
Запуск Haskell HXT вне IO?
Ответы (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
Ответ Трэвиса Брауна был очень полезен. Я просто хочу добавить сюда свое собственное решение, которое, на мой взгляд, является более общим (используя те же функции, просто игнорируя проблемы, связанные с конкретной проблемой).
Ранее я распаковывал с помощью:
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
Я полностью согласен с ним в том, что это дает вам меньше контроля над конфигурацией парсера и т. д., что очень жаль.
runX
читает файл XML и, таким образом, является нечистым вводом-выводом. - person alternative   schedule 10.10.2010