2010-10-10 11 views
27

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?

+1

Una rapida occhiata mi fa sembrare 'runX' legge il file XML ed è quindi un IO impuro. – alternative

+1

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. –

+2

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! –

risposta

28

È 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 
+0

Ciao Travis. È fantastico. Il tuo aiuto è molto utile nel mio tentativo di fare i conti con HXT. – Muchin

1

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.