lua-users home
lua-l archive

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


On Fri, May 1, 2009 at 12:48 PM, Mark Hamburg wrote:
> With regard to injection, it's interesting to look at Michael Franz's paper
> on Protocol Extension.
> ftp://ftp.inf.ethz.ch/doc/tech-reports/2xx/226.ps.gz
> Essentially, he uses the module system in Oberon to avoid name conflicts on
> extensions.
> In the context of something like the method chaining wrapper,

i.e. http://lua-users.org/wiki/MethodChainingWrapper

> one could write:
>        local stringx = require "stringx"
>        S "  test  " : [ stringx.trim ] () ()
> (This assumes a Lua extension to support calling methods identified by
> variables.

Such a patch does not in itself allow us to write the expression how
we might really want to:

    ("  test  "):[stringx.trim]()

But we can achieve it if we allow an adjustment to the string metatable:

    debug.getmetatable"".__index = function(self, k) return k end

or even without patching,

  debug.getmetatable"".__index = function(self, k)
      return string[k] or function(...) return k(self, ...) end
  end
  return ("  test  ")[stringx.trim]()

This does, however, lead to the curious behavior that s[v] ~= nil for
all values v and strings s, unless we add further conditionals.  In
Lua, unlike some other languages, calling a method on an object is not
an atomic operation (passing a message) but is defined in terms of an
index and a function call.  s:v(...) is syntactic sugar for s["v"](s,
...).  This imposes restrictions on what we can do with the index
operator (e.g. method s:v exists implies that s["v"] ~= nil).

I also thought it was convenient in the MethodChainingWrapper that the
namespace for operations was held by the wrapper.  Instead of the
above, where stringx.trim is a variable defined outside of the
expression, the operation names are localized to the call chain
expression by the wrapper:

      -S("  test  "):trim()    -- "trim" is a method in the namespace "S"

There may be uses for both approaches.

Maybe the better solution to all this, in a Lua context, is to use
something other than method calls to do the operation chaining.
Execution-wise, we simply want to do

    m.h(m.g(m.f(x, p...), q...), r...)
        -- for argument lists p, q, r, and operations f, g, h in module m.

Syntax-wise, we'd like a less awkward form of expression, including
one that perhaps defines some automatic namespace lookup for the
operations.

Patching the parser or applying Metalua would allow an equivalent syntax like

    x chain m : f(p...) : g(q...) : h(r...)

e.g.

    ("  test  ") chain stringx : trim() : repeatchars(5) : upper()

Or in standard Lua, we could write a helper function like

    chain(stringx, trim, 1,repeatchars,5, "  test  ")

where a number before an operation name (e.g. 1 before repeatchars)
indicates that the operation takes that many number of arguments.