attoparsec comprensione
-
01-10-2019 - |
Domanda
attoparsec mi è stato suggerito per l'analisi di un file, ora devo capire come usarlo; qualcuno mi ha dato questo pezzo di codice:
#
type Environment = M.Map String String
import Data.Attoparsec (maybeResult)
import qualified Data.Attoparsec.Char8 as A
import qualified Data.ByteString.Char8 as B
environment :: A.Parser Environment
environment = M.fromList <$> A.sepBy entry A.endOfLine
parseEnvironment = maybeResult .flip A.feed B.empty . A.parse environment
spaces = A.many $ A.char ' '
entry = (,) <$> upTo ':' <*> upTo ';'
upTo delimiter = B.unpack <$> A.takeWhile (A.notInClass $ delimiter : " ")
<* (spaces >> A.char delimiter >> spaces)
che funziona molto bene, ma io non so perché: quale sia la ragione di usare fogli mobili, non è più facile mettere l'argomento della A.feed in un ordine diverso? e perché c'è B.empty? c'è qualche tutorial su che posso studiare? grazie in anticipo
Soluzione
C'è una spiegazione della necessità di feed
nelle risposte alle questa domanda StackOverflow . Come Bryan O'Sullivan (il creatore di Attoparsec) dice che ci:
Se si scrive un parser attoparsec che consuma più input possibili prima di fallire, è necessario indicare al risultato parziale prosecuzione quando hai raggiunto la fine del vostro input.
Si può fare questo alimentando rende un bytestring vuota.
devo ammettere che ho scritto il codice in questione, e io in realtà non ha usato pointfree
in questo caso. Composizione semplice solo ha senso per me qui: si esegue il parser (A.parse environment
), si dice il gioco è fatto (flip A.feed B.empty
), e si converte in un Maybe
come una sorta di gestione degli errori di base (maybeResult
). A mio parere questo si sente più pulita rispetto alla versione a punta:
parseEnvironment b = maybeResult $ A.feed (A.parse environment b) B.empty
Il resto è credo abbastanza idiomatica applicativa analisi , anche se io non sono sicuro perché avrei >>
usato al posto di *>
.