13

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"]
nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • The [.bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) function returns a new function that passes in arguments to the original. – Pointy Jan 30 '17 at 23:08
  • Many ways to handle this. You can have your function include `"use strict"` and then have it compare `item !== this`, and do `samples.filter(exclude, "foo")`. –  Jan 30 '17 at 23:14
  • You can have your function receive a single argument return a function that also receives a single argument, which then compares them... `return function(item) { return str !== item }` –  Jan 30 '17 at 23:16

6 Answers6

37

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 functorround-robinmake anything you wanthigher-order generators and DIY iteratorsid generatorgeneric function repetitionmerge/flatten arraycustom 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.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • I recognize some of these topics are covered in other answers. My answer aims to be comprehensive on the matter without suffocating the learner by introducing periphery topics (eg function composition) or gratuitous features (eg "auto" currying, à la Ramda et al) – Mulan Jan 31 '17 at 16:16
  • 2
    @NicholasKyriakides there really is an elegance to them. Feel free to ping me if you ever need more help ^_^ – Mulan Feb 01 '17 at 05:53
  • This really is one of the most useful and best written answers I have ever seen in SO. – Martin Bramwell Apr 13 '18 at 12:28
  • @MartinBramwell thank you for your kind words. They fuel my passion to continue learning, helping and contributing to the community. – Mulan Apr 13 '18 at 17:01
6

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

Pabs123
  • 3,385
  • 13
  • 29
  • 2
    It passes a pre-specified argument to the argument list of the new function, which is exactly what he's asking to do – Pabs123 Jan 30 '17 at 23:21
  • that's not what I ask - I want to pass the argument when I'm passing the HO function to the `filter` – nicholaswmin Jan 30 '17 at 23:25
  • 2
    I mean you can do it in place too, I just put it as its own line for readability. Just do: `const foos = samples.filter(exclude.bind(null,"foos"));` where the paramter can be anything you like – Pabs123 Jan 30 '17 at 23:27
  • 1
    @Pabs123 he doesn't want to pre bind "foo" he wants to pass in "foo" – Keith Nicholas Jan 30 '17 at 23:27
  • 1
    how is this use of bind not achieving that? albeit in a different way than using an arrow function but it's still a valid solution – Pabs123 Jan 30 '17 at 23:30
  • @KeithNicholas: He's creating a partially applied function, not terribly different from what your solution is doing. –  Jan 30 '17 at 23:32
  • because now you have to do binds for all the different values you want, rather than encapsulating the bind into a function that works for any filter – Keith Nicholas Jan 30 '17 at 23:33
4

With ES6:

const foos = samples.filter(x => exclude(x, "foos"));

another option would be to use bind(), but I find it difficult to read:

const foos = samples.filter(exclude.bind(null, "foos"))
fafl
  • 7,222
  • 3
  • 27
  • 50
3

you want to curry your function like so :-

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

const exclude = function(s) {
  return item => item !== s;
}

const foos = samples.filter(exclude("foo"));
console.log(foos)

excludeFoos returns a function for filtering. Many functional languages auto curry functions for you so you can do partial application

Note, it is easier to embrace something like Ramda for js which is built around these concepts and allows you to pipe collections / filters etc

Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
2

Here's one for you:

There are a couple of answers that talk about curry, and partial-application.

And that's a great direction.

But once you really get higher-order functions, you can make this stuff really clean and easy to work with.

const curry = (f, ...initialArgs) => (...extraArgs) => {
  const args = [...initialArgs, ...extraArgs];
  return args.length >= f.length ? f(...args) : curry(f, ...args);
};

So what does that do?
It lets you pass in a function, and gives you a function. Until you have passed in enough arguments to run the function, it's going to keep passing you another function that expects more arguments.

What good is that?

const multiply = curry((x, y) => x * y);
const double = multiply(2);
const triple = multiply(3);

double(2); // 4
triple(9); // 27

Now it's really easy to define something like your test.

const notEqual = curry((test, x) => test !== x);

// you could do it like this, to reuse `notFoo`
const notFoo = notEqual("foo");
samples.filter(notFoo);

// you could do it like this, if you don't need `notFoo`
samples.filter(notEqual("foo"));

But wait! There's more!

const filter = curry((predicate, array) => array.filter(predicate));

const removeFoos = filter(notEqual("foo"));
removeFoos(samples);
removeFoos(items);
removeFoos(otherStuff);

Now I have a function that filters out foos and I can just pass it arrays whenever I feel like it.

Last one for now:

