I came across an answer to another SO question about recursion in Javascript, that gave a very terse form in ES6 using the Y-combinator, using ES6's fat arrow, and thought hey, that'd be neat to use - then 15 minutes or so later had circled back to hm, maybe not.
I've been to a few Haskell/Idris talks and run some code before, and familiar with standard JS, so was hoping I'd be able to figure this out, but can't quite see how a simple "do n recursions and return" is supposed to go, and where to implement a decrementing counter.
I just wanted to simplify getting the nth parent node of a DOM element, and there seem to be more dense explain-all guides than examples of simple applications like this.
The example I first saw is:
const Y = a => (a => a(a))(b => a(a => b(b)(a)));
while this more recent answer gives:
const U = f => f (f)
const Y = U (h => f => f (x => h(h)(f)(x)))
...which is given with examples of what the internal functions might be, and some example outputs, but introducing the U-combinator doesn't really help clarify this for me.
In the first example I can't really begin to understand what b might be in my case - I know I need one function a to return the parent node:
const par = function(node) {
return node.parentNode;
}
I came up with the below:
function RecParentNode(base_node, n) {
// ES6 Y-combinator, via: https://stackoverflow.com/a/32851197/2668831
// define immutable [const] functions `Y` and `fn` [`fn` uses `Y`]
// the arguments of `Y` are another two functions, `a` and `b`
const Y = par=>(par=>par(par))(b=>par(par=>b(b)(par)));
const fn = Y(fn => n => {
console.log(n);
if (n > 0) {
fn(n - 1);
}
});
}
but then couldn't see what to do with the spare b lying around, and was about to delete it all and just forget I bothered.
All I'd like is to apply the par function n times, since the only alternatives I'm aware of are chaining .parentNode.parentNode.parentNode... or cheating and turning a string into an eval call.
Hoping someone familiar with functional JS could help me get the idea here for how to use the Y-combinator to make this helper function RecParentNode - thanks!
due diligence
Hey, that answer you found is mine! But before looking at various definitions of the Y combinator, we first review its purpose: (emphasis mine)
In functional programming, the Y combinator can be used to formally define recursive functions in a programming language that doesn't support recursion (wikipedia)
Now, let's review your question
I just wanted to simplify getting the nth parent node of a DOM element, and there seem to be more dense explain-all guides than examples of simple applications like this.
JavaScript supports direct recursion which means functions can call themselves directly by name. No use of U or Y combinators is necessary. Now to design a recursive function, we need to identify our base and inductive case(s)
base case – n is zero; return the node
inductive case 1 – n is not zero, but node is empty; we cannot get the parent of an empty node; return undefined (or some error if you wish)
inductive case 2 - n is not zero and node is not empty; recur using the node's parent and decrement n by 1.
Below we write nthParent as a pure functional expression. To simplify the discussion to follow, we will define it function in curried form.
const Empty =
Symbol ()
const nthParent = (node = Empty) => (n = 0) =>
n === 0
? node
: node === Empty
? undefined // or some kind of error; this node does not have a parent
: nthParent (node.parentNode) (n - 1)
const Node = (value = null, parentNode = Empty) =>
({ Node, value, parentNode })
const data =
Node (5, Node (4, Node (3, Node (2, Node (1)))))
console.log
( nthParent (data) (1) .value // 4
, nthParent (data) (2) .value // 3
, nthParent (data) (3) .value // 2
, nthParent (data) (6) // undefined
)
but what if...
So suppose you were running your program with a JavaScript interpreter that did not support direct recursion... now you have a use case for the combinators
To remove the call-by-name recursion, we wrap our entire function in another lambda whose parameter f (or name of your choosing) will be the recursion mechanism itself. It is a drop-in replacement for nthParent –
changes in bold
const nthParent = Y (f => (node = Empty) => (n = 0) =>
n === 0
? node
: node === Empty
? undefined
: nthParent f (node.parentNode) (n - 1))
Now we can define Y
const Y = f =>
f (Y (f))
And we can remove direct recursion in Y with U using a similar technique as before– changes in bold
const U = f =>
f (f)
const Y = U (g => f =>
f (Y U (g) (f)))
But in order for it to work in JavaScript, which uses applicative order evaluation, we must delay evaluation using eta expansion – changes in bold
const U = f =>
f (f)
const Y = U (g => f =>
f (x => U (g) (f) (x)))
All together now
const U = f =>
f (f)
const Y = U (g => f =>
f (x => U (g) (f) (x)))
const Empty =
Symbol ()
const nthParent = Y (f => (node = Empty) => (n = 0) =>
n === 0
? node
: node === Empty
? undefined // or some kind of error; this node does not have a parent
: f (node.parentNode) (n - 1))
const Node = (value = null, parentNode = Empty) =>
({ Node, value, parentNode })
const data =
Node (5, Node (4, Node (3, Node (2, Node (1)))))
console.log
( nthParent (data) (1) .value // 4
, nthParent (data) (2) .value // 3
, nthParent (data) (3) .value // 2
, nthParent (data) (6) // undefined
)
Now I hope you see why the Y combinator exists and why you wouldn't use it in JavaScript. In another answer, I attempt to help readers gain a deeper intuition about the Y combinator by use of a mirror analogy. I invite you to read it if the topic interests you.
getting practical
It doesn't make sense to use the Y combinator when JavaScript already supports direct recursion. Below, see a more practical definition of nthParent in uncurried form
const nthParent = (node = Empty, n = 0) =>
n === 0
? node
: node === Empty
? undefined // or some kind of error; this node does not have a parent
: nthParent (node.parentNode, n - 1)
But what about those maximum recursion depth stack overflow errors? If we had a deep tree with nodes thousands of levels deep, the above function will produce such an error. In this answer I introduce several ways to work around the problem. It is possible to write stack-safe recursive functions in a language that doesn't support direct recursion and/or tail call elimination!
If imperative programming is an option:
function getParent(el, n){
while(n--) el = el.parentNode;
return el;
}
Using functional recursion you could do:
const Y = f => x => f (Y (f)) (x); // thanks to #Naomik
const getParent = Y(f => el => n => n ? f(el.parentNode)(n - 1) : el);
console.log(getParent(document.getElementById("test"))(5));
Lets build this Y-Combinator from the ground up.
As it calls a function by the Y-Combinator of itself, the Y-Combinator needs a reference to itself. For that we need a U-Combinator first:
(U => U(U))
Now we can call that U combinator with our Y combinator so that it gets a self reference:
(U => U(U))
(Y => f => f( Y(Y)(f) ))
However that has a problem: The function gets called with an Y-Combinator reference which gets called with a Y-Combinator reference wich gets called .... weve got Infinite recursion. Naomik outlined that here. The solution for that is to add another curried argument(e.g. x) that gets called when the function is used, and then another recursive combinator is created. So we only get the amount of recursion we actually need:
(U => U(U))
(Y => f => x => f( Y(Y)(f) )(x) )
(f => n => n ? f(n - 1): n)(10) // a small example
We could also restructure it like this:
(f => (U => U(U))(Y => f(x => Y(Y)(x))))
(f => n => n ? f(n - 1): n)(10) // a small example
To get your first snippet, so basically its the same thing just a bit reordered and obfuscated through shadowing.
So now another combinator gets only created when f(n-1) gets called, which only happens when n?, so weve got an exit condition now. Now we can finally add our node to the whole thing:
(U => U(U))
(Y => f => x => f( Y(Y)(f) )(x) )
(f => el => n => n ? f(el.parentNode)(n - 1): el)
(document.getElementById("test"))(10)
That would be purely functional, however that is not really useful as it is extremely complicated to use. If we store function references we dont need the U combinator as we can simply take the Y reference. Then we arrive at the snippet above.
Related
import {flow, curry} from 'lodash';
const add = (a, b) => a + b;
const square = n => n * n;
const tap = curry((interceptor, n) => {
interceptor(n);
return n;
});
const trace2 = curry((message, n) => {
return tap((n) => console.log(`${message} is ${n}`), n);
});
const trace = label => {
return tap(x => console.log(`== ${ label }: ${ x }`));
};
const addSquare = flow([add, trace('after add'), square]);
console.log(addSquare(3, 1));
I started by writing trace2 thinking that trace wouldn't work because "How can tap possibly know about n or x whatever?".
But trace does work and I do not understand how it can “inject” the x coming from the flow into the tap call. Any explanation will be greatly appreciated :)
Silver Spoon Evaluation
We'll just start with tracing the evaluation of
addSquare(3, 1) // ...
Ok, here goes
= flow([add, trace('after add'), square]) (3, 1)
add(3,1)
4
trace('after add') (4)
tap(x => console.log(`== ${ 'after add' }: ${ x }`)) (4)
curry((interceptor, n) => { interceptor(n); return n; }) (x => console.log(`== ${ 'after add' }: ${ x }`)) (4)
(x => console.log(`== ${ 'after add' }: ${ x }`)) (4); return 4;
console.log(`== ${ 'after add' }: ${ 4 }`); return 4;
~log effect~ "== after add: 4"; return 4
4
square(4)
4 * 4
16
= 16
So the basic "trick" you're having trouble seeing is that trace('after add') returns a function that's waiting for the last argument. This is because trace is a 2-parameter function that was curried.
Futility
I can't express how useless and misunderstood the flow function is
function flow(funcs) {
const length = funcs ? funcs.length : 0
let index = length
while (index--) {
if (typeof funcs[index] != 'function') {
throw new TypeError('Expected a function')
}
}
return function(...args) {
let index = 0
let result = length ? funcs[index].apply(this, args) : args[0]
while (++index < length) {
result = funcs[index].call(this, result)
}
return result
}
}
Sure, it "works" as it's described to work, but it allows you to create awful, fragile code.
loop thru all provided functions to type check them
loop thru all provided functions again to apply them
for some reason, allow for the first function (and only the first function) to have special behaviour of accepting 1 or more arguments passed in
all non-first functions will only accept 1 argument at most
in the event an empty flow is used, all but your first input argument is discarded
Pretty weird f' contract, if you ask me. You should be asking:
why are we looping thru twice ?
why does the first function get special exceptions ?
what do I gain with at the cost of this complexity ?
Classic Function Composition
Composition of two functions, f and g – allows data to seemingly teleport from state A directly to state C. Of course state B still happens behind the scenes, but the fact that we can remove this from our cognitive load is a tremendous gift.
Composition and currying play so well together because
function composition works best with unary (single-argument) functions
curried functions accept 1 argument per application
Let's rewrite your code now
const add = a => b => a + b
const square = n => n * n;
const comp = f => g => x => f(g(x))
const comp2 = comp (comp) (comp)
const addSquare = comp2 (square) (add)
console.log(addSquare(3)(1)) // 16
"Hey you tricked me! That comp2 wasn't easy to follow at all!" – and I'm sorry. But it's because the function was doomed from the start. Why tho?
Because composition works best with unary functions! We tried composing a binary function add with a unary function square.
To better illustrate classical composition and how simple it can be, let's look at a sequence using just unary functions.
const mult = x => y => x * y
const square = n => n * n;
const tap = f => x => (f(x), x)
const trace = str => tap (x => console.log(`== ${str}: ${x}`))
const flow = ([f,...fs]) => x =>
f === undefined ? x : flow (fs) (f(x))
const tripleSquare = flow([mult(3), trace('triple'), square])
console.log(tripleSquare(2))
// == "triple: 6"
// => 36
Oh, by the way, we reimplemented flow with a single line of code, too.
Tricked again
OK, so you probably noticed that the 3 and 2 arguments were passed in separate places. You'll think you've been cheated again.
const tripleSquare = flow([mult(3), trace('triple'), square])
console.log(tripleSquare(2)) //=> 36
But the fact of the matter is this: As soon as you introduce a single non-unary function into your function composition, you might as well refactor your code. Readability plummets immediately. There's absolutely no point in trying to keep the code point-free if it's going to hurt readability.
Let's say we had to keep both arguments available to your original addSquare function … what would that look like ?
const add = x => y => x + y
const square = n => n * n;
const tap = f => x => (f(x), x)
const trace = str => tap (x => console.log(`== ${str}: ${x}`))
const flow = ([f,...fs]) => x =>
f === undefined ? x : flow (fs) (f(x))
const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)
console.log(addSquare(3,1))
// == "add: 4"
// => 16
OK, so we had to define addSquare as this
const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)
It's certainly not as clever as the lodash version, but it's explicit in how the terms are combined and there is virtually zero complexity.
In fact, the 7 lines of code here implements your entire program in less than it takes to implement just the lodash flow function alone.
The fuss, and why
Everything in your program is a trade off. I hate to see beginners struggle with things that should be simple. Working with libraries that make these things so complex is extremely disheartening – and don't even get me started on Lodash's curry implementation (including it's insanely complex createWrap)
My 2 cents: if you're just starting out with this stuff, libraries are a sledgehammer. They have their reasons for every choice they made, but know that each one involved a trade off. All of that complexity is not totally unwarranted, but it's not something you need to be concerned with as a beginner. Cut your teeth on basic functions and work your way up from there.
Curry
Since I mentioned curry, here's 3 lines of code that replace pretty much any practical use of Lodash's curry.
If you later trade these for a more complex implementation of curry, make sure you know what you're getting out of the deal – otherwise you just take on more overhead with little-to-no gain.
// for binary (2-arity) functions
const curry2 = f => x => y => f(x,y)
// for ternary (3-arity) functions
const curry3 = f => x => y => z => f(x,y,z)
// for arbitrary arity
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)
Two types of function composition
One more thing I should mention: classic function composition applies functions right-to-left. Because some people find that hard to read/reason about, left-to-right function composers like flow and pipe have showed up in popular libs
Left-to-right composer, flow, is aptly named because your eyes will flow in a spaghetti shape as your try to trace the data as it moves thru your program. (LOL)
Right-to-left composer, composer, will make you feel like you're reading backwards at first, but after a little practice, it begins to feel very natural. It does not suffer from spaghetti shape data tracing.
In Professor Frisby Introduces Composable Functional JavaScript the identity functor was introduced:
const Box = x =>
({
map: f => Box(f(x)),
fold: f => f(x) // for testing
})
I spent the better part of the day understanding functors and why the above JavaScript code is actually the identity functor. So I thought I would alter it to get a "real" functor that is not the identity functor. I came up with this:
const Endo = x =>
({
map: f => Endo(f(x).split('')),
fold: f => f(x).split('') // for testing
})
My reasoning is that with Box, Id_Box: Box -> Box and Id_Box f = f. Endo would also map to itself but Endo(f): Endo(x) -> Endo(y) (if f: x -> y).
Am I on the right track?
EDIT:
Replaced string with the more generic x as it was in the original examples.
As pointed out in this answer, for our purposes as programmers we can treat all functors as endofunctors so don't get too caught up on the differences.
As for what a functor is, in brief it is
a data structure (Box in your example)
that can support a mapping operation (think Array.prototype.map)
and that mapping operation respects identity: xs === xs.map(x => x)
...and composition: xs.map(f).map(g) === xs.map(f . g) where . is function composition.
That's it. No more, no less. Looking at your Box, it's a data structure that has a map function (check 1 & 2) and that map function looks like it should respect identity and composition (check 3 & 4). So it's a functor. But it doesn't do anything, which is why it's the identity functor. The fold function isn't strictly necessary, it just provides a way to 'unwrap' the box.
For a useful functor, let's look at JavaScript arrays. Arrays actually do something: namely they contain multiple values rather than just a single one. If an array could only have one element, it'd be your Box. For our purposes we'll pretend that they can only hold values of the same type to simply things. So an array is a data structure, that has a map function, that respects identity and composition.
let plus = x => y => x + y;
let mult = x => y => x * y;
let plus2 = plus(2);
let times3 = mult(3);
let id = x => x;
let compose = (...fs) => arg => fs.reverse().reduce((x, f) => { return f(x) }, arg);
// Here we need to stringify the arrays as JS will compare on
// ref rather than value. I'm omitting it after the first for
// brevity, but know that it's necessary.
[1,2,3].map(plus2).toString() === [3,4,5].toString(); // true
[1,2,3].map(id) === [1,2,3]; // true
[1,2,3].map(plus2).map(times3) === [1,2,3].map(compose(times3, plus2)); // true
So when we map a function over a functor (array) we get back another instance of the same functor (a new Array) with the function applied to whatever the functor (array) was holding.
So now lets look at another ubiquitous JavaScript data structure, the object. There's no built in map function for objects. Can we make them a functor? Assume again that the object is homogenous (only has keys to one type of value, in this example Number):
let mapOverObj = obj => f => {
return Object.entries(obj).reduce((newObj, [key, value]) => {
newObj[key] = f(value);
return newObj;
}, {});
};
let foo = { 'bar': 2 };
let fooPrime = mapOverObj(foo)(plus2); // { 'bar': 4 }
And you can continue on to test that the function accurately (as far as is possible in JavaScript) supports identity and composition to satisfy the functor laws.
I am working in TS but will show the tsc -> ES6 code below.
I have a function 'isDigit' that returns true if the the character code is in the range of digits 0-9. This function (isDigit) must be passed as an argument into a higher order function.
const isDigit = (char, charC = char.charCodeAt(0)) => (charC > 47 && charC < 58);
As part of another higher order function, I need to know if a character is NOT a digit. Of course something like below would work...
const notDigit = (char, charC = char.charCodeAt(0)) => !isDigit(char);
BUT it would be more satisfying if I could compose isDigit with another function (I'll call notFun) to apply a not operator to the result of isDigit to make notDigit. In the code below 'boolCond' serves to control if the not operator is applied or not. The code below 'almost' works, but it returns a boolean not a function which does not work when dealing with higher order functions.
const notFun = (myFun, boolCond = Boolean(condition)) => (boolCond) ? !myFun : myFun;
As is usually the case, while preparing this question I wound up finding an answer, so I will share my answer and see what improvements come from the community.
The issue observed above (getting a boolean instead of a function) is an issue of 'functional composition, I found several optional approaches in the post of Eric Elliot, from which I selected the 'pipe' functional composition method.
see Eric Elliot's excellent post
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
The implementation of this pipe composition function looked like the below TS... For those following along at home, I have included the recursive count while 'recCountWhile' function that is the ultimate consumer of the composed (i.e. piped) function (please excuse the inverted order that these functions appear but this was done for clarity).
export const removeLeadingChars: (str: string) => string =
(str, arr = str.split(''),
dummy = pipe(isDigit, notFun), cnt = recCountWhile(arr, dummy, 0)) =>
arr.slice(cnt).reduce((acc, e) => acc.concat(e),'');
export const recCountWhile: (arr: string[], fun: (char: string) => boolean, sum: number) => number =
(arr, fun, sum, test = (!(arr[0])) || !fun(arr[0]) ) =>
(test) ? sum : recCountWhile(arr.slice(1), fun, sum + 1);
The result is a composed function 'removeLeadingChars' that composes the 'isDigit' with the 'notFun' (using the pipe function ) into 'dummy' function that is passed to the recCountWhile function. This returns the count of 'not digits' (i.e. characters other than digits) that lead the string, these characters that are then 'sliced' from the head of the array, and the array is reduced back to a string.
I would be very keen to hear about any tweaks that may improve on this approach.
Good on you to find your answer and still post the question. I think this is a nice way to learn.
For the sake of a function composition exercise, here's how I might structure your functions.
see Keep it simple below for how I would handle this with practical code
const comp = f => g => x => f(g(x))
const ord = char => char.charCodeAt(0)
const isBetween = (min,max) => x => (x >= min && x <= max)
const isDigit = comp (isBetween(48,57)) (ord)
const not = x => !x
const notDigit = comp (not) (isDigit)
console.log(isDigit('0')) // true
console.log(isDigit('1')) // true
console.log(isDigit('2')) // true
console.log(isDigit('a')) // false
console.log(notDigit('0')) // false
console.log(notDigit('a')) // true
Code review
Btw, this thing you're doing with the default parameters and leaked private API is pretty wonky
// charC is leaked API
const isDigit = (char, charC = char.charCodeAt(0)) => (charC > 47 && charC < 58);
isDigit('9') // true
isDigit('9', 1) // false wtf
isDigit('a', 50) // true wtf
I understand you're probably doing it so you don't have to write this
// I'm guessing you want to avoid this
const isDigit = char => {
let charC = char.charCodeAt(0)
return charC > 47 && charC < 58
}
... but that function is actually a lot better because it doesn't leak private API (the charC var) to the external caller
You'll notice the way I solved this in mine was to use my own isBetween combinator and currying which results in a pretty clean implementation, imo
const comp = f => g => x => f(g(x))
const ord = char => char.charCodeAt(0)
const isBetween = (min,max) => x => (x >= min && x <= max)
const isDigit = comp (isBetween(48,57)) (ord)
More of your code that does this awful default parameters thing
// is suspect you think this is somehow better because it's a one-liner
// abusing default parameters like this is bad, bad, bad
const removeLeadingChars: (str: string) => string =
(str, arr = str.split(''),
dummy = pipe(isDigit, notFun), cnt = recCountWhile(arr, dummy, 0)) =>
arr.slice(cnt).reduce((acc, e) => acc.concat(e),'');
Try to avoid compromising the quality of your code for the sake of making everything a one-liner. The above function is much worse than this one here
// huge improvement in readability and reliability
// no leaked variables!
const removeLeadingChars: (str: string) => string = (str) => {
let arr = str.split('')
let dummy = pipe(isDigit, notFun)
let count = recCountWhile(arr, dummy, 0)
return arr.slice(count).reduce((acc, e) => acc.concat(e), '')
}
Keep it simple
Instead of splitting the string into an array, then iterating over the array to count the leading non digits, then slicing the head of the array based on the count, then finally reassembling the array into an output string, you can... keep it simple
const isDigit = x => ! Number.isNaN (Number (x))
const removeLeadingNonDigits = str => {
if (str.length === 0)
return ''
else if (isDigit(str[0]))
return str
else
return removeLeadingNonDigits(str.substr(1))
}
console.log(removeLeadingNonDigits('hello123abc')) // '123abc'
console.log(removeLeadingNonDigits('123abc')) // '123abc'
console.log(removeLeadingNonDigits('abc')) // ''
So yeah, I'm not sure if the code in your question was merely there for an exercise, but there's really a much simpler way to remove leading non digits from a string, if that's really the end goal.
The removeLeadningNonDigits function provided here is pure function, does not leak private variables, handles all inputs for its given domain (String), and maintains an easy-to-read style. I would suggest this (or something like this) compared to the proposed solution in your question.
Function Composition and "Pipe"
Composing two functions is usually done in right-to-left order. Some people find that hard to read/reason about, so they came up with a left-to-right function composer and most people seem to agree that pipe is a good name.
There's nothing wrong with your pipe implementation, but I think it's nice to see how if you strive to keep things as simple as possible, the resulting code cleans up a bit.
const identity = x => x
const comp = (f,g) => x => f(g(x))
const compose = (...fs) => fs.reduce(comp, identity)
Or if you'd like to work with my curried comp presented earlier in the post
const identity = x => x
const comp = f => g => x => f(g(x))
const uncurry = f => (x,y) => f(x)(y)
const compose = (...fs) => fs.reduce(uncurry(comp), identity)
Each of these functions have their own independent utility. So if you define compose in this way, you get the other 3 functions for free.
Contrast this to the pipe implementation provided in your question:
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
Again, it's fine, but it mixes all of these things together in one function.
(v,f) => f(v) is useful function on its own, why not define it separately and then use it by name?
the => x is merging the identity function which has countless uses
Arrow functions in ES6 do not have an arguments property and therefore arguments.callee will not work and would anyway not work in strict mode even if just an anonymous function was being used.
Arrow functions cannot be named, so the named functional expression trick can not be used.
So... How does one write a recursive arrow function? That is an arrow function that recursively calls itself based on certain conditions and so on of-course?
Writing a recursive function without naming it is a problem that is as old as computer science itself (even older, actually, since λ-calculus predates computer science), since in λ-calculus all functions are anonymous, and yet you still need recursion.
The solution is to use a fixpoint combinator, usually the Y combinator. This looks something like this:
(y =>
y(
givenFact =>
n =>
n < 2 ? 1 : n * givenFact(n-1)
)(5)
)(le =>
(f =>
f(f)
)(f =>
le(x => (f(f))(x))
)
);
This will compute the factorial of 5 recursively.
Note: the code is heavily based on this: The Y Combinator explained with JavaScript. All credit should go to the original author. I mostly just "harmonized" (is that what you call refactoring old code with new features from ES/Harmony?) it.
It looks like you can assign arrow functions to a variable and use it to call the function recursively.
var complex = (a, b) => {
if (a > b) {
return a;
} else {
complex(a, b);
}
};
Claus Reinke has given an answer to your question in a discussion on the esdiscuss.org website.
In ES6 you have to define what he calls a recursion combinator.
let rec = (f)=> (..args)=> f( (..args)=>rec(f)(..args), ..args )
If you want to call a recursive arrow function, you have to call the recursion combinator with the arrow function as parameter, the first parameter of the arrow function is a recursive function and the rest are the parameters. The name of the recursive function has no importance as it would not be used outside the recursive combinator. You can then call the anonymous arrow function. Here we compute the factorial of 6.
rec( (f,n) => (n>1 ? n*f(n-1) : n) )(6)
If you want to test it in Firefox you need to use the ES5 translation of the recursion combinator:
function rec(f){
return function(){
return f.apply(this,[
function(){
return rec(f).apply(this,arguments);
}
].concat(Array.prototype.slice.call(arguments))
);
}
}
TL;DR:
const rec = f => f((...xs) => rec(f)(...xs));
There are many answers here with variations on a proper Y -- but that's a bit redundant... The thing is that the usual way Y is explained is "what if there is no recursion", so Y itself cannot refer to itself. But since the goal here is a practical combinator, there's no reason to do that. There's this answer that defines rec using itself, but it's complicated and kind of ugly since it adds an argument instead of currying.
The simple recursively-defined Y is
const rec = f => f(rec(f));
but since JS isn't lazy, the above adds the necessary wrapping.
Use a variable to which you assign the function, e.g.
const fac = (n) => n>0 ? n*fac(n-1) : 1;
If you really need it anonymous, use the Y combinator, like this:
const Y = (f) => ((x)=>f((v)=>x(x)(v)))((x)=>f((v)=>x(x)(v)))
… Y((fac)=>(n)=> n>0 ? n*fac(n-1) : 1) …
(ugly, isn't it?)
A general purpose combinator for recursive function definitions of any number of arguments (without using the variable inside itself) would be:
const rec = (le => ((f => f(f))(f => (le((...x) => f(f)(...x))))));
This could be used for example to define factorial:
const factorial = rec( fact => (n => n < 2 ? 1 : n * fact(n - 1)) );
//factorial(5): 120
or string reverse:
const reverse = rec(
rev => (
(w, start) => typeof(start) === "string"
? (!w ? start : rev(w.substring(1), w[0] + start))
: rev(w, '')
)
);
//reverse("olleh"): "hello"
or in-order tree traversal:
const inorder = rec(go => ((node, visit) => !!(node && [go(node.left, visit), visit(node), go(node.right, visit)])));
//inorder({left:{value:3},value:4,right:{value:5}}, function(n) {console.log(n.value)})
// calls console.log(3)
// calls console.log(4)
// calls console.log(5)
// returns true
I found the provided solutions really complicated, and honestly couldn't understand any of them, so i thought out a simpler solution myself (I'm sure it's already known, but here goes my thinking process):
So you're making a factorial function
x => x < 2 ? x : x * (???)
the (???) is where the function is supposed to call itself, but since you can't name it, the obvious solution is to pass it as an argument to itself
f => x => x < 2 ? x : x * f(x-1)
This won't work though. because when we call f(x-1) we're calling this function itself, and we just defined it's arguments as 1) f: the function itself, again and 2) x the value. Well we do have the function itself, f remember? so just pass it first:
f => x => x < 2 ? x : x * f(f)(x-1)
^ the new bit
And that's it. We just made a function that takes itself as the first argument, producing the Factorial function! Just literally pass it to itself:
(f => x => x < 2 ? x : x * f(f)(x-1))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120
Instead of writing it twice, you can make another function that passes it's argument to itself:
y => y(y)
and pass your factorial making function to it:
(y => y(y))(f => x => x < 2 ? x : x * f(f)(x-1))(5)
>120
Boom. Here's a little formula:
(y => y(y))(f => x => endCondition(x) ? default(x) : operation(x)(f(f)(nextStep(x))))
For a basic function that adds numbers from 0 to x, endCondition is when you need to stop recurring, so x => x == 0. default is the last value you give once endCondition is met, so x => x. operation is simply the operation you're doing on every recursion, like multiplying in Factorial or adding in Fibonacci: x1 => x2 => x1 + x2. and lastly nextStep is the next value to pass to the function, which is usually the current value minus one: x => x - 1. Apply:
(y => y(y))(f => x => x == 0 ? x : x + f(f)(x - 1))(5)
>15
var rec = () => {rec()};
rec();
Would that be an option?
Since arguments.callee is a bad option due to deprecation/doesnt work in strict mode, and doing something like var func = () => {} is also bad, this a hack like described in this answer is probably your only option:
javascript: recursive anonymous function?
This is a version of this answer, https://stackoverflow.com/a/3903334/689223, with arrow functions.
You can use the U or the Y combinator. Y combinator being the simplest to use.
U combinator, with this you have to keep passing the function:
const U = f => f(f)
U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y combinator, with this you don't have to keep passing the function:
const Y = gen => U(f => gen((...args) => f(f)(...args)))
Y(selfFn => arg => selfFn('to infinity and beyond'))
You can assign your function to a variable inside an iife
var countdown = f=>(f=a=>{
console.log(a)
if(a>0) f(--a)
})()
countdown(3)
//3
//2
//1
//0
i think the simplest solution is looking at the only thing that you don't have, which is a reference to the function itself. because if you have that then recusion is trivial.
amazingly that is possible through a higher order function.
let generateTheNeededValue = (f, ...args) => f(f, ...args);
this function as the name sugests, it will generate the reference that we'll need. now we only need to apply this to our function
(generateTheNeededValue)(ourFunction, ourFunctionArgs)
but the problem with using this thing is that our function definition needs to expect a very special first argument
let ourFunction = (me, ...ourArgs) => {...}
i like to call this special value as 'me'. and now everytime we need recursion we do like this
me(me, ...argsOnRecursion);
with all of that. we can now create a simple factorial function.
((f, ...args) => f(f, ...args))((me, x) => {
if(x < 2) {
return 1;
} else {
return x * me(me, x - 1);
}
}, 4)
-> 24
i also like to look at the one liner of this
((f, ...args) => f(f, ...args))((me, x) => (x < 2) ? 1 : (x * me(me, x - 1)), 4)
Here is the example of recursive function js es6.
let filterGroups = [
{name: 'Filter Group 1'}
];
const generateGroupName = (nextNumber) => {
let gN = `Filter Group ${nextNumber}`;
let exists = filterGroups.find((g) => g.name === gN);
return exists === undefined ? gN : generateGroupName(++nextNumber); // Important
};
let fg = generateGroupName(filterGroups.length);
filterGroups.push({name: fg});
Is it possible to define my own infix function/operator in CoffeeScript (or in pure JavaScript)? e.g. I want to call
a foo b
or
a `foo` b
instead of
a.foo b
or, when foo is global function,
foo a, b
Is there any way to do this?
ES6 enables a very Haskell/Lambda calculus way of doing things.
Given a multiplication function:
const multiply = a => b => (a * b)
You can define a doubling function using partial application (you leave out one parameter):
const double = multiply (2)
And you can compose the double function with itself, creating a quadruple function:
const compose = (f, g) => x => f(g(x))
const quadruple = compose (double, double)
But indeed, what if you would prefer an infix notation? As Steve Ladavich noted, you do need to extend a prototype.
But I think it can be done a bit more elegant using array notation instead of dot notation.
Lets use the official symbol for function composition "∘":
Function.prototype['∘'] = function(f){
return x => this(f(x))
}
const multiply = a => b => (a * b)
const double = multiply (2)
const doublethreetimes = (double) ['∘'] (double) ['∘'] (double)
console.log(doublethreetimes(3));
Actually adding this as an answer: no, this is not possible.
It's not possible in vanilla JS.
It's not possible in CoffeeScript.
You can with sweet.js. See:
http://sweetjs.org/doc/main/sweet.html#infix-macros
http://sweetjs.org/doc/main/sweet.html#custom-operators
Sweet.js extends Javascript with macros.
It acts like a preprocessor.
This is definitely not infix notation but it's kinda close : /
let plus = function(a,b){return a+b};
let a = 3;
let b = 5;
let c = a._(plus).b // 8
I don't think anyone would actually want to use this "notation" since it's pretty ugly, but I think there are probably some tweaks that can be made to make it look different or nicer (possibly using this answer here to "call a function" without parentheses).
Infix function
// Add to prototype so that it's always there for you
Object.prototype._ = function(binaryOperator){
// The first operand is captured in the this keyword
let operand1 = this;
// Use a proxy to capture the second operand with "get"
// Note that the first operand and the applied function
// are stored in the get function's closure, since operand2
// is just a string, for eval(operand2) to be in scope,
// the value for operand2 must be defined globally
return new Proxy({},{
get: function(obj, operand2){
return binaryOperator(operand1, eval(operand2))
}
})
}
Also note that the second operand is passed as a string and evaluated with eval to get its value. Because of this, I think the code will break anytime the value of operand (aka "b") is not defined globally.
Javascript doesn't include an infix notation for functions or sections for partial application. But it ships with higher order functions, which allow us to do almost everything:
// applicator for infix notation
const $ = (x, f, y) => f(x) (y);
// for left section
const $_ = (x, f) => f(x);
// for right section
const _$ = (f, y) => x => f(x) (y);
// non-commutative operator function
const sub = x => y => x - y;
// application
console.log(
$(2, sub, 3), // -1
$_(2, sub) (3), // -1
_$(sub, 3) (2) // -1
);
As you can see I prefer visual names $, $_ and _$ to textual ones in this case. This is the best you can get - at least with pure Javascript/ES2015.
You can get close by function currying:
const $ = (a) => (f) => f(a);
const plus = (a) => (b) => (a+b);
const twoPlusThree = $ (2) (plus) (3);
But I still haven't figured out a neat way to compose this construction.