Talk:Haskell/Simple input and output

Rewrite
Hi Paul,

I hope I haven't been too brusque in editing this page here. I have replaced most of your content with bits of YAHT and some of my attempts at spoon-feeding. The goal here is to get the reader doing IO as early as conveniently possible so that he or she is off and running building useful programmes as quickly as possible. I'm hoping that that being able to do IO from the almost the very beginning (it's sometimes tempting to just start from here!) would help us build a much more hands-on wikibook. But as a result of trying to explain things early, I've made it much shallower and aimed it towards using Haskell IO without neccesarily understanding how IO works. What do you think? -- Kowey 04:47, 28 November 2006 (UTC)

Output buffering
Hi, you might want to add a note about output buffering. I'm a newbie, and I was really thrown by the fact that my output was being buffered and not printing! Thanks. -Drew

I think this is in reference to the 'putStr' that was used in the first example, which runs afoul of line buffering used by default. I hit this too and changed it to 'putStrLn', solving the problem. Smcpeak (discuss • contribs) 01:40, 30 June 2016 (UTC)

Questions
Doesn't the Haskell type system look first at the last line of the code:  to derive the fact that doGuessing returns a  and then uses this information when checking the if-else before it? [This question from user Hathal]

Question on behaviour of doGuessing
doGuessing num = do putStrLn "Enter your guess:" guess <- getLine case compare (read guess) num of   EQ -> do putStrLn "You win!" return

-- we don't expect to get here unless guess == num if (read guess < num) then do print "Too low!"; doGuessing else do print "Too high!"; doGuessing First of all, if you guess correctly, it will first print "You win!," but it won't exit, and it will check whether  is less than. Of course it is not, so the else branch is taken, and it will print "Too high!" and then ask you to guess again.

On the other hand, if you guess incorrectly, it will try to evaluate the case statement and get either  or   as the result of the. In either case, it won't have a pattern that matches, and the program will fail immediately with an exception.


 * It seems like the whole purpose of this was to point out that return doesn't do what imperative programmers think it does. But it's hard to come up with an illustrative example without this leaky case.  Any ideas? -- Kowey 00:48, 19 January 2007 (UTC)


 * Comment struck out because I'm just being tired and stupid -- Kowey 00:56, 19 January 2007 (UTC)

---

The second paragraph above should refer to if-else not LT or GT. Also, doesn't infinite recursion result in all cases?


 * No. The second paragraph is referring to this block of code:

case compare (read guess) num of   EQ -> do putStrLn "You win!" return
 * There are two cases missing here: LT and GT.
 * You are right, however, that no matter what the user guesses, doGuessing will be called again -- Kowey 00:56, 19 January 2007 (UTC)

Semantic difference between String and IO String
In the "Mind your action types" section we see an  given where a   is expected. But the explanation glosses over the difference between the two, and it ends up sounding like the compiler is just being unnecessarily picky and we have to use a "conversion tool" (the  operator) to placate it.

It'd help to explain the difference in practical terms: we want a , but what we have is an action that can return a   when run, so the solution is to run the action to obtain a. -- 21:20, 23 January 2007 (UTC)


 * Hey, this is kind of comment I love to have. Is my recent edit any better?  Any suggestions? -- Kowey 21:31, 23 January 2007 (UTC)

It's a bit better, but a little too wordy. You could remove a sentence or two and get something like:


 * We do not have a, but something tantalisingly close, an  .  This represents an action that will give us a   when it's run.  To obtain the   that   wants, we need to run the action, and we do that with the ever-handy left arrow,.


 * I think this is more clear and succinct. -- 21:54, 25 January 2007 (UTC)


 * Yes. It's like coding in Haskell; the more I remove, the better.  By the way, I appreciate the discussion, but might also point out that in the wikimedia projects, we have a culture of bold editing, you know, just going in and fixing stuff.  So if you feel like it, don't be shy :-) [both approaches are good] -- Kowey 22:14, 25 January 2007 (UTC)


 * Fair enough. I'm not very experienced with editing wiki (which is why I don't have an account here), and I'm a beginner with Haskell (which is why I'm reading this wikibook &mdash; I find it easier to understand than the Gentle Introduction, btw), so I figured it'd be best to just suggest and let you make the real change.  (Plus, you asked for suggestions.   ) -- 23:14, 25 January 2007 (UTC)

