lua-users home
lua-l archive

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


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