The blog post introduces functions, types, and bindings, which are all important concepts in Haskell.

Functions
Let's start coding. Functions are an essential component of Haskell, and they can be defined as follows:
name arg1 arg2 ... argn = <expr>
where name
is the name of the function, arg1 arg2 ... argn
are the arguments/inputs of the function, and <expr>
is the
expression of the function. It is important to note here that return in Haskell is not the same as return in other programming
languages like Python. When you use the defined function, you use it like the following:
name arg1 arg2 ... argn
Examples
Let's try working with examples to understand how functions work in Haskell. The following functions, double
and inRange
,
double an input and check if a value is within a provided range, respectively.
double x = 2 * x
double 2 --- => 4
double 100 --- => 200
inRange min max x = min <= x && x <= max
inRange 10 15 11 --- => True
inRange 1 2 3 --- => False
Try defining them in your .hs file and running the functions in GHCi.
Types
If you defined the above functions in VSCode with the Haskell extension set up (If you haven't done this, I recommend reading Road to Haskeller #0 - Setting Up Environment and setting up the same environment on your device), you should see small strings appear just above your functions. These are the inferred types of the functions, but do not click them for now. Every value (a function is also a value) in Haskell has its type, and we can assign a type to anything like the following:
x :: <type>
Examples
Let's look at some examples to understand how types can be assigned in Haskell.
x :: Integer
x = 1
y :: Bool
y = True
The above shows how types can be assigned to variables. Let's see how types can be assigned to
double
and inRange
.
double :: Integer -> Integer
double 1 --- => 2
double 1.2 --- => Type Error
inRange :: Float -> Float -> Float -> Bool
inRange 1.1 1.3 1.2 --- => True
inRange 1 3 2 --- => Type Error
Let's look at the double
function first. The first part of the type definition specifies that the input has the type Integer
(1, 2, 3, etc.). Then, the latter part defines that its output also has the type Integer
.
Notice here that we don't distinguish between input and output in the type definition.
This is related to a very important concept in Haskell (Currying), but more on that much later.
The same goes for the inRange
function. The types of the first three arguments are set to be Float
and the output is set to
be Bool
(True
or False
). However, if you are following along by using the same VSCode setup, you might be wondering why you
see a difference between the above and the inferred types displayed earlier. This is because the inferred types use the concept
of typeclasses (like Num
and Ord
), which is outside the scope of this article. For now, always try using types, not typeclasses,
that are often recommended by VSCode to learn more about types.
Bindings
Sometimes, we would like to have some kind of intermediary variables to clean up our code. However, the following is illegal.
inRange min max x =
lower_bound = x >= min
upper_bound = x <= max
return lower_bound && upper_bound
How do we achieve the same thing without being scolded by Haskell? This is where bindings come in.
Let Bindings
The let
bindings work like the following:
inRange min max x =
let lower_bound = x >= min
upper_bound = x <= max
in
lower_bound && upper_bound
We use let
to define intermediary variables, and in
to operate on those variables.
Where Bindings
Depending on the functions, we may want to use where
bindings for better code readability.
inRange min max x = lower_bound && upper_bound
where lower_bound = x >= min
upper_bound = x <= max
We first define the final operations we want to perform with intermediary variables,
and then specify what those variables mean after where
.
If Statements
If you have experience coding in other programming languages, you might be wondering how to write an if statement. You can do so like the following:
inRange min max x =
if lower_bound then upper_bound else False
where lower_bound = x >= min
upper_bound = x <= max
This is pretty intuitive and easy to write and read. Today, we covered the essential components: functions, types, bindings, and if statements in Haskell. I hope you learned something from this article.
Exercises
From this article, there will be an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.
Resources
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #2 - Functions, Types, let & where. YouTube.