Let block gives indentation error - haskell

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.

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.

Scope of input haskell

I'm having trouble taking in an input. Running it through functions. And outputting it. I have tried to do this in 2 different ways but neither work. Looking around online I see everyone only using the variable of the input inside the main. This would explain why I'm getting a "not in scope" error. But how would this be possible? Here are my two attempts.
result = lcm 3 inp
main = do
inp <- getLine
putStr result
and this:
main = do
inp <- getLine
putStr result
where
result = lcm 3 inp
inp exists only within the scope of the do expression, which explains why you get an error in the first version.
As for the second version. it can be rewritten to:
main = e
where
e = do
inp <- getLine
putStr result
result = lcm 3 inp
The two where bindings have different scopes, which is why a local binding from one expression is inaccessible from the other.
This, on the other hand, should work:
main = do
inp <- getLine
let result = lcm 3 inp
putStr result
result is now defined within the do notation scope, so it can use inp. If you still want to use the where clause, result will need to accept inp as an argument:
main = do
inp <- getLine
putStr result
where
result inp = lcm 3 inp
Let's see the types of the things you use:
*Main> :t lcm
lcm :: Integral a => a -> a -> a
*Main> let result inp = lcm 3 inp
*Main> :t result
result :: Integral a => a -> a
But you read in a String:
*Main> :t getLine
getLine :: IO String
So, you need to convert a String to something like an integer, and convert an Inegral the result returned back to Striing for printing.
main = do
inp <- getLine
putStr $ show $ result (read inp)

Can I be sure of order of IO actions in this example?

