Pregunta

Todos los ejemplos que he visto hasta ahora usando el kit de herramientas XML Haskell, HXT, usos runX para ejecutar el programa de análisis. runX se ejecuta dentro de la mónada IO. ¿Hay una manera de utilizar este XML analizador fuera de IO? Parece ser una operación pura para mí, no entiendo por qué estoy obligado a estar dentro de IO.

¿Fue útil?

Solución

Puede utilizar de HXT xread junto con runLA para analizar una cadena XML fuera de IO.

xread tiene el tipo siguiente:

xread :: ArrowXml a => a String XmlTree

Esto significa que puede componer con cualquier tipo de flecha (ArrowXml a) => a XmlTree Whatever para tener una a String Whatever.

runLA es como runX, pero por cosas del tipo LA:

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

LA es una instancia de ArrowXml.

Para poner todo esto junto, la siguiente versión de mi respuesta a su pregunta anterior utiliza HXT a analizar una cadena que contiene XML bien formado sin ninguna IO involucrados:

{-# 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 y getValues son similares a la versión anterior, con algunos pequeños cambios para adaptarse a la entrada y la salida esperada. La principal diferencia es que aquí usamos xread y runLA en lugar de readString y runX.

Sería bueno poder leer algo así como un ByteString perezoso de una manera similar, pero por lo que sé que esto no es posible en la actualidad con HXT.


Un par de otras cosas: puede cadenas de análisis sintáctico de esta manera sin IO, pero es probable que sea mejor uso runX siempre que pueda: le da más control sobre la configuración del analizador, el error mensajes, etc.

También: Me trató de hacer que el código del ejemplo sencillo y fácil de extender, pero los combinadores en Control.Arrow y Control.Arrow.ArrowList hacen posible el trabajo con flechas mucho más concisa si te gusta. La siguiente es una definición equivalente de classes, por ejemplo:

classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
  where pairs = getAttrValue "class" &&& deep getText

Otros consejos

La respuesta de Travis Brown fue muy útil. Sólo quiero añadir mi propia solución a este problema, que creo que es un poco más general (utilizando las mismas funciones, simplemente haciendo caso omiso de las cuestiones de problemas específicos).

Me deserialiación previamente con:

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

lo que yo era capaz de cambiar a la siguiente:

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

Estoy completamente de acuerdo con él en que esta forma tiene menos control sobre la configuración del analizador etc, lo cual es lamentable.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top