lua-users home
lua-l archive

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

Rici Lake wrote:
> local function collect(finalizers, ind, ...)
>   for i = #finalizers, 1, -1 do pcall(finalizers[i]) end
>   return ind, ...
> end
> local function addf(self, func, targ)
>   self[#self+1] = function() func(targ) end
> end
> function protect(f, ...)
>   local finalizers = {add = addf}
>   return collect(finalizers, pcall(f, finalizers, ...))
> end
> function close(x) print("Closing: ", x) end
> function doSomeWorkBadly(finally, a, b)
>   local c = "some precious resource"
>   finally:add(close, c)
>   print("a+3 is ", a+3)
>   local d = "another precious resource"
>   finally:add(close, d)
>   print("b+3 is ", b+3)
>   finally:add(print, "Almost done")
>   return a+b
> end

Rici omitted the usage example:

  > protect(doSomeWorkBadly, 1, 100)
  a+3 is 	4
  b+3 is 	103
  Almost done
  Closing: 	another precious resource
  Closing: 	some precious resource

I think this solution is along the right lines.  A good description of
the problem is given in the D Language guide [1].  To summarize, there
are three general solutions to the problem:

    * "resource acquisition is initialization" pattern - this is where a
resource is represented by a variable.  To support this, Lua would need
a new type of variable that was always collected on scope exit.  While I
think that would be interesting, this pattern suffers from the need to
make little one-off classes to manage various resources, cluttering your

    * try-finally construct - basically use the finally clause of a
try-except construct for cleanup.  While Lua doesn't have such a native
construct, it's trivial to make a functional version [2].  The problem
with this solution (besides Lua pcall not playing nice with coroutines)
is that your cleanup code is not grouped with your acquisition code,
making things hard to follow when the try block is large.

    * scope exit hook - similar to Rici's solution, this is where you
queue up various actions to take place at the exit of the current scope.
 The D language has native support for this [3], allowing you to execute
actions on scope failure (i.e. when an exception occurred), success, or
both.  Functional/object implementations work just as well, such as the
ScopeGuard class for C++ [4].  Again, in Lua we have to rely on
something triggering the cleanup explicitly since we don't have
variables which are destroyed on scope exit.  Thinking purely of
interface, perhaps something like the following could be implemented in Lua:

  function doSomeWorkBadly(a, b)
      local c = "some precious resource"
      on_exit (function() print("Closing:", c) end)
      print("a+3 is ", a+3)
      local d = "some transaction object"
      on_failure (function() print("Aborting:", d) end)
      on_success (function() print("Committing:", d) end)
      print("b+3 is ", b+3)
      return a+b

or perhaps, with some token filtering magic:

  $function doSomeWorkBadly(a, b)
    local c = "some precious resource"
    $on_exit print("Closing:", c) end
    print("a+3 is ", a+3)
    local d = "some transaction object"
    $on_failure print("Aborting:", d) end
    $on_success print("Committing:", d) end
    print("b+3 is ", b+3)
    return a+b