• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

SOLVED Closure issue

gnysek

Member
I was playing a little with functions today, and I wanted to reproduce same code from JS into GML.

So, first, JS code:

JavaScript:
function test(a) {
        let b = a;
        return function(c) {
                return b + c;
        }
}
var t1 = test(1);
var t2 = test(2);
console.log(t1(10)); //returns 11
console.log(t2(10)); //returns 12
so in GML:

GML:
function test(a) {
    b = a;
    return function(c) {
        return self.b + c;
    }
}

var t1 = test(1);
var t2 = test(2);

show_debug_message( t1(10) ); // shows 12
show_debug_message( t2(10) ); // shows 12
So it seems that b becames available in instance scope, and I can't do same. However, changing function test to:

GML:
    function test(a) {
        return method({b: a}, function(c) {
            return self.b + c;
        });
    }
Seems to work OK.

My question is - is this a bug, that in first case it doesn't work, am I missing something, or I'm just wrongly assume that closures should work in GML similar to other languages?
 
Last edited:

Roldy

Member
I was playing a little with functions today, and I wanted to reproduce same code from JS into GML.

So, first, JS code:

JavaScript:
function test(a) {
        let b = a;
        return function(c) {
                return b + c;
        }
}
var t1 = test(1);
var t2 = test(2);
console.log(t1(10)); //returns 11
console.log(t2(10)); //returns 12
so in GML:

Code:
function test(a) {
    b = a;
    return function(c) {
        return self.b + c;
    }
}

var t1 = test(1);
var t2 = test(2);

show_debug_message( t1(10) ); // shows 12
show_debug_message( t2(10) ); // shows 12
So it seems that b becames available in instance scope, and I can't do same. However, changing function test to:

GML:
    function test(a) {
        return method({b: a}, function(c) {
            return self.b + c;
        });
    }
Seems to work OK.

My question is - is this a bug, that in first case it doesn't work, am I missing something, or I'm just wrongly assume that closures should work in GML similar to other languages?
There is no bug. Before you proceed, Explain to yourself what is occurring in the first GML example, it should be obvious. Once you understand it, then understand the difference between lambdas, closures, functors and templates, and classify which of those best describes your second GML example.

Then please decide to not pursue any of it in GML ;)
 

Alice

Darts addict
Forum Staff
Moderator
From manual on method variables:
NOTE: While the variable will be in the chosen scope, the actual function will be bound to the scope that it was initially defined in. For example, script functions are all global scope and "unbound" (ie: they are not associated with any instances), but if you have a script function that creates another function as a method variable within it and then you call this script function from an instance, the function used within the script will be bound to the instance variable as a method. In general this is not something you ever need to think about but for more complex operations with method variables it's worth taking into consideration. This also applies when using other constructs like with - when you create a method variable inside a with, the function will be bound to the instance that is currently in scope.
So what happens in your first case:
- we define script function test(a), which returns a method variable
- from some context we execute var t1 = test(1);, which sets the context variable b to 1 and returns a method bound to this context
- from the same context we execute var t2 = test(2);, which sets the context variable b to 2 and returns (same? another?) method bound to this context
- we call t1(10) which returns b (currently: 2) + 10, 12 in total
- we call t2(10) which returns b (still 2) + 10, 12 in total

This is perfectly in line with what the manual says - the variable "b" set in the test(a) script function belongs to the context of the function calling the script. Both t1 and t2 are bound to the same context, using the same "b" variable.
Only specifically adding this "closure struct" - as you do in your second example - results in creating methods bound to different contexts with different values of "b".
 

gnysek

Member
Then please decide to not pursue any of it in GML ;)
Yeah, I thought I can do that if GML started to remind more advanced languages...

OK, so as I understand the only way to have a function with it's own scope variables, is to use method(). Otherwise, function will always take values from instance scope.
Also, while in JavaScript I can use var/let and it will be passed to functions created in same scope as local variable, in GML var won't be passed/visible to newly created methods at all, and that's why this trick doesn't work (however, in GML var is passed when used in with() ). Usage of var in JS example is crucial difference between those two examples from what I see.
 

Roldy

Member
Yeah, I thought I can do that if GML started to remind more advanced languages...

OK, so as I understand the only way to have a function with it's own scope variables, is to use method(). Otherwise, function will always take values from instance scope.
Also, while in JavaScript I can use var/let and it will be passed to functions created in same scope as local variable, in GML it won't be passed/visible at all, and that's why this trick doesn't work (however, in GML var is passed when used in with() ). Usage of var in JS example is crucial difference.
You can definitely do all sorts of things with GML (anything really). And there is nothing stopping you from attempting it., or implementing language X like features into GML in some kludgey way. But its a situation of 'right tool for the job.' Can you hammer with a screwdriver? Sure, but its not ideal.

Software engineering is mostly writing. And like all writing it is best when it is clear, concise and meaning/intent are conveyed effortlessly. Anytime you write something, then read it back (now or six months from now) and you have to scratch your head to figure out what is going on it is a good sign you are abusing the language or are confused about what you are trying to do.

Additionally, the fact you thought there may be a bug in the first GML snippet and its output, suggest you are maybe getting confused about how things are working. Slow down, get a solid foundation, try to make a game. If you are still bored after that, then go all in making things overly complicated or fancy architectured systems.

IMO, simpler is better. Even if it means you have to type more.
 

gnysek

Member
get a solid foundation, try to make a game
I don't get that sense of humour. Your whole post is not helping, or bringing any new light into this topic. I would even take it somewhere between little offensive and "random chinese fortune cookie" as for someone who is programming for 18 years now in at least 5 languages, and after asking a question that novice would never ask, I'm getting proposals of "getting solid foundation", and "typing more"...
---
I've asked moderators to move this topic into Programming, as this indeed isn't a bug then, so don't report it too :)
 
Top