Haskell Guards involving Boolean Operations and Do - haskell

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?

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.

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"

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.

Related

Guard inside 'do' block - haskell

I want to write a simple game "guess number" - with n attempts. I want to add some conditions and hits. Is it possible to use guards inside do block ?
Here is my code:
game = return()
game n = do putStrLn "guess number: 0-99"
number<-getLine
let y = read number
let x =20
| y>x = putStrLn "your number is greater than x"
| y<x = putStrLn "your number is less than x"
| y==x putStrLn "U win!!"
| otherwise = game (n-1)
already got error
error: parse error on input ‘|’
Is it fixable with some white space, or just impossible to do?
A do expression [Haskell-report] only consists out of exp, pat <- exp, and let … statements, and the compiler will desugar these. Hence without some language extensions, you can not write guards in a do block. Furthermore it is likely not a good idea to enable that anyway. What if you for example would want to use two "guard blocks" next to each other? Then the two would "merge" and thus the guards of the first block would already eleminate (nearly) all cases.
You can use another let clause here:
game :: IO ()
game 0 = return ()
game n = do
putStrLn "guess number: 0-99"
number <- getLine
let y = read number
let x = 20
let action | y > x = putStrLn "your number is greater than x" >> game (n-1)
| y < x = putStrLn "your number is less than x" >> game (n-1)
| otherwise = putStrLn "U win!!"
action
Note that the otherwise in the original question will never get triggered, since a value is less than, greater than, or equal to another value.
Lots of problems there.
First, you can't say game = something and game n = something, so remove the game = return () line. (You may have been trying to write a type signature, but that's not one.)
Second, you can't drop into guard syntax in arbitrary places. The closest valid thing to what you wrote are multi-way if-expressions, which would let you write this:
{-# LANGUAGE MultiWayIf #-}
game n = do putStrLn "guess number: 0-99"
number<-getLine
let y = read number
let x =20
if
| y>x -> putStrLn "your number is greater than x"
| y<x -> putStrLn "your number is less than x"
| y==x-> putStrLn "U win!!"
| otherwise -> game (n-1)
Third, the Ord typeclass is supposed to be for types with a total order, so unless you're using unlawful things like NaN, you'll always have one of y>x, y<x, or y==x, so the otherwise will never be entered.
Fourth, comparing with <, ==, and > is unidiomatic and slow, since it has to keep repeating the comparison. Instead of doing that, do something like this:
case y `compare` x of
GT -> _
LT -> _
EQ -> _
You could also just use case or LambdaCase.
{-# LANGUAGE LambdaCase #-}
game :: Int -> IO ()
game n = case n of
0 -> putStrLn "used all attempts"
n ->
putStrLn "guess a number: 0 - 99" >>
(`compare` 20) . read <$> getLine >>=
\case
EQ -> putStrLn "U win"
GT -> putStrLn "your number is greater than x" >>
game (n - 1)
LT -> putStrLn "your number is less than x" >>
game (n - 1)
The other answers are very informative. Reading those led me to see that you can also call a function to solve this, e.g.
game = do putStrLn "guess number: 0-99"
number <- getLine
let y = read number
let x = 20
action y x
where
action y x
| y>x = putStrLn "your number is greater than x" >> game
| y<x = putStrLn "your number is less than x" >> game
| otherwise = putStrLn "U win!!"

haskell parse error on input `=' Perhaps you need a 'let' in a 'do' block? e.g. 'let x = 5' instead of 'x = 5'

Can't get my haskell program to work
sort [] = []
sort (x:xs) = sort [a | a <- xs , a<=x ] ++ [x] ++ sort [a | a <- xs , a > x]
getList:: Int->[IO Int]
getList 0 = [] --declaring the empty list
getList n = [a | a <- [getNumber] ] ++ getList (n-1)
getNumber::IO Int --get number function
getNumber = do
s <- getLine
return (read s)
--Main function to handle
main = do
p <- getNumber -- taking the number of variable
lst <- sequence (getList p) --calling gtlistFunction to input the list
print (sort lst) --print
Error:
quicksort.hs:13:6: error:
parse error on input `='
Perhaps you need a 'let' in a 'do' block?
e.g. 'let x = 5' instead of 'x = 5'
|
13 | main = do
| ^
You must indent all do-blocks:
-- NOT correct: this will fail
main = do
putStrLn "This is..."
putStrLn "WRONG!"
-- Correct:
main = do
putStrLn "This is..."
putStrLn "correct!"
The number of spaces doesn't matter as long it is consistent.
To avoid this you could use c-like notation:
-- Also correct:
main = do {
putStrLn "This is...";
putStrLn "also correct!";
}
And this leaves you free to indent as you like. Example:
-- Also correct:
main = do
{ putStrLn "This is..."
; putStrLn "also correct!"
;}
Without indentation, the parser doesn't know that main = ... starts a new definition, separate from the preceding definition of getNumber. The following would work fine:
getNumber::IO Int
getNumber = do
s <- getLine
return (read s)
main = do
p <- getNumber
lst <- sequence (getList p)
print (sort lst)
The transition from one-space indentation to no indentation is enough to tell the parser that return (read s) is the last line of the do block defining getNumber, and that main = do is the beginning of a new top-level definition.
Either indenting your code, or using the explicit brace syntax in AJFarmar's answer, lets the parser know where one definition ends and the next begins.

Let block gives indentation error

I know what an indentation error is, but I have no idea why I'm getting this error here, while every is aligned, trying to solve it for 2 hours.
Account.hs:40:25: error:
parse error (possibly incorrect indentation or mismatched brackets)
|
40 | let amount = readLn :: IO Int
| ^
Failed, 0 modules loaded.
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
I also tried this version but this also gives incorrect indentation or mismatched brackets:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
The problem is here:
-- |<---- "column 0" of this 'do' block
then do putStrLn $ "Enter your name "
-- | still good; a 'let' statement:
let name = getLine
-- |<---- "column 0" of this 'let' block
putStrLn $ "Enter the initial amount"
-- | Huh, there's no '=' in ^this^ declaration?
let amount = readLn :: IO Int
-- ^^^ Why is there a 'let' within another let binding?
-- I still haven't seen a '='. Better throw a parse error.
Basically, putStrLn $ "Enter the initial amount" is aligned with name = ... in the preceding line, so the compiler reads it as a declaration (part of the same let block).
To fix your indentation errors, it should be:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
But then you'll run into type errors:
read g is wrong: read takes a String, but g :: IO String
g == 1 is wrong: 1 is an Int, but g :: IO String
show value is wrong: show returns a String, but you're using it as an IO action
You haven't shown the declaration of Account, but you're likely going to have issues with name and amount, too
You probably want something like:
main = do
putStrLn $ "Press one to create a new account"
g <- getLine
let enteredValue = read g :: Int
if enteredValue == 1
then do putStrLn $ "Enter your name "
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
putStrLn (show value)
else do putStrLn $ "Nothing"
Basically, use v <- expr to go from expr :: IO Something to v :: Something.
Other notes:
g <- getLine; let enteredValue = read g :: Int better written as enteredValue <- readLn :: IO Int
putStrLn (show value) can be shortened to print value
you don't need do for a single expression (nor $ for a single operand): ... else putStrLn "Nothing"
There is more wrong to your code than just the Indentation Errors - so my first suggestion would be reading a bit of learn you a haskell for great good.
Next there are two assignment operators in haskell - one binds the result of an action … <- … and the other one is a local definition/declaration of a pure computation let … = ….
Moreover you can improve your reading a value by taking account of the possible false input, that someone could give you (intentionally and unintentionally) by replacing read with readMaybe, where the latter returns a Maybe something, for example readMaybe "1" = Just 1 :: Maybe Int or readMaybe "foo" = Nothing :: Maybe Int.
Regarding your indentation it is best that you compare one solution to your program with yours own:
import Text.Read (readMaybe)
data Type = Saving | Checking
deriving (Show)
data Account = Account (String,Int,Int) Type
deriving (Show)
main :: IO ()
main = do
putStrLn "Press one to create a new account"
g <- getLine
let enteredValue = readMaybe g :: Maybe Int
here the result of getLine and entered value have the same scope so they have the same indentation - we only change the scope after the next if where the then-block - and the else-block do not share the 'declarations' of each branch, so you couldn't use name in the else-block, but enteredValue can be used in both.
if enteredValue == Just 1
then do putStrLn "Enter your name "
name <- getLine
putStrLn "Enter the initial amount"
amount' <- fmap readMaybe getLine
here again name and amount' share the same scope, and pattern matching on amount' creates a new scope where amount is visible and the match on Nothing where you cannot use this variable.
case amount' of
Just amount -> print $ Account (name,1,amount) Saving
Nothing -> putStrLn "Nothing"
else putStrLn "Nothing"
let is for binding values, which is done in the form let x = y+z, where x is the name (aka "identifier") being bound, and y+z is the expression to which it is being bound.
In your example, I see three bindings: name, amount, and value. The rest are not value bindings, but actions.
In the do notation, actions do not need a let. You just write them one after another. So:
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
let value = Account (name,1,amount) Saving
show value
But wait! This is not all!
getLine is not actually an expression of type String, as you seem to be hoping here. Rather, getLine is an action. In order to get it to "run" and "produce" a String value, you need to use the <- construct instead of let:
name <- getLine
Similarly with readLn:
amount <- readLn :: IO Int
Finally, show value is not actually an action that would print the value to the screen. show is a function that takes a value and return a String. It doesn't "do" anything (i.e. doesn't produce any outside effects), so you can't use it in place of an action in the do notation. If you wanted an action that would print a value to the screen, that would be print:
print value
Gathering everything together:
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
print value
And after fixing all of that, you'll have similar difficulties with the first part of your program, where you have let g = getLine instead of g <- getLine.

howto make a simple selection in haskell?

i have a problem, this code dont work and i dont know why:
foo :: [String] -> IO [String]
foo input = do
choice <- getLine
if choice == "1" then do
putStrLn "good choice"
return input
else
return []
As is (with a bit of retabbing) the code works for me. if/else is difficult to get correct with the tabs. The Wiki article if/then/else should help.
foo :: [String] -> IO [String]
foo input = do
choice <- getLine
if choice == "1" then do
putStrLn "good choice"
return input
else
return []
Eliminating the inner do expression makes the if/then block a little more easy to indent.
foo2 input = do
choice <- getLine
if (choice == "1")
then (putStrLn "good choice" >> return input)
else (return [])

Help In Solving Problem Using Guards - UPDATED With CODE

I am using guards inside a function but not immediately after the function signature. The guards are under a do statement inside the body of the function. I get this error:
parse error on input `|'
I thought maybe the error comes from the indentation but i have tried many indentation but still i get the errors. Am asking is it because the guards are not immedaitely after the function signature that is why am getting the errors?
thanks
UPDATE 1
CODE:
The user is suppose to guess a number , and the number will be compared with the random number if they are the same. If it is not correct then the user will guess till the "guess" variable in the function is zero. in every interation that value(guess) is decreased by one.
for instance : puzz 12 5. the user can guess for five times, the random number will be picked between 1 and 12. that is how the function is suppose to do, but it is not working.
puzz :: Int -> Int -> IO ()
puzz boundary guess = do
putStr "Guess"
-- putStr -- I have to print (1 .. guess) here in each iteration
putStr ":"
x <- randomRIO (1, boundary :: Int)
n <- getLine
let
nTo = read n::Int
in print x
| guess == 0 = putStr "You couldn't guess right,the correct answer is" ++ x
| nTo > x = putStr "lower"
| nTo < x = putStr "higer"
| nTo == x = putStr "Congrat, You guess right."
| otherwise raad boundary (guess - 1)
the ouput must be like this:
Main> puzz 50 6
Guess a number betwee 1 en 50.
Guess 1: 49
lower
Guess 2: 25
lower
Guess 3: 12
higher
Guess 4: 18
higher
Guess 5: 21
higher
Guess 6: 23
lower
You couldn't guess correct, the answer was: 22.
thanks for your help
You’re using guards incorrectly. From the report:
Top level patterns in case expressions and the set of top level patterns in function or pattern bindings may have zero or more associated guards.
So they’re only for cases and function bindings. If you just want to concisely introduce a series of true-false tests, while inside a do-notation, perhaps the case () of () trick would work:
main = do
putStrLn "hello world"
n <- getLine
let nTo = read n :: Int
case ()
of () | cond -> putStrLn "foo"
| cond' -> putStrLn "bar"
| otherwise -> putStrLn "baz"
It should be noted that there are several things that are a bit off with your code, in addition to using guards wrong. By default output is buffered in haskell so if you want Guess to be on the same line as input you have to either say that stdOut should not be buffered (hSetBuffering stdOut NoBuffering), or you have to flush output with hFlush. It's not necessary to write boundary :: Int, the compiler knows it is an Int. Here is a bit more complete example, I'm sure it could be done better but atleast it works:
import Control.Monad(liftM,unless)
import System.IO(hFlush,stdout)
import System.Random(randomRIO)
guessMyNumber upper guesses = do
putStrLn $ "Guess a number between 1 and " ++ show upper ++ "!"
randomRIO (1, upper) >>= puzz guesses
puzz 0 number = do
putStrLn $ "Sorry, no more guesses, the number was "
++ show number ++ "."
puzz guesses number = do
putStr "Guess:" >> hFlush stdout
guess <- liftM read getLine
printMessage guess number guesses
printMessage guess number guesses
| number > guess = putStrLn "higer" >> puzz (guesses-1) number
| number < guess = putStrLn "lower" >> puzz (guesses-1) number
| number == guess = putStrLn "Congratulations! You guessed right!"

Resources