Is there a Javascript depipe method? like bind or call but for unpiping a piped prototype

Viewed 62

Is there an official way to pass the head of a pipe function as a normal function argument? Like how you can modify functions with .call and .bind, I want to modify String.prototype.includes so that it looks like includes('a')('acd'). Basically, I want a function that makes it's first argument, say 'abc', the String instance in String.prototype.includes('a')

He'res my best attempt... that doesn't work, because, why would it ['abc'].some(String.prototype.includes('b'))

Basically I want a cool confusing version of this x => x.includes('b')

Is there a version of call, bind or depipe method in Javascript that does what I need?

Related: What does "Function.call.bind(Function.bind)" mean?

2 Answers

There is no built-in way to do this. Ignoring libraries like ramda/lodash.fp that let you do this through helper methods - you would need to implement your own helper. As the arguments are "reversed" you cannot simply eta-reduce (avoid the extra lambda):

// all these functions exist in Ramda
function makeThisAnArgument(fn) { // uncurry this
  return (arg, ...args) => fn.call(arg, ...args)
}
function reverseArguments(fn) {
  return (...args) => fn.call(null, ...args.reverse());
}
// this would let you easily do:
const reversed = reverseArguments(makeThisAnArgument(String.prototype.includes))
const includesA = reversed.bind(null, 'a');
includesA('abc'); // true
includesA('bc'); // false

In general, consider seeing the great talk about Ramda Hey Underscore, You're doing it Wrong! that explains the call order, convention and method signatures needed to enable a composable API like the one you're asking for which is the one Ramda exposes.

So as far as i understand you need a curry utility function that will convert a multiple argument function into a partially applicable one. Like;

var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),

Now the String.prototype.includes() normally takes the this argument in the includes function direcly from the calling object. However we can still enforce JS to manifest includes as a dual argument function like (x,t) => String.prototype.includes.call(t,x) where t is the this and x is the existing argument.

Now with these tools at hand we can go ahead and implement your functionality.

var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),
    incl  = curry((x,t) => String.prototype.includes.call(t,x));
console.log(incl("llo")("Hello World..!"));
console.log(incl("g")("abcdefg"));
console.log(incl("i")("abcdefg"));

Related