Why FP Matters - Part 1½ 💫

December 20, 202012 min read


apparatus
Picture by Elena Mozhvilo

A Quick Recap

I the last post, we saw how functional programming affects software development. We learned that it plans to address our concerns of handling complexity, modular-design, and re-usability. It claims to reduce the time and cost of development and aims at increasing developer productivity. We also found out where it exists in the real world and why language developers are so interested.

So we are here and ready to look at the new glues. Actually, not yet. I've chosen Haskell for this purpose, which means that we will have to learn a few basics first. Don't worry, it is not much and I promise that it will be fun.

If you don't know what I am talking about, this is a series of articles. You might want to go here for better context - Why FP Matters - Part 1 🐘 : A brief introduction

Why Haskell?

  • Haskell is purely functional
  • It has an elegant syntax with less clutter
  • It presents a smaller semantic gap between the programmer and the language - less clutter.
  • It has the basic features that we need for this series

For a person new to Haskell the code will be more intuitive. Having said that, I'd still like to take a little bit of time to explain a few things so we get used to it.

Functions

Ok, we've heard enough of the F-word too many times now. It must have got to do something about functions? Let's get to it then. We will start with baby steps - a simple function that takes in an input and adds it to itself. We'll call it double'.

double' x = x + x

Here, double' is the identifier of the new function being defined, while x is the input parameter. These are the pieces on the left-hand side. The right hand contains the function definition, which is simply adding x to itself - x + x. Simple?

Note: I am appending a quote (') to the function names. I am doing so to avoid clashing with any in-built functions. Also, it is a valid character to be included in a function name, so it shouldn't be a problem.

Let's define a couple of more functions, say square' and add'. The former should return the square of an input and the latter take in two numbers and return their sum.

Do you want to guess how they look like?

square' x = x * x
add' x y = x + y

See! Nothing scary.

Operators

Now let's take a closer look at operators in Haskell. Nothing, alien-like over here. Simple and earthly. In fact, we used them to we define our functions above.

1 * 2
3 + 4

But wait, there is more. 🧙🏽‍♂️ "lo, behold the first act of magic". Excuse me? ...

Anyway, Haskell is so functional, the operators can be used as average functions too. Thus, the above can be written as follows.

(*) 1 2
(+) 3 4

We had to wrap the operator within parenthesis, and then they could be moved to the beginning of the expression to make it a prefix notation. Without the parenthesis, they were used with the infix notation.

A little different, but not that too drastic for you I hope. In Haskell, operators are just plain old functions under the hood.

Pattern Matching

Let's say we need to define a function. Well, just indulge me for a bit. Say the function takes in a number

  • if the number is 1, the function should return 1000
  • if the number is 2, the function should return 2000
  • else, the function should return the number incremented by 1

Below is a valid definition for it in Haskell.

foo' x =
if x == 1
then 1000
else if x == 2
then 2000
else
x + 1

🧙🏽‍♂️ "yikes! that is just too much code son!" ... Erm, OK weird wizard man who has just appeared out of nowhere for the second time?

Let's try to rewrite the above function.

foo' 1 = 1000
foo' 2 = 2000
foo' x = x + 1

This is the second magical thing about Haskell, we defined a function to make it more declarative. We say that foo' 1 is 1000 and foo' 2 is 2000. No ifs not buts, it just is. Also, the code is more clear and lesser.

OK, you've see two new things.

The Prepend Operator

🧙🏽‍♂️ "I shall call it cons". Err, ok. Whatever! I guess you're not going away.

Haskell has a prepend operator (:) which helps prepend values to the beginning of a list.

[] == []
3:[] == [3]
2:[3] == [2, 3]
1:[2, 3] == [1, 2, 3]

Above are equivalent values on both sides of the equality operator (==) on each line. The first one contains empty lists, the second one contains two lists, both containing the number 3. You get the idea. 🧙🏽‍♂️ "No, they don't!" ... Okay! There is a little magic here as well.

Haskell recognizes the shapes of data structures. For now, just note the representations of a list. We will step a little deeper into it next.

Lists

A list of type X in Haskell can be defined as the below

(listof X) ::= [] | X : listof X

i.e. As list of X can be one of the below

  • [] -- an empty list
  • X : listof X -- a value of type X prepended to a listof X

🧙🏽‍♂️ "A group of wizards is a wizard and group of wizards." ... yeah, the definition is recursive. So a list with one wizard is the same as a wizard and an empty group. doh!, I mean a group with a single element of type X ... (grinds teeth) ... you get it! 🧙🏽‍♂️ "Yes, they do!" ... hey! I'm trying to write here?

All the below representations of the list containing the numbers 1, 2 and 3 are equivalent and can be used interchangeably.

[1, 2, 3]
1 : [2, 3]
1 : 2 : [3]
1 : 2 : 3 : []

Deconstruction

... 🧙🏽‍♂️ (clears throat!) ... are you going to? ... 🧙🏽‍♂️ "no, go ahead" ... hmm, thank ... 🧙🏽‍♂️ "I want to see you try." ... what the hell!

A square container catches a square

A circular container, a circle

And now my friends, we will try to catch

🧙🏽‍♂️ "The elements of a list without any hassle!"

...

🤜🏽 🧙🏽‍♂️✨

Sorry about that, just had to get it out of my system. Let's have a look at the code below.

x:xs = [1, 2, 3]
x == 1
xs == [2, 3]

Just using one line of code, we have assigned the first element of the list to the variable x and the remaining part of the list has been assigned to the variable xs. If you notice closely, the shape of the list could match the expression on the left-hand side. Hence, the respective assignments were made. To make it more clear, the first line could be re-written as below.

x:xs = 1:[2, 3]

It is clear that x matches 1, while xs matches [2, 3]. We didn't have to explicitly change the representation of the list. As long as it is a list, Haskell will take in any representation and it will also use the best representation to match the expression on the left-hand side.

Let's make it more concrete but changing the example to pluck the first two values. The code will look as below.

x1:x2:xs = [1, 2, 3]
x1 == 1
x2 == 2
xs == [3]

Let's flex

Alright, you have learned a bit of Haskell, let's put it to use for a little practice.

Practice Problem 1

Suppose we want a function rest' that takes in a list and returns another list. Let's say that the list returned by our function is identical to the input list, except for one difference. It should be missing the first element from the input list. We could define it as below.

rest' [] = []
rest' (x:xs) = xs

Practice Problem 2

Now let us define a function, that takes in a list and returns the first element of the list.

first' (x:xs) = x

Practice Problem 3

Finally, let us define a function, that takes in a list and returns the second element of the list.

second' (x1:x2:xs) = x2

Note: We haven't handled any boundary conditions for the functions first' and second'. The above functions will fail when called with empty lists. That is fine for now. There are ways to handle that, but those are not the focus of this series. Perhaps I will come back to them in the future.

Recap

We learned about small bits of Haskell in this article

  1. Functions
  2. Operators as Functions
  3. Pattern Matching
  4. Prepend Operator
  5. Lists
  6. Deconstruction

Finally, we wrote a few functions to exercise what we have learned. So cheers! You've learned a bit of Haskell. If you are interested in dabbling around with it you have the below options.

If you don't want to get your hands dirty, that's fine too. We'll continue here with the next one in this series - Why FP Matters.

Up Next

Why FP Matters - Part 2 🎈 - Higher-order functions And this time, I promise. We will dive into the first type of glue that is higher-order functions and see how they can help modularize code. I promise, there will be more code 🙂.

For more fun posts like these, click here to see a list of all my posts

"See you on the other side of this spell!"

the wizard
Picture by Chase Clark

...