cf.completefrontend

Write the codeMedium

Write partial(fn, ...presetArgs), with partial.placeholder to leave gaps you fill in later.

Before you start

What's partial application?

Partial application means fixing some of a function's arguments now and getting back a new function that takes the rest later. Calling the new function calls the original with the fixed arguments first, then the new ones. It saves repeating arguments that are always the same, and gives the more specific function a name of its own. Think of a form with the fields that never change already filled in: each time, you fill in only the rest.

const greet = (greeting, name) => `${greeting}, ${name}`;
 
// By hand: a new function with greeting fixed as "Hi"
const sayHi = (name) => greet("Hi", name);
sayHi("Ada");   // "Hi, Ada"
sayHi("Grace"); // "Hi, Grace"
 
// Built in: bind fixes this (null here), then any arguments from the left
const sayHello = greet.bind(null, "Hello");
sayHello("Ada"); // "Hello, Ada"

bind can only fix arguments from the left, and it fixes this as well. Lodash's _.partial leaves this alone: the new function passes on whatever this it's called with. It also takes a placeholder, _, that leaves a gap to fill later. The later call's arguments fill the gaps in order, any left over go after the fixed ones, and a gap with nothing left to fill it gets undefined.

Fixed nowCalled later withSo greet is called with
_.partial(greet, "Hi")"Ada"greet("Hi", "Ada")
_.partial(greet, _, "Ada")"Hello"greet("Hello", "Ada")
_.partial(greet, _)"Hey", "Ada"greet("Hey", "Ada"): "Ada" was left over
_.partial(greet, _, "Ada")Nothinggreet(undefined, "Ada")

Partial application isn't currying. A curried function takes its arguments over a chain of calls, f(a)(b)(c), and keeps returning functions until all of them have arrived. A partially applied function is an ordinary function again: one call with the remaining arguments runs it.

You meet it whenever a callback needs some arguments decided in advance: a click handler made for one item's ID, or prices.map((price) => format("GBP", price)), which fixes the currency by hand.

partial(fn, ...presetArgs) returns a new function. Calling it calls fn with the preset arguments followed by the arguments of this call, and returns fn's result.

A preset can be partial.placeholder, which leaves a gap. Each gap is filled by the next argument of the call, in order, and the arguments left over go after the presets. A gap with no argument left to fill it becomes undefined.

The new function passes its own this on to fn.

Write this
function partial(fn, ...presetArgs) {
  // your code
}
partial.placeholder = Symbol("placeholder");
 
// const greet = (greeting, name, mark) => `${greeting}, ${name}${mark}`;
// const sayHi = partial(greet, "Hi");
// sayHi("Ada", "!"); // "Hi, Ada!"
//
// const _ = partial.placeholder;
// const askAda = partial(greet, _, "Ada", "?");
// askAda("Hello"); // "Hello, Ada?": "Hello" fills the gap
// askAda();        // "undefined, Ada?": nothing fills the gap
//
// const toAda = partial(greet, _, "Ada");
// toAda("Hello", "!"); // "Hello, Ada!": "Hello" fills the gap, "!" goes after "Ada"

Solve it in the editor, with tests

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…