minetest.after and variable scope

Post Reply
User avatar
runs
Member
Posts: 3225
Joined: Sat Oct 27, 2018 08:32

minetest.after and variable scope

by runs » Post

What's the difference between

Code: Select all

local pos = {x=1,y=2,z=3}
local str = "hello world"
minetest.after(3.5, function(pos, str) 
    print("3.5 seconds later, we can do " .. str .. " at " .. minetest.pos_to_string(pos))
end, pos, str)
and

Code: Select all

local pos = {x=1,y=2,z=3}
local str = "hello world"
minetest.after(3.5, function() 
    print("3.5 seconds later, we can do " .. str .. " at " .. minetest.pos_to_string(pos))
end)
or even

Code: Select all

local pos = {x=1,y=2,z=3}
local str = "hello world"
minetest.after(3.5, function() 
    print("3.5 seconds later, we can do " .. str .. " at " .. minetest.pos_to_string(pos))
end, pos, str)
?
Both tree work the same.

But sometimes I have issues with minetest.after when the vars are passed and, before the after function is run, they changed.

I mean, in the example above, if the var pos i.e changed to another value, what is the printed value, the new or the old (passed) one? What are passed to minetest.after, the vars or the values?

User avatar
v-rob
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: minetest.after and variable scope

by v-rob » Post

The second method uses upvalues, so the function must be defined in the scope that the variables "pos" and "str" are in. Changes to the variables will be reflected inside "minetest.after". The first method passes the variables to the function explicitly. This could be useful if the function is defined outside the scope of the variables. However, the variables passed in this first method are copies (unless they are tables), so if they change, "minetest.after" will not see the change since it has copies. The third method makes no sense; the extra arguments to "minetest.after" do absolutely nothing.

I would recommend the second method in most cases. I've never used the first method myself, and everything the first method can do the second method can also do.
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: minetest.after and variable scope

by rubenwardy » Post

runs wrote:
Sat Feb 20, 2021 15:52

Code: Select all

local pos = {x=1,y=2,z=3}
local str = "hello world"
minetest.after(3.5, function(pos, str) 
    print("3.5 seconds later, we can do " .. str .. " at " .. minetest.pos_to_string(pos))
end, pos, str)
This is a weird way of writing it, don't do this. You're shadowing variables - redeclaring them and hiding the original - and it's a pointless repeat. In this particular version, I'd recommend the second one. You're not changing the variables afterwards, and it's nicer to write.

The first way is pointless in this situation. However, it might be useful in others.

For example, it allows you to pass in arguments to a function without using upvalues (locals above the function definition). You can send a chat message after a certain time like so:

Code: Select all

minetest.after(10, minetest.chat_send_all, "It's been 10 seconds!")
Or you could use it to pass in copies
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
v-rob
Developer
Posts: 970
Joined: Thu Mar 24, 2016 03:19
GitHub: v-rob
IRC: v-rob
Location: Right behind you.

Re: minetest.after and variable scope

by v-rob » Post

rubenwardy wrote:
Sun Feb 21, 2021 03:43
The first and second are exactly the same
Not quite true. The first copies the variables, while the second has upvalues. Therefore, the second will reflect any changes to the variables, while the first will only show what the variables were like at the point of calling minetest.after.
Core Developer | My Best Mods: Bridger - Slats - Stained Glass

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: minetest.after and variable scope

by rubenwardy » Post

v-rob wrote:
Sun Feb 21, 2021 04:48
rubenwardy wrote:
Sun Feb 21, 2021 03:43
The first and second are exactly the same
Not quite true. The first copies the variables, while the second has upvalues. Therefore, the second will reflect any changes to the variables, while the first will only show what the variables were like at the point of calling minetest.after.
They're both the same as they both use upvalues - they are missing arguments in the definition of the functions

It also doesn't necessarily copy, if you change the position table it'll still change in the function. It reassigns, which happens to copy primatives

Edit: I've just noticed that I missed a code snippet, I was talking about the second and third. I've edited my earlier reply
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
runs
Member
Posts: 3225
Joined: Sat Oct 27, 2018 08:32

