lua-users home
lua-l archive

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


On Wed, 2005-01-26 at 04:43, PA wrote:
> On Jan 25, 2005, at 18:29, Mark Hamburg wrote:
> 
> > I can see an argument for wanting to protect consumers from sloppy or 
> > malicious authors.
> 
> What would be that argument? Getting out of your way to _enforce_ some 
> notion of what is good and what is not is a bit condescending to your 
> potential users, no?

Really?

Do you feel condescended to when I provide you a function,
and you can't get at the local variables of the function?

You, the programmer CAN get at the local variables,
in fact you can edit the function!

But given the function -- or any other function,
including one you yourself edited, the code
that calls it can't get at its local variables,
or any of its implementation for that matter.

As I'm sure you know this means you can reprogram
the function to make it more efficient, or fix a bug,
without client code being affected... that's surely
useful to you -- as the functions author -- and also
you -- as the functions client.

Well, as I see it a class is just a collection of
functions with some 'local variables' shared between
then, namely the private data of the object.

So it just looks like a bug if you can't hide that
data the same way you can hide the private parts
of a single function. Using my implementation,
which is to return a table of closures,
the private members are hidden precisely
*because* the technique uses lexical scoping
and closures:

	function class(args)
		local x = ..
		local y = ..
		function method1 (..) .. end
		function method2 (.. ) .. end
		return { ["f"]=method1, ["g"]=method2..}
	end

With some sugar this would be

	class X(args)
		local x = .
		local y = ..
		method f(..) ..end
		method g(..) .. end
	end


The sugar is trivial to implement, and the result
looks very OO .. the main problem as pointed out
is that this doesn't allow for inheritance,
and those locals are wholly private, not merely
protected.

However no 'self' is required in the method
definitions, and you can just use

	x.f (args)

to call a method -- since he result is just a table x,
with key "f" for method f.

So this is the minimal change to get classes with
proper encapsulation, and only requires sugar in
the interpreter, and no core changes at all.

To get inheritance you might try this sugar:

	inherit B(args)

which, when used in a class calls the function B,
grabs the resulting table, and then modifies
it with the current class methods.

Actually, even better -- you start with an empty table
and pass it in to the inherited function to populate,
and then you get multiple inheritance for free:

	class X(args)
		local x ..
		local y .
		method f(..) ...
		inherit A (..)
		method g( .. )
		inherit B ( ..)
		method h (.. )
	end

This is quite interesting -- f goes in the table first,
then A's methods, then g, then B's methods, then h.
This means A can actually override f, and B can override
anything A put in, as well as f and g.

This whole thing would be syntactic sugar and
the result is just an ordinary table without
any metamethods .. which means the user can
add the usual metamethods if they want, the
system doesn't ursurp them.

Note you can still have public members, suppose the
table is called __methods then

	__methods['a'] = 1

would just add the variable a to the method table
(as well as the methods), and you could do:

	x.a = 2

and even add things like

	x.b = 42 -- new variable added!
	x.c = function .. end -- new method added

Only tricky thing is passing the __methods table in for
inherits .. but not needing it for a client
constructor, so it should default to {},
but the actual default is 'nil' ..  so you'd
need to add

	if __methods == nil then _method = {} end

at the top of each class body.

-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net