IO action in a when statement - haskell

Can anyone tell me why I cannot add a IO statement within a do block? This code only prints "test" and then completes execution. The last two lines do not seem to be executing.
putStrLn "Do you want to add a task. Press y to add:"
option <- getChar
when (option == 'y') $ do
print "test"
newText <- getLine
appendFile "todoList.txt" (newText ++ "\n")

getChar will peak the y char but will let the \n in the input stream.
So you need to flush the input stream before going further.
Alternatively you can use the readLn providing you define a new data type:
data Choice = Y | N
deriving (Read, Show, Eq)
putStrLn "Do you want to add a task. Press y to add:"
option <- readLn
when (option == Y) $ do
print "test"
getLine >>= appendFile "todoList.txt" . (++"\n")

Related

How do I save changes I made to a list in an immutable way?

I need to ask user for input, do something with it, ask for next input and do something with it but I still need to take the previous input into account.
I was thinking(in an OOP way) of saving all the input <- getLines into a list and then be able to make changes to that list(and store them), but I can't do that because stuff immutable in haskell.
main = do
args <- getArgs
contents <- readFile $ head args
putStrLn contents
let inputs = [] --THIS
forever $ do
putStrLn $ "Filtering: " ++ appendFilters inputs
input <- getLine
let inputs = updateInputs inputs input --THIS
newInput <- getUserInputs inputs input
putStrLn $ unlines (filterContent (lines contents) newInput)
How do I do this right in the eyes of functional programming?
Edit:
So what my program is supposed to do is:
Read contents from file
Get user input in order to only display lines containing those words
Get more user input, if input == "pop" then delete last filter, otherwise add to the list of filtering words
If user tries to pop an empty list of filters, exit program
First off, you can have mutable values in Haskell, you just need to make all the updates in the IO monad. We usually avoid that, but if your code is all about IO interactivity anyway then it may be the most straightforward solution.
import Data.IORef
main = do
args <- getArgs
contents <- readFile $ head args
putStrLn contents
inputs <- newIORef []
forever $ do
currentInputs <- readIORef inputs
putStrLn $ "Filtering: " ++ appendFilters currentInputs
input <- getLine
updateInputs inputs input
newInput <- getUserInputs inputs input
putStrLn $ unlines (filterContent (lines contents) newInput)
The more functional alternative is generally to explicitly pass updated versions of the stateful variables through the recursion “loops”. You won't be able to use forever then, but it's easy to change that to manual recursion:
main = do
args <- getArgs
contents <- readFile $ head args
putStrLn contents
let loop inputs = do
putStrLn $ "Filtering: " ++ appendFilters inputs
input <- getLine
newInput <- getUserInputs inputs input
putStrLn $ unlines (filterContent (lines contents) newInput)
loop $ updateInputs inputs input
loop []

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.

Haskell: Print case number

