Here's a 'compose' function which I need to improve:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];
Here's a practical implementation of one:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];
const fn = compose([
(x) => x - 8,
(x) => x ** 2,
(x, y) => (y > 0 ? x + 3 : x - 3),
]);
console.log(fn("3", 1)); // 1081
console.log(fn("3", -1)); // -8
And here's an improvement my mentor came to.
const compose = (fns) => (arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
If we pass arguments list like that func(x, [y]) with first iteration, I still don't understand how do we make function work with unpacked array of [y]?
Let's analyse what the improved compose does
compose = (fns) =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
When you feed compose with a number of functions, you get back... a function. In your case you give it a name, fn.
What does this fn function look like? By simple substitution you can think of it as this:
(arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
where fns === [(x) => x - 8, (x) => x ** 2, (x, y) => (y > 0 ? x + 3 : x - 3)].
So you can feed this function fn with some arguments, that will be "pattern-matched" against (arg, ...restArgs); in your example, when you call fn("3", 1), arg is "3" and restArgs is [1] (so ...restArgs expands to just 1 after the comma, so you see that fn("3", 1) reduces to
fns.reduceRight((acc, func) => func(acc, 1), "3");
From this you see that
the rightmost function, (x, y) => (y > 0 ? x + 3 : x - 3) is called with the two arguments "3" (the initial value of acc) and 1,
the result will be passed as the first argument to the middle function with the following call to func,
and so on,
but the point is that the second argument to func, namely 1, is only used by the rightmost function, whereas it is passed to but ignored by the other two functions!
Conclusion
Function composition is a thing between unary functions.¹ Using it with functions with higher-than-1 arity leads to confusion.²
For instance consider these two functions
square = (x) => x**2; // unary
plus = (x,y) => x + y; // binary
can you compose them? Well, you can compose them into a function like this
sum_and_square = (x,y) => square(plus(x,y));
the compose function that you've got at the bottom of your question would go well:
sum_and_square = compose([square, plus]);
But what if your two functions were these?
apply_twice = (f) => ((x) => f(f(x))); // still unary, technically
plus = (x,y) => x + y; // still binary
Your compose would not work.
Even though, if the function plus was curried, e.g. if it was defined as
plus = (x) => (y) => x + y
then one could think of composing them in a function that acts like this:
f = (x,y) => apply_twice(plus(x))(y)
which would predictably produce f(3,4) === 10.
You can get it as f = compose([apply_twice, plus]).
A cosmetic improvement
Additionally, I would suggest a "cosmetic" change: make compose accept ...fns instead of fns,
compose = (...fns)/* I've only added the three dots on this line */ =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
and you'll be able to call it without groupint the functions to be composed in an array, e.g. you'd write compose(apply_twice, plus) instead of compose([apply_twice, plus]).
Btw, there's lodash
There's two functions in that library that can handle function composition:
_.flow
_.flowRight, which is aliased to _.compose in lodash/fp
(¹) This is Haskell's choice (. is the composition operator in Haskell). If you apply f . g . h to more than one argument, the first argument will be passed thought the whole pipeline; that intermediate result will be applied to the second argument; that further intermediate result will be applied to the third argument, and so on. In other words, if you had haskellCompose in JavaScript, and if f was binary and g and h unary, haskellCompose(f, g, h)(x, y) would be equal to f(g(h(x)), y).
(²) Clojure's comp instead takes another choice. It saturates the rightmost function and then passes the result over to the others. So if you had clojureCompose in JavaScript, and f and g where unary while h binary, then clojureCompose(f, g, h)(x, y) would be equal to f(g(h(x,y))).
Might be because I'm used to Haskell's automatically curryed functions, but I prefer Haskell's choice.
Related
I want to create a functions that returns the parameter of another function. I know I can use argument or rest operator to access parameter inside a function itself, but how can I get them outside that function?
const returnValue = (fn) => {
//How can I get the parameter of fn? Assume I know its arity.
}
For example:
const foo = 'bar'
const hello = 'world'
const returnFirstArgument = (val1, val2) => val1
const returnArgumentByPosition = (fn, num) => {
//Take fn as input, return one of its parameter by number
}
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1) //Expect it returns 'world'
What you want isn't possible to do without modifying how returnFirstArgument behaves. Take for example the below piece of code:
const x = 1 + 2;
console.log(x); // 3
Before a value is assigned to x, the expression 1 + 2 needs to be evaluated to a value. In this case 1 + 2 gets evaluated to 3, so x gets assigned to 3, that way when we print it, it prints the literal number 3 out in the console. Since it is now just a number, we can't tell how 3 was derived (it could have come from 0 + 3, 1 * 3, etc...).
Now take a similar example below:
const max = Math.max(1, 2);
console.log(max); // 2
The same idea here applies from above. First Math.max(1, 2) is evaluated to the value of 2, which is then assigned to max. Again, we have no way of telling how 2 was derived.
Now consider a function:
const add = (x, y) => x + y;
const ans = add(1 + 2, Math.max(1, 2));
console.log(ans); // 5
When we call the function, the function's arguments are first evaluated to values. The parameters within the function are then assigned to copies of these values:
const ans = add(1 + 2, Math.max(1, 2));
// ^--------^------------- both evaluate first before function is invoked
so the above function call becomes:
const ans = add(3, 2);
As a result, inside the add function, x becomes 3 and y becomes 2. Just like with the above first two examples with variables, we have no way of knowing the 3 came from the expression 1+2 and that 2 came from the function call of Math.max(1, 2).
So, relating this back to your original question. Your function call is analogous to the add function call shown above:
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1)
just like in the other examples, the arguments passed to the function can't be expressions, so they need to be evaluated first to values. returnFirstArgument(foo, hello) is evaluated to a value before the returnArgumentByPosition function is invoked. It will evaluate to the string "bar". This results in fn becoming "bar" inside of your returnArgumentByPosition. As "bar" is just a string, we again have to way of telling where it came from, and so, won't have access to the function which created it. As a result, we can't access the second argument of the function, since this information is not retained anywhere.
One approach to do what you're after is to create a recall function. The recall function is able to "save" the arguments you passed into it, and then expose them later. Put simply, it wraps your original function but is able to save the arguments and the result of calling your original function:
const recall = fn => (...args) => {
return {
args,
result: fn(...args),
}
};
const add = recall((x, y) => x + y);
const getN = ({args}, n) => {
return args[n];
}
const res = getN(add(1, 2), 1);
console.log(res);
The above approach means that add() will return an object. To get the result of calling add, you can use .result. The same idea applies to get the arguments of add(). You can use .args on the returned object. This way of saving data is fine, however, if you want a more functional approach, you can save the data as arguments to a function:
const recall = fn => (...args) => {
return selector => selector(
args, // arguments
fn(...args) // result
);
};
// Selectors
const args = args => args;
const result = (_, result) => result;
const getN = (wrapped, n) => {
return wrapped(args)[n];
}
const add = recall((x, y) => x + y);
const wrappedAns = add(1, 2);
const nth = getN(wrappedAns, 1);
console.log(nth); // the second argument
console.log(wrappedAns(result)); // result of 1 + 2
above, rather than returning an object like we were before, we're instead returning a function of the form:
return selector => selector(args, fn(...args));
here you can see that selector is a function itself which gets passed the arguments as well as the result of calling fn() (ie: your addition function). Above, I have defined two selector functions, one called args and another called result. If the selector above is the args function then it will be passed args as the first argument, which it then returns. Similarly, if the selector function above is the result function, it will get passed both the args and the result of calling fn, and will return the result the return value of fn(...args).
Tidying up the above (removing explicit returns etc) and applying it to your example we get the following:
const foo = 'bar';
const hello = 'world';
const recall = fn => (...args) => sel => sel(args, fn(...args));
const returnFirstArgument = recall((val1, val2) => val1);
const returnArgumentByPosition = (fn, num) => fn(x => x)[num];
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1);
console.log(returnSecondArgument); // world
Side note (for an approach using combinators):
In functional programming, there is a concept of combinators. Combinators are functions which can be used as a basis to form other (more useful) functions.
One combinator is the identity-function, which simply takes its first argument and returns it:
const I = x => x;
Another combinator is the K-combinator, which has the following structure:
const K = x => y => x;
You may have noticed that the first selector function args is missing an argument. This is because JavaScript doesn't require you to enter all the parameters that are passed as arguments into the function definition, instead, you can list only the ones you need. If we were to rewrite the args function so that it showed all the arguments that it takes, then it would have the following structure:
const args = (args, result) => args;
If we curry the arguments of this function, we get:
const args = args => result => args;
If you compare this function to the K-combinator above, it has the exact same shape. The K-combinator returns the first curried argument, and ignores the rest, the same applies with our args function. So, we can say that args = K.
Similarly, we can do a similar thing for the result selector shown above. First, we can curry the arguments of the results selector:
const result = _ => result => result;
Notice that this almost has the same shape as the K combinator, except that we're returning the second argument rather than the first. If we pass the identify function into the K-combinator like so K(I), we get the following:
const K = x => y => x;
K(I) returns y => I
As we know that I is x => x, we can rewrite the returned value of y => I in terms of x:
y => I
can be written as...
y => x => x;
We can then alpha-reduce (change the name of y to _ and x to result) to get _ => result => result. This now is the exact same result as the curried result function. Changing variable names like this is perfectly fine, as they still refer to the same thing once changed.
So, if we modify how selector is called in the recall function so that it is now curried, we can make use of the I and K combinators:
const I = x => x;
const K = x => y => x;
const recall = fn => (...args) => sel => sel(args)(fn(...args));
const args = K;
const result = K(I);
const getN = (fn, n) => fn(args)[n];
const add = recall((x, y) => x + y);
const addFn = add(1, 2);
const nth = getN(addFn, 1);
console.log(nth); // the second argument
console.log(addFn(result)); // result of 1 + 2
I’ve been trying to implement an Apply/Applicative type based on Javascript Functor, Applicative, Monads in pictures and the blog series Fantas, Eel, and Specification.
I think I’m making good progress, but I ran in to a case I couldn’t really find in any of the articles.
TL;DR Question
If lift2 is
f -> A.of(x) -> A.of(y) -> A.of(f (x) (y)) -or- A(y).ap(A(x).map(f))
what is the theory/name/type behind
A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y)) -or- A(y).ap(A(x).ap(A(f)))
Introduction
I’m working with knockout.js, which gives me observable values. I’m trying to use them in a kind of functional fashion, extending them when needed.
First, I implemented map to make myself a functor:
ko.subscribable.fn.map = function(f) {
// note: calling without arguments is knockout's
// way of "unwrapping"
return ko.pureComputed(
() => f(this())
);
}
This allows me to do things like:
// "Pure" part describing my app
const myValue = ko.observable(2);
const doubleThat = myValue.map(x => x * 2);
// In- and output (IO?)
doubleThat.subscribe(console.log);
myValue(3); // Logs 6
Then, I ran in to the problem of working with functions that take multiple arguments. For example:
const filter = (pred, xs) => xs.filter(pred);
I solved my issues by implementing ap and currying my functions:
ko.subscribable.fn.ap = function(sf) {
return ko.pureComputed(
() => sf () (this())
);
};
const filter = pred => xs => xs.filter(pred);
With these changes in place, I can do:
const odd = x => x % 2 === 1;
const myPred = ko.observable(odd);
const myValues = ko.observable([ 1, 2, 3 ]);
const myFilter = myPred.map(filter);
const myResult = myValues.ap(filter); // S([ 1, 3 ])
The definition of lift2 gives me another way of writing the same thing.
const myResult = lift2 (filter) (myPred) (myResult)
So far, so good. I can use the dot-calls if the interim result is reusable, and a liftN call if I only care about the final outcome.
The problem
The liftN way of chaining one map with N - 1 calls to ap only works if I use plain functions. In my app however, I often deal with functions that are themselves wrapped in subscribables! For example:
const sum = x => y => x + y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(2);
const v2 = ko.observable(3);
My attempts
Chaining works, but quickly gets very hard to understand.
// Ugly...
const myResult = v2.ap(v1.ap(mathStrategy)); // S(5)
I can use liftN, but only if I make sure my first function is id.
// Also ugly...
const id = x => x;
const myResultL = lift3 (id) (mathStrategy) (v1) (v2); // S(5)
My questions
If lift2 handles f -> A.of(x) -> A.of(y) -> A.of(f (x) (y)), what is the theory/name/type behind A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y))
If such a thing does not really “exist”, would it be okay to write an implementation of ap that unwraps A(f) on the go? (i.e. f => ko.unwrap (f) (x))
Code example
Object.assign(ko.subscribable, {
of: function(x) {
return ko.pureComputed(() => x)
}
});
Object.assign(ko.subscribable.fn, {
map: function(f) {
return ko.pureComputed(() => f(this()));
},
ap: function(sf) {
return ko.pureComputed(() => sf () (this()));
},
toString: function() {
return `S(${JSON.stringify(this())})`;
}
});
// Example code:
const sum = x => y => x + y;
const mult = x => y => x * y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(1);
const v2 = ko.observable(3);
const result = v2.ap(v1.ap(mathStrategy));
console.log(result); // S(4)
v1(2);
mathStrategy(mult);
console.log(result); // S(6)
.as-console-wrapper { min-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Note: This is kind of related to a question I asked earlier. Back then, I was focusing on lazily unwrapping A(x) and A(y), which I have given up on by now 😉.
I think the question you meant to ask, with proper notation for the types, is
If lift2 handles Apply f => (x -> y -> z) -> f x -> f y -> f z, what is the theory/name behind Apply f => f (x -> y -> z) -> f x -> f y -> f z?
Such a function is rare, because everyone just uses ap or the respective infix operator, like in Haskell
let az = af <*> ax <*> ay
However, I saw some two references on the internet to it being called ap2 [1][2], which makes sense as it is analogous to lift becoming lift2.
You can write this helper function as
const ap2 = af => ax => ay => ap(ap(af, ax), ay)
or like you already did
const ap2 = lift3(id)
I'm trying to make middleware function lets call it debug, which should take some parameters, log them and pass to the next function:
const debug = (...args) => {
console.log(...args)
return args // will return array, not argument type variable
}
const compose = (...fns) => (...arg) => (
fns.slice().reverse().reduce(
(acc, fn) => !acc ? fn(...arg) : fn(acc), null
)
)
const f = x => x * x
const g = (a, b) => a + b
const makeMagic = compose(
f,
g,
debug
)
makeMagic(1, 2)
If I remove debug from composition everything works as expected, as soon as I place it at end, it breaks. Because it takes arguments but returns array.
I tried to rewrite debug this way:
function debug() {
console.info(arguments)
return arguments
}
But no way it fails.
A function always has only a single result value. You can't write a function that accepts multiple arguments and "returns" multiple values without some kind of container around them (like an array), it's just not a feature JavaScript has (nor do most other programming languages).
You can make debug a pure pass-through for a single argument, but not multiple. So for instance, you could make this work:
const result = debug(makeMagic(1, 2))
...where result would be the value makeMagic returned (after debug logs it), but you can't include it in the composed makeMagic the way you're trying to.
To have debug (or any other function) in the middle of a chain with your compose, you'd have to use a convention with it and all composeable functions about how they return multiple values, probably by having them all return an array. That also helps with the fact taht right now, your compose function has what I assume is a bug: It uses ...args any time a previous function returned a falsy value (0, "", NaN, null, undefined, or false), where I'm fairly sure you meant to do that only on the first call.
Here's a version of compose that expects composable functions, which are functions that return an array of their results:
const debug = (...args) => {
console.log(...args);
return args;
};
const compose = (...fns) => (...args) => (
fns.slice().reverse().reduce(
(acc, fn) => fn(...(acc || args)),
null
)
);
const f = x => [x * x];
const g = (a, b) => [a + b];
const makeMagic = compose(
f,
g,
debug
);
const result = makeMagic(1, 2);
console.log(result);
I was trying to implement the Y combinator in Javascript.
I managed to implement the following:
const y0 = gen => (f => f(f))( f => gen( x => f(f)(x) ) );
const factorial0 = y0( fact => n => n<=2 ? n : n * fact(n-1) );
console.log(factorial0(5));
// 120
It works well.
Then I was considering the expression x => f(f)(x).
My understanding is that an expression x => g(x) is equivalent to g. Applying any y to x => g(x) evaluates to g(y), while applying y to g also evaluates to g(y).
So I replaced x => f(f)(x) by f(f).
const y = gen => (f => f(f))( f => gen( f(f) ) );
const factorial = y( fact => n => n<=2 ? n : n * fact(n-1) );
console.log(factorial(5));
// RangeError: Maximum call stack size exceeded
But this version crashes with a stack overflow.
So what is the difference between x => f(f)(x) and f(f) so that the one works and the other crashes.
Well
x => f(f)(x)
is a function taking one parameter, x. When the function is called, it in turn calls the function f, passing a reference to f as a parameter. Function f returns another function, and then that is invoked, with x passed as a parameter.
In old-school syntax, it's
function(x) {
return f(f)(x);
}
That's significantly different than just f(f) by itself. That's just an invocation of the function "f", with "f" passed as a parameter.
So both x => f(f)(x) and f(f) are expressions, but they represent significantly different semantics. The value of the first is a reference to a function; the expression itself doesn't do anything else — in particular, the function f() is not called. The value of f(f) is whatever function f() returns when called — that expression does do something, that being whatever function f() does.
What I think is happening is that those 2 expressions are not exactly the same.
On one hand x => f(f)(x) - this creates a new function literal (so it is not invoked right away - it is invoked only when this function is called)
On the other hand f(f) - this in Javascript is a an expression that calls the f function. So it results in a stack overflow in your case.
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});