Functional Programming — Curry

E.Y.
5 min readAug 28, 2020
Photo by Who’s Denilo ? on Unsplash

Not long ago, I came across functional programming by chance and really fall for it. Even I’m not intended to go too deep into (so it won’t distract me too much from python learning), I decided to give it a go and look at some basic concepts.

So we often heard of Functional Programming as some kind of doctrinal thinking like one input, one output. But what exactly is it?

In a nutshell, Functional Programming has three pillars:

  • Total: have a corresponding output for every input
  • Deterministic: the same output given the same input
  • No Observable Side-Effects: not even a log!

For example:

// not a function programming  const signUp = (attrs) => {
let user = saveUser(attrs)
welcomeUser(user)
}
//not a function
const headerText = header_selector =>
querySelector(header_selector).text()
// function
const signUp = (attrs) => {
return () => {
let user = saveUser(attrs)
welcomeUser(user)
}

An there a few attributes of it:

// associative
add(add(x, y), z) == add(x, add(y, z))
// commutative
add(x, y) == add(y, x)
// identity
add(x, 0) == x
// distributive
add(multiply(x, y), multiply(x, z)) == multiply(x, add(y,z))

Don’t worry if you don’t get some of the points yet, through the following series of blogs, you will understand as we go.

Curry

The first and foremost concept we will look at is curry.

Currying is a process in functional programming in which we can transform a function with multiple arguments into a sequence of nesting functions. It will always return a new function that takes a single argument one at a time until it running out the arguments. All the arguments are accessible via closure and executed when the final function is returned.

Let’t see an example.

function multiply(a, b, c) {
return a * b * c;
}
multiply(1,2,3); // 6

The above function can be written as:

function multiply(a) {
return (b) => {
return (c) => {
return a * b * c
}
}
}
(multiply(1)(2)(3)) // 6

In JavaScript, as some functional programming concepts are not built in, there is this really amazing library “ramda” to help us implement most of the concepts easily.

const { curry } = require(“ramda”);const curry = (f) => (x) => (y) => f(x, y);
const modulo = curry((x, y) => y % x);
const isOdd = modulo(2); // noted it's (2, y) => 2 % y as now x = 2const result = isOdd(3); //false

Another example:

const filter = curry((f, xs) => xs.filter(f));// the sequence matters: can’t have
// const filter = curry((xs, f) => f.filter(xs));
//1st arg: to be remembered; 2nd arg: input
const getOdds = filter(isOdd);
const toFiltered = getOdds([1, 2, 3, 4, 5]);
//[1, 2, 3, 4, 5] each modulo 2

Let’s make a function that takes any function and returns a curried version of the function:

function curry(fn, ...args) {
return (..._arg) => {
return fn(...args, ..._arg);
}
}

So we receive a function and a list of arguments in rest parameter. Next, we return a function that also collects the rest of the parameters as …_args. This function invokes the original function fn passing in ...args and ..._args through the use of the spread operator as parameters.

There’s a related concept call Partial Application.

What is a partial application?

A partial application as its name suggested, is a function where some of its arguments are used(fixed).

Partial applications can take as many or as few arguments while Curried functions only takes one argument at a time.

Partial application transforms a function into another function with smaller arity (the number of arguments) comparing to its original arguments number. In the above multiply example we can rewrite it to a partial application:

function multiply(a) {
return (b, c) => {
return a * b * c
}
}

Apart from partial application, another key concept is point-free.

Point-free style is a style of programming where function definitions do not mention the function’s arguments.

How can this be possible? Well, you can call a function that returns a function.

To make a function that will add 1 to any number:

// the original function:
const add = a => b => a + b;
const addOne = add(1);
addOne(3) //4

We can also have add 2, add 3 as we want. We can see that the returned function is just a more specialized of the more general add() function.

const addThree = add(3);
const addFifty = add(50);
addThree(3); // => 6
addFifty(4); // => 54

Does this form remind you of anything?

Yes all curried functions are higher-order function which allows you to fine tune the original function.

const partiallyAppliedOnClick = handler => $ ('button').on ('click', handler);
const curryOn = handler => event => $ ('button').on (event, handler);
const curryOnClick = handler => curryOn (handler) ('click');

With curry functions we can have a containers of reusable code that we can fixed some certain arguments if we know for certain, while having a list of undecided arguments that we can generate on the fly. So we are able to only need the undecided arguments while keep it DRY.

Finally some examples using ramda.

import "ramda" as R from "ramda"const _ = R;
const split = _.curry((delimiter, string) => string.split(delimiter))
//use map to make a new words fn that not only works on 1 string, but on an array of strings.// const sentences = xs => _.map(words, xs);
const sentences = _.map(words);
==============
// const filterQs = function(xs) {
// return _.filter(function(x){ return _.test(/q/ig, x); }, xs);
// }
// const filterQs = _.filter(function(x){ return _.test(/q/ig, x)});
const filterQs = _.filter( _.test(/q/ig));
==============
// Use the helper function _keepHighest to refactor max
const _keepHighest = (x,y) => x >= y ? x : y // <- leave be// TODO: rewrite max in its "simplest" form
// const max = function(xs) {
// return _.reduce(function(acc, x){
// return _keepHighest(acc, x);
// }, 0, xs);
// }
// const max = _.reduce(function(acc, x){
// return _keepHighest(acc, x);
// }, 0);
const max = _.reduce( _keepHighest, 0);============
// wrap array's built in slice to be functional and curried like ramda fn's.
// [1,2,3].slice(0, 2)
const slice = _.curry((start,end,xs)=>xs.slice(start,end))

--

--