¿Ejecutar Haskell HXT fuera de IO?
(2)
Todos los ejemplos que he visto hasta ahora utilizando el kit de herramientas de Haskell XML, HXT, utilizan runX
para ejecutar el analizador. runX
corre dentro de la mónada IO. ¿Hay alguna forma de usar este analizador XML fuera de IO? Me parece que es una operación pura, no entiendo por qué me veo obligado a estar dentro de IO.
La respuesta de Travis Brown fue muy útil. Solo quiero agregar mi propia solución aquí, que creo que es un poco más general (usando las mismas funciones, simplemente ignorando los problemas específicos del problema).
Anteriormente me estaba deshaciendo con:
upIO :: XmlPickler a => String -> IO [a]
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)
que pude cambiar a esto:
upPure :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str
Estoy completamente de acuerdo con él en que hacer esto te da menos control sobre la configuración del analizador, etc., lo cual es desafortunado.
Puede usar el xread de xread
junto con runLA
para analizar una cadena XML fuera de IO
.
xread
tiene el siguiente tipo:
xread :: ArrowXml a => a String XmlTree
Esto significa que puede componerlo con cualquier flecha de tipo (ArrowXml a) => a XmlTree Whatever
para obtener a String Whatever
.
runLA
es como runX
, pero para cosas de 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 para analizar una cadena que contiene XML bien formado sin ningún IO
involucrado:
{-# 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 cambios menores para adaptarse a la entrada y salida esperadas. La principal diferencia es que aquí usamos xread
y runLA
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é, esto no es posible actualmente con HXT.
Algunas otras cosas: puede analizar cadenas de esta manera sin IO
, pero probablemente sea mejor usar runX
siempre que pueda: le da más control sobre la configuración del analizador, mensajes de error, etc.
También: traté de hacer que el código en el ejemplo fuera sencillo y fácil de extender, pero los combinadores en Control.Arrow
y Control.Arrow.ArrowList
hacen que trabajar con flechas sea mucho más conciso si lo desea. La siguiente es una definición equivalente de classes
, por ejemplo:
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText