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?
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 followinggetLinereads 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,so the
getCharreceives the input without a newline being typed. Alternatively, instead of usinggetCharfor the option, useso 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
to flush the output buffer, or turn off buffering for
stdout.Most simple is probably to define
and replace the calls to
putStrwith calls toprompt.Critique of coding style:
You enclose the arguments of the
IOtype constructor in parentheses. That’s not necessary and unidiomatic. The normal way to write it isIO String. (You probably have the parentheses from seeingIO ()somewhere, but those are not parentheses enclosing no type, that’s the type constructor for the()type. Confusing? Yes.)That should become a
case,Apart from that, the code looks clean.