Meta Tricks with user data

I just wanted to throw up this post to bring attention to @davebollinger’s great post on saving data with metatables. As Dave mentions, this may be old hat to most, but for newer and novice devs, this is great information that helps optimize our apps.

http://davebollinger.org/corona-quick-thought-meta-tricks-example-user-data/

Don’t get me wrong, but what’s the advantage of using metatables, compared to a simple table with two subtables, one that will be saved, and the other that won’t be? For example
 

myDataTable = {}; --load the perm for a function that loads a table from a json file, returns nil if no file exists myDataTable.perm = loadFromJson() or {}; myDataTable.temp = {}; myDataTable.perm.sampleVariable = "hello"; --save it with a function that simply outputs to json the content of a table saveToJson(myDataTable.perm);

Just really trying to see if there’s a particular benefit using one way or the other. Internally, the metatable version seems to work exactly the same (with 2 tables), except that it requires a bit of initial planning while initiating it. The version above requires very few lines of code and no planning. Again, not saying one is better than the other, but just really trying to understand if a “loss” in simplicity gives actual benefits I’m missing.

granted.  i tried to acknowledge/disclaim all of those points, in both the text and code itself.

cuz isn’t it even easier to just do

local perm = loadjson()

local temp = { foo=“bar” }

that is, one could legitimately ask why have a wrapper *at all*??  (because the point was to demo the proxy technique, not necessarily a practical usage, and certainly not the easier/most-straightforward way to do something equivalent)

one reason you might choose to proxy a table is if you wished to enforce the structure defined by the prototype.  as long as the wrapper doesn’t itself contain those keys, then all access will go through the metamethods, meaning you can detect access of a not-yet-defined property, and act accordingly.  your on-the-fly “sampleVariable” for example, could be prevented.  (as written, it instead creates an on-the-fly instance property)

maybe just to prevent typos – say you prototyped it with { knownprop=123 }, you could raise an error on a reference to proxy.knownProp=234 (camelCased) instead of allowing Lua to create a brand new accidental property for you.  (in fact, really the only reason it thought userdata *might* make a suitable example is that often you’d like your savegame format to be rigorously defined, fe to more easily detect errors on load, not that it’s simpler or easier to use or better or even reasonable)

hth

The idea of the wrapper is absolutely fine. We use a wrapper in our products as well.
I was more curious about going for it with metatables, but I now understand that it was meant more as an example of what you could use them for, rather than it having actual benefits compared to other solutions. Given the nature of LUA, we usually try to simplify things down as much as possible and to not overcomplicate things (for example by trying to replicate other languages standards with it) as long as there’s no compromises, that’s why I felt like asking, as maybe we were missing on something.

Thank you for your exhaustive answer.

  fwiw:  i suspect the vast majority of Corona users don’t even know meta exists, much less what it is or when/why they’d benefit from its use.  nor do they need to… probably.  (and if they do, probably the first most likely use is diy oop) 

  and i’m with ya that complexity should dictate solution.  fe a 1kloc flappy clone might not benefit from anything beyond the simplest possible implementation.  (not necessarily “sloppy”, just not complex-beyond-need)  but Lua’s flexibility is also its downfall, sooo many bugs exist just cuz of simple typos.  these aren’t *syntax* errors, but don’t have the intended effect.  (heck, every other day or so there’s a bit of code posted to the forum with some obscure bug from an unintended nil somewhere) classic example:

local function doSomethingConditionally(aCamelCaseBoolean)   if (aCamelcaseBoolean) then     -- will never occur   else     -- always end up here   end end

(btw, that particular error could have been easily caught by locking on-the-fly access to _G … via meta, of course)

  on a more ambitious project, the devs might appreciate a bit more rigorous approach, and desire (fe…) proper inheritance that works the way they’ve learned elsewhere (or prototyping, or yet-another-object-oriented approach, depending on background), or “typed” (or at least predeclared) variables, or overloaded operands (fe add in a vector class?), or etc (pick your fav modern programming feature). and Lua can accommodate most such requests, and there are plenty of third-party middlewares (middleclass, fe) to do the oop stuff, and so on, but for some stuff they’d likely be on their own and have to diy, so worth learning.

  but, it’s just another tool in an arsenal, and getting overly-clever just for the sake of being clever serves no purpose.  (which I assume is what it appears i was doing, but perhaps just because i picked an easily-refuted example case to demo the technique)

Just to add on to this as well, while there are other ways to accomplish the example above, I thought it would be an interesting experiment Develephant’s tutorial to play nice with JSON. I was having some difficulty and lamenting the fact on twitter, and Dave took pity on me, kind soul that he is.

The original thought behind my experimenting with metatable data persistence was from digging in the tutorial crates on Develephant’s excellent series on metatables and RPG classes. It’s a great resource, but I got to thinking, what good is RPG-style info (hit points, experience, money, yadda yadda) without it being saved from session to session. I normally just use some simple JSON table functions to save, load, and create new fields in my other games, but, as Dave points out above, what happens when that particular field doesn’t exist and you yearn for the ease of a metatable lookup. It might not be the most engrossing application of metatables, but still good to have a proof of concept, and I wanted Dave’s work to be acknowledged.

