Esecuzione di Haskell HXT al di fuori di IO?
Domanda
Tutti gli esempi che ho visto finora usando il toolkit XML Haskell, HXT, usi runX
per eseguire il parser. corre runX
all'interno del IO monade. C'è un modo di utilizzare questo parser XML al di fuori di IO? Sembra essere un'operazione di puro a me, non capisco il motivo per cui sono costretto a essere dentro IO.
Soluzione
È possibile utilizzare di HXT xread
insieme a runLA
per analizzare un esterno stringa XML di IO
.
xread
ha il tipo seguente:
xread :: ArrowXml a => a String XmlTree
Questo significa che è possibile comporre con qualsiasi freccia di tipo (ArrowXml a) => a XmlTree Whatever
per ottenere un a String Whatever
.
runLA
è come runX
, ma per le cose di tipo LA
:
runLA :: LA a b -> a -> [b]
LA
è un'istanza di ArrowXml
.
Per mettere tutto questo insieme, la seguente versione di la mia risposta alla tua domanda precedente utilizza HXT per analizzare una stringa contenente XML ben formato senza alcuna IO
coinvolti:
{-# 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
e getValues
sono simili alla versione precedente, con qualche piccola modifica per soddisfare l'input e l'output previsto. La differenza principale è che qui usiamo xread
e runLA
invece di readString
e runX
.
Sarebbe bello per essere in grado di leggere qualcosa di simile a un ByteString
pigro in modo simile, ma per quanto ne so questo non è attualmente possibile con HXT.
Un paio di altre cose: si possono stringhe parse in questo modo senza IO
, ma probabilmente è meglio usare runX
ogni volta che è possibile: ti dà più controllo sulla configurazione del parser, errore messaggi, ecc.
Inoltre: ho cercato di rendere il codice nell'esempio semplice e facile da estendere, ma i combinatori di Control.Arrow
e Control.Arrow.ArrowList
permettono di lavorare con le frecce molto più conciso, se vi piace. La seguente è una definizione equivalente di classes
, ad esempio:
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
Altri suggerimenti
La risposta di Travis Brown è stato molto utile. Voglio solo aggiungere la mia soluzione qui, che credo sia un po 'più generale (con le stesse funzioni, solo ignorando i problemi problema-specifiche).
Sono stato in precedenza deserializzazione con:
upIO :: XmlPickler a => String -> IO [a]
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)
che sono stato in grado di cambiare a questo:
upPure :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str
Sono completamente d'accordo con lui che facendo questo ti dà meno controllo sulla configurazione del parser ecc, che è un peccato.