Re: minetest.after and variable scope

by runs » Post

THanks to all, very aclarative :-)

User avatar
Hybrid Dog
Member
Posts: 2828
Joined: Thu Nov 01, 2012 12:46
GitHub: HybridDog

Re: minetest.after and variable scope

by Hybrid Dog » Post

Here's a comparison of the byte codes shown by luajit -bl.

first:

Code: Select all

-- BYTECODE -- first.lua:3-5
0001    GGET     2   0      ; "print"
0002    KSTR     4   1      ; "3.5 seconds later, we can do "
0003    MOV      5   1
0004    KSTR     6   2      ; " at "
0005    GGET     7   3      ; "minetest"
0006    TGETS    7   7   4  ; "pos_to_string"
0007    MOV      9   0
0008    CALL     7   2   2
0009    CAT      4   4   7
0010    CALL     2   1   2
0011    RET0     0   1

-- BYTECODE -- first.lua:0-6
0001    TDUP     0   0
0002    KSTR     1   1      ; "hello world"
0003    GGET     2   2      ; "minetest"
0004    TGETS    2   2   3  ; "after"
0005    KNUM     4   0      ; 3.5
0006    FNEW     5   4      ; first.lua:3
0007    MOV      6   0
0008    MOV      7   1
0009    CALL     2   1   5
0010    RET0     0   1
second:

Code: Select all

-- BYTECODE -- second.lua:3-5
0001    GGET     0   0      ; "print"
0002    KSTR     2   1      ; "3.5 seconds later, we can do "
0003    UGET     3   0      ; str
0004    KSTR     4   2      ; " at "
0005    GGET     5   3      ; "minetest"
0006    TGETS    5   5   4  ; "pos_to_string"
0007    UGET     7   1      ; pos
0008    CALL     5   2   2
0009    CAT      2   2   5
0010    CALL     0   1   2
0011    RET0     0   1

-- BYTECODE -- second.lua:0-6
0001    TDUP     0   0
0002    KSTR     1   1      ; "hello world"
0003    GGET     2   2      ; "minetest"
0004    TGETS    2   2   3  ; "after"
0005    KNUM     4   0      ; 3.5
0006    FNEW     5   4      ; second.lua:3
0007    CALL     2   1   3
0008    UCLO     0 => 0009
0009 => RET0     0   1
third:

Code: Select all

-- BYTECODE -- third.lua:3-5
0001    GGET     0   0      ; "print"
0002    KSTR     2   1      ; "3.5 seconds later, we can do "
0003    UGET     3   0      ; str
0004    KSTR     4   2      ; " at "
0005    GGET     5   3      ; "minetest"
0006    TGETS    5   5   4  ; "pos_to_string"
0007    UGET     7   1      ; pos
0008    CALL     5   2   2
0009    CAT      2   2   5
0010    CALL     0   1   2
0011    RET0     0   1

-- BYTECODE -- third.lua:0-6
0001    TDUP     0   0
0002    KSTR     1   1      ; "hello world"
0003    GGET     2   2      ; "minetest"
0004    TGETS    2   2   3  ; "after"
0005    KNUM     4   0      ; 3.5
0006    FNEW     5   4      ; third.lua:3
0007    MOV      6   0
0008    MOV      7   1
0009    CALL     2   1   5
0010    UCLO     0 => 0011
0011 => RET0     0   1

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪

User avatar
Desour
Member
Posts: 1469
Joined: Thu Jun 19, 2014 19:49
GitHub: Desour
IRC: Desour
In-game: DS
Location: I'm scared that if this is too exact, I will be unable to use my keyboard.

Re: minetest.after and variable scope

by Desour » Post

I'd like to add that creating a closure is a NYI (see http://wiki.luajit.org/NYI#bytecode). So the first version might be able to be a little bit faster. But this probably also depends on the minetest.after implementation, and you probably have to define your function somewhere else so you don't create a new function every time.
he/him; Codeberg; GitHub; ContentDB; public personal TODO list; "DS" is preferred (but often too short)

Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests