Ejecución de Haskell HXT fuera del IO?
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.
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.