4

I'm writing a compiler from a functional language to JavaScript. Since my language is based on expressions, it is natural to compile it to a JavaScript expression too. The problem is that when compiling let expressions, we need the ability to declare and assign variables "inline". For example:

function foo(x) {
  return (let y = x * x; y);
}

This code, obviously, doesn't work since we can't use let inside expressions. One solution would be to wrap everything inside a lambda:

function foo(x) {
  return (()=>{let y = x*x; return y})();
} 

But this has a significant runtime cost in some cases. The other alternative would be to just adjust the compiler to produce statements instead of expressions, but this would be a non-trivial change and I'd rather avoid it if possible.

Is there any way to declare and assign local variables to JavaScript as an expression rather than a statement that has no extra runtime costs?

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • A true lambda version would be `return (y => y)(x * x);` This is probably also much easier to postprocess for simplification. – Bergi Jun 01 '20 at 20:12
  • 1
    why wouldn't it just be `function foo (x) { return x*x }` – epascarello Jun 01 '20 at 20:12
  • This is a great alternative, but also has significant runtime costs, sadly, specially in very numeric code. I'm not sure why JS runtimes can't optimize it away as it is just a single inline step, but that's how it is. – MaiaVictor Jun 01 '20 at 20:13
  • 1
    [How to simulate let expressions in JavaScript?](https://stackoverflow.com/q/51407631/1048572) – Bergi Jun 01 '20 at 20:15
  • @epascarello consider a function like `pow2(x) { return (let y = pow2(x-1); x === 0 ? 1 : y + y); }`. If we just removed the `let` as you suggest, i would become `pow2(x) { return x === 0 ? 1 : pow2(x-1) + pow2(x-1); }`. Notice that the first function is `O(n)`, and the second is `O(2^n)`. Doing that could result in exponential slowdowns in some cases. – MaiaVictor Jun 01 '20 at 20:16
  • @Bergi interesting thread but the suggested solutions are my examples of incorrect solutions (since the extra lambda makes the program slower in most modern runtimes). – MaiaVictor Jun 01 '20 at 20:18

3 Answers3

1

No, there is not.

You should consider using the standard lambda calculus technique where let x = … in … is equivalent to (x => …)(…). Then have another pass of your compiler remove the superfluous function expressions, introducing statements where possible.

An alternative might be to use the do { let x = …; … } syntax from the do expressions proposal, allowing you to use their transpiler plugin for generating function-less code.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

You should avoid creating new variables and just return the value directly:

function foo(x) {
  return  x * x;
}

But if you really need to inline a let, this should work:

const foo = (x) => {let y=x*x;return y;}
Chewy
  • 111
  • 1
  • 6
0

Is there any way to declare and assign local variables to JavaScript as an expression rather than a statement that has no extra runtime costs?

Not really, but you can use eval() with the comma operator. I know, I know, eval() is evil, but it's the only way to declare local variables in an expression (AFAIK), though I wouldn't recommend it because engines don't optimize eval() calls.

function foo(x) {
  return (eval("var y"), y = x * x, y);
}

Edit: As Bergi pointed out, using let makes the variable local to the code inside executed using eval(), causing y = x * x to create a global variable named y. var should be used instead.

D. Pardal
  • 6,173
  • 1
  • 17
  • 37
  • Hey, this *is* an interesting solution that i didn't think of. Of course this doesn't solve the problem as an extra function call would be massively faster than invoking `eval` on each `let`, but you get the points for the creativity. – MaiaVictor Jun 01 '20 at 20:21
  • Actually, this does declare a global variable (for some reason that I don't quite understand yet), and it definitely will fail in strict mode where `let`-declared variables are subject to a local scope inside the `eval`ed expression. – Bergi Jun 01 '20 at 20:22
  • @Bergi I found the problem and edited my answer. Thanks. – D. Pardal Jun 01 '20 at 20:31
  • @D.Pardal Oh, of course, it's not the `let y` getting evaled globally that creates the global variable but rather the `y = …` assignment in sloppy mode. Thanks for the update. Notice however that even `var` doesn't work in strict mode. – Bergi Jun 01 '20 at 21:04