Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing additional parameters in higher-order functions

Consider this example:

const samples = ["foo", "bar"];

const excludeFoos = function(item) {
  return item !== "foo";
}

const foos = samples.filter(excludeFoos);

How can I pass an additional parameter in excludeFoos?

For example:

const samples = ["foo", "bar"];

const exclude = function(item, str) {
  return item !== str;
}

// obviously won't work but you get the point
const foos = samples.filter(exclude("foo"));
console.log(foos); // ["bar"]
like image 785
nicholaswmin Avatar asked Jan 30 '17 23:01

nicholaswmin


2 Answers

You can use bind() to create a new function with the bound params;

//you can replace the param with anything you like, null is for the context
var excludeFoos = exclude.bind(null,"foos")
const foos = samples.filter(excludeFoos);

Live example here

like image 43
Pabs123 Avatar answered Sep 17 '22 12:09

Pabs123


Naming things

"If you have the name of a spirit, you have power over it." – Gerald Jay Sussman

Can you think of a better name for your exclude function? I know I can. It's known as notEqual. Simply knowing it as its true name makes it much more versatile when it comes to problem solving. "exclude" makes sense in the context of filtering an array, but somehow it makes less sense if we wanted to use the exclude function elsewhere.

if (exclude(a,b))
  console.log("a and b are not equal")

Functional programming is all about making functions as reusable as possible, so as we move forward, let's stick with

const notEqual = (x,y) => x !== y

Function.prototype.bind

Function.prototype.bind is used to bind values to function parameters. It's commonly used because it's been native since ECMAScript 5 – meaning you can accomplish your goal without adding any additional dependencies or making any changes to your existing code.

const notEqual = (x,y) => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(notEqual.bind(null, 'foo'))

console.log(foos) // ["bar"]

Partial Application

Partial application takes a function and some arguments and produces another function of smaller arity – arity is a fancy word for "the number of arguments a function takes"

Now that you're familiar with Function.prototype.bind, you already know partial application. The only difference is bind forces you to provide the context of a binding. Contexts are a bother in most functional programs, so sometimes it's easier to have a function that lets us partially apply without concerning ourselves with context.

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)

const notEqual = (x,y) => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(partial(notEqual, 'foo'))

console.log(foos) // ["bar"]

Currying

Currying, while similar to partial application, is another way to approach your problem. Currying takes a function of multiple arguments and transforms it into a sequence of unary functions – functions that take one argument each.

const notEqual = (x,y) => x !== y

const curry = f => x => y => f(x,y)

const samples = ['foo', 'bar']

const foos = samples.filter(curry(notEqual)('foo'))

console.log(foos) // ["bar"]

If you're having trouble seeing how this is different than partial application, note you won't see much of a difference until function arity is greater than two – See also: contrast currying with partial application.

As you can see, readability is starting to suffer a little bit. Instead of currying on the fly, if notEqual is under our control, we could define it in curried form from the start

const notEqual = x => y => x !== y

const samples = ['foo', 'bar']

const foos = samples.filter(notEqual('foo'))

console.log(foos) // ["bar"]

You may not have even noticed it, but partial (above) is defined in curried style!

Related: "What do multiple arrow functions mean in JavaScript?"

Currying is a massively powerful concept and useful in a wide variety of ways. You might say it's overkill for solving this single, isolated problem, and you'd be right. You'll really only start to see the benefits of currying when it is widely used in a program or language as it has a systemic effect – and ultimately, it provides abstraction over function arity itself.

const apply = f => x => f (x)

const notEqual = x => y => x !== y

const filter = f => xs => xs.filter(apply(f))

const notFoo = filter(notEqual('foo'))

const samples = ['foo', 'bar']

console.log(notFoo(samples)); // ["bar"]

Final Remarks

There's a lot of options available to you and you might be wondering which is the "correct" one to choose. If you're looking for a silver bullet, you'll be sad to learn there isn't one. As with everything there are trade-offs.

I find partial/procedural application to be an indispensable tool, and therefore I try to write all of my JavaScript functions in fully curried form. That way I avoid dropping calls to partial and curry all over my program. The consequence of this is the code ends up looking a little foreign, at first – comparison functor • round-robin • make anything you want • higher-order generators and DIY iterators • id generator • generic function repetition • merge/flatten array • custom iteration

Not all parts of your programs are fully under your control tho, right? Of course you're probably using some external dependencies and it's unlikely that they're going to have the perfect functional interface you're looking for. In such a case, you'll end up using partial and curry to interface with other code that you cannot change.

Lastly, look at some of the functional libraries out there like folktalke or Ramda. I don't recommend either for beginner functional programmers, but something worth looking into after you cut your teeth.

like image 139
Mulan Avatar answered Sep 17 '22 12:09

Mulan