Prompt message disappears in compiled executable? - haskell

I have a Haskell program that shows a prompt and then accepts input from the command line. I'm doing this as:
main = do putStr "Please enter program source file name: "
programFileName <- getLine
programFileHandle <- openFile programFileName ReadMode
program <- hGetContents programFileHandle
putStr "Please enter initial file configuration file name: "
initConfigFileName <- getLine
initConfigFileHandle <- openFile initConfigFileName ReadMode
initConfigStr <- hGetContents initConfigFileHandle
print (evaluateProgram (lines program) (readReg initConfigStr))
When I run it on the GHCi interpreter, the prompts show up fine and I am able to enter my inputs (and everything else works).
e.g.
*Main> main
Please enter program source file name: sum.urm
Please enter initial file configuration file name: sum.conf
9
When I compile it though (on Mac OS X or Windows), it produces an executable that does not show my prompts. It waits for the two input strings, and then once I have put in the valid filenames, it prints the prompts and the result.
e.g.
$ ./a.out
sum.urm
sum.conf
Please enter program source file name: Please enter initial file configuration file name: 9
Any ideas why this is happening?
For the curious, I was implementing an Unlimited Register Machine in Haskell.

The standard output stream, stdout, is line buffered by default. That means that it will only be written to the console every time you output a \n character, or finally when the program terminates. You can fix this by importing System.IO and doing hFlush stdout after every putStr that doesn't contain a \n at the end.

This is a buffering issue. Here are related questions, with several choices for solutions:
Why isn't my IO executed in order?
Execution order with (>>=) not what I expected

Related

Why doesn't my Haskell cmd line program get arguments from Vim Bang?

Vim has the possibility to let you replace selected text with the output of an external program. I'd like to take advantage of this with programs that I'd write in Haskell. But it doesn’t get the selected text as args.
-- show-input.hs
module Main where
import System.Environment
main = do
input <- getArgs
putStr ("Input was: " ++ (show input))
When I run it from the command line (NixOS GNU/Linux, BASH), I get the expected behavior:
$ ./show-input test
Input was: ["test"]
When I select some text in Vim and invoke :'<,'>!~/show-input, I get this :
Input was: []
There is something weird here, but I can't tell if it is from the way Vim passes arguments or from the way Haskell gets them. I have tried with both console Vim and graphical gVim (8.0.1451), with the same result.
NB: I can successfully use Vim Bang! with other external programs, such as grep. It works great.
---
Correct version after chepner's answer
So, for anyone interested, just replace getArgs with getContents and you get your input all in a string (instead of a list of strings).
module Main where
import System.Environment
main = do
input <- getContents
putStr ("Input was: " ++ (show input))
The ! command sends the seleted text to the program via standard input, not as a command line argument. The command line equivalent would be somecommand | ./show-input.

Why does my Haskell program not accept standard input redirection?

I'm trying to read a file in Haskell by supplying the file name as a command line argument.
I have read that you can accomplish this by:
./program < input.txt
I wrote this code:
main = do
[fileName] <- getArgs
file <- readFile fileName
print file
But I get this error: "pattern match failure in do expression". If I omit the < sign it works, is this the only way to accomplish this? I would much rather not omit it. What should I change?
./program < input.txt calls the program with 0 arguments and redirects stdin to the contents of input.txt.
So you get a pattern matching error because getArgs is empty. So if you want your program to always read from stdin, don't use the command line arguments at all and read from stdin instead of a file.
If you want your program to read from stdin only if no file name was given, check the length of the arguments first and then read from the given file name or from stdin depending on that.
If you run ./program arg then arg is passed as an argument. The standard input is left on its default -- usually reading from keyboard from a terminal.
If you run ./program < filename then no arguments are passed to the program. The standard input now is redirected so to read from file filename.
This is just how the OS shell works.
In Haskell, getArgs gets the program arguments. In the second case, they are empty, and [fileName] <- getArgs fails with your runtime error.

