OOP sortof

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

OOP sortof

Steve Litt
Hi all,

This code runs and yields two point objects -- one at 1,1 and one at 80,25:

================================
#!/usr/bin/lua

do
        POINT = {}
        POINT.new = function(pnt, x, y)
                local pt = {}
                pt.x = x
                pt.y = y
                function pt.display(pnt, comment)
                        print(comment)
                        print(pnt.x)
                        print(pnt.y)
                        print("")
                end
                return pt
        end
end

pul = POINT:new(1,1)
plr = POINT:new(80,25)

print(type(pul))

pul:display("Upper left.")
plr:display("Lower right.") -- This fails if you uncomment it

pul.x=2000
pul:display("Upper left after strongarm.")

================================

Pretty cool, but I couldn't figure out a way to make x and y private (at which
time getx() and setx() etc would need to be added. Do I need to put all of
POINT.new in a closure of some kind? Do I need to use metatables? From what
I've heard it's necessary to use metatables to do inheritance.

Thanks

SteveT

Steve Litt
Recession Relief Package
http://www.recession-relief.US
Twitter: http://www.twitter.com/stevelitt


Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Dirk Laurie
On Thu, Dec 30, 2010 at 07:03:34AM +0200, Steve Litt wrote:
> Pretty cool, but I couldn't figure out a way to make x and y private
> (at which time getx() and setx() etc would need to be added.

Ah, those marvellous days when I was a C++ programmer!  One could
spend hours without pausing for thought, engaged in writing header
files with declarations headed private, and providing public inline
functions get.. and set... for every field.  Being able to expand
what in Lua would have been
    local a,b,c,d,e,f
to eighteen lines of code was a real treat when I was paid at so
much per line.  The only horror was trying to remember the difference
between "const int f(x)" and "int f(x) const".

Just why must x and y be private if you plan to provide read and
write access to them?

Dirk

Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

steve donovan
On Thu, Dec 30, 2010 at 9:20 AM, Dirk Laurie <[hidden email]> wrote:
> Just why must x and y be private if you plan to provide read and
> write access to them?

Having had this moment with some Java[2] I was charged with sorting
out, I must agree. The classic answer is that a.x confuses
implementation with access, but _if_ a.x needs to invoke some code at
some later date, well then there's always the property pattern.[1]

A much bigger problem than classic privacy/implementation hiding is
not spelling a field name correctly, since this is not a runtime
error.

steve d.

[1] the table doesn't contain 'x', so __index will always fire, and
some code gets called; if we cannot find a field x try to call the
method get_x. Ditto for x/set_x.
[2] in any case with Java the IDEs are so good that you can find all
the references to a field and correct them very quickly. Technology
should trump ideology.

Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Philippe Lhoste
On 30/12/2010 08:45, steve donovan wrote:

> On Thu, Dec 30, 2010 at 9:20 AM, Dirk Laurie<[hidden email]>  wrote:
>> Just why must x and y be private if you plan to provide read and
>> write access to them?
>
> Having had this moment with some Java[2] I was charged with sorting
> out, I must agree. The classic answer is that a.x confuses
> implementation with access, but _if_ a.x needs to invoke some code at
> some later date, well then there's always the property pattern.[1]
>
> [2] in any case with Java the IDEs are so good that you can find all
> the references to a field and correct them very quickly. Technology
> should trump ideology.

Well, it is harder and more annoying when that's a library that changes
that: it means changing all the projects depending on it. Even on a
closed project, management can frown upon making a change list of
hundred of files just replacing variable access with a getter or setter.

Scala (to remain in the JVM realm) resolved that quite elegantly: just
leave the variables in the open, and should you add code around access
(get or set), you can do it without disturbing calling code.
That's indeed a point that a modern language should address elegantly.

--
Philippe Lhoste
--  (near) Paris -- France
--  http://Phi.Lho.free.fr
--  --  --  --  --  --  --  --  --  --  --  --  --  --


Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

steve donovan
On Sat, Jan 1, 2011 at 3:33 PM, Philippe Lhoste <[hidden email]> wrote:
> Scala (to remain in the JVM realm) resolved that quite elegantly: just leave
> the variables in the open, and should you add code around access (get or
> set), you can do it without disturbing calling code.
> That's indeed a point that a modern language should address elegantly.

Yes, there are cases where the property pattern is the most elegant
solution - VB, borrowed by Delphi, brought back into MS by Delphi's
creator when he did C#.

With Lua one can write general setters and getters. For instance, a
common pattern is some GUI element where setting a property must cause
the element to update itself. Classic OOP would look like this,
repeated for each property:

function Box.set_width(x)
   if x ~= self.width then
      self.width = x
      self:update()
   end
end

A more dynamic solution could look like this:

local property = {width=true,height=true,text=true}

function Box.__newindex(self,prop,value)
   if property[prop] then
     local name = '_'..prop  -- i.e. actual field is _width, etc
     if self[name] ~= value then
        self[name] = value
        self:update()
    end
  else
    error("not a property or method",2)
  end
end

steve d.

Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Lucas Zawacki
Here's a variation of  your code using closures to create private
fields https://gist.github.com/762006
Notice that it pretty much makes the a.foo(a,...) and a:foo(...)
unecessary but I left it to conform with your example.

Additional reading on the subject can be done here:
http://lua-users.org/wiki/ObjectOrientationClosureApproach

PS.:

steve code using _newindex is quite interesting! Lua once again
surprises me (in a good way :)

Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Norbert Kiesel
In reply to this post by steve donovan
On Sat, 2011-01-01 at 15:44 +0200, steve donovan wrote:

> A more dynamic solution could look like this:
>
> local property = {width=true,height=true,text=true}
>
> function Box.__newindex(self,prop,value)
>    if property[prop] then
>      local name = '_'..prop  -- i.e. actual field is _width, etc
>      if self[name] ~= value then
>         self[name] = value
>         self:update()
>     end
>   else
>     error("not a property or method",2)
>   end
> end
>
Nice.  Should the "self[name] = value" not better be a "rawset(self,
name, value)"?  Also: how would an auto-wrapper for a getter look like?

