Haskell: Lifting a reads function to a parsec parser
As part of the 4th exercise here
I would like to use a reads
type function such as readHex
with a parsec Parser
.
To do this I have written a function:
liftReadsToParse :: Parser String -> (Stri开发者_StackOverflowng -> [(a, String)]) -> Parser a
liftReadsToParse p f = p >>= \s -> if null (f s) then fail "No parse" else (return . fst . head ) (f s)
Which can be used, for example in GHCI, like this:
*Main Numeric> parse (liftReadsToParse (many1 hexDigit) readHex) "" "a1"
Right 161
Can anyone suggest any improvement to this approach with regard to:
(f s)
be memoised, or evaluated twice in the case of a null (f s)
returning False
?
length (f s)
is greater than one, I do not know how parsec deals with this.
(snd . head) (f s)
.This is a nice idea. A more natural approach that would make
your ReadS
parser fit in better with Parsec would be to
leave off the Parser String
at the beginning of the type:
liftReadS :: ReadS a -> String -> Parser a
liftReadS reader = maybe (unexpected "no parse") (return . fst) .
listToMaybe . filter (null . snd) . reader
This "combinator" style is very idiomatic Haskell - once you get used to it, it makes function definitions much easier to read and understand.
You would then use liftReadS
like this in the simple case:
> parse (many1 hexDigit >>= liftReadS readHex) "" "a1"
(Note that listToMaybe
is in the Data.Maybe
module.)
In more complex cases, liftReadS
is easy to use inside any
Parsec do
block.
Regarding some of your other questions:
- The function
reader
is applied only once now, so there is nothing to "memoize". - It is common and accepted practice to ignore all except the first parse in a
ReadS
parser in most cases, so you're fine.
To answer the first part of your question, no (f s)
will not be memoised, you would have to do that manually:
liftReadsToParse p f = p >>= \s -> let fs = f s in if null fs then fail "No parse"
else (return . fst . head ) fs
But I'd use pattern matching instead:
liftReadsToParse p f = p >>= \s -> case f s of
[] -> fail "No parse"
(answer, _) : _ -> return answer
精彩评论