## DEV Community

mpodlasin

Posted on • Originally published at mpodlasin.com

# Haskell - The Most Gentle Introduction Ever (Part II)

This is the second article in my little series explaining the basics of Haskell.

If you haven't yet, I would recommend you to read the first article, before diving into this one.

So far we've learned the basics of defining functions and datatypes, as well as using them, all based on the Haskell `Bool` type.

In this article, we will deepen our understanding of functions in particular, while also learning a bit about built-in Haskell types representing numbers.

#### Type of Number 5

Before we begin this section, I must warn you. In the previous article, I purposefully used the `Bool` type, due to its simplicity.

You might think that types representing numbers would be equally simple in Haskell. However, that's not really the case.

Not only does Haskell have quite a lot of types representing numbers, but there is also a significant effort in the language to make those types as interoperable with each other as possible. Because of that, there is a certain amount of complexity, which can be confusing to beginners.

To see that, type in the following in the `ghci`:

``````:t 5
``````

You would probably expect to see something simple and concrete, like `Number` or `Int`. However, what we see is this:

``````5 :: Num p => p
``````

Quite confusing, isn't it?

For a brief second try to ignore that whole `Num p =>` part. If it wasn't there, what we would see, would be just `5 :: p` So what is written here is that the value `5` has type `p`.

This is the first time we see the name of the type being written using a small letter. That's important. Indeed, `p` is not a specific type. It is a type variable. This means that `p` can be potentially many different, concrete types.

It wouldn't however make sense for `5` to have - for example - `Bool` type. That's why `Num p =>` part is also written in the type description. It basically says that this `p` has to be a numeric type.

So, overall, `5` has type `p`, as long as `p` is a numeric type. For example, writing `5 :: Bool` would be forbidden, thanks to that restriction.

The exact mechanism at play here will not be discussed right now. We still have to cover a few basics before we can explain it fully and in detail. But perhaps you've heard it about it already - it's called typeclasses. We will learn about typeclasses very soon. After we do, this whole type description will be absolutely clear to you.

For now, however, we don't need to go into specifics. Throughout this article, we will use concrete, specific types, so that you don't get confused. I am just warning you about the existence of this mechanism so that you don't get unpleasantly surprised and discouraged when you investigate the types of functions or values on your own.

Which I encourage you to do! Half of reading Haskell is reading the types, and you should be getting used to that.

#### Functions and Operators

Let's begin by doing some simple operations on numbers, familiar from other programming languages.

We can, for example, add two numbers. Writing in `ghci`:

``````5 + 7
``````

results in a fairly reasonable answer:

``````12
``````

But to get a deeper insight into what is happening there, let's write a "wrapper" function for adding numbers.

We will call it `add` and we will use it like so:

``````add 5 7
``````

As a result, we should see the same answer as just a moment ago:

``````12
``````

We would like to, of course, begin with a type signature of that function. What could it potentially be?

``````add :: ???
``````

As I mentioned before, Haskell has many different types available for numerical values.

Let's say that we want to work only with integers for now. Even for integers, there are multiple types to choose from.

The two most basic ones are `Integer` and `Int`.

`Int` is a type that is "closer to the machine". It's a so-called fixed precision integer type. This means that - depending on the architecture of your computer - each `Int` will have a limited number of bits reserved for its value. Going out of those bounds can result in errors. This is a type very similar to C/C++ `int` type.

`Integer` on the other hand is an arbitrary precision integer type. This means that the values can potentially get bigger than those of `Int`. Haskell will just reserve more memory if that becomes necessary. So those integers are "arbitrarily" large, but, of course, only in principle - if you completely run out of computer memory, nothing can save you.

At the first glance, it would seem that `Integer` has some clear advantages. That being said, `Int` is still being widely used where memory efficiency is important or where we have high confidence that numbers will not become too large.

For now, we will use the `Integer` type. We can finally write the signature of our function:

``````add :: Integer -> Integer -> Integer
``````

