If you are a programmer, you’ve probably stumbled upon the term “lambda calculus” at some point. Maybe some Haskell-bro told you that “it’s like the foundation of all programming man, just trust me”, then proceeds to whip out this
\begin{equation} λf.(λx.f(x x))(λx.f(x x)) \end{equation}
Apparently it’s the “Y-combinator” (yes ycombinator.com is named after this) and it’s like “really deep” because it “gives you recursion” but also “has no normal form”. This Haskell-bro insists that this is somehow related to that div you’re trying to center.
It’s true that lambda calculus is like “really important”, but usually people are not too good at explaining why without resorting to mathematical jargon. This blog post is intended to explain lambda calculus to the working programmer, to remove all that mathematical fuss and reveal that lambda calculus is deeply connected to “real” programming.
First, let’s get two things out of the way
-
Lambda refers to the Greek letter λ, and “lambda calculus” is just a name; it carries no more meaning than “JavaScript” or “C”. If the inventor of lambda-calculus, Alonzo Church had known how his arbitrary use of λ’s would alienate future programmers the world over, then maybe he would have chosen something more sensible, but here we are.
-
There are different kinds of lambda calculi, the one we’ll explore here is called untyped lambda calculus. Here untyped is to be understood as it’s in a programming context, the lambda calculus is untyped just like JavaScript and Python are. Then there are various typed lambda calculi, the simplest of them being the.. simply typed lambda calculus. Makes sense.
A story that is not true
When Alonzo Church introduced lambda calculus in the 1930s well before computers and programming as we know it today he was interested in buzzwords such as computability and decidability. For him the lambda calculus was a model of computation, a tool for him to use in exploring the topics he was interested in. We’ll tell a different story.
You are a frontend dev in 2026, and so is your colleague Alonzo Church. Both you and Alonzo are very curious, and over the years you’ve developed this special relationship. It revolves mostly in the philosophical conversations you have around lunchtime, they can continue for months. One slow Friday you and Alonzo ask yourselves what it really means for something to be a programming language. JavaScript is one for sure, but HTML and JSON are not. Is CSS? Well, not quite. C is also definitely a programming language. Apparently there are amalgamations like Lisp and Haskell which are also definitely programming languages. How can it be that JavaScript, C, Lisp and Haskell are all definitely programming languages when they are so different? Are there some essential qualities that they all have in common? You are thinking about this intensely on your bike ride home from work. Alonzo does the same on the bus. When he gets home he starts experimenting.
The next Monday you come in to work, just in time for the dreaded 9AM standup – your brain still operating on weekend chronology. Just as you cross the threshold you scan across the office landscape and meet Alonzo’s eyes at his desk. You have seen those eyes before, Alonzo uncovered something big this weekend. You know. He knows you know.
Finally lunch rolls around, there is no need for ceremony when you sit down across Alonzo in the corner of the cafeteria.
I’ve figured it out, the essential nature of programming languages. The kernel is smaller and more eloquent than I could ever imagine. You see, all programming is about three things only: Variables, functions, and applying functions. Everything else is syntactic sugar.
Eeh, okay.. (it’s gone too far this time). Like, how would I console.log("hello world") or 1 + 1 == 2 with only variables, functions and function application??
We’ll get there but it’s not that simple you see. First let me show you what I mean, all JavaScript can be built from these three building blocks:
Alonzo whips out a notebook and starts writing down a “definition.”
- Variables You can always use variables
x
Here x might be undeclared, in which case you will get a ReferenceError just like in JS.
- Function definitions If
<a>is a stand-in for some valid REJS then you may writex => <a>. (xcan be any variable name) For instance, with rule (1) and (2) you may write
x => x
and
x => y
- Function application If
<a>and<b>are stand-in for valid REJS code then you may write<a>(<b>).
Everything that does not follow from these three rules gives a SyntaxError.
Okay.. like.. what do you mean?? How can I for instance add two numbers together if I’m not even allowed to write 1 + 2?
It’s simple: Numbers are functions!
..You’re crazy..
I will show you how, but first we need to get some preliminaries out of the way.
Variable assignment
One of the most basic things a programmer might want is variables, we want to write for instance const x = 10. This is not allowed in REJS, but it turns out that variable assignment is actually a function in disguise. Instead of writing
const x = 10
console.log(x)
we can write
(x =>
console.log(x)
)(10)
In both cases the variable x refers to the value 10, but in the second example we have done so without using const, let or var! Variable assignment is nothing more than syntactic sugar for functions.
Just to clarify, can console.log(x) be used in REJS?
Well yes and no, REJS doesn’t support the “.” in console.log, but of course record access is just syntactic sugar for functions, but we haven’t gotten there yet. Also we need to actually define console and log in terms of REJS which is totally possible, but a bit ahead of us. For now, whenever I use things like console.log or number and string literals, just imagine that they are stand-ins for something arbitrary we don’t really care about.
I understand. I guess it’s a clever trick to use functions instead of const, though not very useful.. What about multiple variable assignments?
You fail to see that this “trick” is the very first hint of a significant truth: We don’t need all of this JS bloat when we have functions! But as to your question, I’d like to ask how you would do it.
I guess I would do this
const foo = "foo"
const bar = "bar"
const baz = "baz"
...
\(\implies\)
((foo, bar, baz) =>
...
)("foo", "bar", "baz")
You certainly are heading in the right direction, but remember that rule (2) in the definition of REJS stipulates that only functions of one argument are allowed! We get around that restriction like this
(baz =>
(bar =>
(foo =>
...
)("foo")
)("bar")
)("baz")
or when we have many variables we might do it in a more compact style
(baz => (bar => (foo =>
...
)("foo")
)("bar")
)("baz")
But I already hear your voice in my mind: “How can one do ANYTHING with this one-argument restriction” you object. This segues us neatly into the second “trick” as you might call it.
Functions with multiple arguments
If we want to find an equivalent for 1 + 2 in REJS we face two immediate problems: What are numbers and what is “+”. For the problem of “+” it’s at least clear that it should be a function that takes two numbers and returns a number, instead of writing 1 + 2 we may write add(1, 2). Here we run into the problem of the single-argument restriction again, but this time it’s not only inconvenient, it may be devastating. However, the solution is simple. Instead of writing
const add = (x, y) => ...
we write
const add = x => (y => ...) // or without parenthesis x => y => ...
and instead of add(1, 2) we write add(1)(2). So now add isn’t a function that takes two numbers and returns a number, it’s a function that takes one number and returns another function that takes one number and returns a number. To help you get a grasp of this I’ve written an example of how this trick is used. Please compare the code in the three tabs, they all do the exact same thing.
(first => (second =>
second(1)(first(2)(3))
)(x => y => y) // second
)(x => y => x) // first
// Given two arguments x and y, returns x.
const first = x => y => x
// Given two arguments x and y, returns y.
const second = x => y => y
second(1)(first(2)(3))
// Given two arguments x and y, returns x.
const first = (x, y) => x
// Given two arguments x and y, returns y.
const second = (x, y) => y
second(1, first(2, 3))
In fact — and Schönfinkel from the JS meetup showed me this — not only does this trick work, it gives us the power of partial application², for instance I can now write this:
const add = x => y => x + y
const add3 = add(3) // same as: const add3 = y => 3 + y
add3(2) // same as: (y => 3 + y)(2) -> 5
Okay I guess these two tricks are neat, but Alonzo these are all very basic things. I cannot possibly imagine how we could ever do something interesting in REJS, please give me something to be excited for.
I’m afraid this does take some time, but I promise you it’s worth it! We’ll get to numbers, but first can I show you how booleans and if-statements can be expressed in REJS?
Sure, I mean, that would be a start.
Syntactic sugar
Before we begin with booleans I want to explain something. The overall claim I am making is this:
All JavaScript is syntactic sugar around REJS. For any piece JS code, there is REJS code that does the same thing.
Now this quite a big bite to chew, and I fear we might not finish before lunch is over, but I have already shown you that whenever we come across a piece of JavaScript that uses const foo = ... or defines a function with many arguments, that those things are just syntactic sugar for things that are in REJS. Soon I will show that true, false and if { ... } else { ... } are also just syntactic sugar. Are you following?
Yes, I mean it’s very cumbersome to write these things in the REJS-style and no-one would ever want to do it.. but I accept that it’s possible.
My point exactly, it’s called syntactic sugar for a reason. For convenience we shall use this syntactic sugar of variable assignment and functions with many arguments. But just so that there is never any shadow of a doubt that everything really can be done in REJS, I will whenever appropriate present up to three versions of the same idea:
- The JS version.
- The high-level REJS version with
constand multiple arguments. - The pure REJS version where everything is always made explicit!
Will you accept this?
Sure just show me the booleans.
Booleans
Of course the values true and false must be functions! But what functions? Let’s start by examining the primary use-case for booleans: if-statements. We are of course not allowed to write if (...) { ... } else {...} in REJS, but by looking at the “shape” of the if statement we can imagine that it’s actually a function
const ifThenElse = (x, ifTrue, ifFalse) => ...
If x is true we return ifTrue, if x is false we return ifFalse. Now, consider this
const ifThenElse = (x, ifTrue, ifFalse) => x(ifTrue, ifFalse)
does this make any sense to you?
I mean.. no not really. x is supposed to be a value true or false, but when you write x(ifTrue, ifFalse) you are pretending that it’s actually a function.. Ooh I see! Clever, instead if having an if-statement “look” at a boolean and then pick a branch ifTrue or ifFalse you let the boolean itself make the choice: true is a function that when given the true-branch and the false-branch, it returns the true-branch. false on the other hand returns the false-branch. We can define booleans and if-statements like this:³ ⁵
(True => (False => (ifThenElse => ifThenElse(True)(1)(2)
)(x => ifTrue => ifFalse => x(ifTrue)(ifFalse)) // ifThenElse
)(ifFalse => ifFalse => ifFalse) // False
)(ifTrue => ifFalse => ifTrue) // True
const True = (ifTrue, ifFalse) => ifTrue
const False = (ifTrue, ifFalse) => ifFalse
const ifThenElse = (x, ifTrue, ifFalse) => x(ifTrue, ifFalse)
ifThenElse(True, 1, 2)
Excellent. Now that we have the basics of boolean logic we can simply define !, && and || in terms of it.
(True => (False => (ifThenElse => (not => (and => (or =>
or(not(True))(and(True)(False))
)(x => y => // or
ifThenElse(x)
(True)
(ifThenElse(y)(True)(False)))
)(x => y => // and
ifThenElse(x)
(ifThenElse(y)(True)(False))
(False))
)(x => ifThenElse(x)(False)(True)) // not
)(x => ifTrue => ifFalse => x(ifTrue)(ifFalse)) // ifThenElse
)(ifTrue => ifFalse => ifFalse) // False
)(ifTrue => ifFalse => ifTrue) // True
const True = (ifTrue, ifFalse) => ifTrue
const False = (ifTrue, ifFalse) => ifFalse
const ifThenElse = (x, ifTrue, ifFalse) => x(ifTrue, ifFalse)
const not = x => ifThenElse(x, False, True)
const and = (x, y) =>
ifThenElse(x,
ifThenElse(y, True, False),
False)
const or = (x, y) =>
ifThenElse(x,
True,
ifThenElse(y, True, False))
or(not(True), and(True, False))
const not = x => { if (x) { return false } else { return true } }
const and = (x, y) => {
if (x)
{ if (y) { return true } else { return false } }
else { return false }
}
const or = (x, y) => {
if (x)
{ return true }
else { if (y) { return true } else { return false } }
}
or(not(true), and(true, false))
Numbers
When I said earlier that “numbers are functions! I didn’t mean it metaphorically, numbers are literal functions. For instance, 10 is mere syntactic sugar for a function that takes two arguments f and x and returns f(f(f(f(f(f(f(f(f(f(x)))))))))). 0 is a function that given f and x always returns x as it is. Can you generalize this pattern?
This doesn’t make any sense.. but the pattern is simple. Following your description:
const zero = (f, x) => x
const one = (f, x) => f(x)
const two = (f, x) => f(f(x))
const three = (f, x) => f(f(f(x)))
const four = (f, x) => f(f(f(f(x))))
const five = (f, x) => f(f(f(f(f(x)))))
But how can this be used? What is addition? How to check if two numbers are equal?
Let’s start with addition.. in fact let’s start even simpler, how can we implement const add1 = n => n + 1 in REJS?
Let’s see.. all these functions everywhere confuse me.. let’s do it in small steps. It takes a single number, so it starts like this
const add1 = n => ...
Then add1 returns another number, and numbers look like this (f, x) => ..., so the we continue like this
const add1 = n => (f, x) => ...
Now I know this will be wrong, but suppose we simply return n(f, x)
const add1 = n => (f, x) => n(f, x)
This isn’t right of course, add1(two)(f, x) returns f(f(x)) when it should return f(f(f(x))). Adding an extra f will fix it
const add1 = n => (f, x) => f(n(f, x))
This is the final answer.
And to avoid this back-and forth, I already see that the general pattern is this
const add0 = n => (f, x) => n(f, x)
const add1 = n => (f, x) => f(n(f, x))
const add2 = n => (f, x) => f(f(n(f, x)))
const add3 = n => (f, x) => f(f(f(n(f, x))))
const add4 = n => (f, x) => f(f(f(f(n(f, x)))))
const add5 = n => (f, x) => f(f(f(f(f(n(f, x))))))
Very good, but can you refactor this using zero, one, two, three, four and five defined earlier?
Yes, it’s this
const add0 = n => (f, x) => zero(f, n(f, x))
const add1 = n => (f, x) => one(f, n(f, x))
const add2 = n => (f, x) => two(f, n(f, x))
const add3 = n => (f, x) => three(f, n(f, x))
const add4 = n => (f, x) => four(f, n(f, x))
const add5 = n => (f, x) => five(f, n(f, x))
And now I see, BEHOLD
(zero => (one => (two => (three => (four => (five => (add =>
add(two)(three)
)(n => m => f => x => m(f)(n(f)(x))) // add
)(f => x => f(f(f(f(f(x)))))) // five
)(f => x => f(f(f(f(x))))) // four
)(f => x => f(f(f(x)))) // three
)(f => x => f(f(x))) // two
)(f => x => f(x)) // one
)(f => x => x) // zero
const zero = (f, x) => x
const one = (f, x) => f(x)
const two = (f, x) => f(f(x))
const three = (f, x) => f(f(f(x)))
const four = (f, x) => f(f(f(f(x))))
const five = (f, x) => f(f(f(f(f(x)))))
const add = (n, m) => (f, x) => m(f, n(f, x))
add(two, three)
this must be addition.
For-loops
Excellent, now we have numbers and addition. There are for sure many more things one might want to do with numbers, like subtraction for instance (which is more involved than one might think). However, I want to immediately move on to for-loops because — and this I find so exciting — numbers give rise to a notion of for-loops. In other words, some for-loops in JS can now be seen a syntactic sugar for REJS, I will show you how.
This is a snippet of code that we see quite a lot in our day to day work
let x = <initial value>
for (let i = 0; i < n; i++) {
...
x = ...
}
I’m starting to get a little excited too, you’re telling me that mutating variables (as is done with x = ...) has an REJS counterpart?
Yes and no. We’ll show in the future how mutation can always be seen as syntactic sugar for some REJS, but that’s in the future, here I am only making a claim about how a very specific for-loop pattern has a counterpart in REJS.
So imagine, that the body of the for-loop can be refactored into a function f. The function f takes the current value of x, does something and then returns the new value of x, so we rewrite the for-loop like this
let x = <initial value>
for (let i = 0; i < n; i++) {
x = f(x)
}
Refactoring the body into a function f means that the for-loop body can’t contain any references to the variable i, and it cannot use break or continue. Imposing these restrictions makes my argument easier to follow, but they all have workarounds.
Suppose for the sake of argument that n = 3. Now we unroll the loop, and so we get this piece of code that does the same thing⁶
let x = <initial value>
let i = 0;
// first iteration
x = f(x)
i++
// second iteration
x = f(x)
i++
// third iteration
x = f(x)
i++
Of course the variable i is never used so we may omit it
let x = <initial value>
x = f(x)
x = f(x)
x = f(x)
Finally we can compress these four lines into a single line
const x = f(f(f(<initial value>)))
Now this looks like numbers! We can rewrite it to this
const x = three(f, <initial value>)
Of course the reason we ended up with three f’s is because we assumed for the sake of argument that n == 3 == three, so in the general for-loop with any value of n we have
const x = n(f, <initial value>)
Now we simply refactor it into a forLoop function that takes an initialValue, the number of iterations n, and the for-loop body f.
const forLoop = (initialValue, n, f) => n(f, initialValue)
This is very cool, it means that any for-loop like the one above is just syntactic sugar for REJS. Here is for instance an implementation of multiply using for-loops
(zero => (one => (two => (three => (four => (five => (add => (forLoop => (multiply =>
multiply(two)(three)
)(n => m => forLoop // multiply
(zero)
(n)
(x => add(x)(m)))
)(initialValue => n => f => n(f)(initialValue)) // forLoop
)(n => m => f => x => m(f)(n(f)(x))) // add
)(f => x => f(f(f(f(f(x)))))) // five
)(f => x => f(f(f(f(x))))) // four
)(f => x => f(f(f(x)))) // three
)(f => x => f(f(x))) // two
)(f => x => f(x)) // one
)(f => x => x) // zero
const zero = (f, x) => x
const one = (f, x) => f(x)
const two = (f, x) => f(f(x))
const three = (f, x) => f(f(f(x)))
const four = (f, x) => f(f(f(f(x))))
const five = (f, x) => f(f(f(f(f(x)))))
const add = (n, m) => (f, x) => m(f, n(f, x))
const forLoop = (initialValue, n, f) => n(f, initialValue)
const multiply = (n, m) => forLoop(
zero,
n,
(x => add(x, m)),
)
multiply(two, three)
const multiply = (n, m) => {
let x = 0
for (let i = 0; i < n; i++) {
x = x + m
}
return x
}
multiply(2, 3)
This is indeed very cool, I can see how, in some sense, the boolean values gave rise to if-statements with const ifThenElse = (x, t, f) => x(t, f). Now I see how the numbers give rise to for-loops with const forLoop = (x, n, f) => n(f, x). In both cases the “magic” if you will is in the booleans and numbers themselves..
What value in REJS would correspond to a while(true) { ... }?
Well, a while-loop would correspond to the so-called Y-combinator, which is also the thing that enables recursive functions. But the Y-combinator is also what lets you write programs that go on in infinite loops, and personally I’m only interested in programs that work, so neither while-loops, recursion nor the Y-combinator interests me.
Well.. while I do find what I’ve learned thus far interesting, being able to flex on those pesky Haskell-bros that are always up in my business would really be the thing that makes it all worthwhile. How about I continue to indulge you if you tell me about the Y-combinator eventually?
If you really want to learn how to shoot yourself in the foot then I will teach you in exchange for your attention. It’s a deal.
But before then, I want to pose three exercises that makes use of all the things we’ve learned thus far:
-
Exercise 1. How would you implement a function
isEventhat takes a number and returnsTrueif it’s an even number. I will give you a hint but you need to fill in the blanks.(True => (False => (ifThenElse => (not => (zero => (one => (two => (three => (four => (five => (forLoop => (isEven => isEven(five) )(n => forLoop // isEven (True) (n) (x => /* Fill in this blank */)) )(initialValue => n => f => n(f)(initialValue)) // forLoop )(f => x => f(f(f(f(f(x)))))) // five )(f => x => f(f(f(f(x))))) // four )(f => x => f(f(f(x)))) // three )(f => x => f(f(x))) // two )(f => x => f(x)) // one )(f => x => x) // zero )(x => ifThenElse(x)(False)(True)) // not )(x => ifTrue => ifFalse => x(ifTrue)(ifFalse)) // ifThenElse )(ifTrue => ifFalse => ifFalse) // False )(ifTrue => ifFalse => ifTrue) // Trueconst True = (ifTrue, ifFalse) => ifTrue const False = (ifTrue, ifFalse) => ifFalse const ifThenElse = (x, ifTrue, ifFalse) => x(ifTrue, ifFalse) const not = x => ifThenElse(x, False, True) const zero = (f, x) => x const one = (f, x) => f(x) const two = (f, x) => f(f(x)) const three = (f, x) => f(f(f(x))) const four = (f, x) => f(f(f(f(x)))) const five = (f, x) => f(f(f(f(f(x)))))const forLoop = (initialValue, n, f) => n(f, initialValue) const isEven = n => forLoop( True, n, (x => /* Fill in this blank */) ) isEven(five)const isEven = n => { let x = true for (let i = 0; i < n; i++) { // Fill in this blank } return x } isEven(5) -
Exercise 2. Implement a function
isZerothat takes a number and returnsTrueif that number is zero. I will not give you any help except for the knowledge that if you can solve exercise 1 then you can definitely solve this one.const True = (ifTrue, ifFalse) => ifTrue const False = (ifTrue, ifFalse) => ifFalse const ifThenElse = (x, ifTrue, ifFalse) => x(ifTrue, ifFalse) const zero = (f, x) => x const one = (f, x) => f(x) const two = (f, x) => f(f(x)) const three = (f, x) => f(f(f(x))) const four = (f, x) => f(f(f(f(x)))) const five = (f, x) => f(f(f(f(f(x))))) -
Exercise 3. If you look at the definitions of
Falseandzero, you see that they actually are the exact same functions apart from the names of their arguments. Can numbers be seen as a generalization of booleans? CanTrue,FalseandifThenElsebe implemented in terms of numbers and for-loops?
As you are about to start thinking intensely about these exercises it dawns on both you and Alonzo that the cafeteria is deserted, and it’s time to head back to the stations before the engineering manager notices. You know that this conversation will continue.
Comments
Use this good ol’ mailing list for discussions.
Footnotes
-
This is very close to actual formulations of the syntax of lambda calculus used in the literature but with less fuss. However, there is nonetheless a subtle but significant difference in what counts as an error in lambda calculus and REJS:
First, REJS and lambda calculus are similar in that they have a notion of
SyntaxError; there are things you are simply not allowed to write! Likex =>!:-) xinstead ofx => x. On the other hand there are noReferenceErrors in lambda calculus, unlike REJS. For instance, consider this code in REJS(x => h(e(l(l(o(x))))))(w(o(r(l(d)))))This is clearly a
ReferenceError, none of the functionsh,e,l,o,wrdhave been defined! But if we wanted REJS to more closely resemble lambda calculus we would say that this code is perfectly fine and that, in fact, it evaluates to the followingh(e(l(l(o(w(o(r(l(d)))))))))So in lambda calculus, whenever faced with undefined references we just carry on with what is defined as best we can. Finally, in REJS we can cause stack overflows and out of memory errors, this isn’t a thing in lambda calculus because the lambda calculus is executed on a hypothetical computer with infinite memory.
I could be wrong, but I will tentatively claim that the only REJS errors can be
SyntaxError,ReferenceErrorand various errors related to memory, but nothing more. We can of course emulate the full JS error system withtry-catchsyntax in REJS, but these are the only “native” errors. -
Partial application is actually such a popular technique in functional programming that it has a cute name: currying after Haskell Curry. This idea that all functions are actually single-argument functions even shows up in the very syntax of a language like Haskell. In Haskell the type signature of
addlooks like thisadd :: Int -> (Int -> Int)or equivalently without parenthesis:
add :: Int -> Int -> Intso in a language like Haskell one views
addsimultaneously as a function of one argument returning another function, and as a function of two arguments returning a number. -
There is a slight historical inaccuracy here: In reality you didn’t invent this, Alonzo Church did and it’s called the Church encoding of booleans. The historical Alonzo didn’t know about JavaScript and so he wrote it like this⁴
\begin{align} \text{true} &= λa.λb.a \\ \text{false} &= λa.λb.b \\ \text{if} &= λp.λa.λb.pab \end{align}
-
I actually have no clue if this is how Alonzo wrote it, it’s how mathematicians today do and let’s not complicate things with history or whatever that is.
-
There is an important point to be made here about laziness: In JavaScript (and most other languages) if-statements are lazy, i.e. in this snippet
if (x) { // true-branch } else { // false-branch }only code in one of the branches is executed. However, our naive translation from if-statements to REJS doesn’t capture this, for instance, consider this JS
if (true) { return 1 } else { return loopForever() }It’s very weird code, but it always returns
1. When we “translate” this to REJS we getifThenElse(True, 1, loopForever())This will execute
loopForeverand our program freezes. It seems this translation wasn’t perfect. The solution is to instead translate the if-statement to thisifThenElse(True, dummy1 => 1, dummy2 => loopForever())Now the two branches are functions that take dummy arguments, and so none of the branches are executed. The
ifThenElsewould change to something like thisifThenElse = (x, ifTrue, ifFalse) => { const branch = x(ifTrue, ifFalse) // figure out which branch to pick return branch(null) // execute the branch }It doesn’t matter what argument we give the branch we execute, here we gave
null, but it could be anything. If we want to keep strictly to REJS we can just give it some random function like(x => x).This method of wrapping an expensive computation inside a function with a dummy argument is a common pattern in so-called strict (as opposed to lazy) functional programming languages.
-
This doesn’t do exactly the same things as the for-loop, because in the for-loop the variable
iis only in scope inside the curly-braces, whereas in the unrolled loop it remains. This can be fixed by manually creating a scope like thislet x = <initial value> { // Start a new scope let i = 0; // first iteration x = f(x) i++ // second iteration x = f(x) i++ // third iteration x = f(x) i++ } // Here i no longer exists