[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Macros and expressivity
- From: Mark Hamburg <mhamburg@...>
- Date: Fri, 11 Jan 2008 13:35:24 -0800
In the discussion of the utility of macros, I got thinking about Ruby which
seems to get by without them. My Ruby is weak, so I'm going to write
examples in pseudo-Lua.
A class definition in Ruby essentially sets up an execution context in which
other code then can add methods or make other alterations. For example:
class "Foo" from Baz
function display( self ) print( self:name() ) end
function set_name( self, newName )
local oldName = self._name
if oldName ~= newName then
self._name = newName
self:send_change_notification( "name" )
end
end
function name( self )
return self._name
end
end
What's more, if accessors with that pattern were common, we could somewhere
define:
function readWriteField( publicName, privateName )
local env = get_calling_environment()
env[ publicName ] = function( self )
return self[ privateName ]
end
env[ "set_" .. publicName ] = function( self, newValue )
local oldValue = self[ privateName ]
if oldValue ~= newValue then
self[ privateName ] = newValue
self:send_change_notification( publicName )
end
end
end
Then our class definition becomes:
class "Foo" from Baz
function display( self ) print( self:name() ) end
readWriteField( "name", "_name" )
end
Another example, this time sparked by reading Rob Pike's structural regular
expressions paper, what if one could write something like:
local filter = TextFilters.Filter
match "(.+/n)+" -- split into multi-line chunks
if_find "%%A.*Bimmler" -- check for author
match ".+/n" -- split into lines
if_find "%%T" -- find title lines
print_text() -- print the current line
end
Each of the operations would work by adding an element to the filter chain.
Running the filter would then process the chain.
I'm sure that the various macro systems floating around could support this,
but they aren't going to be easy to construct. Ruby manages to make these
sort of constructs easy without needing to resort to macros. Let's explore
why...
The first obvious thing is that it needs to be lightweight to support
creating function closures and passing them to other functions. While it
would be good for this to be lightweight from a runtime perspective, for now
the key point is that it be lightweight from a syntactic perspective.
If we were prepared to change the meaning of "do" (and if not consider doing
this with "begin") in the cases where it wasn't at the beginning of a line
and wasn't already part of some other syntactic construct, we could say that
"do" by itself was short for "function()" and that "do" followed on the same
line by a left parenthesis was short for specifying a function with
arguments.
Secondarily, we introduce the policy that a function specified after an
existing function reference or function call -- particularly if specified
using the new syntax -- gets passed as an extra parameter. Then we could
have something like:
class "Foo" do
inherit_from( Baz )
function display( self ) print( self:name() ) end
function set_name( self, newName )
local oldName = self._name
if oldName ~= newName then
self._name = newName
self:send_change_notification( "name" )
end
end
function name( self )
return self._name
end
end
The other piece that is needed is a way to get some form of dynamic scoping.
Assume, for example, that as part of Lua's core, we track a stack of dynamic
scopes. In fact, we don't really need a stack so much as a way to get and
set the current dynamic scope table and an easy way to refer to it. For
example, we might use the @ sign and the above examples would become:
function readWriteField( publicName, privateName )
@[ publicName ] = function( self )
return self[ privateName ]
end
@[ "set_" .. publicName ] = function( self, newValue )
local oldValue = self[ privateName ]
if oldValue ~= newValue then
self[ privateName ] = newValue
self:send_change_notification( publicName )
end
end
end
class "Foo" do
inherit_from( Baz )
function @display( self ) print( self:name() ) end
readWriteField( "name", "_name" )
end
The point of this message is that while all of this could be done with macro
packages, it is probably worthwhile looking to see whether Lua could get 80%
of the expressive benefit for 20% of the effort and avoid a proliferation of
competing macro-based language changes.
Mark