Hi,
>> * Is there a better way to write this in stock Lua?
If
while true do ... end
pattern is common then I would recommend creating a helper function loop
local function loop (object, actions)
while true do object:receive (actions) end
end
this would at least solve problems with Increment/Value/Lock messages.
>> * If one were to make changes to Lua to try to get both clarity and efficiency, what would those changes be?
0) Honestly speaking I don't think that stock Lua needs actors.
1) Simplest loop-invariant code motion optimization should be able to hoist closure creation out of the loop.
LuaJIT2 may already be capable of something like this.
2) In Haller&Odersky papers receive looks more like pattern matching, so you could call metalua and ask for some help:
match mbox:receive() with -- mbox:receive() yields or blocks depending on implementation
| Value -> dst:send( msg or 'Result', value )
| _ -> -- bark
end.
Actually with metalua on your side you could even hack together Odersky-style 'receive' block.
--
e.v.e
On Tue, Feb 9, 2010 at 10:53 PM, Mark Hamburg
<mark@grubmah.com> wrote:
I was reading one of the Haller and Odersky papers on event-based actors and was thinking about how their example would translate to Lua. This is what I came up with for their Counter example:
function makeCounter( value )
return makeActor( function( mbox ) -- Make an actor passing its mailbox to its handler code
while true do
mbox:receive{
Increment = function() value = value + 1 end,
Value = function( dst, msg ) dst:send( msg or 'Result', value ) end,
Lock = function( dst, msg )
dst:send( msg or 'Result', value )
mbox:receive{
Unlock = function( v ) value = v or value end
}
end
}
end
end )
end
The Increment message increments the counter. The Value message delivers the value to a particular mailbox. The Lock message delivers the value to a particular mailbox and waits for an Unlock message. It's the change in behavior after the Lock message that makes the message handling different from a simple object model. Haller and Odersky allow unhandled messages to sit in the mailbox, but that's not what I'm worrying about here.
Rather I'm worrying about code clarity and optimization.
On the clarity front, I think it actually does fairly well. It's a little bit noisy with all of the explicit uses of "function" for short operations, but the current lambda _expression_ proposals wouldn't really help with that. The more annoying part is the need for a comma after each "end". All in all, though, it's probably fairly workable.
The problem is that every iteration of the outer loop generates three new closures and a new table and the inner receive generates a new closure and a new table. Some of this could be mitigated with a looping construct for the outer receive that would use the same table repeatedly, but that wouldn't address the inner receive problem.
The optimized version looks more like:
function makeCounter_optimized( value )
return makeActor( function( mbox ) -- Make an actor passing its mailbox to its handler code
local lockedReceive = {
Unlock = function( v ) value = v or value end
}
local mainReceive = {
Increment = function() value = value + 1 end,
Value = function( dst, msg ) dst:send( msg or 'Result', value ) end,
Lock = function( dst, msg )
dst:send( msg or 'Result', value )
mbox:receive( lockedReceive )
end
}
while true do
mbox:receive( mainReceive )
end
end )
end
The control flow is now more scattered and the overall effect seems less clear. It might be approaching a reasonable format for a state machine, but it feels like it has lost something compared to the first version.
So, the questions I'm musing over are:
* Is there a better way to write this in stock Lua?
* If one were to make changes to Lua to try to get both clarity and efficiency, what would those changes be?
I've got a few thoughts on the latter but nothing clear enough to propose at this time.
Mark