lua-users home
lua-l archive

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


On Sun, Jan 25, 2009 at 4:48 PM, Tommy Pettersson <ptp@lysator.liu.se> wrote:
> On Mon, Jan 19, 2009 at 06:37:35PM -0500, John Belmonte wrote:
>>    [1] Challenge: implement Alex Mania's "appendud" example function
>> in plain Lua while keeping the code easy to follow.  See
>> <http://lua-users.org/lists/lua-l/2008-02/msg00243.html>.
>
> If one doesn't care too much about syntactic sugar I think I can
> come pretty close.
>
> Well, there's an awful lot of apack/aunpack/pcall, so maybe not
> super-easy to follow.
>
> Btw, would it be cool with syntactic sugar for "application".
> Something like apply@fun(a1, a2, ...) that just gets expanded to
> apply(fun, a1, a2, ...). The idea is that a@b@c:d(x) is expand
> to a@b@c.d(c, x) -> a@b(c.d, c, x) -> a(b, c.d, c, x). That
> would make things like pcall@dofile(f), lazy@fib(n-1), and
> finalize@tab:endupdate() much easier to read. And write. I often
> write pcall(fun(a,b)) by mistake, but maybe that's just me.
>
>
> -- pack and unpack argument lists
> local function apack (...) return { n=select('#',...), ... } end
> local function aunpack (a,i,j) j=j or a.n return unpack(a,i,j) end
>
> -- protected scope with sequential finalizers
> local function finalized (fun)
>    local pcall = pcall
>    return function (...) -- create wrapper
>        local finalizers = {}
>        local function add_finalizer (...)
>            finalizers[#finalizers+1] = apack(...)
>        end
>        local presult = apack( pcall( fun, add_finalizer, ... ))
>        local errors
>        for i = #finalizers, 1, -1 do
>            local ok, err = pcall( aunpack( finalizers[i] ))
>            if not ok then
>                if not errors then errors = {} end
>                table.insert( errors, 1, tostring(err) )
>            end
>        end
>        if not presult[1] then
>            -- preserve custom error type if possible
>            if not errors then error( presult[2], 0 ) end
>            table.insert( errors, 1, tostring(presult[2]) )
>        end
>        if errors then
>            error( table.concat(errors,'\n'), 0 )
>        else
>            return aunpack(presult, 2)
>        end
>    end
> end
>
> -- stubs
> local tab = {
>    beginupdate = function (this) print('tab.beginupdate') end;
>    endupdate = function (this) print('tab.endupdate') end;
> }
> local ud = {
>    lock = function (this) print('ud.lock') end;
>    unlock = function (this) print('ud.unlock') end;
>    1,2,3,4,5;
> }
>
> -- here we go
> appendud = finalized(function (fin, tab, ud)
>    tab:beginupdate() fin( tab.endupdate, tab )
>    ud:lock() fin( ud.unlock, ud )
>    for i = 1, #ud do
>        tab[#tab+1] = ud[i]
>    end
>    return 7, 17
> end)
>
> -- test
> print( appendud( tab, ud ))

Tommy, thanks for responding to the challenge.  The infrastructure
you've come up with is very close to my scope manager code in the Gems
article.  This is about the best that can be done in standard Lua.
The issues are:

    * it takes a fairly knowledgeable Lua programmer to follow what's
going on (and I'm only speaking about the usage, not the
implementation)

    * the average programmer won't even realize they need your util
functions-- they'll just inline a giant pcall mess

    * pcall interferes with coroutines

    * lambda's can only approximate blocks - Unlike my scope manager,
you've added support for propagating return arguments.  However, this
totally breaks down if you want to do finalization within a plain
block-- for example, after each iteration of a loop.  In that case any
return statement in the body will not have the desired effect, which
is to exit the function containing the loop.  This is an artifact of
using lambda's to represent a block of code.

So I want to state this very clearly:  A main selling point of Lua is
its powerful metamechanisms, which provide simple building blocks for
solving common programming problems while keeping the language simple
and general.  The preceding class of problem can be solved much more
elegantly and robustly if we had some type of hook on values or
variables falling out of scope.  We've got experimental patches which
show this in practice.  I'm all for keeping Lua minimal and lean, but
this is a fundamental metamechanism which needs to be added.

--John