Reading line by line in Haskell - haskell

I'm trying to read from standard input line by line, and process each line with the function of the type foo :: String -> Int. Is there any way to do that provided that we don't know the number of lines we want to read OR given that the number of lines is provided on the first line?
What I've tried
A lot of things that give meaningless errors, such as "parser error".
For example
main = do {
getLine <- getContents;
let result = show (foo getLine);
putStrLn (foo result);
}
Edit
Strange, but this does not print the length of a
main = do {
a <- getContents;
putStrLn (show (length a));
}
but, this does print 5.
main = do {
a <- getContents;
putStrLn (show 5);
}

The main example of doing that will look as this:
main = do
line <- getLine
yourfunction line
main
this will take lines forever and process them with your function, in case you want it to stop sometime, just check for a command for example:
main = do
line <- getLine
let res = yourfunction line
if res == "Exit" then IO () else main

You may use the function lines to convert the String into [String]. Afterwards map your foo over this list of lines.
Regarding the edit: Your program printing the length works for me. Try either inputting a file or - if entering input interactively - terminating it correctly (via Ctrl-d).
Sidenote: curly brackets and semicolons are rarely seen usually. But this is just style.

Related

Convert data read from a file to a variable in Haskell

I have a problem - I am writing in a file in Haskell (I wanna write a sentence in the file and everytime I write in it I want to overwrite the content of the file so this func does the work for me completely fine)
writeFunc message = writeFile file message where
file = "abc.txt"
And then reading from the same file
readFunc = do
let file = "abc.txt"
contents <- readFile file
return contents
And then I wanna save the things I have read in a variable:
In the terminal doing this
let textFromAFile = readFunc
results into this:
*Main> let textFromAFile = readFunc
*Main> textFromAFile
"okay"
But when I use let textFromAFile = readFunc inside my code, the code wont compile
[1 of 1] Compiling Main ( tree.hs, interpreted )
tree.hs:109:29: error:
parse error (possibly incorrect indentation or mismatched brackets)
Failed, modules loaded: none.
I wanna save it in a variable so I can use it later in other functions. Why it works in the terminal but wont compile and what I can do to make it work? ReadFunc returns IO String and is there a possible way to convert it to s String so I can use it in a pure func?
readFunc has type IO String, you can use it in another IO expression with:
someIO = do
textFromAFile <- readFunc
-- use textFromFile (String) …
-- …
for example:
someIO = do
textFromAFile <- readFunc
writeFunc (textFromAFile ++ "/")
The reason it works in the GHCi terminal is that the terminal evaluates IO a objects, so while textFromAFile is an IO String, and the terminal will thus evaluate textFromAFile.

How to make a Haskell program display a preliminary result in response to user input?

I am writing a program in Haskell which repeatedly takes its most recent result and uses this to compute the next result. I want to be able to see the newest result in response to user input, so I tried something like this:
main = mainhelper 0
mainhelper count = do
count <- return (count + 1)
line <- getLine
if null line
then do mainhelper count
else do
putStrLn $ show count
return ()
I was hoping that getLine would return an empty line if the user hasn't entered anything, but this doesn't happen, instead the program does nothing until it receives user input. Is there a way around this?
One simple solution is to fork a thread for the complicated computation and communicate with the main UI thread via MVar. For example:
import Control.Exception
import Control.Monad
import Control.Concurrent
thinkReallyHard x = do
threadDelay 1000000 -- as a proxy for something that's actually difficult
evaluate (x+1)
main = do
v <- newMVar 0
forkIO (forever (modifyMVar_ v thinkReallyHard))
forever (getLine >> readMVar v >>= print)
You may wonder about the role of evaluate in thinkReallyHard. The subtlety there is that MVars are lazy -- they can contain thunks just as easily as computed values. In particular, this means it's easy to accidentally push all the pure computation from the forked thread into the thread that's reading and using the contents of the MVar. The call to evaluate simply forces the forked thread to finish the pure computation before writing to the MVar.
It does return an empty line if you hit enter without entering text -- you just immediately prompt for more input, so it might look like nothing is happening. But if you run the program, hit enter three times, then enter something non-empty, you'll see that the final count reflects the multiple entries.
Here's a modified version of your code that does the same thing, but is slightly more canonical:
main = mainhelper 0
mainhelper count = do
let count' = count + 1
line <- getLine
if null line
then mainhelper count'
else print count'
Rather than count <- return (count + 1), you can write let count' = count + 1 -- this is a pure binding, not something that needs to invoke the IO monad (as you're doing with <- and return). But I used count' instead of count because otherwise that will create a recursive binding. The '-suffixing is a standard idiom for a "modified version" of an identifier.
Next I switched putStrLn . show to print, which is part of the Prelude and does exactly that.
I got rid of the return () because print (and putStrLn) already have the type IO (). This allows you to elide the do statements, as there's now a single IO expression in each branch of the if.
It's not really clear what you're trying to do here that's different from what you are doing -- the code does (in imperative terms) increment a counter every time the user presses enter, and displays the state of the counter every time the user enters some non-empty text.
Here's another version that prints the counter every time, but only increments it when prompted, which may or may not be helpful to you:
main = mainhelper 0
mainhelper count = do
print count
line <- getLine
mainhelper (if null line then count else succ count)
I'm using succ, the successor function, instead of the explicit + 1, which is just a style preference.

