So You Want to be a Functional Programmer (Part 4)

Charles Scalfani
7 min readSep 24, 2016

--

Taking that first step to understanding Functional Programming concepts is the most important and sometimes the most difficult step. But it doesn’t have to be. Not with the right perspective.

Previous parts: Part 1, Part 2, Part 3

Currying

If you remember from Part 3, the reason that we were having problems composing mult5 and add (in ) is because mult5 takes 1 parameter and add takes 2.

We can solve this easily by just restricting all functions to take only 1 parameter.

Trust me. It’s not as bad as it sounds.

We simply write an add function that uses 2 parameters but only takes 1 parameter at a time. Curried functions allow us to do this.

A Curried Function is a function that only takes a single parameter at a time.

This will let us give add its first parameter before we compose it with mult5. Then when mult5AfterAdd10 is called, add will get its second parameter.

In Javascript, we can accomplish this by rewriting add:

var add = x => y => x + y

This version of add is a function that takes one parameter now and then another one later.

In detail, the add function takes a single parameter, x, and returns a function that takes a single parameter, y, which will ultimately return the result of adding x and y.

Now we can use this version of add to build a working version of mult5AfterAdd10:

var compose = (f, g) => x => f(g(x));
var mult5AfterAdd10 = compose(mult5, add(10));

The compose function takes 2 parameters, f and g. Then it returns a function that takes 1 parameter, x, which when called will apply f after g to x.

So what did we do exactly? Well, we converted our plain old add function into a curried version. This made add more flexible since the first parameter, 10, can be passed to it up front and the final parameter will be passed when mult5AfterAdd10 is called.

At this point, you may be wondering how to rewrite the add function in Elm. Turns out, you don’t have to. In Elm and other Functional Languages, all functions are curried automatically.

So the add function looks the same:

add x y =
x + y

This is how mult5AfterAdd10 should have been written back in Part 3:

mult5AfterAdd10 =
(mult5 << add 10)

Syntactically speaking, Elm beats Imperative Languages like Javascript because it’s been optimized for Functional things like currying and composition.

Currying and Refactoring

Another time currying shines is during refactoring when you create a generalized version of a function with lots of parameters and then use it to create specialized versions with fewer parameters.

For example, when we have the following functions that put brackets and double brackets around strings:

bracket str =
"{" ++ str ++ "}"
doubleBracket str =
"{{" ++ str ++ "}}"

Here’s how we’d use it:

bracketedJoe =
bracket "Joe"
doubleBracketedJoe =
doubleBracket "Joe"

We can generalize bracket and doubleBracket:

generalBracket prefix str suffix =
prefix ++ str ++ suffix

But now every time we use generalBracket we have to pass in the brackets:

bracketedJoe =
generalBracket "{" "Joe" "}"
doubleBracketedJoe =
generalBracket "{{" "Joe" "}}"

What we really want is the best of both worlds.

If we reorder the parameters of generalBracket, we can create bracket and doubleBracket by leveraging the fact that functions are curried:

generalBracket prefix suffix str =
prefix ++ str ++ suffix
bracket =
generalBracket "{" "}"
doubleBracket =
generalBracket "{{" "}}"

Notice that by putting the parameters that were most likely to be static first, i.e. prefix and suffix, and putting the parameters that were most likely to change last, i.e. str, we can easily create specialized versions of generalBracket.

Parameter order is important to fully leverage currying.

Also, notice that bracket and doubleBracket are written in point-free notation, i.e. the str parameter is implied. Both bracket and doubleBracket are functions waiting for their final parameter.

Now we can use it just like before:

bracketedJoe =
bracket "Joe"
doubleBracketedJoe =
doubleBracket "Joe"

But this time we’re using a generalized curried function, generalBracket.

Common Functional Functions

Let’s look at 3 common functions that are used in Functional Languages.

But first, let’s look at the following Javascript code:

for (var i = 0; i < something.length; ++i) {
// do stuff
}

There’s one major thing wrong with this code. It’s not a bug. The problem is that this code is boilerplate code, i.e. code that is written over and over again.

If you code in Imperative Languages like Java, C#, Javascript, PHP, Python, etc., you’ll find yourself writing this boilerplate code more than any other.

That’s what’s wrong with it.

