Can I nest in Haskell?

Haskell is new here, trying to write code to parse mathematical expressions. The code:

isDigit :: Char -> Bool isDigit c = c >= '0' && c <= '9' parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h | p == Nothing = Just([h], ls) -- Digit found <<< ERROR!! | otherwise = Just (h:fst d, snd d) -- Ends in a digit | h == '.' | p == Nothing = Nothing -- Ends in a point | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) -- We don't want multiple dots | otherwise = Nothing -- Not a number, stop looking! where p = parseNumber ls Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 

This function should take a string that starts with a number and returns a number separated from the rest of the expression. Example:

parseNumber "123.0 + 2"

("123.0", "+ 2")

I think the syntax of the nested guards reads really nicely, but it doesn't work. An error is read for the highlighted line:

Parsing error at input `| ''

Chained guards banned in Haskell? Or am I so wrong? Also, what alternatives do I need for simple circuit logic?

+5
haskell
source share
8 answers

No, but you can use cases if you want:

 parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = case () of () | p == Nothing = Just([h], ls) | otherwise = Just (h:fst d, snd d) -- Ends in a digit | h == '.' = case () of () | p == Nothing = Nothing | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) | otherwise = Nothing where p = parseNumber ls Just d = parseNumber ls 

Alternatively, multway if works the same way ( if True | p1 -> b ; | p2 -> c ).

+12
source share

No, you can’t. We all want this, but no one can come up with a reasonable syntax.

+11
source share

When your function becomes extremely complex, and you cannot support logic that is implemented with only one guards, consider writing a function with abstract control functions:

 import Control.Applicative import Control.Monad isDigit :: Char -> Bool isDigit c = c >= '0' && c <= '9' parseNumber :: String -> Maybe (String, String) parseNumber [] = return ("", "") parseNumber (h:ls) = dig <|> dot where -- h is either a digit or a dot p = parseNumber ls dig = do guard (isDigit h) -- ensure h is a digit fmap (\(ds,r) -> (h:ds,r)) p <|> return ([h],ls) -- the alternative between two computations -- either the tail is parsed and h prepended to the result -- or the digit is returned by itself dot = do guard (h == '.') -- ensure h is a dot (ds,r) <- p -- parse the tail guard $ not $ '.' `elem` ds -- ensure there is no dot in the tail return (h:ds,r) -- result 

This uses instances of Monad , Functor and MonadPlus Maybe to implement parsing logic. In fact, this function is generalized to the type MonadPlus m => String -> m (String, String) - here the actual use of Maybe constructors is absent.

The function is also easy to read. It is much more obvious what happens in the version with the guards.

+5
source share

No, It is Immpossible. Why not just write it linearly, like

 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, this is probably a sign that you need to extract the function.

+3
source share

You can attach safety devices with,. This is basically the same as && in fjarri's answer, but more universal when it comes to template protection.

What is impossible is the nesting of the guards. Well, in your example, which is actually needed only in the first sentence. You can write

 parseNumber (h:ls) | isDigit h = if isNothing p then Just ([h], ls) -- Digit found <<< ERROR!! else Just (h:fst d, snd d) -- Ends in a digit | h == '.' , not ('.' `elem` snd d) = Just (h:fst d, snd d) -- We don't want multiple dots | otherwise = Nothing -- Not a number, stop looking! 
+3
source share

Using where Just d = ... is dangerous: if you ever access it when p is Nothing , your whole program will crash. In doing this, you must add such checks to your code (as you have already done), and be careful not to forget any of them.

There are safer ways, such as using case p of Nothing -> ... ; Just d -> ... case p of Nothing -> ... ; Just d -> ... , using maybe rectifier or using functor / applative / monad tools. Let case be used to make 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 match the pattern by subcomponents 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 
+1
source share

The recent GHC now has a MultiWayIf :

 {-# LANGUAGE MultiWayIf #-} parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = if | p == Nothing -> Just ([h], ls) | otherwise -> Just (h:fst d, snd d) | h == '.' = if | p == Nothing -> Nothing | not ('.' `elem` (snd d)) -> Just (h:(fst d), snd d) | otherwise = Nothing where p@ (~(Just d)) = parseNumber ls 

But it is better written a little differently, without partiality.

 {-# LANGUAGE MultiWayIf #-} parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = if | Nothing <- p -> Just ([h], ls) -- PatternGuards, on by default | Just d <- p -> Just (h:fst d, snd d) | h == '.' = if | Nothing <- p -> Nothing | Just d <- p, not ('.' `elem` snd d) -> Just (h:(fst d), snd d) | otherwise = Nothing where p = parseNumber ls 

and you can also use maybe .

 parseNumber :: String -> Maybe (String, String) parseNumber "" = Just ("", "") parseNumber (h:hs) | isDigit h = maybe (Just ([h], hs)) (\(num, rest') -> Just (h:num, rest')) rest | h == '.' = maybe Nothing (\(num, rest') -> if '.' `elem` num then Nothing else Just (h:num, rest') ) rest -- This logic is a bit wonky; it doesn't really work | otherwise = Nothing where rest = parseNumber hs 
+1
source share

Put them in separated functions.

 isDigit :: Char -> Bool isDigit c = c >= '0' && c <= '9' parseNumber :: String -> Maybe (String, String) parseNumber [] = Just ("", "") parseNumber (h:ls) | isDigit h = f_p (h:ls) | h == '.' = temp (h: ls) | otherwise = Nothing -- Not a number, stop looking! f_p :: String -> Maybe (String, String) f_p (h:ls) | parseNumber ls == Nothing = Just([h], ls) -- Digit found <<< ERROR!! | otherwise = Just (h:fst d, snd d) -- Ends in a digit where Just d = parseNumber ls -- Float version of p. Not used if p is Nothing temp :: String -> Maybe (String, String) temp (h:ls) | parseNumber ls == Nothing = Nothing -- Ends in a point | not ('.' `elem` (snd d)) = Just (h:(fst d), snd d) -- We don't want multiple dots where Just d = parseNumber ls -- Float version of p. Not used if p is Nothing 

Frankly, I have not tested this code.

0
source share

All Articles