lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> On Tue, Dec 22, 2009 at 6:40 PM, Todd Berkebile
> wrote:
>> local pos = propBag.Position -- pos is now the proxy object wrapping the Point userdata
>> pos.z = 10 -- this will use the proxy's __newindex thus setting the value in propBag
>> otherBag.Position = pos
>> assert(propBag.Position ~= otherBag.Position) -- this would fail as both were modified
>>
>> With the above syntax I wouldn't want the original propBag to be modified.
>
> Why not? To me (and effectively to the Lua compiler), the following
> two bits of code are equivalent:
> ---------
> local temp = propBag.Position
> temp.z = 10
> ---------
> propBag.Position.z = 10
> ---------
> Hence in both cases, I would want the original propBag to be modified.

Hum, I guess there are three reasons I can think of, though one is perhaps mistaken. The perhaps mistaken reason is that it seems like the normal Lua behavior is for a stack variable to be a copy, not a reference. Consider the following code:
 
local a = 10
local b = a   -- 'b' is a new number with the same value as 'a'
b = b + 1     -- this only changes 'b', not 'a', because its a copy
 
Normal tables work the same way:
 
local a = { member=10 }
local b = a.member -- again 'b' is a copy, not a reference
b = 5  -- only 'b' is modified, not 'a'
 
As such, I would expect my userdata to work exactly the same as the plain Lua types and tables in the example above:
 
local a = GetMyUserData()
local b = a.member -- b is a new object with the same value as 'a.member'
b = 5   -- I expect this to only change 'b', not 'a'
 
I'm a C++ guy, not a Lua guy, so perhaps my expectations are incorrect due to language bias? Do userdata types normally act differently?
 
 
A better reason specific to my app is that setting properties in the bag is fairly expensive (changes trigger various callbacks in the C++ code) so if someone is doing ten steps to compute a new Position they should be able to generate just a single update. A manual ":clone()" function could certainly work around this problem, however my concern would be that isn't obvious to users when this is needed so they would tend to write very non-optimal scripts.
 
 
The final reason is that to have a consistent API I would end up needing to wrapper every type of property in order to perform the back assignment, even the properties that are plain Lua string or number types. Most properties are not compound values, they are simple strings or numbers. For example, if 'Count' returns a plain Lua number for consistency I'd still need the wrapper for cases like this:
 
  local c = propBag.Count -- 'c' is a proxy in order to implement reference semantics
  if type(c) == "number" then -- doesn't work anymore, type is always 'proxy'
    c = c + 5 -- proxy makes this back assignment update propBag
    print tostring(c + 5) -- ugh, c doesn't act like a number anymore
  end
  
If I make 'c' a proxy rather than just a number now it behaves very different, simple things like "c + 5" and "tostring(c)" now require complex metatables to make my proxy act like a number. If the returned value is only a proxy when the property type is a userdata then the API is inconsistent and confusing as compound types and simple types have completely different usage patterns.
 
 
It looks like the short answer is Lua just doesn't work this way. I can certainly understand that, I suspect it would take a significantly more complex parser to support the syntax I was hoping for and that would mean a slower parser.
 
 
-Todd