Last exercise
That last exercise is pretty complex. I was hoping to check my solution against the official answer, but I don't see any... I couldn't figure out how to handle the case where the file does not exist, so it would print out "Sorry, that file does not exist!" instead of erroring out with a file-not-found exception - none of the IO functions listed seemed to help, and I couldn't find any examples in the article: the section on Exceptions is only a redlink. Here's what I wrote (it could probably be simplified with case statements, but I like my s). Improvements are welcome. --Gwern (contribs) 20:00, 8 February 2007 (UTC)


 * Hi! I've been a bit busy lately, so I'm afraid I can't really pay much attention to this. Maybe you could try #haskell on irc.freenode.net or the Haskell café mailing list?  #haskell is great because you can use the http://hpaste.org pastebin and have people comment on or annotate or code. -- Kowey 07:06, 9 February 2007 (UTC)


 * I took your suggestion, Kowey. Thanks to their help, it's definitely a lot better. I can take credit for merging the functions and using case, but removing the returns was their idea. :) --Gwern (contribs) 01:28, 13 February 2007 (UTC)

Partial answer
module Main where import IO

main :: IO main = do putStrLn "Do you want to [read] a file, [write] a file or [quit]?" command <- getLine case command of   "quit" -> do putStrLn "Goodbye!" "read" -> do openFileName main "write" -> do writeFileName main _ -> do putStrLn ("I don't understand the command " ++ command ++ ".") main

--Reading is easy. Get the name, open it, and print out the contents. openFileName :: IO openFileName = do putStrLn "Enter a file name to read:" filename <- getLine bracket (openFile filename ReadMode) hClose (\h -> do contents <- hGetContents h                        putStrLn contents)

--Writing is harder. We need to recursively loop for the lines, --and then use unlines to restore the newlines, so it prints right. writeFileName :: IO writeFileName = do putStrLn "Enter a file name to write:" filename <- getLine putStrLn "Enter text (dot on a line by itself to end)" contents <- getLinesUntilDot writeFile filename (unlines contents)

getLinesUntilDot :: IO [String] getLinesUntilDot = do x <- getLine if x == "." then return [] else do xs <- getLinesUntilDot return (x:xs)

Lambda
In the chapter 'A File Reading Program' you show lambdas in action. This is an unknown concept at this point. You should either give a pointer to further discussion or rewrite the function without them.

I cannot do this myself because I am a Haskell Newbie.


 * To follow this up: does anything think this chapter should be moved to after Haskell/Recursion? I was unable to find a solution to the last exercise which didn't use recursion, and this chapter in general seems to expect more proficiency than its placement would suggest. --Gwern (contribs) 02:06, 15 February 2007 (UTC)


 * I was thinking maybe of Haskell/Hierarchical_libraries/IO -- Kowey 06:51, 15 February 2007 (UTC)


 * Perhaps we could split them up into simple IO and more advanced IO? We could remove some stuff from this chapter that I and the anon were complaining about, and put them in the advanced chapter; I don't know what else we'd include, though. I noticed some nice blog posts/articles in which the author reimplements a number of the GNU Unix coreutils in Haskell, using stuff like  - maybe we could incorporate them? --Gwern  (contribs) 16:48, 15 February 2007 (UTC)


 * That could be an interesting idea! In the meantime, I would like to see a little bit about reading files and handles, either at the end of this chapter, or in the beginning of the advanced one.  Also, we could continue the Haskell wikibook tradition of asking authors of particularly excellent write-ups if they would be willing to donate to the wikibook (this means allowing their text to be GFDLed).  Be bold! :-) -- Kowey 06:42, 16 February 2007 (UTC)


 * I was looking at Simple unix tools on the Haskell wiki, and it seems to provide a good start: it could be written piece-wise in which the reader tries to reimplement simple versions of the Unix tools (which are well-documented and probably even directly available to most readers), but broken up into exercises. That is, cat would be one exercise, figuring out which function to use in uniq would be a second and actually writing uniq would be a third, and so on. I don't see any chapters on documentation so that could be worked in at the end, once all the programs are written and have type signatures.
 * As far as licenses go, the Haskell wiki seems to use something of a BSD license for text, so I think we can just copy it straight over.
 * A good location would be at the end of the Beginner's track, I think. --Gwern (contribs) 20:58, 17 February 2007 (UTC)


 * Yep! The Simple Permissive License is quite permissive... a good chunk of our content already comes from there -- Kowey 07:45, 18 February 2007 (UTC)

