Haskell newbie here, trying to write code to parse math expressions. Code:
isDigit :: Char -> Bool
isDigit c = c >= \'0\' && c <= \'9\'
pars
No, it's not possible. Why not just write it linearly as
isDigit :: Char -> Bool
isDigit c = c >= '0' && c <= '9'
parseNumber :: String -> Maybe (String, String)
parseNumber [] = Just ("", "")
parseNumber (h:ls)
-- Digit found
| isDigit h && p == Nothing = Just([h], ls)
-- Ends in a digit
| isDigit h = Just (h:fst d, snd d)
-- Ends in a point
| h == '.' && p == Nothing = Nothing
-- We don't want multiple dots
| h == '.' && not ('.' `elem` (snd d)) = Just (h:(fst d), snd d)
-- Not a number, stop looking!
| otherwise = Nothing
where
p = parseNumber ls
Just d = parseNumber ls -- Float version of p. Not used if p is Nothing
main = print $ parseNumber "123.0 + 2"
If your guards become too involved it's probably a sign that you need to extract a function.
Using where Just d = ...
is dangerous: if you ever access it when p
is Nothing
your whole program will crash. By doing that, you have to add such checks in your code (as you correctly already did), and be careful not to forget any one of these.
There are safer ways, such as using case p of Nothing -> ... ; Just d -> ...
, using the maybe
eliminator, or using functor/applicative/monad tools. Let's use case
to keep it simple:
parseNumber :: String -> Maybe (String, String)
parseNumber [] = Just ("", "")
parseNumber (h:ls)
| isDigit h = case p of
Nothing -> Just([h], ls) -- Digit found <<< ERROR!!
Just d -> Just (h:fst d, snd d) -- Ends in a digit
| h == '.' = case p of
Nothing -> Nothing -- Ends in a point
Just d | not ('.' `elem` (snd d))
-> Just (h:(fst d), snd d) -- We don't want multiple dots
_ -> Nothing -- Not a number, stop looking!
where
p = parseNumber ls
We can also directly pattern match on the subcomponents of d
:
parseNumber :: String -> Maybe (String, String)
parseNumber [] = Just ("", "")
parseNumber (h:ls)
| isDigit h = case p of
Nothing -> Just([h], ls) -- Digit found <<< ERROR!!
Just (hs,rest) -> Just (h:hs, rest) -- Ends in a digit
| h == '.' = case p of
Nothing -> Nothing -- Ends in a point
Just (hs, rest) | not ('.' `elem` rest)
-> Just (h:hs, rest) -- We don't want multiple dots
_ -> Nothing -- Not a number, stop looking!
where
p = parseNumber ls