haskellmonadplus

Does Maybe MonadPlus Parsers need to be in certain order?


Im working through the exercises on wikibooks/haskell and there is an exercise in the MonadPlus-chapter that wants you to write this hexChar function. My function works as shown below, but the thing is that when I try to switch the 2 helper parsers (digitParse and alphaParse) around the function ceases to work properly. If I switch them around I can only parse digits and not alphabetic chars anymore.

Why is this so?

char :: Char -> String -> Maybe (Char, String)
char c s = do
    let (c':s') = s
    if c == c' then Just (c, s') else Nothing

digit :: Int -> String -> Maybe Int
digit i s | i > 9 || i < 0 = Nothing
        | otherwise      = do
    let (c:_) = s
    if read [c] == i then Just i else Nothing

hexChar :: String -> Maybe (Char, String)
hexChar s = alphaParse s `mplus` digitParse s -- cannot switch these to parsers around!!
    where alphaParse s = msum $ map ($ s) (map char (['a'..'f'] ++ ['A'..'F']))
          digitParse s = do let (c':s') = s
                            x <- msum $ map ($ s) (map digit [0..9])
                            return (intToDigit x, s')

Solution

  • if read [c] == i then Just i else Nothing

    The marked code has a flaw. You're using Int's Read instance, e.g. read :: String -> Int. But if it's not possible to parse [c] as an int (e.g. "a"), read will throw an exception:

    > digit 1 "doesnt start with a digit"
    *** Exception: Prelude.read: no parse
    > -- other example
    > (read :: String -> Int) "a"
    *** Exception: Prelude.read: no parse
    

    Instead, go the other way:

    if [c] == show i then Just i else Nothing

    This will always works, since show won't fail (not counting cases where bottom is involved).