const compose = (...fs) => x => fs.reduceRight((x, f) => f(x), x);

Instead of writing

h(g(f(x)));

Compose lets me write

const hgf = compose(h, g, f);
hgf(x);
hgf(y);
hgf(z);

// it's read from right to left
const tto = compose(three, two, one);

// or from bottom to top
const tsf = compose(
  third,
  second,
  first
);

// because it runs like
y = third(second(first(x)));

So now, let's try something wild...

// lib functions (Ramda would work fine)
const map = curry((transform, array) => array.map(transform));
const reduce = curry((summarize, seed, array) => 
  array.reduce(summarize, seed));
const flatMap = curry((transform, array) =>
  array.map(transform).reduce((a, b) => a.concat(b), []));

// business functions
const castToEmployee = personData => new Employee(personData);
const isWorking = ({ active }) => active;
const removeSuperiors = curry((user, employee) =>
  employee.role <= user.role);

const customEmployeeCriteria = (criteria, employee) => { /*...*/ };
const removeDuplicates = (arr, employee) =>
  arr.some(person => person.id === employee.id)
    ? arr
    : arr.concat(employee);

Library Code

const performCustomSearch = searchCriteria => 
  filter(cutomEmployeeCriteria(searchCriteria));

const getAuthorizedEmployeeList = currentUser =>
  filter(removeSuperiors(currentUser));

const buildEmployees = compose(
  filter(isWorking),
  map(castToEmployee),
);

const cleanResults = compose(
  filter(removeBrokenItem),
  map(removePrivateMembers),
  reduce(removeDuplicates, []),
);

const handleEmployeeRequest = (currentUser, searchCriteria) => compose(
  cleanResults,
  performCustomSearch(searchCriteria),
  getAuthorizedEmployeeList(currentUser),
  buildEmployees
);

API Code

//(maybe /employees/?search={...}&token=123)
router.get("/employees", (req, res) => {
  PersonService.getAll()
    .then(handleEmployeeRequest(req.user, req.query.search))
    .then(filteredEmployees => res.json(filteredEmployees));
});

And we're done.
Easy as pie.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • Nice! Functions give you a `.length`... might as well use it. –  Jan 31 '17 at 14:03
  • My point is I'm not sure if an abstraction like this defeats the purpose of arrow functions and functions accepting predicates. This is just my opinion, but sometimes we want too much abstraction to feel that everything is more in control.... For example, what's wrong with `filter(cust => cust.name == "matias")`. In fact, what's wrong with storing such filtering predicates in variables or properties and use them later, without currying? At the end of the day, it seems like you're expressing the same thing with functions instead of expressions... – Matías Fidemraizer Jan 31 '17 at 15:28
  • I see the advantage of being able to define a library of reusable filters, but I don't have a clear conclusion about preferring your approach over repeating myself with some filtering conditions. Maybe you get more refactoring power: you define certain conditions in a single place and refactoring is even simpler because a property name change isn't spread across 1000+ files. – Matías Fidemraizer Jan 31 '17 at 15:32
  • Probably a powerful refactoring tool could also do the job without reusable filters. I'm not a big fan of TypeScript, but strong typing can assist on this matter... – Matías Fidemraizer Jan 31 '17 at 15:34
  • This is a terrible curry implementation. –  Jan 31 '17 at 16:30
  • @ftor feel free to tell me why, and how you would have curry support variadic functions in JS. – Norguard Jan 31 '17 at 18:48
  • @MatíasFidemraizer it's because I can break each function into a tiny unit of functionality and compose them all. Without relying on chains, I can unit-test the exact same functions which expect arrays, rather than having to recreate the algorithm or figure out how to have the data there before the chained algorithm is run. With or without curry, higher-order functions are FP dependency inversion. That's not to say that you can't have method chains, obviously, but by separating them out, I have pure unit-testable code, and the aggregate itself is a pure, composable function. – Norguard Jan 31 '17 at 18:55
  • @Norguard Variadic functions and currying are just incompatible. You need to pass the desired arity explicitly. –  Jan 31 '17 at 19:05
  • @ftor Does that mean that all Haskell functions require explicit arity? Because all Haskell functions are curried. – Norguard Jan 31 '17 at 19:09
0

Here's another version with a primitive curry function:

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

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

function curry(func){
  return function(var1){
    return function(var2){
      return func(var1,var2); 
    };
  };
}

console.log(curry(exclude)('foo')('bar'));  // true
console.log(samples.filter(curry(exclude)('foo')));  // ["bar"]
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61