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

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

27 голосов
/ 10 октября 2010

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

Ответы [ 2 ]

28 голосов
/ 10 октября 2010

Вы можете использовать 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
1 голос
/ 21 октября 2014

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

Ранее я не справлялся с:

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

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

...