Haskell series part 3

Thank you for joining us for the third part of our Haskell series, you will find the previous article here where I explain lists and function definitions.

In this article we are going to cover infix and prefix functions, and discover a little bit more about types. Let's start with the functions:

Infix and prefix functions

Most functions are called in a prefix way: name of the function first and then parameters. For instance, let's load our power function which we wrote in the previous article:

Prelude> :l power.hs 
[1 of 1] Compiling Main             ( power.hs, interpreted )
Ok, one module loaded.
*Main> powerOfX 3 2
9.0

powerOfX is the function name, then 3 and 2 are the parameters. So far everything is normal.

Now, If a function takes two parameters, we can call it in the infix way (technically you can do it with more than two parameters as well, but it is not as elegant). Let's try it on our power function to illustrate this concept:

*Main> 3 `powerOfX` 2
9.0

Note that to call a function in the infix way you need to surround it with back ticks. While it is an unconventional sight, especially if you have experience in some imperative languages; you can see in some cases it is even easier to read a function using the infix way :literally you read it as "3 power of (x) 2".

Now you may realize that you recognize this syntax of: <a> f <b> and you would be right. We saw some examples in the first article when we played with GHCI. Exactly, this part:

Prelude> a = 4
Prelude> b = 6
Prelude> c = a + b
Prelude> c
10


The addition on line 3, is actually a function with two parameters called in the infix way.

Let's prove it:

Proof 1: If we wrap it with parenthesis, we can call it the prefix way

Prelude> (+) 2 4
6

Fair enough.

Proof 2: If it's a function, it has a definition

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

Noted.


But, what is this a in the function definition of + ? If we inspect our existing powerOfX we do not have a single letter, we have types starting with Uppercase. Let's check it again:

*Main> :t powerOfX
powerOfX :: Float -> Float -> Float

There are two key concepts to unpack here, first let's explain what is this a:

Type Variables

This a is called a type variable and it means that it can be any type, think of it as generics in other languages.

For instance, in Java, if you want to have a List which can contain a specific type, you would write something like this:

List<T> list = new ArrayList<T>();

Here, we created a variable called list which can contain values of type T.  This T is the same as our a, the difference is that in Haskell, types must start with an uppercase, while type variables are always lower case and usually single letters.

Typeclass

The second concept coming from this + infix operator is Num, it is called a Typeclass, and to continue on comparisons with other languages, think of it as an Interface.

For instance, in Java, if you want to define a behavior for your classes, you would unify it in a single Interface and all your classes would implement it. Like this:

interface Drawable {
  void draw();
}

class Square implements Drawable {

  public void draw() {
    // TODO
   }

}

class Circle implements Drawable {

  public void draw() {
    // TODO
   }

}

In this snippet, we defined the interface Drawable which Square and Circle implement. I left a TODO in the body as the content is not relevant, but the idea is that those two classes implement their own version of draw().

In this situation, Num describes numeric types and has some of the following instances: Int, Integer, Float, Double . The documentation contains the whole list if you are curious. It defines the usual methods for basic arithmetic such as addition, subtraction and so on.

So if you put everything together, it means that Num a => defines that a (type variable) can be any type you want as long as it is an instance of Num (typeclass).

Type inference

Let's test our addition now that we understand its definition:

Prelude> :t (+)
(+) :: Num a => a -> a -> a
Prelude> result = 1 + 2
Prelude> :t result
result :: Num a => a

So, Haskell uses type inference, in short it means that the type will be deduced from how it is used. Here when we want to find out the type of our variable result we are being told it is a Num. Because we did not precisely defined what types were 1 and 2, Haskell used the typeclass from the function definition.

Let's try to use specific types for our addition:

Prelude> first = 1::Int
Prelude> :t first
first :: Int
Prelude> second = 2::Int
Prelude> :t second
second :: Int
Prelude> result = first + second
Prelude> :t result
result :: Int

Nice, we wanted to add 2 Int which are instances of Num and we received an Int. But what would happen if we wanted to add 2 different instances of Num ?

Prelude> first = 1::Double
Prelude> second = 2::Int
Prelude> result = first + second

<interactive>:34:18: error:
    • Couldn't match expected type ‘Double’ with actual type ‘Int’
    • In the second argument of ‘(+)’, namely ‘second’
      In the expression: first + second
      In an equation for ‘result’: result = first + second

It does not work, remember the definition has Num a , meaning a has to be the same type. This is the reason why GHCI tells you that it cannot match Double and Int even though they are both instances of Num.

Conclusion

Three Down, around seven more articles to go. This is going to become a little bit more complicated progressively, but I will try to keep those posts short and to the point. In the next article we will discuss tuples and pattern matching.

PS: Part 4 can be found here.

If you have a problem and no one else can help. Maybe you can hire the Kalvad-Team.