</nk>




Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Steve Litt
In reply to this post by Lucas Zawacki
On Saturday 01 January 2011 16:10:57 Lucas Zawacki wrote:
> Here's a variation of  your code using closures to create private
> fields https://gist.github.com/762006

Excellent!

I just thought of another benefit of this: If you misspell a setter or getter,
you get an error message. If you misspell a key to a table, the program
continues and you get a computational error or a crash much later. I think I'm
going to use your technique in my Lua version of UMENU.

On the self-documentation front, I like the way you call the local table
inside the new() method "self". That makes the code much more understandable
for a newbie like me.

> Notice that it pretty much makes the a.foo(a,...) and a:foo(...)
> unecessary but I left it to conform with your example.

Lucas, I didn't understand exactly what you meant in the preceding sentence
until reading the doc you put down below. Now I understand.

>
> Additional reading on the subject can be done here:
> http://lua-users.org/wiki/ObjectOrientationClosureApproach

Fascinating! And rather Rubyesque. :-)


My one qualm about that is that a person not familiar with what you're doing
will not understand why you use dot instead of colon. It could prove
problematic for the maintenance programmer. But that's nothing that some
decent documentation couldn't solve.

>
> PS.:
>
> steve code using _newindex is quite interesting! Lua once again
> surprises me (in a good way :)

Out-Standing!!!

So in other words, after keeping x and y as locals in the surrounding code
instead of keys of self, I can use __newindex to prevent the application
programmer from setting self.x or self.y (which would not be the same x and y
as set by setX and setY. If I wanted to, I could even use _index to prevent
read access.

All this stuff is overkill for small programs and quick prototypes, but can be
very helpful when writing bigger programs.

I'm about to refactor my (nonworking umenu.lua), and I think I'm going to use
your technique for the item and menu "classes".

Thanks

SteveT

Steve Litt
Recession Relief Package
http://www.recession-relief.US
Twitter: http://www.twitter.com/stevelitt


Reply | Threaded
Open this post in threaded view
|

Re: OOP sortof

Steve Litt
In reply to this post by Lucas Zawacki
On Saturday 01 January 2011 16:10:57 Lucas Zawacki wrote:

> Here's a variation of  your code using closures to create private
> fields https://gist.github.com/762006
> Notice that it pretty much makes the a.foo(a,...) and a:foo(...)
> unecessary but I left it to conform with your example.
>
> Additional reading on the subject can be done here:
> http://lua-users.org/wiki/ObjectOrientationClosureApproach
>
> PS.:
>
> steve code using _newindex is quite interesting! Lua once again
> surprises me (in a good way :)

Hi Lucas,

It turned out that I wrote my Lua implementation of UMENU using your method,
where the private vars are locals of Classname.new(), as is an empty table
called self, and setters, getters and other functions are declared inside
Classname.new() as "self.getwhatever() return whatever end"

I agree with Dirk that this is a lot of extra work and in some cases makes
things less obvious. But I'm a very careless and mistake prone person, and
your method protects me from myself, kind of like wearing a helmet when I ride
my bicycle (or when I walk and chew bubblegum at the same time). Your method
also yields a pretty readable result.

Note that in the "class's" functions, I reference the local variables
directly. Only from outside the "object" do I use the setters and getters.

Anyway, thanks to all of you who helped give me a great introduction to Lua.

SteveT

Steve Litt
Recession Relief Package
http://www.recession-relief.US
Twitter: http://www.twitter.com/stevelitt