Happy Learn Haskell Tutorial Vol 1

Buy now at Leanpub

Please Buy now at Leanpub

Written and illustrated by GetContented.

Published on 2016-08-14.


Contents

Main Table of Contents
Previous chapter: 19. Skwak the Squirrel
20. Basic Input
... 20.1. Guided Program 1
... 20.2. Guided Program 2
... 20.3. Guided Program 3
... 20.4. A Little More About IO
... 20.5. Your Turn
... 20.6. Reader Exercise 1
... 20.7. Reader Exercise 2
... 20.8. Reader Exercise 3
... 20.9. Reader Exercise 4
... 20.10. Reader Exercise 5
... 20.11. The End? Next Steps
Next chapter: 21. Getting Set Up

20. Basic Input 🔗

Covers: basic input, simple do blocks, simple function application, simple definitions.

We’re going to teach you just enough IO that you can write beginning programs. More will come later as we need it to do more capable programs.

We’ll begin straight away on the guided program building.

20.1. Guided Program 1 🔗

Task: Make a program to ask for a name, then say hello to that person using their name.

Taking this problem apart, there seem to be three pieces: Display a request for their name, get the name, and finally print the hello message. Let’s begin with the piece we can do most easily: display the message to ask for a name. This is simply printing a string on the screen, which we know how to do:


main :: IO ()
main = putStrLn "What is your name?"

So we’ve solved a third of the problem. Next, we need to actually get the name of the user. If we think of the functions we know about, the getLine function is the obvious contender here, which has the type IO String. We’re going to have to compose this with our putStrLn action. By now, handily, we know about do syntax which lets us combine multiple IO actions into one.

You can think of each expression in an IO do block as a piece of the composed IO action, which is exactly what it is. You can write any kind of expressions you like in that do block, so long as it will evaluate to IO ().

As we’ve seen, there are two exceptions to this rule, and they are do-block let notation, which is used for definitions of pure expressions, and the <- syntax, which is used for defining variables as the “inner values” of IO actions that we want to connect to some other part of the rest of the code in our do block.

So, right away, we notice getLine’s type isn’t what we need it to be. It’s IO String, not IO (). However, we know by now that we can use <- to “get” a variable representing a pure value out of an IO action so long as the code is still in IO. This do block is an IO action, so we can do it here. Let’s add the do block and then use getLine to define a variable called theirName as the String value it pulls out. Remember, though, that the last expression in a do block must be of type IO (), so for now we’ll use a new function that we haven’t seen called return to create a value of this type. We’ll use the expression return () to put () into IO and make that the end expression of our entire do block:


main :: IO ()
main = do
  putStrLn "What is your name?"
  theirName <- getLine
  return ()

The return function places a value into an IO action. It doesn’t have anything to do with returning values from actions, just with putting values into actions. It’s possibly a bad name to have for your understanding at the beginning, so we apologise about that.

The last piece is to print their name out with hello in front. To do this, we’ll need to use the putStrLn function again, and also the (++) operator that joins (concatenates) two lists into one. String is simply a list of Char as we know, so this will work fine.

Choose your variable names wisely! Good naming is one of the most difficult tasks in programming. It can help or hinder future readers of your code, including yourself. Names should always help to explain your code as much as possible.

Notice in the next piece of code that we don’t need to have the return () function application now because of putStrLn. However, we are using brackets around the application of (++) to its arguments because if we didn’t, putStrLn would just take the one String as its argument, and (++) would be being applied to an IO () value on the left side rather than a String.


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

This program is just about the simplest possible program that we could write that uses both input and output in Haskell. If you’d like to, try playing with it a little, by changing to different String values. Don’t get discouraged if things go wrong.

20.2. Guided Program 2 🔗

Task: Write a program that asks you for your name, then your pet’s name, then tells you the info back.

This is still a very simple program, so we’ll just use what we know about putStrLn, do blocks, (++) and getLine to build our program:


main :: IO ()
main = do
  putStrLn "What is your name?"
  theirName <- getLine
  putStrLn "What's your pet's name?"
  petName <- getLine
  putStrLn ("Your name is "
           ++ theirName
           ++ " and your pet's name is "
           ++ petName)

As we’ve said before, it’s very important to get your indentation correct in Haskell. If you don’t, Haskell won’t know what you mean, and will often complain with strange sounding errors. The indenting is good because it stops us from having to use a lot of bracketing, because Haskell can use the indenting to work out what you mean.

You should experiment and try out different indentations when you have a program that compiles to see what works and what doesn’t, and to get familiar with some of the errors that appear when things go wrong.