Thanks again, Dave!

Don’t get me wrong, but what’s the advantage of using metatables, compared to a simple table with two subtables, one that will be saved, and the other that won’t be? For example
 

myDataTable = {}; --load the perm for a function that loads a table from a json file, returns nil if no file exists myDataTable.perm = loadFromJson() or {}; myDataTable.temp = {}; myDataTable.perm.sampleVariable = "hello"; --save it with a function that simply outputs to json the content of a table saveToJson(myDataTable.perm);

Just really trying to see if there’s a particular benefit using one way or the other. Internally, the metatable version seems to work exactly the same (with 2 tables), except that it requires a bit of initial planning while initiating it. The version above requires very few lines of code and no planning. Again, not saying one is better than the other, but just really trying to understand if a “loss” in simplicity gives actual benefits I’m missing.

granted.  i tried to acknowledge/disclaim all of those points, in both the text and code itself.

cuz isn’t it even easier to just do

local perm = loadjson()

local temp = { foo=“bar” }

that is, one could legitimately ask why have a wrapper *at all*??  (because the point was to demo the proxy technique, not necessarily a practical usage, and certainly not the easier/most-straightforward way to do something equivalent)

one reason you might choose to proxy a table is if you wished to enforce the structure defined by the prototype.  as long as the wrapper doesn’t itself contain those keys, then all access will go through the metamethods, meaning you can detect access of a not-yet-defined property, and act accordingly.  your on-the-fly “sampleVariable” for example, could be prevented.  (as written, it instead creates an on-the-fly instance property)

maybe just to prevent typos – say you prototyped it with { knownprop=123 }, you could raise an error on a reference to proxy.knownProp=234 (camelCased) instead of allowing Lua to create a brand new accidental property for you.  (in fact, really the only reason it thought userdata *might* make a suitable example is that often you’d like your savegame format to be rigorously defined, fe to more easily detect errors on load, not that it’s simpler or easier to use or better or even reasonable)

hth

The idea of the wrapper is absolutely fine. We use a wrapper in our products as well.
I was more curious about going for it with metatables, but I now understand that it was meant more as an example of what you could use them for, rather than it having actual benefits compared to other solutions. Given the nature of LUA, we usually try to simplify things down as much as possible and to not overcomplicate things (for example by trying to replicate other languages standards with it) as long as there’s no compromises, that’s why I felt like asking, as maybe we were missing on something.

Thank you for your exhaustive answer.

  fwiw:  i suspect the vast majority of Corona users don’t even know meta exists, much less what it is or when/why they’d benefit from its use.  nor do they need to… probably.  (and if they do, probably the first most likely use is diy oop) 

  and i’m with ya that complexity should dictate solution.  fe a 1kloc flappy clone might not benefit from anything beyond the simplest possible implementation.  (not necessarily “sloppy”, just not complex-beyond-need)  but Lua’s flexibility is also its downfall, sooo many bugs exist just cuz of simple typos.  these aren’t *syntax* errors, but don’t have the intended effect.  (heck, every other day or so there’s a bit of code posted to the forum with some obscure bug from an unintended nil somewhere) classic example:

local function doSomethingConditionally(aCamelCaseBoolean)   if (aCamelcaseBoolean) then     -- will never occur   else     -- always end up here   end end

(btw, that particular error could have been easily caught by locking on-the-fly access to _G … via meta, of course)

  on a more ambitious project, the devs might appreciate a bit more rigorous approach, and desire (fe…) proper inheritance that works the way they’ve learned elsewhere (or prototyping, or yet-another-object-oriented approach, depending on background), or “typed” (or at least predeclared) variables, or overloaded operands (fe add in a vector class?), or etc (pick your fav modern programming feature). and Lua can accommodate most such requests, and there are plenty of third-party middlewares (middleclass, fe) to do the oop stuff, and so on, but for some stuff they’d likely be on their own and have to diy, so worth learning.

  but, it’s just another tool in an arsenal, and getting overly-clever just for the sake of being clever serves no purpose.  (which I assume is what it appears i was doing, but perhaps just because i picked an easily-refuted example case to demo the technique)

Just to add on to this as well, while there are other ways to accomplish the example above, I thought it would be an interesting experiment Develephant’s tutorial to play nice with JSON. I was having some difficulty and lamenting the fact on twitter, and Dave took pity on me, kind soul that he is.

The original thought behind my experimenting with metatable data persistence was from digging in the tutorial crates on Develephant’s excellent series on metatables and RPG classes. It’s a great resource, but I got to thinking, what good is RPG-style info (hit points, experience, money, yadda yadda) without it being saved from session to session. I normally just use some simple JSON table functions to save, load, and create new fields in my other games, but, as Dave points out above, what happens when that particular field doesn’t exist and you yearn for the ease of a metatable lookup. It might not be the most engrossing application of metatables, but still good to have a proof of concept, and I wanted Dave’s work to be acknowledged.

Thanks again, Dave!