HASKELL --- Using "let" within a "do" syntax in GHCI - haskell

I know this works:
do name <- getLine; let nameTag = "Hello, my name is " ++ name in putStrLn nameTag
As well as this piece of code when loaded into GHCI:
hey = do
name <- getLine
let nameTag = "Hello, my name is " ++ name
putStrLn nameTag
But this does not work:
do name <- getLine; let nameTag = "Hello, my name is " ++ name; putStrLn nameTag
giving the following error message:
<interactive>:142:82:
parse error (possibly incorrect indentation or mismatched brackets)
Why doesn't it work? Can I make it work? If yes, then how?

Yes, you can use braces around the let binding(s) to disambiguate the parsing:
do name <- getLine; let { nameTag = "Hello, my name is " ++ name }; putStrLn nameTag

To complement the answer by McKenna, the error can be explained as follows: compare these two lines
do name <- getLine; let name1 = "One"; putStrLn name1
do name <- getLine; let name1 = "One"; name2 = "Two"; putStrLn name1
A human reader can see that they actually mean
do { name <- getLine; let { name1 = "One" } ; putStrLn name1 }
do { name <- getLine; let { name1 = "One"; name2 = "Two" }; putStrLn name1 }
but the parser is not that smart.
When the Haskell parser sees the common code portion
do name <- getLine; let name1 = "One";
-- ^ --
it has to decide whether the last ; belongs to the do level (as in the first case above), or to the let level (second case). It turns out that it chooses let, and fails later on.

Related

How does Haskell "desugar" getline in this do block?

I've read a few books on Haskell but haven't coded in it all that much, and I'm a little confused as to what Haskell is doing in a certain case. Let's say I'm using getLine so the user can push a key to continue, but I don't really want to interpret that person's input in any meaningful way. I believe this is a valid way of doing this:
main = do
_ <- getLine
putStrLn "foo"
I understand the basic gist of what's this is doing. getLine returns an IO String, and putStrLn takes a String and returns IO (), so if I theoretically wanted to print what the user typed into the console, I'd basically utilize the >>= operator from the Monad class. In my case, I believe my code is equivalent to getLine >> putStrLn "foo" since I'm discarding the return value of getLine.
However, what if I do this instead?
main = do
let _ = getLine
putStrLn "foo"
In this case, we're setting up a sort of lambda to work with something that will take an IO String, right? I could write a printIOString function to print the user's input and that would work fine. When I'm not actually using that IO String, though, the program behaves strangely... getLine doesn't even prompt me for input; the program just prints out "foo".
I'm not really sure what the "desugared" syntax would be here, or if that would shed some light on what Haskell is doing under the hood.
Let's warm up with a few more complicated examples.
main = do
x
x
x
putStrLn "foo"
where
x = do
getLine
What do you expect this to do? I don't know about you, but what I expect is for the program to get three lines and then print something. If we desugar the second do block, we get
main = do
x
x
x
putStrLn "foo"
where x = getLine
Since this is the desugaring of the other one, it behaves the same, getting three lines before printing. There's another line of thought that arrives at the same answer, if you don't find this first one intuitive. "Referential transparency", one of the defining features of Haskell, means exactly that you can replace a "reference" to something (that is, a variable name) with its definition, so the previous program should be exactly the same program as
main = do
getLine
getLine
getLine
putStrLn "foo"
if we are taking the equation x = getLine seriously. Okay, so we have a program that reads three lines and prints. What about this one?
main = do
x
x
putStrLn "foo"
where x = getLine
Get two lines and print. And this one?
main = do
x
putStrLn "foo"
where x = getLine
Get one line and then print. Hopefully you see where this is going...
main = do
putStrLn "foo"
where x = getLine
Get zero lines and then print, i.e. just print immediately! I used where instead of let to make the opening example a bit more obvious, but you can pretty much always replace a where block with its let cousin without changing its meaning:
main = let x = getLine in do
putStrLn "foo"
Since we don't refer to x, we don't even need to name it:
main = let _ = getLine in do
putStrLn "foo"
and this is the desugaring of the code you wrote.
The first case is desugared like you expected:
main = getLine >>= \_ -> putStrLn "foo"
which is equivalent to
main = getLine >> putStrLn "foo"
In the second case,
main = do
let _ = getLine
putStrLn "foo"
is desugared as
main = let _ = getLine in putStrLn "foo"
Since the _ = getLine value is not needed to evaluate the RHS of the let expression, the compiler is free to ignore it and the IO effect is never executed, which is why you're not prompted for CLI input anymore.
Even though both cases ignored the result of getLine the difference is that the first case evaluates getLine in an IO context while the second case evaluates getLine as a pure value. In IO the side-effects must executed and sequenced together, but in a pure context the compiler is free to ignore unused values.
I wouldn't recommend doing this as it's not very idiomatic, but you could write something like
printIOString :: IO String -> IO ()
printIOString ios = ios >>= putStrLn
and use it like printIOString getLine
According to https://stackoverflow.com/tags/do-notation/info,
do { let { _ = getLine } ; putStrLn "foo" }
=
do { let { _ = getLine } in putStrLn "foo" }
=
let { _ = getLine } in putStrLn "foo"
which by Haskell semantics is equivalent to
getLine & (\ _ -> putStrLn "foo")
=
putStrLn "foo"
(with x & f = f x), whereas indeed
do { _ <- getLine ; putStrLn "foo" }
=
getLine >>= (\ _ -> putStrLn "foo")
which can't be further simplified.

Menu option issue [duplicate]

