Functional Programming in Javascript
with Ramda.js
var R = require('ramda');
Functions are Values
A language where functions are values has first class functions
Functions can be stored in variables and passed as arguments like any other value
var add = function(x, y) {
return x + y;
};
Pure Functions
the best kind
A pure function has only inputs and output
A pure function mutates no state
var someArray = [1, 2, 3];
someArray.push(4);
This mutates someArray
.
var purePush = function(item, array) {
var newArray = array.slice();
newArray.push(item);
return newArray;
};
purePush(4, someArray);
This returns a new array and leaves
someArray
alone.
Higher Order Functions
A higher order function takes a function as an argument
> R.inc(1);
2
> R.map(R.inc, [1, 2, 3]);
[2, 3, 4]
Currying
not the food
When not given enough arguments:
- A normal Javascript function substitutes undefined for the rest
- A curried function returns a partially applied function that accepts the rest
> var incEach = R.map(R.inc);
> incEach([1, 2, 3]);
[2, 3, 4]
Argument Order
Initial arguments to a function are like configuration
The last argument is usually the data to operate on
This makes it easy to create specialized functions with currying
var getNamed = R.filter(R.has("name"));
> getNamed([
{id: 0, name: "foo"},
{id: 1},
{id: 2, name: "bar"}
]);
[{id: 0, name: "foo"},
{id: 2, name: "bar"}]
var firstNamed = R.find(R.has("name"));
> firstNamed([
{id: 0, name: "foo"},
{id: 1},
{id: 2, name: "bar"}
]);
{id: 0, name: "foo"}
var isEven = function(x) {
return x % 2 === 0;
};
var evensOnly = R.filter(isEven);
> evensOnly([1, 2, 3, 4, 5]);
[2, 4]
var duplicate = function(x) {
return [x, x];
};
var duplicateEach = R.chain(duplicate);
> duplicateEach([1, 2, 3]);
[1, 1, 2, 2, 3, 3]
Thus Ends Section One
Let's take a quick break now
Function Composition
putting it all together
Mathematical Functions
A function of one argument f(x)
and another function of one argument g(x)
can be composed together to make the function f(g(x))
> duplicateEach([1, 2, 3]);
[1, 1, 2, 2, 3, 3]
> evensOnly([1, 2, 3, 4, 5]);
[2, 4]
var duplicateEvens = R.compose(
duplicateEach,
evensOnly
);
> duplicateEvens([1, 2, 3, 4, 5]);
[2, 2, 4, 4]
Exercises!
not the sweaty kind
Let's tackle some exercises and apply these concepts
Suppose we're working with a JSON API that provides a giant catalog of users.
Each user has an id
,
a name
, and
an age
{
users: [
{id: 3, name: "Thad", age: 36},
{id: 5, name: "Lucian", age: 23},
{id: 2, name: "Justine", age: 29},
{id: 4, name: "Katie", age: 26},
{id: 0, name: "Jerold", age: 52},
{id: 1, name: "Nona", age: 33}
]
}
You can find this catalog in the "exercises" folder of the class GitHub repository.
Sorting by ID
npm run-script ex1
It would be useful to see a list of users sorted by ID
Ramda provides a sortBy
function to make this easier.
Note: it's a higher order function!
var sortCatalogUsersById = R.compose(
R.sortBy(R.prop("id")),
R.prop("users")
);
Users With an Even Age
npm run-script ex2
Now for some filtering
Let's say we want only the users in the catalog with an even age
Tip - if you've got isEven
,
check out Ramda's propSatisfies
.
var isEven = function(x) {
return x % 2 === 0
};
var hasEvenAge = R.propSatisfies(
isEven,
"age"
);
var getUsersWithEvenAge = R.compose(
R.filter(hasEvenAge),
R.prop("users")
);
Names of the Three Youngest Users
npm run-script ex3
Instead of sorting by ID, let's sort by age
Additionally, let's get the 3 youngest users instead of all of them
As a final twist - let's only get their names, not the whole user objects
var getNamesOfThreeYoungestUsers =
R.compose(
R.map(R.prop("name")),
R.take(3),
R.sortBy(R.prop("age")),
R.prop("users")
);
Users in their Thirties
npm run-script ex4
Now a somewhat more complex query - let's get all the users with age 30-39
Ramda provides R.lt
,
and R.gt
for comparing, but their argument order makes things tricky
Maybe try
R.flip
or R.__
var isInThirties = R.both(
R.gte(R.__, 30),
R.lt(R.__, 40)
);
var userIsInThirties = R.propSatisfies(
isInThirties,
"age"
);
var getUsersInTheirThirties = R.compose(
R.filter(userIsInThirties),
R.prop("users")
);
Congratulations!
you survived!
Now you know the main techniques of functional programming in Javascript