Strict Structs

lua-users home
wiki

We all know that global variables can be a pain (see DetectingUndefinedVariables) 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:

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

})

--SteveDonovan


RecentChanges · preferences
edit · history
Last edited September 29, 2009 1:46 am GMT (diff)