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