System.IO
Is the import really needed here?

terms I wasn't familiar with in introduction
You suggested that printing to screen could return a "unit ". I don't know what that is. Is that haskell's equivalent of "null" or "undef"?

Then you said that we clearly can't do that because of what "referential transparency" tells us. Also not too helpful (although I can guess by the context that it's something about if x returns the same as y, you should be able to replace y with x anywhere and have everything work the same). -- Msouth 16:24, 15 July 2007 (UTC)

a convention on the introduction of a new term might help
If you would consistently put triple single quotes around new terms, it might keep people from wondering if they had missed the definition earlier. For example, here you launch into the fact that monads are a good way to deal with certain things, but that made me wonder if I had missed what monads were. It would be good to have a convention for the introduction of a new term, perhaps with a footnote pointing to where it is discussed in detail. -- Msouth 16:30, 15 July 2007 (UTC)

too much in first example
I didn't have any idea what all that "module Main where" stuff was, and I saw the question about whether you really had to import System.IO, so I did the following experiment:

create a file called "action.hs" containing this:

action = do  putStrLn "Please enter your name: " name <- getLine putStrLn ("Hello, " ++ name ++ ", how are you?")

Then did this:

Prelude> :l action.hs [1 of 1] Compiling Main            ( action.hs, interpreted ) Ok, modules loaded: Main. *Main> action Please enter your name: bob Hello, bob, how are you?

At this point in the game, when people are very new, I think it's very, very desirable to add nothing that is not absolutely necessary, as every bit of information is another possible distraction. I don't want to know what I'm "allowed to run as a program" right now, if I don't have to. -- Msouth 16:44, 15 July 2007 (UTC)

Consider eliminating discussion of monads
You're not really telling us what monads are. You could reduce everything here about monads to one sentence like "For those who are curious about how something as obviously side-effecty as IO is handled in a language that prides itself on being free of side effects, the answer is that side effects are isolated into things called monads. It's too much to bring up right now, but if you're curious you can go to [link about monads] ."

As part of this change I would clean up the texts that makes side references to monads. I don't know what they are, so they aren't doing anything but distracting me from what I'm trying to understand right now.

Msouth 22:55, 15 July 2007 (UTC)