I have written a Haskell code as:
loop = do
x <- getLine
if x == "0"
then return ()
else do arr <- replicateM (read x :: Int) getLine
let blocks = map (read :: String -> Int) $ words $ unwords arr
putStr "Case X : output = "; -- <- What should X be?
print $ solve $ blockPair blocks;
loop
main = loop
This terminates at 0 input. I also want to print the case number eg. Case 1, 2 ...
Sample run:
1
10 20 30
Case 1: Output = ...
1
6 8 10
Case 2: Output = ...
0
Does anyone know how this can be done? Also, If possible can you suggest me a way to print the output line at the very end?
Thanks in advance.
For the first part of your question, the current case number is an example of some "state" that you want to maintain during the course of your program's execution. In other languages, you'd use a mutable variable, no doubt.
In Haskell, there are several ways to deal with state. One of the simplest (though it is sometimes a little ugly) is to pass the state explicitly as a function parameter, and this will work pretty well given the way you've already structured your code:
main = loop 1
loop n = do
...
putStr ("Case " ++ show n ++ ": Output = ...")
...
loop (n+1) -- update "state" for next loop
The second part of your question is a little more involved. It looks like you wanted a hint instead of a solution. To get you started, let me show you an example of a function that reads lines until the user enters end and then returns the list of all the lines up to but not including end (together with a main function that does something interesting with the lines using mostly pure code):
readToEnd :: IO [String]
readToEnd = do
line <- getLine
if line == "end"
then return []
else do
rest <- readToEnd
return (line:rest)
main = do
lines <- readToEnd
-- now "pure" code makes complex manipulations easy:
putStr $ unlines $
zipWith (\n line -> "Case " ++ show n ++ ": " ++ line)
[1..] lines
Edit: I guess you wanted a more direct answer instead of a hint, so the way you would adapt the above approach to reading a list of blocks would be to write something like:
readBlocks :: IO [[Int]]
readBlocks = do
n <- read <$> getLine
if n == 0 then return [] else do
arr <- replicateM n getLine
let block = map read $ words $ unwords arr
blocks <- readBlocks
return (block:blocks)
and then main would look like this:
main = do
blocks <- readBlocks
putStr $ unlines $
zipWith (\n line -> "Case " ++ show n ++ ": " ++ line)
[1..] (map (show . solve . blockPair) blocks)
This is similar in spirit to K. A. Buhr's answer (the crucial move is still passing state as a parameter), but factored differently to demonstrate a neat trick. Since IO actions are just normal Haskell values, you can use the loop to build the action which will print the output without executing it:
loop :: (Int, IO ()) -> IO ()
loop (nCase, prnAccum) = do
x <- getLine
if x == "0"
then prnAccum
else do inpLines <- replicateM (read x) getLine
let blocks = map read $ words $ unwords inpLines
prnAccumAndNext = do
prnAccum
putStr $ "Case " ++ show nCase ++ " : output = "
print $ solve $ blockPair blocks
loop (nCase + 1, prnAccumAndNext)
main = loop (1, return ())
Some remarks on the solution above:
prnAccum, the action which prints the results, is threaded through the recursive loop calls just like nCase (I packaged them both in a pair as a matter of style, but it would have worked just as fine if they were passed as separate arguments).
Note how the updated action, prnAccumAndNext, is not directly in the main do block; it is defined in a let block instead. That explains why it is not executed on each iteration, but only at the end of the loop, when the final prnAccum is executed.
As luqui suggests, I have removed the type annotations you used with read. The one at the replicateM call is certainly not necessary, and the other one isn't as well as long as blockPair takes a list of Int as an argument, as it seems to be the case.
Nitpicking: I removed the semicolons, as they are not necessary. Also, if arr refers to "array" it isn't a very appropriate name (as it is a list, and not an array), so I took the liberty to change it into something more descriptive. (You can find some other ideas for useful tricks and style adjustments in K. A. Buhr's answer.)

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.

Loop with StateT: Why this loop doesn't loop

I don't understand why this code does loop only once then exit ?
in Ghci I can answer only to the first loop then it seems the variable cont is set to false and I don't have the prompt to answer.
The result is:
*Main> testLoop1 td10
test
Do you want to continue? (y/N)
y
we continue
test
Do you want to continue? (y/N)
We stop
code:
type TDeckSTIO = StateT TableDecks IO
continue = do
putStrLn "Do you want to continue? (y/N)"
c <- getChar
return $ c == 'y'
loop1 :: TDeckSTIO ()
loop1 = do
liftIO $ putStrLn "test"
cont<- liftIO continue
if cont
then do
liftIO $ putStrLn "we continue"
liftIO $ testLoop1 td
else liftIO $ putStrLn "We stop"
testLoop1 td = runStateT (loop1 ) td >> return ()
The problem is that when you type y and hit enter, that's actually typing two characters: 'y' itself, and the newline character that gets sent by pressing the return key. The first time round, the loop sees the 'y', but the next time round, it sees the '\n', and since '\n' isn't 'y', it exits.
You can either do hSetBuffering stdin NoBuffering before you enter your loop (you'll need to import System.IO), which will let you process characters without waiting for a newline, or specifically process lines at a time:
continue = do
putStrLn "Do you want to continue? (y/N)"
s <- getLine
return $ s == "y"
By the way, instead of writing liftIO $ testLoop1 td, you can just stay in the same state monad: you can replace it with loop1 and it'll work exactly the same.
Also, testLoop1 is better written as:
testLoop1 = evalStateT loop1
evalStateT is like runStateT, but doesn't include the final state, so you don't have to explicitly discard the value with >> return ().

Resources