[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Possible Lua enhancements (mostly for private data)
- From: Mark Hamburg <mark@...>
- Date: Wed, 17 Dec 2008 13:33:11 -0800
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