Happy Learn Haskell Tutorial Vol 1

Buy now at Leanpub

Please Buy now at Leanpub

Written and illustrated by GetContented.

Published on 2017-12-25.


Main Table of Contents
Previous chapter: 5. Function Magic
6. Sockets & Plugs
... 6.1. Reusability
... 6.2. Functions are Values
... 6.3. Plugging Values into Functions
... 6.4. Defining Functions
... 6.5. Operator Sections
... 6.6. What is commutivity?
... 6.7. Homework
Next chapter: 7. Output Other Things

6. Sockets & Plugs 🔗

In this chapter, we'll explain another way that might help you to think about functions work when they’re used. All of these views of functions can help you to get a rounded idea of how to use them well. We're spending so much time on this because it's the underpinning of everything in Haskell.

We could think of them like they were electronic devices, waiting to have something plugged into them. Depending on what you plug in, you will get a different result.

6.1. Reusability 🔗

Functions are one of the most basic ways to re-use expressions in a program. They save us repeating ourselves as we write programs. Functions take variables or parameters that can have different values (of a type) for each new time the function is applied.

6.2. Functions are Values 🔗

Functions are also themselves values, but they are a special kind of mapping-value from values to other values.

This is why putStrLn "hi" means to “apply the putStrLn function to the String value "hi".

Another way to think of it is that putStrLn makes a kind of mapping between any String value to a corresponding IO () value.

Let’s imagine there’s a function called plus5 which takes a whole number as its single parameter. When we give it a value, the result is whatever its input is, but 5 more.

If we were applying this function to the number 53, for example, we would write the function application plus5 53.

-- we apply the function plus5
-- to the number 53
plus5 53

Above, we show this function as a machine box with a plug and a screen that shows the “answer”.

Int is one of Haskell’s names for the type of whole numbers; both negative and positive. The mathematical name for a whole number is integer, which comes from latin and means “entire”.

The function’s type is written in Haskell as Int -> Int, which means “the type of all functions that map from an Int value, to another Int value”.

We can also say that Int -> Int is “an Int that is a function of another Int”. Its value isn’t just an Int until you “give” it an Int.

6.3. Plugging Values into Functions 🔗

So in the graphic, we “plug” 53 into this plus5 box, and it displays 58. Comparing our graphic with the way function application is actually written, we can see it’s reversed. In Haskell, as well as in Math, we usually plug values in to the right of the function, (or sometimes, with operators, the left and right), whereas in our box graphic, or with audio equipment, we’re plugging values in on the left of the boxes (which represent functions).

This is an important point. The way people think and do things is often put a different way around compared to the way you have to write things in Haskell. This is often the same thing with Math. In Math and Haskell, you have to be more consistent and precise. For example, in everyday life, we'd say "add five to three", but in Math and Haskell, we'd write 5 + 3.

It's good to realise there are usually many ways to look at something. For example, if we look at a toggle switch, and it’s marked “off”, does that mean it’s currently off, or that pressing it will “action” the off functionality? (ie turn it off). There is no one true right answer, so it’s good to be aware there are many ways to express the same thing that can often appear as complete opposites to each other.

Getting back to our machine, the moment we plug our 53 :: Int into the box, the type of the box changes from Int -> Int to just Int. We can then plug that whole expression (plus5 53) into any other function box that takes an Int as an argument. We may have to use brackets, though, in Haskell.

-- apply plus5 to 53:
plus5 53

-- apply plus5 to
-- the application of plus5 to 6
plus5 (plus5 6)

What if we wanted to plug the expression plus5 53, into the function plus5 again? Well, we could do that like this: plus5 (plus5 53), and the result would be 63:

We can clearly see that the “output” of the first box as 58 can be plugged into the “input” of the second box. (Don’t get confused, this is not an IO action! We’re not talking about actually outputting these numbers on the screen or anything, just plugging values into functions as a metaphor).

6.4. Defining Functions 🔗

So, let’s move on and read a definition for this plus5 function. We already know what it does:

plus5 :: Int -> Int
plus5 x = x + 5

