2
votes

I'm just starting to learn Haskell using the wikibook, doing fine so far since I have taken very basic courses in HS with Visual Basic and Java, but some of the exotic features of Haskell are confusing me when trying to combine more than one. One of the wikibooks has a exercise problem in writing three different strings based on certain names given as input for the user. This is fine when using if-then-else statements, but when I try to use guards I am getting a parsing error on row 6.

    main = do
        putStrLn "What is your name?"
        n <- getLine
        |(n == "Simon") || (n == "John") || (n == "Phil")
            = putStrLn "Help me make this stuff"
        |n == "Koen" = putStrLn "How is this a parse error"
        |otherwise "Line 11 looks fine to me"

Which reads " error: parse error on input ‘|’"
Is this a problem with the guard | or the operator ||? the error lists it is on 6:9 if that helps.

EDIT: I have another question regarding a very similar topic now that someone has answered my first question. Wikibooks Haskell tutorials has listed this as another solution to their exercise, using where statements instead of if-then-else:

main = do
  putStrLn "Hello, what is your name?"
  name <- getLine
  putStrLn (message name)
    where
    greatlanguage   = "I think Haskell is a great programming language."
    message "Simon" = greatlanguage
    message "John"  = greatlanguage
    message "Phil"  = greatlanguage
    message "Koen"  = "I think debugging Haskell is fun."
    message _       = "Sorry, I don't know you."

Is it possible to use the || operator to somehow condense the 3 Simon, John, and Phil lines that call greatlanguage into a single line?

3
Please don't edit unrelated questions into your post. Ask them as separate questions instead.duplode

3 Answers

2
votes

Guards can only be inserted in function definitions (in the form func a b | condition = ...) and case blocks (in the form case x of pattern | condition -> ...); you can't insert them inside do blocks the way you're trying to. You will need to use if ... then ... else here instead.

2
votes

There's many ways to go about this. I'd suggest you consider moving that piece of code out of the do block to its own function.

for n :: String -> IO ()
foo n | n == "Simon" ||
        n == "John"  ||
        n == "Phil"  = putStrLn "Help me make this stuff"
      | n == "Koen"  = putStrLn "How is this a parse error"
      |otherwise     = putStrLn "Line 11 looks fine to me"

And then call it in your do block,

main = do
    putStrLn "What is your name?"
    n <- getLine
    foo n

Also it may be more sensible to make your auxiliary function "pure",

for n :: String -> String
foo n | n == "Simon" ||
        n == "John"  ||
        n == "Phil"  = "Help me make this stuff"
      | n == "Koen"  = "How is this a parse error"
      |otherwise     = "Line 11 looks fine to me"

and call it via,

main = do
    putStrLn "What is your name?"
    n <- getLine
    putStrLn (foo n)

If you prefer you can also make the 1st guard simpler,

foo n | n `elem` ["Simon", "John", "Phil"] = "Help me make this stuff"
      | n == "Koen" = "How is this a parse error"
      |otherwise    = "Line 11 looks fine to me"

If you really want to inline it in the do block then you could adapt one of those solutions via a case construction, e.g.

main = do
    putStrLn "What is your name?"
    n <- getLine
    putStrLn $ case n of
      _ | n `elem` ["Simon", "John", "Phil"] -> "Help me make this stuff"
      _ | n == "Koen" -> "How is this a parse error"
      _               -> "Line 11 looks fine to me"
2
votes

To complement the other answers, I'll add two alternatives (not necessarily better ones).

main = do
    putStrLn "What is your name?"
    n <- getLine
    case () of
       _ | n == "Simon" || n == "John" || n == "Phil"
            -> putStrLn "Help me make this stuff"
         | n == "Koen" 
            -> putStrLn "How is this a parse error"
         | otherwise
            -> putStrLn "Line 11 looks fine to me"

This requires the MultiWayIf extension to be turned on.

{-# LANGUAGE MultiWayIf #-}  --  at the top of the file

main = do
    putStrLn "What is your name?"
    n <- getLine
    if | n == "Simon" || n == "John" || n == "Phil"
          -> putStrLn "Help me make this stuff"
       | n == "Koen" 
          -> putStrLn "How is this a parse error"
       | otherwise
          -> putStrLn "Line 11 looks fine to me"

Remember that even after otherwise we need ->, or we trigger a parse error.