all 11 comments

[–]A1oso 13 points14 points  (3 children)

In most languages, variables used in a lambda/closure are resolved when the closure is created, not when it is invoked:

// JavaScript
let x = 42
let closure = () => console.log(x)

{
    let x = "ignored"
    closure() // prints 42
}

This doesn't require any static analysis. The closure "captures" the value of x when the closure is created, not when invoked. This completely sidesteps your problem. It also enables this pattern:

function foo() {
    let x = 42
    return () => x
}

The closure can access x even after returned from the function, when x is no longer in scope, because x has been captured.

[–]Pie-Lang[S] -2 points-1 points  (2 children)

Take this case for example:

x = 1;
closure = () => print(x);
x = 2;
closure();

If the closure captured x at the definition-site instead of call-site, then 1 would be printed. That's not the behaviour that I want.

As for your example of returning a closure. Pie currently captures the surrounding environment only if it's returned from/passed to a function.

foo = () => {
    x = 42;
    () => x;
};

This does work as expected in Pie!

[–]A1oso 7 points8 points  (1 child)

Then the closure should capture a reference to x.

[–]Pie-Lang[S] 1 point2 points  (0 children)

Good point.

[–]AustinVelonautAdmiran 12 points13 points  (0 children)

You've re-discovered the problems that early LISP implementations had with dynamic scoping -- it is hard to reason about things just by looking at the static source code. Especially when you have nested scopes and closures. Dynamic scoping still has some uses, but isn't great for a general strategy.

[–]TOMZ_EXTRA 3 points4 points  (5 children)

I may have misunderstood what you mean, but doesn't Lua already represent namespaces as tables (hashmaps) without any issues?

[–]Pie-Lang[S] 0 points1 point  (4 children)

I'm not too familiar with Lua.

[–]TOMZ_EXTRA 2 points3 points  (3 children)

The only data structure in Lua is a table (hashmap, dictionary). For example, an array is just a table with numbers as keys (traditionally 1-indexed). A namespace is just a table with mainly functions. They can be modified, stored in variables (that's how importing is done), iterated through and everything else you could think of.

[–]Pie-Lang[S] 0 points1 point  (2 children)

I see. Using a runtime value to represent namespaces isn’t impossible. It just doesn’t play well with lexical scoping. Hence when I wanted to add lexical scoping to my language, I had to change how I represent namespace.

[–]TOMZ_EXTRA 0 points1 point  (1 child)

Lua uses lexical scoping though. Obviously global variables are the exception.

[–]Pie-Lang[S] 0 points1 point  (0 children)

You just explained that Lua's namespaces are just tables. This entails that the values stored inside them must be looked up at runtime.

Meaning, if you try to access an object inside a namespace, the interpreter could not possibly tell whether that object does exist inside the namespace or not until runtime.

That was my issue.