This is the type declaration and definition for a function that takes one argument of type Int. We’re naming that argument x here in this definition (that’s why the x appears on the left of the = sign). We could have named it almost anything we liked. To “use” this function, you supply plus5 with an Int value by placing it to the right of it like this: plus5 7.

The definition uses something special we haven’t seen yet called an operator. Here it's an infix function called (+) that is named as the + symbol, and it takes two numeric arguments, one on either side! A function is called an infix function when it appears between its arguments. Normal functions are called prefix because they are placed before their argument(s). Functions that are named as symbols like (+) here are called operators, and they almost always only ever take exactly two arguments, and are usually infix, like (+). You'll also notice when we talk about them, we put parentheses around them. In Haskell this is how they're referred to outside of when you're using them in a function application.

Let’s look at another program that does almost the same thing. Notice we’re using a different variable name here (we called it number), rather than x.

plus6 :: Int -> Int
plus6 number = number + 6

Now we’ll present another way to write this. If you have an infix operator such as (+), and you want to use it as a prefix function, you can just wrap it in parentheses. This function works exactly the same as plus6:

plus6' :: Int -> Int
plus6' number = (+) number 6

6.5. Operator Sections 🔗

Next we’ll present another identical function, but using what’s called a section. A section is a partially applied operator. That means it has one of its two arguments supplied, and that becomes a function. It always uses round brackets.

plus6'' :: Int -> Int
plus6'' number = (+6) number

See if you can guess the type of (+6) right now. First, you might want to think about the type of the (+) operator. It takes two numeric arguments, and returns one numeric argument. So, if one of its arguments are supplied, it will become a function of only one argument. Then, we apply this function to the number variable to get our result.

So, you can think of the type of the function (+6) as taking a single number, then returning a number.

It doesn't matter which side you put the value on with the operator (+), because (+) takes two identical arguments, and is an operation that works the same no matter the order. In math, this property is called the commutative property.

6.6. What is commutivity? 🔗

The word commute can be broken into com-, which means altogether, and mut- which means to change. Commutativity means the ability to interchange, so we can see that we can interchange the numbers between either side of (+), and it makes no difference.

Here are four functions that are identical in result:

sevenPlus :: Int -> Int
sevenPlus number = (7+) number

sevenPlus' :: Int -> Int
sevenPlus' = (7+)

plusSeven :: Int -> Int
plusSeven number = (+7) number

plusSeven' :: Int -> Int
plusSeven' = (+7)

6.7. Homework 🔗

Your homework is to get familiar with more definitions, and seeing how function arguments are used in function bodies. Don’t get too worried or confused by the many tricky weird looking things you’ll see as you look at other code.

We’ll show you some code below. You should also do an internet search for ”haskell defining functions”, go through the first 10 or so returned pages and quickly scan through them for value and function definitions. Remember there are two ways to define functions: either with the lambda syntax like sevenPlus = \number -> (7+) number or with the “normal” syntax like sevenPlus number = (7+) number. Your aim here is simply to recognise where the definitions are.

Here are the examples. Remember, don’t get caught on what you don’t know, just look for what you do know. Remember, you’re just looking for the definitions, especially the function definitions:

squaredNum :: Integer -> Integer
squaredNum x = x ^ 2

lengthNum :: Show a => a -> Int
lengthNum n = length $ show n

bool1, bool2 :: Bool
bool1 = True
bool2 = False

notBool :: Bool -> Bool
notBool b = if b == bool1 then bool2 else bool1

veryNotBool :: Bool -> Bool
veryNotBool = \aBool -> notBool aBool

sumFrom1To :: Integral a => a -> a
sumFrom1To 0 = 0
sumFrom1To n = n + sumFrom1To (n - 1)

isEven :: Integral a => a -> Bool
isEven n = n `mod` 2 == 0

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: 5. Function Magic
6. Sockets & Plugs
... 6.1. Reusability
... 6.2. Functions are Values
... 6.3. Plugging Values into Functions
... 6.4. Defining Functions
... 6.5. Operator Sections
... 6.6. What is commutivity?
... 6.7. Homework
Next chapter: 7. Output Other Things