map and foldr haven't been introduced yet
You mention in the big secret about actions and expressions that it's cool that you can run map and foldr on actions, but I don't know what those are yet (well, I have looked at the first part of YAHT so I know they do fun things on lists, but for someone being introduced via this work, it's new information, which again is taking you back through wondering where that was talked about/how you missed it.--Msouth 23:10, 15 July 2007 (UTC)

Didn't know you were contrasting actions with expresssions
When you get to the "big secret" part and tell me "Guess what--you're going to love this--Actions _are_ actually expressions!", I hadn't noticed that you were contrasting actions with anything. You might want to put something up at the top where you talk about action, maybe like

"An action, as opposed to a mere expression which is what we've been working with up to now, ..."

That still doesn't lie to them--an action, while a subset of expression (if I understand correctly), is not "merely an expression", it's an expression with something else (sequence/side effect/both--I don't know yet I'm a newbie :). Msouth 23:16, 15 July 2007 (UTC)

explain the payoff
One of the examples goes from this (incorrect):

main = do putStrLn "What is your name? " putStrLn ("Hello " ++ getLine)

to this (correct):

main = do putStrLn "What is your name? " name <- getLine putStrLn ("Hello " ++ name)

Coming here as a perl programmer, one question that is raised is

(a) OK, but *is* there a way to do without the extra variable?

and

(b) if not, what's the payoff gained by requiring the extra variable? (type safety or whatever it is, just let me know)

even if it's hard and covers something I don't know how to do now, I want to see the code that does that in one line, or I'm thinking "seems like a limitation", and there's a good chance someone this new to the language is also still deciding on whether to use it. Not a good time to raise doubts about its power. -- Msouth 23:36, 15 July 2007 (UTC)

I came up with

getLine >>= (putStrLn . ("Hello "++))

99.0.80.238 (talk) 23:31, 23 June 2010 (UTC)

why does this work in GHCi?
This (doesn't work) example:

main = do putStrLn getLine

suprisingly this work in GHCi: Prelude> putStrLn <- getLine

can anyone explain why? Sorry, I'm a newbie in haskell.


 * Well, it doesn't really work either, you're just defining the value of the new variable "putStrLn" to be the result of the getLine action.

Prelude> putStrLn <- getLine Test "Test" Prelude> :type putStrLn putStrLn :: String


 * In other words, you're redefining the library function.
 * What you want is

main = do    s <- getLine putStrLn s


 * -- apfe&lambda;mus 09:20, 27 December 2007 (UTC)

Left arrow '<-' — assignment operator?
Can somebody tell me whether '<-' is an assignment operator or not? I know that there is no changing of stored values in variables in Haskell, but still I'm not sure if '<-' can be seen that way.


 * is not an assignment operator. It merely binds the result of the right-hand-side to the name on the left-hand-side. It may look like an assignment since something like

do   x <- getLine x <- return (length x)   return x


 * is possible, but it's just the second x shadowing the first definition of x. It's entirely equivalent to

do   x <- getLine y <- return (length x)   return y


 * Something similar can happen with lambda-expressions like

map (\x -> map (\x -> x) x) (tails [1..5])


 * Do notation is just syntactic sugar, for a translation into "ordinary" Haskell, see also
 * apfe&lambda;mus 19:33, 21 January 2008 (UTC)
 * Whoa, that was fast and helpful, thanks! Zo (talk) 09:29, 30 January 2008 (UTC)
 * But it was not quite correct :-) which is fixed now (see the diff). For even faster and more helpful help, check out the haskell irc channel and the haskell-cafe mailing list. -- apfe&lambda;mus 10:22, 31 January 2008 (UTC)

syntactic sugar
As a native English speaker I feel that this phrase is meaningless. A suggested alternative 'English words' --MacNala (talk) 16:00, 26 February 2009 (UTC)


 * You mean the expression "syntactic sugar"? It's a technical term.
 * -- apfe&lambda;mus 09:06, 27 February 2009 (UTC)

side effects
The first occurrence of the phrase 'side effects' needs explanation for readers coming to this sort of programming for the first time. --MacNala (talk) 16:35, 26 February 2009 (UTC)

delete me
well, i am missing a guestbook here, because i wanted to say how much i appreciate this book. great work. thank you


 * Thank you, in the name of all authors. :-) -- apfe&lambda;mus 07:07, 29 April 2009 (UTC)

Not sure about core return
All I got from reading this tutorial is not to use core return, because it doesn't do what I would intuitively think it does coming from $otherLang. I have NO IDEA what it does after reading the material, probably because I have no idea what a monad does. Based on the flow of the book, I would highly suggest its removal. EvanCarroll (talk) 03:28, 23 November 2009 (UTC)


 * I agree that the section needs restructuring, but I'm not sure whether it's a good idea to remove  entirely because it's indispensable in certain cases, like

loop = do       c <- getChar if c == "Q" then return else loop putStr "End of the loop."


 * To learn more about, I'd currently recommend Real World Haskell. Maybe a kind contributor can clarify in the wikibook?
 * -- apfe&lambda;mus 13:20, 23 November 2009 (UTC)


 * I might have just stumbled upon a solution for this: after moving the return discussion to "Control structures" (which I had to do due to the relocation of the case expressions explanation), everything suddenly makes a lot more sense :) I even considered adding a variation on this loop example there, but haven't made my mind about it yet. Duplode (discuss • contribs) 08:23, 1 April 2012 (UTC)

putStrLn for non-english text? (Special chars)
The exercises are quite nice. One thing I miss is reg. the text which the user sees. Especially when it is local-text, then the text might contain in German öäüß or for Slavic countries many more. The question would be how to include them in the text-line prior 2 the input? Something I would mention here (cause otherwise you wouldn't use ;) )

Is an action an IO value or a function that returns an IO value ?
There is this statement in the text:

"We'll call IO values, such as the result of putStrLn, actions."

A paragraph further the text says this:

"In a compiled program, this action is called main, and has type IO ."

But main is a function. So is an action an IO value or a function that returns an IO value ?

Thanks.

Jrpgt (discuss • contribs) 14:49, 19 March 2015 (UTC)


 * is not a function.  is a value with type  . By way of contrast,   is a function which takes a   and gives an   result. --Duplode (discuss • contribs) 08:51, 21 November 2016 (UTC)