What do you think?
Doing testing on closures. This also makes my head hurt.
What do you think this will print?
local t = {
bar = 100
f = function() {
print(this.bar)
}
}
t2 <- {
bar = 200
}
t2.f <- t.f
local r = {
bar = 1000
function do_call(f) {
f()
}
}
r.do_call(t2.f)
Now with Answers
Well, not answers in the definitive truth of the universe way...
If you compile and run this code in Squirrel, the answer is 1000. This surprised me a little. I was expecting 200, but would have taken 100. As I build this new compiler, being some-what compatible with Squirrel is important, since I have a shit-lot of code already written in Squirrel that needs to run in the new compiler.
My new language (called Dinky, btw) is about 90% syntactically compatible with Squirrel, but subtile functionality like what 'this' means might be more important since it can fundamentally change the nature of the game's scripting code I've already written.
I don't think I've ever written anything as convoluted as the last function call shown, so it might not be important to adhere to, and instead treat 'this' more conventionally. I do wish I knew what the philosophy behind Squirrel's notion of 'this' is. I'm hesitant to just change it and miss some genius buried in why it works that way it does.
Currently my compiler and interrupter produces the same output as Squirrel and I'll probably stay with that until I understand the 'why' a little better.
I've spent three weeks on the new compiler and am now ready to move it over the my new game and start using it. I figure it will take a good part of this week to get the game fully functional under the new compiler and back to where I was with Squirrel.
Anyway, if t2.f<- t.f is allowed, then it should print 100
Alternatively, it could be going "this" as sort an implicit parameter of its execution (in method form, say), like python and javascript-this (but not javascript closures in general). If that is the case, I would either expect it be unable to resolve "this" (because it was called as a free function), or to return 200 if the expression "t2.f" implicitly returns the function bound to t2.
I think it largely depends on what sorts of patterns you are trying to support.
The actual function you're calling is on the t2 object, where bar==200.
I first thought it would be 100 but the way it appears you want the language to work seems to me to be that in the 'local' calls, you're both defining and instantiating an object.
So then when you define the f function on the t2 class which is also instantiated, you're actually making a copy of the function into the t2 object.
So then when you call it the instance that t2.f will see will be the one where bar==200.
Closures shouldn't be treated as being part of the class/object they are declared in and not magically inherit any variables unles specifically declared using a ‘use' or ‘.bind()'
I would have guessed 100, but If I convert this to JS, I get an undefined.
Why should the 'this' keyword point to the scope of the function? Should it not refer to the scope of the enclosing object like it does in other languages?
BTW why did you name it "Dinky"?

Object R has bar set to 1000, so if the function is print(this.bar) it seems sensible to me that it returns the bar of the current object, hence it returns 1000. It just takes the function from another object,rather than the object itself it would not take that bar along.
I interpret it as a very picky inheritance.
Even if it would have been:
local r(t) = {
bar = 1000
function do_call(f) {
f()
}
}
where (t) indicates inheritance it would return 1000, as bar is overwritten after the init, without the bar=1000, it would now return 100, as it is assigned in t.
Finally without inheriting from t and without assigning bar, I'd expect an error, because there is no bar assigned.
local r = {
function do_call(f) {
f()
}
}
but, by reading the Squirrel documentation, it seems every function in Squirrel has an implicit "this" parameter, and every direct function call passes its "this" implicitely:
explicit: obj.f() <--> f(obj)
implicit: f() <--> this.f() <--> f(this).
So f is like a static function, and r is calling it passing its "this" value as first parameter.
It's not a real closure, because "this" in such sense is just a function parameter, not a free variable.
Thus,
t2.f <- t.f
is just assigning a function pointer, it's not storing a <faddress, functionaddress> pair,
neither is compiling a generated class (as it would be in C#).
It seems you have to use explicitely a function called bindenv() when you want that "faddress" is used for "this" when the function is called.
Actually, I like it, once you've got that by default what "this" is passed it's decided by the caller, the results are quite predictable. While if you want to implement more complex metaprogramming stuff you must explicitely use bindenv().
Click this https://games.lol/brain/
t = {
bar : 100,
f : function() {
console.log("The answer: " + this.bar)
}
}
t2 = {
bar : 200
}
t2.f = t.f
r = {
bar : 1000,
do_call: function (f) {
f()
}
}
r.do_call(t2.f) // prints undefined
t2.f() // prints 200
I guess it doesn't handle closures that well.
Since the function is in "t", that means that "this" is interpret from "r". It's dynamic. I like the languages that do this stuff.
This seems like the least surprising choice.
Though you could go for a dynamic scope and print 1000 because it's called on r,
but that's a lot harder to reason about when reading the code.