# How to simulate let expressions in JavaScript?

Consider the following implementation of `take`:

``````const take = (n, [x, ...xs]) =>
n === 0 || x === undefined ?
[] : [x, ...take(n - 1, xs)];

console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1]));  // []``````

As you can see, it doesn't work for arrays with `undefined` because `x === undefined` is not the best way to test whether an array is empty. The following code fixes this problem:

``````const take = (n, xs) =>
n === 0 || xs.length === 0 ?
[] : [xs[0], ...take(n - 1, xs.slice(1))];

console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1]));  // [undefined]``````

However, writing `xs[0]` and `xs.slice(1)` isn't as elegant. In addition, it's problematic if you need to use them multiple times. Either you'll have to duplicate code and do unnecessary extra work, or you'll have to create a block scope, define constants and use the return keyword.

The best solution would be to use a let expression. Unfortunately, JavaScript doesn't have them. So, how to simulate let expressions in JavaScript?

In Lisp, a let expression is just syntactic sugar for a left-left lambda (i.e. an immediately-invoked function expression). For example, consider:

``````(let ([x 1]
[y 2])
(+ x y))

; This is syntactic sugar for:

((lambda (x y)
(+ x y))
1 2)
``````

In ES6, we can use arrow functions and default parameters to create an IIFE that looks like a let expression as follows:

``````const z = ((x = 1, y = 2) => x + y)();

console.log(z);``````

Using this hack, we can define `take` as follows:

``````const take = (n, xxs) =>
n === 0 || xxs.length === 0 ?
[] : (([x, ...xs] = xxs) => [x, ...take(n - 1, xs)])();

console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1]));  // [undefined]``````

Hope that helps.

Instead of using an IIFE just use a normal function with a proper name to make things more explicit:

``````const _let = f =>
f();

const collateBy = f => xs =>
xs.reduce((m, x) =>
_let((r = f(x), ys = m.get(r) || []) =>
m.set(r, (ys.push(x), ys))), new Map());

const includes = t => s =>
s.includes(t);

xs = ["Dev", "Jeff", "Kalib", "Amy", "Gemma"];

const collation = collateBy(includes("e")) (xs);

console.log(collation.get(true));
console.log(collation.get(false));``````