Weird buffering with prompt in haskell [duplicate]

This question already has answers here:
Why doesn't Haskell sequence these IO actions properly?
(2 answers)
Closed 7 years ago.
I was playing around in Haskell and noticed something weird. I've defined a simple prompt function below.
-- file: test.hs
main :: IO ()
main = putStrLn . ("Hello, " ++) =<< (putStr "Name: " >> getLine)
When I runhaskell this, it works as expected, printing the prompt, waits for my input, then prints the greeting.
$ runhaskell test.hs
Name: kwarrtz
Hello, kwarrtz
When I compile it, however, things get weird. When I run it, it doesn't print the prompt, instead giving me a blank line and waiting for input. When I type my name and hit enter, it prints the prompt and the greeting, on the same line. In otherwords, the getLine happens before the putStr.
$ ghc test.hs
$ ./test
kwarrtz
Name: Hello, kwarrtz
Any thoughts on what's happening? I imagine it has something to do with the line buffering on my terminal, but I'm not sure how (that or I've just made some really ridiculous mistake in my code).
I'm running GHC 7.8.3 on Mac OS X El Capitan and using the default Terminal app.
Buffering.
hSetBuffering stdout NoBuffering
I think you can get all that from System.IO

IO monad and ordering [duplicate]

I have the following code:
main = do
putStr "Test input : "
content <- getLine
putStrLn content
When I run it (with runhaskell) or compile it (ghc 6.10.4) the result is like this:
asd
Test input : asd
Why is Test input : asd being printed after asd?
In the code sample on http://learnyouahaskell.com/, which uses putStr, the getLine's presented output is different than mine. When I use putStrLn the program works as expected (print, then prompt, and print).
Is it a bug in ghc, or it is the way that it should work?
This is because ghci disables buffering, while a program compiled with ghc has line buffering by default. You can see this by running this:
import System.IO
main = print =<< hGetBuffering stdout
In ghci you see NoBuffering while with runghc you get LineBuffering. Since the newline character doesn't print until after the user input, the prompt doesn't either.
Fix it by adding hFlush stdout after your prompt (or disable buffering with hSetBuffering stdout NoBuffering, but that’s probably bad).

Haskell compilation with an input file, error openFile: does not exist (No such file or directory)

I apologize if this is a simple question.
I have specified an input file which is in the same directory of the code source file.
isprime :: Int -> [Int] -> Bool
isprime ...
main = do
handle <- openFile "primes-to-100k.txt" ReadMode
contents <- hGetContents handle
i <- getLine
print $ isprime (read i::Int) $ map (\x->read x::Int) $ lines contents
hClose handle
The code runs well when I use "runhaskell Q111.hs" in shell,
but when I compile it with "ghc --make Q111.hs and run, I got an error message
Q111: primes-to-100k.txt: openFile: does not exist (No such file or directory)
logout
The point is that the code runs well with ghci and runhaskell, but the executable can't find the input txt.
Do I have to provide the input txt to compiler using someway?
How are you running the executable? The text file would have to be in the directory you run the program from, which may or may not be where the program is. If you are running ./Q111 from the command line, or if you are double-clicking Q111.exe in Windows, then the text file must be in the same folder as the executable. If you are in a different directory on the command line, then the text file would have to be wherever your current directory is, and not the directory where the executable is.
EDIT: Just saw from your other comment that you are on OS X, and (I assume) are double-clicking the program. From what you said I guess that OS X sets the current directory of such-executed programs to be your home. If you want to get the directory of your program, see the answers to this question. If you use the FindBin package they mention:
main = do
exedir <- getProgPath
handle <- openFile (exedir ++ "/primes-to-100k.txt") ReadMode
...
Try the curdDir <- getCurrentDirectory and paths <- getDirectoryContents and check the paths. If it contains the filename, the path is right(and try using contents <-readFile "file" for simpler programs, but this isn't neccessary).
Edit:
...and use isexist <- doesFileExist "f" for checking the existence.

Resources