What we have written here is probably a bit confusing at the first glance.

So far, we've only written declarations of functions that accept a single value and return a single value.

But when adding numbers, we have to accept two values as parameters (two numbers to add) and then return a single result (a sum of those numbers). So in our type signature, the first two `Integer` types represent parameters and the last `Integer` represents the return value:

``````add :: Integer {- 1st parameter -} -> Integer {- 2nd parameter -} -> Integer {- return value -}
``````

Let's now write the implementation:

``````add :: Integer -> Integer -> Integer
add x y = x + y
``````

Simple, right?

Create a file called `lesson_02.hs` and write down that definition. Next, load the file in `ghci` (by running `:l lesson_02.hs`) and type:

``````add 5 7
``````

As expected, you will see the reasonable response:

``````12
``````

I will now show you a neat trick. If your function accepts two arguments - like it is the case with `add` - you can use an "infix notation" to call a function. Write:

``````5 `add` 7
``````

You will again see `12` as a response.

Note that we didn't have to change the definition of `add` in any way to do that. We just used backticks and we were immediately able to use it in infix notation.

You can use this feature with literally any function that accepts two parameters - this is not a feature restricted only to functions operating on numbers.

At this point those two calls:

``````5 `add` 7
``````

and

``````5 + 7
``````

look eerily similar.

That's not an accident.

Indeed, you can do the reverse, and call the `+` operator as you would call a regular function - in front of the parameters. If it's an operator, you just have to wrap it in parentheses:

``````(+) 5 7
``````

Try it in `ghci`. This works and returns `12` again!

Perhaps you already know where am I going with this.

I am trying to show you that the built-in `+` operator is indeed just a regular function.

There are of course syntactical differences (like using backticks or parentheses for certain calls), but conceptually it is good to think of `+` as being no different than `add`. Both are functions that work on the `Integer` type - accept two `Integer` numbers and return an `Integer` number as a result.

Indeed, you can even investigate the type of an operator in `ghci`, just as you would investigate the type of `add` function.

Typing:

``````:t (+)
``````

results in:

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

This means that `+` has type `a -> a -> a`, where `a` has to be a numeric type. So it's a function that accepts two parameters of numeric type `a` and returns the result of the same type.

I hope that at this point it makes sense why it is beneficial for the `+` operator to have such an abstract definition. A clear benefit `+` has over our custom `add` function is that `+` works on any numeric type. No matter if it's `Integer`, `Int`, or any other type that somehow represents a number - `+` can be used on it. Meanwhile, our `add` function works only on `Integer` types. For example, if you try to call it on - very similar - `Int` numbers, the call will fail.

So you can see that complexity introduced in number types doesn't come out of nowhere. It keeps the code typesafe, while still allowing huge flexibility. Types might seem complex, but this makes writing actual implementations a breeze.

#### Partial Application

Let's go back to the type of `add` function, which is probably still friendlier to read at this point.

``````add :: Integer -> Integer -> Integer
``````

The way we have written the type definition here might be surprising to you. We see two `->` arrows in the definition, almost suggesting that we are dealing with two functions.

And indeed we are!

To increase the readability even more, we can use the fact that `->` is right-associative. This means that our type definition is equivalent to this:

``````add :: Integer -> (Integer -> Integer)
``````

Let's focus on the part that is outside of the parentheses first:

``````add :: Integer -> (...)
``````

This says that `add` is a function that accepts an `Integer` and returns something.

What is that something? To find out, we have to look inside the parentheses:

``````(Integer -> Integer)
``````

That's again a function! This one also accepts an `Integer` as an argument. And as a result, it returns another `Integer`.

So if we look at the type of `add` again:

``````add :: Integer -> Integer -> Integer
``````

we see that - in a certain sense - I was lying to you the whole time!

I was saying that this is how we describe a function that accepts two parameters. But that's false! There is no function that accepts two parameters here!

There is only a function that accepts a single parameter and then... returns another function!

