Okay, I've been browsing through others' code and there's a few things I keep seeing and I'm curious about:
[php]frame:SetScript("OnEvent",
function(self, event, ...)
if self[event] then
return self[event](self, event, ...)
end
end
)[/php]
I'm guessing that this allows frame:EVENT() type calls, but I thought that was available already natively. Or is that only available to code where AceAddon is used?
Also, I see a lot of instances where copies of variables are made for no *apparent* reason and it's confusing me. For example:
frame:EVENT() functions are default only in addons using AceEvent because that library created the same type of event handler/dispatcher.
The extra copies are a speed issue. Local lookups are faster than a table lookup. In your first case, addon = {} create a table at the global level (inside the global table), then local addon = addon create a variable at the local level that is a reference to the global variable, but is faster to access.
Same thing with the second one, create the frame locally, store a reference in the mod table (which is likely global, meaning two lookups), then use the local variable in all the accesses of the frame.
In both cases, the extra copy will still have all the data of the first variable because everything is references to the table. One table, but multiple ways to access it, the local method being faster. Its the same reason some addons have a huge section of "local someBlizzFunc = someBlizzFunc".
Okay, I've been browsing through others' code and there's a few things I keep seeing and I'm curious about:
[php]frame:SetScript("OnEvent",
function(self, event, ...)
if self[event] then
return self[event](self, event, ...)
end
end
)[/php]I'm guessing that this allows frame:EVENT() type calls, but I thought that was available already natively. Or is that only available to code where AceAddon is used?
The latter. And actually that's only where AceEvent is used. The code that AceEvent has to implement frame:EVENT() is essentially what you have shown there.
Also, I see a lot of instances where copies of variables are made for no *apparent* reason and it's confusing me. For example:
frame:RegisterEvent(...)[/php]In both examples, a "copy" is made.
Think of those as C pointers. The copy is of the pointer variable, not the table being pointed to. There is no implicit deep copy in Lua.
In the first example, I can see that whenever "addon" is referenced, it will always use the local copy.
Right. Local variables are very slightly faster to reference than global ones are. If you're going to be using a particular variable a lot in your code -- whether that variable refers to a table, a function, etc -- then it's worth making a local copy of that global variable. (Or more precisely, a local copy of that global reference-to-whatever.) The tiny speedups accumulate when those references are manipulated inside loops, event callbacks, etc.
In the second example, "mod.frame" becomes a copy, but is never called again. Instead, the local copy is used.
The local reference is used for the performance reasons above. But if the frame needs to be accessed by code which can't see the local variable, you'd be boned. That's why a copy of the "handle", so to speak, is kept around. Other code, running at some other point, can use mod.frame even though they couldn't see the other variables due to scoping rules.
That other code, if it were going to be doing a lot of operations with the frame, would probably start off with "local f = mod.frame", again for slight performance gains.
I think I understand now. Unlike, say, PHP for example, which actually makes a copy of the assigned variable, LUA actually references the original? IE:
And thanks for pointing out the speed benefits, etc. That helps. :D
The latter. And actually that's only where AceEvent is used. The code that AceEvent has to implement frame:EVENT() is essentially what you have shown there.
Wrong. print(vara) will still print "foo" in Lua. Each string is a unique object in memory.
vara = "foo" <-- assign object at memory locationX to vara
local varb = vara <--- copy the memory locationX to varb
varb = "bar" <--- assign the object at memory locationY to varb
print(vara) <-- prints "foo"
In Lua, tables, strings, functions are "objects" with unique memory addresses. They are pointers essentially. With regards to strings specifically, the same string is the same object. That is
local t = "abc"
local u = "abc"
t == u will return true, because they are both the same object. Lua is smart about strings like that, it checks to see if any string you create already exists, if it does, it points it to the already existing one.
To hammer this point home, every unique string is a unique immutable object. Strings in Lua are NOT like strings in C, where they are mutable and 2 variables pointing to the same string can get changed simultaneously. In Lua, if you do "local a = 'abc'..'def'" what this code really does is create 3 strings "abc" "def" and "abcdef" in memory, but the first 2 eventually gets garbage collected after they become unused and only "abcdefg" remains in memory.
In essence, tables, strings, functions in Lua are really 4-byte memory address pointers. Everything else are direct 4-byte values (number, boolean, nil).
Ok, now I'm back to lost. :p So what's the point of having a global if the local is faster? Because going by what you're saying, if one created a global object, then any methods, etc, that are defined within the local "copy" of that object wouldn't be available in the global object because of scoping rules.
I understand the reasoning behind creating a local copy of an existing global object, but not sure why you would create a global object and then immediately make a copy and start defining methods, etc within the local copy.
Debugging and standard common conventions.
It was decided a long time ago that nice addons should ONLY have 2 Global Variables, the Addon Object itself and the Saved Variable. So it's standard convention to have the main addon table a global. Thus the "addon = {}; local addon = addon" This places the object into the global space for debugging and sanity, often useful in multi-file addons.
As for local vs global, "_G.mod.frame = frame", the "frame" in the first part isn't "global". It's a sub key of a global table. Also used for sanity in debugging and that you don't want to loose your event frame. But that's common methods with frames. As for the rest it's just coding style. Think of Lua Objects as unique things you handle in the real world... now give these objects different names, then give them different names again, they are however still the same objects just with different names. (that's tables for ya).
A table ({}) is the only variable type that is passed by reference no matter what you're doing. Any other variables are always passed by value. The following are the same.
# PHP
$vara = array();
$varb =& $vara; # passed by reference.
$varb[1] = "blah";
echo $vara[1]; # prints "blah"
-- Lua
vara = {}; -- Lua's version of array()
varb = vara; -- passed by reference.
varb[1] = "blah";
print(vara[1]); -- prints "blah"
Ok, that's what I was asking. I was just using strings instead of tables for example. My bad. Sorry. >.< I should've just said, "Are tables/objects always passed by reference?" :p
So, effectively, the following would work:
Main mod:
[php]MyAddon = {} -- new object
local mod = MyAddon
mod.title = "My Add-On"
function mod.getTitle()
return self.title
end[/php]Some other mod:
[php]function getMATitle()
return MyAddon.getTitle()
end[/php]
Edit: Sorry for being such a pain, I just want to understand what's going on. Thanks for all the responses. :)
MyAddon = {} -- new object
local mod = MyAddon
mod.title = "My Add-On"
function mod.getTitle()
return self.title
end
Some other mod:
function getMATitle()
return MyAddon.getTitle()
end
Almost. The function would need to be "return mod.title" and then everything shown here would work.
The magic "self" only works when using OO-style function names, with ":" instead of "." in the function call. The equivalent would be:
MyAddon = {}
local mod = MyAddon
mod.title = "My Add-On"
function mod:getTitle() -- same as "mod.getTitle(self)"
return self.title
end
Some other mod:
function getMATitle()
return MyAddon:getTitle() -- same as "MyAddon.getTitle(MyAddon)"
end
The difference between dot-notation and colon-notation is purely syntactic sugar. You can even mix and match them, but for readability it's best to do all of one or all of the other.
[php]frame:SetScript("OnEvent",
function(self, event, ...)
if self[event] then
return self[event](self, event, ...)
end
end
)[/php]
I'm guessing that this allows frame:EVENT() type calls, but I thought that was available already natively. Or is that only available to code where AceAddon is used?
Also, I see a lot of instances where copies of variables are made for no *apparent* reason and it's confusing me. For example:
[php]addon = {}
local addon = addon[/php][php]local frame = CreateFrame("frame")
mod.frame = frame
frame:RegisterEvent(...)[/php]In both examples, a "copy" is made.
In the first example, I can see that whenever "addon" is referenced, it will always use the local copy.
In the second example, "mod.frame" becomes a copy, but is never called again. Instead, the local copy is used.
I don't get it. Is this some sort of error handling hack or something?
The extra copies are a speed issue. Local lookups are faster than a table lookup. In your first case, addon = {} create a table at the global level (inside the global table), then local addon = addon create a variable at the local level that is a reference to the global variable, but is faster to access.
Same thing with the second one, create the frame locally, store a reference in the mod table (which is likely global, meaning two lookups), then use the local variable in all the accesses of the frame.
In both cases, the extra copy will still have all the data of the first variable because everything is references to the table. One table, but multiple ways to access it, the local method being faster. Its the same reason some addons have a huge section of "local someBlizzFunc = someBlizzFunc".
The latter. And actually that's only where AceEvent is used. The code that AceEvent has to implement frame:EVENT() is essentially what you have shown there.
Think of those as C pointers. The copy is of the pointer variable, not the table being pointed to. There is no implicit deep copy in Lua.
Right. Local variables are very slightly faster to reference than global ones are. If you're going to be using a particular variable a lot in your code -- whether that variable refers to a table, a function, etc -- then it's worth making a local copy of that global variable. (Or more precisely, a local copy of that global reference-to-whatever.) The tiny speedups accumulate when those references are manipulated inside loops, event callbacks, etc.
The local reference is used for the performance reasons above. But if the frame needs to be accessed by code which can't see the local variable, you'd be boned. That's why a copy of the "handle", so to speak, is kept around. Other code, running at some other point, can use mod.frame even though they couldn't see the other variables due to scoping rules.
That other code, if it were going to be doing a lot of operations with the frame, would probably start off with "local f = mod.frame", again for slight performance gains.
Does that help?
PHP:
[php]vara = "foo";
varb = vara;
varb = "bar";
print(vara); // "foo"[/php]LUA:
[php]vara = "foo"
local varb = vara
varb = "bar"
print(vara) -- "bar"[/php]Right?
And thanks for pointing out the speed benefits, etc. That helps. :D
Ah, OK. Thanks! :D
vara = "foo" <-- assign object at memory locationX to vara
local varb = vara <--- copy the memory locationX to varb
varb = "bar" <--- assign the object at memory locationY to varb
print(vara) <-- prints "foo"
In Lua, tables, strings, functions are "objects" with unique memory addresses. They are pointers essentially. With regards to strings specifically, the same string is the same object. That is
local t = "abc"
local u = "abc"
t == u will return true, because they are both the same object. Lua is smart about strings like that, it checks to see if any string you create already exists, if it does, it points it to the already existing one.
To hammer this point home, every unique string is a unique immutable object. Strings in Lua are NOT like strings in C, where they are mutable and 2 variables pointing to the same string can get changed simultaneously. In Lua, if you do "local a = 'abc'..'def'" what this code really does is create 3 strings "abc" "def" and "abcdef" in memory, but the first 2 eventually gets garbage collected after they become unused and only "abcdefg" remains in memory.
In essence, tables, strings, functions in Lua are really 4-byte memory address pointers. Everything else are direct 4-byte values (number, boolean, nil).
I understand the reasoning behind creating a local copy of an existing global object, but not sure why you would create a global object and then immediately make a copy and start defining methods, etc within the local copy.
It was decided a long time ago that nice addons should ONLY have 2 Global Variables, the Addon Object itself and the Saved Variable. So it's standard convention to have the main addon table a global. Thus the "addon = {}; local addon = addon" This places the object into the global space for debugging and sanity, often useful in multi-file addons.
As for local vs global, "_G.mod.frame = frame", the "frame" in the first part isn't "global". It's a sub key of a global table. Also used for sanity in debugging and that you don't want to loose your event frame. But that's common methods with frames. As for the rest it's just coding style. Think of Lua Objects as unique things you handle in the real world... now give these objects different names, then give them different names again, they are however still the same objects just with different names. (that's tables for ya).
As for strings... sorry got nothing simple.
So, effectively, the following would work:
Main mod:
[php]MyAddon = {} -- new object
local mod = MyAddon
mod.title = "My Add-On"
function mod.getTitle()
return self.title
end[/php]Some other mod:
[php]function getMATitle()
return MyAddon.getTitle()
end[/php]
Edit: Sorry for being such a pain, I just want to understand what's going on. Thanks for all the responses. :)
Almost. The function would need to be "return mod.title" and then everything shown here would work.
The magic "self" only works when using OO-style function names, with ":" instead of "." in the function call. The equivalent would be:
Some other mod:
The difference between dot-notation and colon-notation is purely syntactic sugar. You can even mix and match them, but for readability it's best to do all of one or all of the other.
or even
do the same
the only "rule" really is that, if you call ANY function in "object:method"-style it's first parameter will be "object".
If you wanna do
and then call it with
it'll STILL work fine