[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Injecting names & method chaining (was Re: [ANN] Penlight Libraries, First release)
- From: David Manura <dm.lua@...>
- Date: Fri, 1 May 2009 23:31:48 -0400
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.