And then that second function accepts yet another parameter and just then returns a result!

To state the same thing in terms of another language, here is how you would write a regular function that accepts two parameters in JavaScript:

``````function add(x, y) {
return x + y;
}
``````

However what we are creating in Haskell is something closer to this JavaScript code:

``````function add(x) {
return function(y) {
return x + y;
}
}
``````

Note how we have two functions here, each accepting only a single parameter.

In the code snippet above, function `add` accepts parameter `x` and the second, anonymous, function accepts the parameter `y`. There is no function here that accepts two parameters.

So, based on what we have said so far, in Haskell we should be able to call the `add` function with only one parameter and get a function, right?

Let's try that in `ghci`:

``````add 5
``````

Regrettably, we get an error message:

``````<interactive>:108:1: error:
â€¢ No instance for (Show (Integer -> Integer))
arising from a use of â€˜printâ€™
(maybe you haven't applied a function to enough arguments?)
â€¢ In a stmt of an interactive GHCi command: print it
``````

But that doesn't happen, because we did something wrong. The problem arises, because `add 5` returns - as we stated - a function, and Haskell doesn't know how to print functions.

We can however check the type of the `add 5` expression and this way convince ourselves that this indeed works:

``````:t add 5
``````

As a result, we see:

``````add 5 :: Integer -> Integer
``````

So it's a success! The expression `add 5` has type `Integer -> Integer`, just as we wanted!

We can convince ourselves even more that that's the case, by calling that `add 5` function on a number:

``````add 5 7
``````

Oh... Wait. We just discovered something!

It was probably confusing so far why Haskell has this strange way of calling functions on values, especially on multiple values. We were just separating them by space, like so:

``````f x y z
``````

Now it becomes clear, that Haskell simply deals with single-argument functions all the time, and calling a function on multiple values is simply an illusion!

Our call:

``````add 5 7
``````

is equivalent to:

``````(add 5) 7
``````

First, we apply `add` to `5` and as a result, we get a function of type `Integer -> Integer`, as we've just seen.

Then we apply that new function (`add 5`) on a value `7`. As a result, we get an `Integer` - number `12`.

Note that this means that in Haskell function call is left-associative:

``````(add 5) 7
``````

Contrasting with what we found out before - that type definition of function is right-associative:

``````add :: Integer -> (Integer -> Integer)
``````

It is of course done this way so that we get a sane default. Thanks to those properties, in the case of both defining and calling the `add` function, we can simply forget about parentheses:

``````add :: Integer -> Integer -> Integer
``````
``````add 5 7
``````

How else can we convince ourselves that the result of calling `add 5` is an actual, working function? Well... let's give a name to that function and use it that way!

In your `lesson_02.hs` file add the following line:

``````addFive = add 5
``````

Load the file in `ghci`.

First let's investigate the type one more time, just to be sure what we are dealing with:

``````:t addFive
``````

This gives us:

``````addFive :: Integer -> Integer
``````

Exactly what we expected after applying the `add` function to one argument.

We could have also written down that type definition explicitly in our file:

``````addFive :: Integer -> Integer
``````

Do that to convince yourself that it all compiles when written this way.

Now you can use your, highly specific, `addFive` function to... add five to integers.

``````addFive 7
``````

This results in the expected:

``````12
``````

Now, I am sure this example with adding the number five seems a bit silly to you, and rightfully so.

But I hope that it shows you the power of partial application in Haskell, where you can easily use highly general functions, accepting a higher number of parameters, to create something more specific and fitting your particular needs.

For example, you can imagine a function that needs some kind of complex configuration in order to work - let's call it `imaginaryFunction`.

Let's assume that this configuration is some kind of data structure of type `ComplexConfiguration`. We can make that configuration the first argument of our function:

``````imaginaryFunction :: ComplexConfiguration -> OtherParameter -> Result
``````

Why do we want to pass it as a parameter instead of just having it "hardcoded" inside the function? Who knows, perhaps different versions of our app need different configurations. Or perhaps we just need to change its value in our unit test suite.

If we do that, then, in the actual app, we can simply apply `imaginaryFunction` to a specific `ComplexConfiguration`:

``````configuredImaginaryFunction = imaginaryFunction config
``````

After that, we can use the `configuredImaginaryFunction` directly, without the need to explicitly import `config` object, whenever we want to use `imaginaryFunction` in our code. Haskell just carries that config around for us!

Sweet!

#### More Numbers and Operations

This section will be far from a complete rundown, but I still want to quickly take you up to speed with basic operations on numbers in Haskell.

So far we've covered two numeric types - `Integer` and `Int` and we've shown that we can add them.

Just as in other languages, we can also subtract and multiply them in the usual way.

So:

``````5 - 2
``````

results in:

``````3
``````

while:

``````5 * 3
``````

results in:

``````15
``````

Other popular number types are `Float` and `Double`. Those are number types that can represent numbers beyond simple integers. The main difference between `Float` and `Double` is how big their storage capacity is. Most of the time you will likely use `Double` unless you have some specific reason to use `Float`.

`Float` and `Double` values can be added, subtracted, and multiplied just like `Int` and `Integer` values.

Let's see some examples, just to make ourselves comfortable with that:

``````1.1 - 0.1
``````

results in:

``````1.0
``````
``````1 + 0.1
``````

result in:

``````1.1
``````

Note that in Haskell's standard library there are much more numeric types available. Some of them are fairly common, others are fairly specific. There are also many more built-in functions.

This section was just meant to make you comfortable with making simple operations on basic number types. In the future we will come back to numbers for sure - there is a great deal of interesting type-level stuff at play here, and we will want to cover that for sure!

#### Identity

Let's now take a small break from numbers and go back to our beloved booleans.

Previously, we have written a `not` function, which was negating the booleans - converting `True` to `False` and `False` to `True`.

But what if we wanted to do... the opposite?

What if we wanted to create a function that... returns `True` when passed `True` and returns `False` when passed `False`?

This might sound a bit nonsensical. In a way, this function would do literally nothing. However, we will see that it will have a tremendous educational value for us, so let's curb our doubts and let's try to write it anyway.

In mathematics, a function that takes a value and returns the exact same value is usually called an identity function. Since this one will operate on the `Bool` type, we will call it `boolIdentity`.

`boolIdentity` will receive a single argument of type `Bool` and return the same thing, so... `Bool` as well! Therefore its type signature is this:

``````boolIdentity :: Bool -> Bool
``````

Now let's get started with actual implementation. Your first instinct might be to write something like this:

``````boolIdentity True = True
boolIdentity False = False
``````

This will work perfectly fine and is a valid solution, but there is a way to write the same thing in a more terse way.

After all, we are returning the same value that we are receiving as a parameter, so we can simply write:

``````boolIdentity x = x
``````

In the end, our whole definition looks like that:

``````boolIdentity :: Bool -> Bool
boolIdentity x = x
``````

Write that down in your `lesson_02.hs` file and reload the file in `ghci`.

You can convince yourself that our function works, by running it:

``````boolIdentity True
``````

This call results in:

``````True
``````

And at the same time `boolIdentity False` returns `False` (hopefully not surprisingly).

Now let's create a similar function, but for numbers - let's say for `Integer` type. We want a function that will take an `Integer` value and return the same value. For example, if we call it with `5`, we want to see `5` again.

Let's call it `integerIdentity`. Let's begin by writing the type signature:

``````integerIdentity :: Integer -> Integer
``````

This was simple.

Now let's think about the implementation. Well... we want to take the parameter passed to the function and... just return it!

So we get:

``````identityInteger x = x
``````

But... but this is exactly the same implementation as in the case of identity for `Bool` values!

Let's compare the two:

``````boolIdentity :: Bool -> Bool
boolIdentity x = x
``````
``````integerIdentity :: Integer -> Integer
integerIdentity x = x
``````

Everything looks the same. The only difference here is the types. In the first function, we operate on the `Bool` type. In the second we operate on the `Integer` type.

Now, if only there was a way to write that function only once. In the current state of things, we would have to write an identity function for each type in existence, which... sounds daunting, to say the least.

It luckily turns out that Haskell does have a mechanism to deal with that easily. Not only that - we've already encountered that mechanism!

Remember how the type of number `5` was `Num p => p`? The `p` in the type description was a type variable - basically a placeholder for actual, concrete types.

We've also seen that the `+` operator was quite general - it could be called on any numeric type. It also had a type variable in its type signature.

So the question is, can we use a type variable, to write the most generic version of the identity function possible? The answer is... absolutely!

Let's remove the two previous identity functions and replace them with only one:

``````identity :: a -> a
identity x = x
``````

Note how we used `a` as a type variable here. The 3 type definitions we've seen so far, share the same "shape". You can see that the type definitions of identity for `Bool` type and for `Integer` type both "fit" this new type definition if you imagine variable `a` being a placeholder for other types:

``````boolIdentity :: Bool -> Bool
``````
``````integerIdentity :: Integer -> Integer
``````
``````identity :: a -> a
``````

Let's run the code in `ghci` and convince ourselves that we can indeed use this new, generic identity on both `Bool` and `Integer` values:

``````identity True
``````

works and results in:

``````True
``````

And at the same time:

``````identity 5
``````

works as well and results in:

``````5
``````

At this point, it's important to emphasize a certain point. Given the implementation that we've used for the identity function:

``````identity x = x
``````

we could not give it, for example, the following type:

``````identity :: Integer -> Bool
``````

This type definition in itself is not absurd. You can easily imagine functions that accept integers and return true or false (for example based on some condition).

However, in this particular case, where we take argument `x` and immediately return it, without doing anything else, it's clearly impossible for `x` to "magically" change the type.

And this fact is reflected even in the most generic type definition of identity:

``````identity :: a -> a
``````

Note that this definition states that `identity` accepts a value of type `a` and returns a value of that same type - namely `a` again.

On the flip side, the following definition:

``````identity :: a -> b
``````

wouldn't be allowed. In fact, it won't compile, with an error, part of which says:

``````Couldn't match expected type â€˜bâ€™ with actual type â€˜aâ€™
``````

So the compiler literally says that in place of `b` there should be the type variable `a` present.

That's because - given the current implementation - it's impossible for the value named `x` to just change the type out of nowhere.

And indeed, when Haskell infers the type of untyped code, it goes for the most general interpretation possible.

You can convince yourself of that, by removing the type definition from `lesson_02.hs` file, and leaving only the implementation:

``````identity x = x
``````

And then compiling it in `ghci` and checking the type of `identity` by running:

``````:t identity
``````

As an answer you will see:

``````identity :: p -> p
``````

This is exactly the same type definition that we wrote by hand. It simply uses a different letter (`p` instead of `a`).

At the very end of this section, it would be good to mention, that you don't actually have to define the `identity` function by yourself. We only did it for educational purposes.

`Prelude` - the standard library for Haskell - has it always available for you under the shorter name `id`.

To convince yourself of that, write the following in `ghci`:

``````:t id
``````

As a response you will see, more than familiar now, type:

``````id :: a -> a
``````

#### Conclusion

In the second part of "The Most Gentle Introduction Ever" to Haskell, we used operators and functions on numbers and discovered that they are in fact almost the same. We've also seen that there are no functions of multiple variables in Haskell - they are just functions that return other functions.

And at the end, we expanded our understanding of what a type definition can be, by showing the simplest possible usage of type variables - using the identity function as an example.

All those things were meant to make you feel more comfortable with the concept of a function in Haskell. And in the future article, we will use a similar approach to enhance our understanding of algebraic data structures, especially custom ones.

So see you next time and thanks for reading!