Haskell: Why does this function keep asking for user input and not terminating

I'm learning some Haskell and I came across this small program
reverseLines :: String -> String
reverseLines input =
unlines (map reverse (lines input))
main :: IO ()
main = interact reverseLines
This program will keep asking the user for more input and reverse the input and print it on the screen.
Most of this is straight forward but one thing I can't wrap my head around is why does this function keeps running and ask the user for more input whereas if I just replace the reverseLines function with a function the simply returns some string it will not happen.
This program will stop after one execution:
foo input = "Stops"
main :: IO ()
main = interact foo
Why?
If you look at the source of interact you see this:
interact f = do s <- getContents
putStr (f s)
see the getContents? This is where the magic starts - it will read everything till EOF
Now in Haskell this is lazy-IO which can be bad but here is almost magical - see the string is read lazily and passed to your reverseLines - this one of course will only generate output as soon as it saw \n characters (the lines) and so it seems your program is some kind of REPL.
In the second one you don't consume any of the lazy-string at all so it stops ASAP
As I wrote in the comments you can play with this by either passing content into the program using a file (or echo) and pipes on the terminal:
echo "Hello World\nBye Bye" | runhaskell LazyIO.hs
or using CTRL-D to pass in the EOF yourself.
To get a feeling for it I would play with the functions more - what happens if you use something that needs to see the complete input first (try reverse without the maps)? What happens with words instead of lines, ...?
Have fun!

Unable to take 2 inputs simultaneously

