Search code examples
haskellsyntaxpattern-guards

My code results in parse error because of my use of guards


I have the following code:

parseExpr :: [String] -> (Ast,[String])
parseExpr [] = error "Incorrect"
parseExpr (s:ss) | all isDigit s = (Tall (read s),ss)
              | s == "-" = let (e,ss') = parseExpr ss in (Min e,ss')
              | s == "+" = (Sum e e',ss'') where
                           (e,ss') = parseExpr ss
                           (e',ss'') = parseExpr ss'
              | s == "*" = (Mult e e',ss'') where
                           (e,ss') = parseExpr ss
                           (e',ss'') = parseExpr ss'

When I try to run it, I get an error saying "parse error on input '|'. The line it's talking about is this one:

...
                           (e',ss'') = parseExpr ss'
->            | s == "*" = (Mult e e',ss'') where
                           (e,ss') = parseExpr ss
...

I think I understand why. I would think it's because I have two lines above that are not shielded by guards, and that haskell then get's confused when there suddenly appears another guard below. But how can I then add additional guards after these cases?

I've tried using semicolons to separate new lines, but I'm very new with Haskell and therefore don't know much about its syntax.


Solution

  • There is no need to use braces (although of course you could). But you do need to be careful with indentation. It is not as hard as it may at first seem.

    That being said, it is true that you cannot use where inside a guarded expression. You have to use let in such case, like this:

    parseExpr :: [String] -> (Ast,[String])
    parseExpr [] = error "Incorrect"
    parseExpr (s:ss) | all isDigit s = (Tall (read s),ss)
                  | s == "-" = let (e,ss') = parseExpr ss in (Min e,ss')
                  | s == "+" = let (e,ss') = parseExpr ss
                                   (e',ss'') = parseExpr ss'
                               in (Sum e e',ss'')
                  | s == "*" = let (e,ss') = parseExpr ss
                                   (e',ss'') = parseExpr ss'
                               in (Mult e e',ss'')
    

    However, in your case here, the guard expressions are so simple they can be replaced with pattern matches. (And it is advisable to use the opportunity. Pattern matching is your best friend in Haskell.) In that case you use where.

    parseExpr :: [String] -> (Ast,[String])
    parseExpr [] = error "Incorrect"
    parseExpr (s:ss) | all isDigit s = (Tall (read s),ss)
    parseExpr ("-":ss) = (Min e,ss') where (e,ss') = parseExpr ss
    parseExpr ("+":ss) = (Sum  e e',ss'') where (e, ss' ) = parseExpr ss
                                                (e',ss'') = parseExpr ss'
    
    parseExpr ("*":ss) = (Mult e e',ss'') where (e, ss' ) = parseExpr ss
                                                (e',ss'') = parseExpr ss'