At the moment, I have this code in and around main:
import Control.Monad
import Control.Applicative
binSearch :: Ord a => [a] -> a -> Maybe Int
main = do
xs <- lines <$> readFile "Cars1.txt"
x <- getLine <* putStr "Registration: " -- Right?
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
My hope is for “Registration: ” to be printed, then for the program to wait for the input to x. Does what I've written imply that that will be the case? Do I need the <*, or will putting the putStr expression on the line above make things work as well?
PS: I know I have to convert binSearch to work with arrays rather than lists (otherwise it's probably not worth doing a binary search), but that's a problem for another day.
The line
x <- getLine <* putStr "Registration: "
orders the IO actions left-to-right: first a line is taken as input, then the message is printed, and finally variable x is bound to the result of getLine.
Do I need the <*, or will putting the putStr expression on the line
above make things work as well?
If you want the message to precede the input, you have to put the putStr on the line above, as follows:
main :: IO ()
main = do
xs <- lines <$> readFile "Cars1.txt"
putStr "Registration: "
x <- getLine
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
Alternatively,
x <- putStr "Registration: " *> getLine
or
x <- putStr "Registration: " >> getLine
would work, but they are less readable.
Finally, since you added the lazy-evaluation tag, let me add that your question is actually not about laziness, but about how the operator <* is defined, and in particular about the order in which it sequences the IO actions.

Haskell, using multiple if statements on input and output IO

Here is my current code, I want to be able to create a MAIN IO that acts as an interface and allows the user to pick which functions to execute depending on their choice.
type Title = String
type Actor = String
type Cast = [Actor]
type Year = Int
type Fan = String
type Fans = [Fan]
type Period = (Year, Year)
type Film = (Title, Cast, Year, Fans)
type Database = [Film]
title (t, _, _, _) = t
cast (_, c, _, _) = c
year (_, _, y, _) = y
fans (_, _, _, fs) = fs
testDatabase :: Database
testDatabase = [("Casino Royale", ["Daniel Craig", "Eva Green", "Judi Dench"], 2006, ["Garry", "Dave", "Zoe", "Kevin", "Emma"]),
("Cowboys & Aliens", ["Harrison Ford", "Daniel Craig", "Olivia Wilde"], 2011, ["Bill", "Jo", "Garry", "Kevin", "Olga", "Liz"]),
("Catch Me If You Can", ["Leonardo DiCaprio", "Tom Hanks"], 2002, ["Zoe", "Heidi", "Jo", "Emma", "Liz", "Sam", "Olga", "Kevin", "Tim"])]
Function 1:
displayAllFilms' :: [Film] -> String -> String
displayAllFilms' [] filmString = filmString
displayAllFilms' ((title,cast,year,fans):films) filmString =
displayAllFilms' films (filmString ++ "\n" ++ title ++ ", " ++ listStuff cast ", " ++ (show year) ++ ", " ++ show (length fans))
Function 2:
filmsByFan f = map title $ filter (elem f . fans) testDatabase
The above code is 100% working and I am able to pull the information out of the given database.
Here is the example MAIN IO that I have created:
main :: IO ()
main = do putStrLn "What function would you like to execute? 1. addFilm / 2.displayAllFilms / 3. filmsByYear / 4. filmsByFan / 5. filmsByActor Period / 6. becomeFan: "
str <- getLine
if str /= "2"
then return ()
else do putStrLn (displayAllFilms' testDatabase str)
if str /= "4"
then return ()
else do putStrLn "Enter an existing fan: "
name <- getLine
putStrLn filmsByFan name <<< **error here**
main
The problem I am having is when more than 2 functions are applied to the IO if statement, it doesn't seem to register when 'name' is assigned by a user input and thus the rest of the code from name <- getLine does not compile.
My question is: Is there a way for the user to select what function to execute depending on what they have chosen and execute the appropriate function...?
Thanks in advance!
Edit:
Example on how I wish the code to execute:
Main
which function would you like to execute...1,2,3,4,5,6?
user enters: 1
displayFilms will execute.
IF USER ENTERS 4
user enters: 4
Enter an existing fan:
user enters: Liz"
execute filmsByFan
You say the error message you get is
ExampleSolution.hs:167:12: parse error on input `do'
You don't say which line is line 167.
You appear to be mixing spaces and tabs. This may be the cause of the problem. Replace all tabs in your source code with spaces.
Edit: I said you appear to be mixing spaces and tabs because the source code in your question mixes spaces and tabs. e.g. the indentation of the line with the error ends with tabs, whereas the indentation in the line above it ends with eight spaces and does not appear to be indented as far.
This is consistent with the lines
name <- getLine
putStrLn filmsByFan name
actually being
name <- getLine
putStrLn filmsByFan name
and being interpreted as
name <- getLine putStrLn filmsByFan name
which would cause the error you now report:
ExampleSolution.hs:174:61: Not in scope: `name'
as name is defined on the left of the <- and thus not available for use on the right: it is only available for use in subsequent lines in the block.
Check that your editor is not converting spaces to tabs when it saves your file, contrary to your wishes. Alternatively, ensure that it uses 8-space-wide tabs (I would guess that you're probably using 4-space-wide tabs).
n.b. You probably intend for these lines to read
name <- getLine
putStrLn (filmsByFan name)
You could put all the functions in a table, and use lookup to select which one to run.
table = [(1, addFilm)
,(2, displayAllFilms)
,(3, filmsByYear)
,(4, filmsByFan)
]
I renamed your filmsByFan to filmsByFan' (prime), so I could add a layer to prompt for the key and call your orig function:
filmsByFan = do putStr "Enter Fan"
f <- getLine
print $ filmsByFan' f
filmsByFan' f = map title $ filter (elem f . fans) testDatabase
main :: IO ()
main = do putStrLn "What function would you like to execute? 1. addFilm / 2.displayAllFilms / 3. filmsByYear / 4. filmsByFan / 5. filmsByActor Period / 6. becomeFan: "
str <- getLine
let n = (read str)::Int
cmd = lookup n table
case cmd of
Just doIt -> doIt
Nothing -> do print "sorry"
main

Syntax confusion (do block)

Sorry for a poor title, feel free to edit. I can't understand what the problem is, so it might be altogether wrong. Below is the code (this is after I've done like a hundred of permutations and different sequences of let-do-if and tabulation, and I'm exhausted):
-- The last statement in a 'do' construct must be an expression
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n"
let intYear = readYear
in if (intYear < 2000 || intYear > 2012)
then error "Year must be withing range: 2000-2012"
else
c <- readIORef connection
[Only i] <- query_ c ("select count('*')" ++
"from table" ++
"where ((acquisition_date <= " ++
(formatDate intYear) ++
") and ((sale_date is null) or " ++
"(sale_date < " ++
(formatDate intYear) ++ ")))")
return i
readYear :: Integer
readYear = do
year <- getLine
read year :: Integer
Something that would meant to be so simple... I still don't understand what is wrong with the code above. Please, if you could kindly explain the source of the error, that would be great.
I did read about do, let-in and if-then-else, and I don't see any errors here from what I could understand from the manual.
Ideally, if there are alternatives, I would like very much to reduce the amount of the wasted white space on the left.
Thank you.
readYear is not an Integer, it's an IO action that can be run to read input and convert the input to an integer -- in other words, IO Integer. And as it's an IO action, you'll need a return to use whatever read year as result of getYear. That is:
getYear :: IO Integer
getYear = do year <- getLine
return (read year)
This also means you use it like intYear <- readYear instead of using let (well, you could, but you'd store the IO action instead of running it, and the type of intYear would be wrong). That is:
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n"
intYear <- readYear
...
do does not extend over if, rather you need to start again with do if you want a sequence of actions in the then or else branch. That is:
else
c <- readIORef connection
...
return i
should be roughly:
else do c <- readIORef connection
...
return i
As for reducing whitespace, consider pushing the validation logic into readYear. Implementing this is left as an exercise to the reader ;)
As an aside, you don't need in when using let in a do block (but only there!), you can simply state:
do do_something
let val = pure_compuation
something_else_using val
You need a new do for every block of monadic functions: simply writing functions in a row has no meaning, regardless of whether they're monadic or pure. And everything where the value comes from the IO monad must itself give its return value in the monad.
numberOfGoods :: IO String
numberOfGoods = do putStrLn "Enter year (2000-2012):\n" -- why extra '\n'?
intYear <- readYear -- readYear expects user input <- must be monadic
if (intYear < 2000 || intYear > 2012)
then error "Year must be withing range: 2000-2012"
else do
c <- readIORef connection
[Only i] <- query_ c ("select count('*')" ++
"from table" ++
"where ((acquisition_date <= " ++
(formatDate intYear) ++
") and ((sale_date is null) or " ++
"(sale_date < " ++
(formatDate intYear) ++ ")))")
return i
readYear :: IO Integer
readYear = do
year <- getLine
return $ read year :: Integer
Why is an extra do needed...
Well, the thing with do in Haskell is that it's really just syntactic sugar. Let's simplify your function a little
nOG :: IO String
nOG = do putStrLn "Prompt"
someInput <- inputSth
if condition someInput
then error "Bloap"
else do c <- inputSthElse
[only] <- query_ c
return only
what this actually means is
nOG :: IO String
nOG = putStrLn "Prompt"
>> inputSth
>>= (\someInput ->
if condition someInput
then error "Bloap"
else inputSthElse
>>= (\s -> query_ c
>>= (\[only] -> return only )
)
)
Where you should be able to see that if behaves in exactly the same way as it does in a pure functional expression like shade (r,g,b) = if g>r && g>b then "greenish" else "purpleish". It doesn't in any way "know" about all the IO monad stuff going on around it, so it can't infer that there should again be a do block in one of its branches.

Resources