lua-users home
lua-l archive

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


Here are a list of possible Lua enhancements I've been thinking about. They are mostly to aid with creating objects with hidden data but some of them have applications beyond that.

--------------------------------------------------
#1: Enhanced message send syntax
--------------------------------------------------

In addition to obj:msg( ... ), allow for obj:[ msg_expression ] ( ... ). As with the existing syntax, this translates to:

	obj[ msg_expression ]( obj, ... )

This has a variety of uses. For example, one can define private methods by using a value such as a table and not exporting it:

	local my_private_method_identifier = { }

	obj:[ my_private_method_identifier ]( ... )

This syntax could also be used in function declarations thereby allowing one to write:

	function obj:[ my_private_method_identifier ]( ... )
		-- implement private method
	end

But beyond private methods, this is also useful for parameterized calling:

	function send_to_all( objects, msg, ... )
		for _, obj in ipairs( objects ) do
			obj:[ msg ]( ... )
		end
	end

------------------------------
#2: Non-iterable keys
------------------------------

The following could be implemented as a C module and a patch on next and pairs or even just in pure Lua with a patch on next and pairs, but it might benefit from support at the type-system level. (The patch on pairs is to deal with the fact that pairs needs to see the patched next.) Essentially, the idea is to introduce a data type that can be generated via either calling a library function or possibly via new syntax (see below) and defining next such that it will skip such keys. (We may need a rawnext that ignores this but that may require more restricted access if one wants to build in protection.)

	t = {
		a = 10,
		[ make_private_key() ] = 20
	}

	table.foreach( t, print )

Generates

	a	10

The existing version would also print out a representation for the private key and 20.

The benefit of adding this feature is that one can implement an object with private data without needing to use proxies to protect the data from discovery via iteration. That saves memory and improves performance at the expense of a little bit of extra logic in next.

------------------------------------------------------------------------------------
#3: Labels -- a syntax to make private keys easier to work with
------------------------------------------------------------------------------------

This is the most complex and probably the most controversial suggestion and hence probably the most likely to get killed or at least refined.

Introduce a new keyword (ugh, I know) such as "label" for use in the following syntax:

	label ident1, ident2, ident3, ...

This creates a series of private keys (see #2) and binds them to ident1, ident2, ident3, etc.

Furthermore, these identifiers follow scoping rules that are essentially equivalent to local variables and can be captured in closures just like local variables.

Where we use these identifiers is when processing expressions of the form:

	< expr > . field_name

Or

	< expr > : message_name

Instead of immediately mapping field_name or message_name to their string equivalents, we first check to see whether there is a label identifier in scope with that name. If so, we use the private key bound to that label in place of the string.

So, now we can write:

	label _angle, _radius

	function Point( x, y )
		
		return {
			_angle = math.atan2( y, x ),
			_radius = math.sqrt( y * y + x * x ),
			coordinates = function( self )
return self._radius * math.cos( self._angle ), self._radius * math.sin( self._angle )
			end
		}

	end

and no one can peek inside to see the actual representation. This could equivalently have been written as:

	local _angle, _radius = make_private_keys( "angle", "radius" )
		-- Names provide for extra debugging support

	function Point( x, y )
		
		return {
			[ _angle ] = math.atan2( y, x ),
			[ _radius ] = math.sqrt( y * y + x * x ),
			coordinates = function( self )
return self[ _radius ] * math.cos( self[ _angle ] ), self[ _radius ] * math.sin( self[ _angle ] )
			end
		}

	end

This isn't horrendous but it makes private keys feel unnatural to use and the point is to make data privacy easy.

Label identifiers should also be allowed anywhere local variables are so that we can pass them into functions such as the send_to_all routine defined above or choose to selectively publish them. That, however, raises questions about how they scope with one another. My inclination would be to say that labels hide locals but locals do not hide labels -- i.e., if we are in a context where we are checking for a local variable, we check both chains but if we are looking for a label we only look for labels.

--------------------

Reactions? I'm hoping that the first two items fit with Lua's preference for mechanisms over policies. The need for a keyword in the syntax of the third suggestion feels harder to swallow, but it makes the mechanisms much more natural to code with.

Mark