[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Strict 'struct' pattern
- From: steve donovan <steve.j.donovan@...>
- Date: Fri, 7 Aug 2009 13:59:51 +0200
Hi all,
We all know that global variables can be a pain [1] and should be
avoided. The 'strict struct' pattern brings these benefits to tables
with named keys
A 'struct' can be declared so:
struct.Alice {
x = 1;
y = 2;
}
And instantiated like so:
a = Alice {x = 10, y = 20}
or
b = Alice {x = 10} -- y will be set to 2
Any attempt to access an unknown field of a and b will be an error,
like a.z = 1 or print(b.zz), or even Alice{z = 4}.
So this brings two things to the party:
(1) typos in fieldnames are errors, not silent problems.
(2) such tables now have an _identity_, and this in particular helps
when trying to write more self-documenting code. In LuaDoc, you can
then confidently give the type of a parameter as Alice, rather than 'a
table with x and y being numbers'
Stronger typing also means that type-specific assertions can be thrown.
A simple overload of __tostring would also give you type-specific
string representations like 'Alice #23' for debugging purposes.
It would be possible (using a suitable proxy table) to enforce dynamic
type checking on field assignments, but of course this would incur a
run-time cost.
---------------------------------
-- struct.lua
--- defining a struct constructor ---
local struct_mt = {
-- instances can be created by calling the struct object
__call = function(s,t)
local obj = t or {} -- pass it a table (or nothing)
local fields = s._fields
-- attempt to set a non-existent field in ctor?
for k,v in pairs(obj) do
if not fields[k] then
s._error_nf(nil,k)
end
end
-- fill in any default values if not supplied
for k,v in pairs(fields) do
if not obj[k] then
obj[k] = v
end
end
setmetatable(obj,s._mt)
return obj
end;
}
-- creating a new struct triggered by struct.STRUCTNAME
struct = setmetatable({},{
__index = function(tbl,sname)
-- so we create a new struct object with a name
local s = {_name = sname}
-- and put the struct in the enclosing context
_G[sname] = s
-- the not-found error
s._error_nf = function (tbl,key)
error("field '"..key.."' is not in "..s._name)
end
-- reading or writing an undefined field of this struct is an error
s._mt = {
_name = s._name;
__index = s._error_nf;
__newindex = s._error_nf;
}
-- the struct has a ctor
setmetatable(s,struct_mt)
-- return a function that sets the struct's fields
return function(t)
s._fields = t
end
end
})
-----------------------
[1] http://lua-users.org/wiki/DetectingUndefinedVariables
steve d.