This question already has answers here:
Why "Empty do" error when my do isn't empty?
(3 answers)
Closed 2 years ago.
I have editing this code for hours just trying to get rid of this indentation problem and I followed the link I did previously. But because I follow those code I need to readjust again because my spaDatabase and updatedDB is not being recognised which I end up need to readjust everything from the top again and now the bottom part part has problem AGAIN.
I keep deleting and adding space but the error is still there. I also try based on one of the recommended answer I get this parse error below. But if I remove it, it end up being second parse error.
let output :: IO ()
parse error (possibly incorrect indentation or mismatched brackets)
--second error
parse error on input `='
Why "Empty do" error when my do isn't empty?
parse error on input `='
--line of error
output option = case option of
main :: IO()
main = do
contents <- readFile "spa.txt"
let spaDatabase = (read contents :: [Spa])
putStrLn "Please Enter Your Name: "
name <- getLine
putStrLn ("Welcome " ++ name)
putStrLn ""
let menu spaDatabase = do
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
option <- getLine
output :: IO ()
output option = case option of
1 -> do putStrLn "Enter Spa ID: "
rid <- getLine
let updatedDB = (addSpa rid br ar (read st) spaDatabase)
putStrLn (spaListStr updatedDB)
2 -> putStrLn (spaListStr updatedDB) >> menu spaDB
3 -> do putStrLn "Enter Spa Area:"
ar <- getLine
putStrLn (spaListStr (read ar) spaDatabase)
Here it is with the indents fixed. It's still not right: it defines menu and output but doesn't call them. But it should at least get you past the syntax errors.
main :: IO()
main = do
contents <- readFile "spa.txt"
let spaDatabase = (read contents :: [Spa])
putStrLn "Please Enter Your Name: "
name <- getLine
putStrLn ("Welcome " ++ name)
putStrLn ""
menu spaDatabase = do
putStrLn "\nPlease select an option:"
putStrLn "1: Add a new spa to the database "
getLine -- This returns the value, so no need for <-
output :: Int -> IO ()
output option = case option of
1 -> do
putStrLn "Enter Spa ID: "
rid <- getLine
let updatedDB = (addSpa rid br ar (read st) spaDatabase)
putStrLn (spaListStr updatedDB)
2 -> putStrLn (spaListStr updatedDB) >> menu spaDB
3 -> do
putStrLn "Enter Spa Area:"
ar <- getLine
putStrLn (spaListStr (read ar) spaDatabase)

Haskell mutliline guard inside do not working

Hello I am new to Haskell and I think that my problem is simple but important for me.
This works:
module Main where
main :: IO ()
main = do
inp <- getLine
let output i | odd i = "Alice" | even i = "Bob" | otherwise = "Weird"
putStrLn (output (read inp))
This does not work
module Main where
main :: IO ()
main = do
inp <- getLine
let output i
| odd i = "Alice"
| even i = "Bob"
| otherwise = "Weird"
putStrLn (output (read inp))
What I know:
Inside do you write "lets" or "let" before every function you declare and you do not write "in". Also when I wrote output as a non local function it worked.
What have I missunderstood?
edit:
would you recommend writing like this?
module Main where
main :: IO ()
main = do
inp <- getLine
let
output i
| odd i = "Alice"
| even i = "Bob"
putStrLn (output (read inp))
You need to indent the guards (with at least one extra space compared to the position of output), for example:
main :: IO ()
main = do
inp <- getLine
let output i
| odd i = "Alice"
| even i = "Bob"
| otherwise = "Weird"
putStrLn (output (read inp))
Since a number is either odd or even, you can just use otherwise for the even case:
main :: IO ()
main = do
inp <- getLine
let output i
| odd i = "Alice"
| otherwise = "Bob"
putStrLn (output (read inp))

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.

'let' and variables in Haskell

I'm a newbie in Haskell and I'm reading "Learn You a Haskell for Great Good!".
An expression that defines with a "name" and a "space", followed by "parameters" could be used as functions.
Consider the following code:
doubleMe = 2
this code actually follows the rule above, so we could see it as a function. but it really seems like an assign operations in java or c++.
So, How to understand the variable in Haskell?
At the top level of a program, name = expression is a definition. It creates a variable, in the sense of a mathematical variable—just a name for some expression. It can be a definition of a value:
two :: Int
two = 2
Or a definition of a function:
twice :: Int -> Int
twice x = x * two
A definition refers to a function if its type has a function arrow ->.
In a do block or GHCi, let name = expression is a local definition.
main :: IO ()
main = do
let greet name = "Hello, " ++ name ++ "!"
putStrLn (greet "world")
Finally, there is another use of the let keyword: let name = expression1 in expression2. This creates a variable local to a single expression:
length (let x = "hello" in x ++ x) == 10
Be aware that let takes a block of bindings, so it’s subject to the layout rules like other layout keywords, such as do, where, of as in case…of, and so on:
main :: IO ()
main = do
-- Bindings must be aligned past the start column.
-- ↓
let greeting name = "Hello, " ++ name ++ "!"
parting name = "Goodbye, " ++ name ++ "!"
putStrLn (greeting "world")
putStrLn (parting "world")
 
length $ let x = "hello"
y = "goodbye"
in x ++ y
As with other layout keywords, can instead put a newline and a fixed amount of indentation after let, and then not worry about alignment:
main = do -- ← newline+indent
let -- ← newline+indent
greeting name = …
parting name = …
…
 
length $ let
x = "hello"
y = "goodbye"
in x ++ y
Or you can always include explicit curly braces and semicolons:
main :: IO ();
main = do {
let {
greeting name = "Hello, " ++ name ++ "!";
parting name = "Goodbye, " ++ name ++ "!";
};
putStrLn (greeting "world");
putStrLn (parting "world");
};
length $ let {
x = "hello";
y = "goodbye";
} in x ++ y

Resources