Tutti gli esempi che ho visto fino ad ora utilizzando il toolkit XML Haskell, HXT, utilizza runX
per eseguire il parser. runX
viene eseguito all'interno della monade IO. C'è un modo per utilizzare questo parser XML al di fuori di IO? Sembra essere una pura operazione per me, non capisco perché sono costretto a stare dentro IO.Esecuzione di Haskell HXT al di fuori dell'IO?
risposta
È possibile utilizzare HXT xread
insieme a runLA
per analizzare una stringa XML all'esterno di IO
.
xread
ha il seguente tipo:
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 cose del tipo: LA
runLA :: LA a b -> a -> [b]
LA
è un'istanza di ArrowXml
.
Per mettere tutto questo insieme, la seguente versione del my answer alla tua domanda precedente utilizza HXT ad 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 precedente versione, con alcune modifiche minori per soddisfare l'input e l'output previsti. La differenza principale è che qui usiamo xread
e runLA
anziché readString
e runX
.
Sarebbe bello poter leggere qualcosa come un pigro ByteString
in un modo simile, ma per quanto ne so questo non è attualmente possibile con HXT.
Un paio di altre cose: si possibile stringhe parse in questo modo senza IO
, ma probabilmente è meglio usare runX
ogni volta che è possibile: ti dà più controllo sulla configurazione del parser, messaggi di errore , ecc
anche: ho cercato di rendere il codice nell'esempio semplice e facile da estendere, ma i combinatori in Control.Arrow
e Control.Arrow.ArrowList
permettono di lavorare con le frecce molto più conciso, se volete. La seguente è una definizione equivalente di classes
, ad esempio: risposta
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
Ciao Travis. È fantastico. Il tuo aiuto è molto utile nel mio tentativo di fare i conti con HXT. – Muchin
Travis di Brown è stato molto utile. Voglio solo aggiungere la mia soluzione qui, che ritengo sia un po 'più generale (utilizzando le stesse funzioni, ignorando solo i problemi specifici).
ero 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 è sfortunato.
Una rapida occhiata mi fa sembrare 'runX' legge il file XML ed è quindi un IO impuro. – alternative
Penso che il parser di HXT sia un parser 'online', cioè non ha bisogno di leggere l'intero input per iniziare a produrre output. Il lato positivo di questo è che (in linea di principio almeno) può funzionare con un footprint di memoria costante, il lato negativo è che deve leggere "chunk" dell'ingresso su richiesta, quindi deve essere in IO. –
Ma non è possibile risolverlo facilmente utilizzando l'IO pigro? Ad esempio, per ottenere un parsing XML ad alte prestazioni, al momento utilizzo Hexpat (per ByteStrings e lazy SAX-parsing.) E polyparse (Poly. Lazy.) Sto ottenendo un footprint di memoria costante, elaborazione veloce e * tutto * l'analisi viene eseguita in pure funzioni! –