开发者

Haskell: Lifting a reads function to a parsec parser

开发者 https://www.devze.com 2023-01-12 21:36 出处:网络
As part of the 4th exercise here I would like to use a reads type function such as readHex with 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:

  • Will the term (f s) be memoised, or evaluated twice in the case of a null (f s) returning False?
  • Handling multiple successful parses, i.e. when length (f s) is greater than one, I do not know how parsec deals with this.
  • Handling the remainder of the parse, i.e. (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:

    1. The function reader is applied only once now, so there is nothing to "memoize".
    2. 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
    
  • 0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号