Happy Learn Haskell Tutorial Vol 1

Buy now at Leanpub

Please Buy now at Leanpub

Written and illustrated by GetContented.

Published on 2016-06-05.


Contents

Main Table of Contents
Previous chapter: 6. Sockets & Plugs
7. Output Other Things
... 7.1. Integer or Int?
... 7.2. Type Variables
... 7.3. Type Variables can be named anything
... 7.4. Typeclasses
... 7.5. The Show Typeclass
... 7.6. Parentheses and Precedence
... 7.7. The print Function
... 7.8. Homework
Next chapter: 8. Make Decisions

7. Output Other Things 🔗

We’ve seen putStrLn. It lets us output any String on the screen.

What if we want to print out a number instead of a String?

We have the following definition for an expression that is adding two numbers, and we want to print the resulting value out in a program.

7.1. Integer or Int? 🔗


number :: Integer
number = 100390 + 29389

(Integer, by the way, is the type of unbounded whole numbers in Haskell. Unbounded means they have no upper or lower limit (or bounds). In contrast, the maxBound of Int that this document is being prepared on now is 9223372036854775807.)

So, we know that the putStrLn :: String -> IO () function takes a String, and leaves us with an IO () value.

We don’t have a String, though, we have an Integer value. How can we match these up so we can print out our number on the screen?

In order to answer that, let’s first look at the type of the (+) function. We know from experience that it takes a number and another number and returns their sum as a number, but the actual type isn't something we’ve seen, and includes some special new stuff for us. Let’s look:

7.2. Type Variables 🔗


(+) :: Num a => a -> a -> a

Ok, breathe. Let’s first look at the right side: a -> a -> a. Why all the a’s?

Well, we know from all the (->)’s that this means it’s a function that takes two values of type “a”, and returns a third “a”. What is “a”, though? It’s what’s called a type variable. That means it can be any type. If it starts with a lowercase letter, it’s a type variable. If it starts with a capital, it’s an actual type, or a typeclass, which we'll explain soon (Num is a typeclass in Num a => a).

One important point about type variables is that while they can be any type at all, all the “a”s must still be the same type as each other when using the function! So if we used an Integer value as our first argument, so saying that the first type a was Integer in (+), then we would need to use an Integer as our second argument, too:


-- both types must be the same,
-- so this will be fine:
goodNumber = (3 :: Integer) + (5 :: Integer)

-- this would cause a type error
willNotWork = (3 :: Int) + (5 :: Integer)

If we don't specify the type of our numbers, Haskell's type inference works it out for us, which saves a lot of time and hassle.

7.3. Type Variables can be named anything 🔗

Ok, so here is another way to write the (+) function, which shows you that variables don’t necessarily have to be named a:


-- it's a lot smaller to write
-- 'a' than 'theNumber'
(+) :: Num theNumber =>
       theNumber ->
       theNumber ->
       theNumber

7.4. Typeclasses 🔗

Now we have to think about the “Num a =>” part. That can be read as “a is constrained to types which are instances of the Num typeclass”. This mouthful means that the (+) function can take two arguments of any type at all (here we’re naming them a), as long as that type (again, here called a) is an instance of a typeclass called Num. Luckily for us, all numbers are!

A typeclass is not a concrete type like Integer, Int or String. It’s a way of tagging many types (a “class” of them, if you like) so that we can have functions or values that work with many similar types that do similar things, but that are actually different. Let's take a look:


-- a small int 5:
intFive = 5 :: Int

-- a "floating-point" value of 10.3
floatTenPointThree = 10.3 :: Float

-- add them together with (+). This will not work...
-- because both types are concretized (or specialized)
errorResult = intFive + floatTenPointThree

-- add them together with (+). This will work
result = (fromIntegral intFive) + floatTenPointThree

-- the result is 15.3

Here we’re using fromIntegral to build an “unspecialised” Num a => a version of the Int value of intFive so we can subsequently add it to floatTenPointThree. The value 5 :: Int is not of type Float, so the types won’t match unless we do this. However, if either of the values are of type Num a => a, then (+) will typecheck because it can match both types together (by concretizing the Num a => a type to Float).

The Float, Int, and Integer types are all instances of the Num typeclass. There are many numeric types in Haskell such as these. To be able to do arithmetic functions on different numeric typed values, they are tagged as Num which allows us to define each of the simple arithmetic functions for each type differently, but use them all with the same name and interchange values.

This “tagging” is called making a type an instance of a typeclass. When a programmer does this, they provide a definition against the particular type, for the functions that the typeclass requires.

So we can see that (+) can add a Float or an Integer to a Num a => a without a problem. The Num typeclass is, in this way, like a kind of contract that programmers of a type can decide to kind of "subscribe to" which gives them the ability to write implementations of the functions that the Num typeclass provides. In turn, the typeclass system gives that type the ability to work with all the other types that are instances of that typeclass.

So the Num typeclass means there actually isn’t only one definition for the functions for addition: (+), subtraction: (-), multiplication: (*), negation: negate, etc but rather that each type — that is, each instance of Num — has its own definition for each of these functions.

7.5. The Show Typeclass 🔗

What does all of this have to do with printing our number on the screen? Remeber, that problem we started the chapter with?

Well, there’s a typeclass called Show (with a big S), and this provides a single function: show (with a small s), that can take any instance of Show, and makes a String version of it. Let’s look at the type of the show function:


-- takes a "showable" thing
-- and returns a String
show :: Show a => a -> String

We see that show is a function which takes a single argument of any type (the “a” type variable above) constrained to the Show typeclass, and returns a String. That single argument is anything that has an instance of Show defined for it. We know this because of the Show a => constraint.

Printing things to the screen is such a common thing to do that many types have an instance of Show already, including of course, Integer and Int, so getting back to the first program of this chapter, we can just apply show to our Integer and then pass it to putStrLn. Let’s see how:


number :: Integer
number = 100390 + 29389

main :: IO ()
main = putStrLn (show number)

7.6. Parentheses and Precedence 🔗

Parentheses are needed on show number because putStrLn only takes one argument, and the function application precedence rules mean that taking them off would give it two. Precedence is a fancy-pants word that simply means “which things come before or after which other things”. If we left off the parentheses, we would have this: putStrLn show number, which Haskell would see as “apply putStrLn to the value show, and then apply that to the value number”. However, putStrLn takes only String values, and show is a function, so that would definitely be a type error.

7.7. The print Function 🔗

We have one last trick up our sleeves to show you (pardon the pun). It’s the print function. This is very similar to putStrLn :: String -> IO (), but rather than taking a String, it can take a value whose type is any instance of Show! Let’s see a version of our program that uses print :: Show a => a -> IO () rather than putStrLn:


number :: Integer
number = 100390 + 29389

main :: IO ()
main = print number

7.8. Homework 🔗

See if you can work out what the following program does.


number1 :: Num a => a
number1 = 1 + 5 + 7 + 3 + 2

number2 :: Num a => a
number2 = number1 * number1

main :: IO ()
main = print number2

Hint: don’t get caught up by the types of the values. This will probably be confusing, and should confuse you at least a little bit. We’ll explain what’s going on later, however, the important thing is just see if you can work out what the program will do when you run it.


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: 6. Sockets & Plugs
7. Output Other Things
... 7.1. Integer or Int?
... 7.2. Type Variables
... 7.3. Type Variables can be named anything
... 7.4. Typeclasses
... 7.5. The Show Typeclass
... 7.6. Parentheses and Precedence
... 7.7. The print Function
... 7.8. Homework
Next chapter: 8. Make Decisions