The above program is almost the same as the first one, except this time we’re extracting two variables from the user using getLine. Notice that you can chain operators if they take two of the same type of arguments (such as (++)) and then the result of the first application will be fed in as the first argument to the second operator, as you would expect it to work.

Let’s look at a different way to write a similar program, which may surprise you a little.

20.3. Guided Program 3 🔗

Task: Write a program to ask for the time, then to write “again!” and then ask the user the time again and print out both times.

It’s virtually the same idea for thinking through how to build this program, however we’re going to create a separate action to be re-used in our main action.

Any time you would end up repeating yourself, it’s a great idea to re-use a fragment or value. There’s no reason not to separate it out by making a definition for it. Then you don’t have to repeat yourself! This is one of the core reasons to write computer programs. You should strive to get the computer to do as much of the repetitious work as possible.


whatTimeIsIt :: IO String
whatTimeIsIt = do
  putStrLn "What time is it now?"
  getLine

main :: IO ()
main = do
  timeString <- whatTimeIsIt
  putStrLn "Again!"
  timeString2 <- whatTimeIsIt
  putStrLn ("Ok, you said it was "
            ++ timeString
            ++ " and then you said it was "
            ++ timeString2)

So whatTimeIsIt is an IO String action. It has the same type as getLine, so we can treat it exactly the same. Looking at it, you can see getLine is the last value in its do block, so it’s just using a do block to connect outputting a question up with asking the answer, and returning that string (in IO, of course).

Here we see the difference between using action results and pure expressions very clearly. We can use the action twice to get two potentially very different values out. We do so using <- again, and set two different variables to their answer.

Then, we just output the result sentence. All of this is joined up using a do block as before. Nice!

20.4. A Little More About IO 🔗

All an IO action is, is a description of how to enact some execution of code later on. The <- syntax doesn’t actually “get” anything out of anything else. You can think of it like it does, but what it really does is tells Haskell what to do with action values to combine them. It tells Haskell how to connect up the producing-part of a piece of code that queries the user for input to the rest of the consuming-part of the program, for example.

When we write do blocks, all we’re really doing is telling Haskell what relationship the smaller pieces should be in compared to each other, and then when the program is executed, things happen in the right order. This will become clearer with more practice, and in later volumes.

20.5. Your Turn 🔗

Now it’s your turn. When you do these exercises, do them without looking at the supporting explanation documents in this lesson. If you have to look, you can, but mark the exercises you had to look for, and re-do them again after you’ve finished doing them all. You should do them again the next day and so on trying without looking until you can easily do them without any problem at all. Make up some programs of your own that involve asking questions and replying with the inputted data. Don’t try to do anything more complicated than this, yet. You can do this. Let’s go.

20.6. Reader Exercise 1 🔗

Task: Write a program to print a welcome on the screen.

20.7. Reader Exercise 2 🔗

Task: Write a program to ask for someone’s last name, then print it out on the screen.

20.8. Reader Exercise 3 🔗

Task: Write a program that asks two personal questions about the user, and tells their responses back.

20.9. Reader Exercise 4 🔗

Task: Write a program that says it’s going to ask the user to pick three numbers, then uses a separate IO String action that prompts a user to pick a number, then does it again two more times, then tells them the numbers they picked (note we’re not actually using numbers here, just String input and output).

20.10. Reader Exercise 5 🔗

Task: Write a program to ask the user where they live, then write a message saying something telling them you’ve heard that’s a great place to live, using the place name in a sentence.

20.11. The End? Next Steps 🔗

Wow, we’ve reached the end of this Volume together. Well done for getting this far. You’re probably thinking something like "but... we only just got to the part where we’re doing exercises!" Yes, you’re right, but don’t worry. The next volume is where things start getting much more exercise-driven, because now you know enough to start to explore and anchor your understanding of the language by writing code more.

We hope to see you there, and that you let us know if you’ve enjoyed this, tweet about it, tell your friends, and give us feedback!


If you’ve enjoyed reading this, please consider purchasing a copy at Leanpub today

Please follow us, and check out our videos Follow @HappyLearnTutes

Also, Volume 2 is now in beta and being written! Show your support and register your interest at its Leanpub site.


Main Table of Contents
Previous chapter: 19. Skwak the Squirrel
20. Basic Input
... 20.1. Guided Program 1
... 20.2. Guided Program 2
... 20.3. Guided Program 3
... 20.4. A Little More About IO
... 20.5. Your Turn
... 20.6. Reader Exercise 1
... 20.7. Reader Exercise 2
... 20.8. Reader Exercise 3
... 20.9. Reader Exercise 4
... 20.10. Reader Exercise 5
... 20.11. The End? Next Steps
Next chapter: 21. Getting Set Up