So let’s kill it. Let’s put it in a function (or a couple of functions) and never write a for-loop again. Well, almost never; at least until we move to a Functional Language.

Let’s start with modifying an array called things:

var things = [1, 2, 3, 4];
for (var i = 0; i < things.length; ++i) {
things[i] = things[i] * 10; // MUTATION ALERT !!!!
}
console.log(things); // [10, 20, 30, 40]

UGH!! Mutability!

Let’s try that again. This time we won’t mutate things:

var things = [1, 2, 3, 4];
var newThings = [];
for (var i = 0; i < things.length; ++i) {
newThings[i] = things[i] * 10;
}
console.log(newThings); // [10, 20, 30, 40]

Okay, so we didn’t mutate things but technically we mutated newThings. For now, we’re going to overlook this. We are in Javascript after all. Once we move to a Functional Language, we won’t be able to mutate.

The point here is to understand how these functions work and help us to reduce noise in our code.

Let’s take this code and put it in a function. We’re going to call our first common function map since it maps each value in the old array to new values in the new array:

var map = (f, array) => {
var newArray = [];
for (var i = 0; i < array.length; ++i) {
newArray[i] = f(array[i]);
}
return newArray;
};

Notice the function, f, is passed in so that our map function can do anything we want to each item of the array.

Now we can call rewrite our previous code to use map:

var things = [1, 2, 3, 4];
var newThings = map(v => v * 10, things);

Look ma. No for-loops. And much easier to read and therefore reason about.

Well, technically, there are for-loops in the map function. But at least we don’t have to write that boilerplate code anymore.

Now let’s write another common function to filter things from an array:

var filter = (pred, array) => {
var newArray = [];
for (var i = 0; i < array.length; ++i) {
if (pred(array[i]))
newArray[newArray.length] = array[i];
}
return newArray;
};

Notice how the predicate function, pred, returns TRUE if we keep the item or FALSE if we toss it.

Here’s how to use filter to filter odd numbers:

var isOdd = x => x % 2 !== 0;
var numbers = [1, 2, 3, 4, 5];
var oddNumbers = filter(isOdd, numbers);
console.log(oddNumbers); // [1, 3, 5]

Using our new filter function is so much simpler than hand-coding it with a for-loop.

The final common function is called reduce. Typically, it’s used to take a list and reduce it to a single value but it can actually do so much more.

This function is usually called fold in Functional Languages.

var reduce = (f, start, array) => {
var acc = start;
for (var i = 0; i < array.length; ++i)
acc = f(array[i], acc); // f() takes 2 parameters
return acc;
});

The reduce function takes a reduction function, f, an initial start value and an array.

Notice that the reduction function, f, takes 2 parameters, the current item of the array, and the accumulator, acc. It will use these parameters to produce a new accumulator each iteration. The accumulator from the final iteration is returned.

An example will help us understand how it works:

var add = (x, y) => x + y;
var values = [1, 2, 3, 4, 5];
var sumOfValues = reduce(add, 0, values);
console.log(sumOfValues); // 15

Notice that the add function takes 2 parameters and adds them. Our reduce function expects a function that takes 2 parameters so they work well together.

We start with a start value of zero and pass in our array, values, to be summed. Inside the reduce function, the sum is accumulated as it iterates over values. The final accumulated value is returned as sumOfValues.

Each of these functions, map, filter and reduce let us do common manipulation operations on arrays without having to write boilerplate for-loops.

But in Functional Languages, they are even more useful since there are no loop constructs just recursion. Iteration functions aren’t just extremely helpful. They’re necessary.

My Brain!!!!

Enough for now.

In subsequent parts of this article, I’ll talk about Referential Integrity, Execution Order, Types, and more.

Up Next: Part 5

If you liked this, click the💚 below so other people will see this here on Medium.

Update circa 2021: I have a book that will teach you everything in this series and so much more, Functional Programming Made Easier: A Step-by-Step Guide.

If you want to join a community of web developers learning and helping each other to develop web apps using Functional Programming in Elm please check out my Facebook Group, Learn Elm Programming https://www.facebook.com/groups/learnelm/

My Twitter: @cscalfani

--

--

Charles Scalfani
Charles Scalfani

Written by Charles Scalfani

Software Engineer and Architect, Teacher, Writer, Filmmaker, Photographer, Artist…

Responses (26)