Refactoring with Currying

intermediate

The Problem

Look at the evaluate function you’ve built so far. Notice anything?

"l + r": (l, r) => evaluate(l) + evaluate(r),
"l - r": (l, r) => evaluate(l) - evaluate(r),
"l * r": (l, r) => evaluate(l) * evaluate(r),
"l / r": (l, r) => evaluate(l) / evaluate(r),
"l < r": (l, r) => evaluate(l) < evaluate(r),
"l > r": (l, r) => evaluate(l) > evaluate(r),
"l == r": (l, r) => evaluate(l) === evaluate(r),
"l != r": (l, r) => evaluate(l) !== evaluate(r),

Eight rules. All doing the same thing: evaluate both sides, apply an operator. The only difference is which operator. This kind of repetition is a signal — we’re missing an abstraction.


Operators are Just Functions

Think about what + actually does: it takes two values and produces a result. That’s exactly what a function does.

In JavaScript, you wouldn’t write eight separate if statements for each arithmetic operation. You’d write one function that takes an operator and two values. Or better — you’d realize + is already a function, just with special syntax.

The expression [2, "+", 3] is really just a function call in disguise: look up +, apply it to 2 and 3.

And there’s nothing special about the string "+" — it’s just a name, like "square" or "double". We chose to call our addition function "+", but we could have called it "add" or anything else. The only thing that makes it feel special is the infix syntax.


The Obstacle

There’s a catch. Our functions only take one argument:

["lambda", "x", body]

But operators need two. So how do we express + as a function?


Currying

The solution: a function that takes one argument and returns another function that takes the second.

In JavaScript:

const add = (x) => (y) => x + y;
add(2)(3); // -> 5

In Python:

def add(x):
    def inner(y):
        return x + y
    return inner

add(2)(3)  # -> 5

In our language:

["add", "=", ["lambda", "x", ["lambda", "y", ["x", "+", "y"]]]]

[["add", 2], 3]  // -> 5

This technique is called currying — named after the logician Haskell Curry. It lets us express multi-argument functions using only single-argument ones.


The Payoff

Once we can curry, every operator is just a named curried function. The expression [2, "+", 3] becomes [["+", 2], 3] — look up +, apply it to 2, then apply the result to 3.

Eight repetitive rules collapse into one.


What You’ll Do

  1. First, you’ll upgrade the function call rule so it can handle curried calls like [["add", 2], 3]
  2. Then, you’ll replace all eight operator rules with a single rule that treats operators as function calls

Let’s clean up.

Steps