Talk:Haskell/Simple input and output

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit] 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)

[edit] 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

[edit] Questions

Doesn't the Haskell type system look first at the last line of the code: else do putStrLn "You Win! 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]

[edit] 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 guess is less than num. 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 LT or GT as the result of the compare. 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)

[edit] Semantic difference between String and IO String

In the "Mind your action types" section we see an IO String given where a String 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 String, but what we have is an action that can return a String when run, so the solution is to run the action to obtain a String. -- 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 String, but something tantalisingly close, an IO String. This represents an action that will give us a String when it's run. To obtain the String that putStrLn 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 — 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. :-P) -- 23:14, 25 January 2007 (UTC)

[edit] 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 ifs). 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)

[edit] 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)

[edit] 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 interact - 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)

[edit] System.IO

Is the import really needed here?

Example

Example: Hello! What is your name?

module Main where

import System.IO

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

[edit] 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)

[edit] 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)

[edit] 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)

[edit] 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)

[edit] 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)

[edit] 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)

[edit] 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)

[edit] 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 putStrLn :: String -> IO ().
What you want is
 main = do
    s <- getLine
    putStrLn s
-- apfeλmus 09:20, 27 December 2007 (UTC)


[edit] 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 [1]
apfeλ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λmus 10:22, 31 January 2008 (UTC)

[edit] 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λmus 09:06, 27 February 2009 (UTC)

[edit] 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)


[edit] 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λmus 07:07, 29 April 2009 (UTC)


[edit] 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 return 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 return, I'd currently recommend Real World Haskell. Maybe a kind contributor can clarify in the wikibook?
-- apfeλmus 13:20, 23 November 2009 (UTC)