I'm a haskell beginner. My code:
module Main (main) where
import System.IO
--Type for identifying Students.
data Student = Student {
name :: String,
regnum :: Integer
} deriving (Show, Read)
getData :: IO (String)
getData = do
putStr "\tEnter the student's name: "
name <- getLine
putStr "\tEnter the registration number: "
regstr <- getLine
let regno = (read regstr) :: Integer
return (show ( Student name regno ))
addData :: String -> IO (Bool)
addData filename = do
fileData <- getData
appendFile filename fileData
return True
printData :: String -> IO (Bool)
printData filename = do
fileData <- readFile filename
putStrLn fileData
return True
resetFile :: String -> IO (Bool)
resetFile filename = writeFile filename "" >> return True
action :: Char -> String -> IO (Bool)
action option filename =
if option == '1'
then addData filename
else if option == '2'
then printData filename
else if option == '3'
then resetFile filename
else return False
main = do
putStr "What do you want to do?\n\t1) Add a new record to a file.\n\t2) Read a record from a file.\n\t3) Reset the file.\n>> "
option <- getChar
action option "records.txt"
The output I'm getting is:
What do you want to do?
1) Add a new record to a file.
2) Read a record from a file.
3) Reset the file.
>> 1
Enter the student's name: Enter the registration number:
Hence I'm unable to provide input to for the student's name. I'm running the code on ghci. When I tried to see how it runs as an executable it gave a even weirder output.
What do you want to do?
1) Add a new record to a file.
2) Read a record from a file.
3) Reset the file.
(Notice it doesn't print ">>"). Only after I press Enter twice does it print ">>".
I can't understand whats happening here. Other improvements to my code are very welcome.
EDIT:: By using getLine instead of getChar , the program works on ghci(thanks to Daniel Fischer). But it still doesn't work when compiled. The new output is:
What do you want to do?
1) Add a new record to a file.
2) Read a record from a file.
3) Reset the file.
1 (Typed my me)
Tom (Typed my me)
234 (Typed my me)
>> Enter the student's name: Enter the registration number:
On re-running to read the file:
What do you want to do?
1) Add a new record to a file.
2) Read a record from a file.
3) Reset the file.
2 (Typed my me)
>> Student {name = "Tom", regnum = 234}
Why is ">>" and getData's putStrs being printed after taking the input?
option <- getChar
With the default buffering settings, the programme only receives the input after a newline has been entered. The getChar, however, removes only the first character entered from the input buffer, so the following getLine reads from the input buffer until it finds a newline. In your case, immediately at the beginning.
You can (at least on *nix-ish systems, buffering control used to not work properly on Windows, I don't know if it now does) solve the problem by turning off buffering for stdin,
main = do
hSetBuffering stdin NoBuffering
...
so the getChar receives the input without a newline being typed. Alternatively, instead of using getChar for the option, use
(option:_) <- getLine
so there doesn't remain anything in the input buffer to auto-satisfy the following getLine.
Also, to get the prompts printed out before the input is entered, call
hFlush stdout
to flush the output buffer, or turn off buffering for stdout.
Most simple is probably to define
prompt :: String -> IO ()
prompt msg = do
putStr msg
hFlush stdout
and replace the calls to putStr with calls to prompt.
Critique of coding style:
getData :: IO (String)
You enclose the arguments of the IO type constructor in parentheses. That's not necessary and unidiomatic. The normal way to write it is IO String. (You probably have the parentheses from seeing IO () somewhere, but those are not parentheses enclosing no type, that's the type constructor for the () type. Confusing? Yes.)
action :: Char -> String -> IO (Bool)
action option filename =
if option == '1'
then addData filename
else if option == '2'
then printData filename
else if option == '3'
then resetFile filename
else return False
That should become a case,
action option filename
= case option of
'1' -> addData filename
'2' -> printData filename
'3' -> resetFile filename
_ -> return False
Apart from that, the code looks clean.

Haskell read first n lines

I'm trying to learn Haskell to get used to functional programming languages. I've decided to try a few problems at interviewstreet to start out. I'm having trouble reading from stdin and doing io in general with haskell's lazy io.
Most of the problems have data coming from stdin in the following form:
n
data line 1
data line 2
data line 3
...
data line n
where n is the number of following lines coming from stdin and the next lines are the data.
How do I run my program on each of the n lines one at a time and return the solution to stdout?
I know the stdin input won't be very large but I'm asking about evaluating each line one at a time pretending the input is larger than what can fit in memory just to learn how to use haskell.
You can use interact, in conjunction with lines to process data from stdin one line at a time. Here's an example program that uses interact to access stdin, lines to split the data on each newline, a list comprehension to apply the function perLine to each line of the input, and unlines to put the output from perLine back together again.
main = interact processInput
processInput input = unlines [perLine line | line <- lines input]
perLine line = reverse line -- do whatever you want to 'line' here!
You don't need to worry about the size of the data you're getting over stdin; Haskell's laziness ensures that you only keep the parts you're actually working on in memory at any time.
EDIT: if you still want to work on only the first n lines, you can use the take function in the above example, like this:
processInput input = unlines [perLine line | line <- take 10 (lines input)]
This will terminate the program after the first ten lines have been read and processed.
You can also use a simple recursion:
getMultipleLines :: Int -> IO [String]
getMultipleLines n
| n <= 0 = return []
| otherwise = do
x <- getLine
xs <- getMultipleLines (n-1)
return (x:xs)
And then use it in your main:
main :: IO ()
main = do
line <- getLine
let numLines = read line :: Int
inputs <- getMultipleLines numLines

Resources