Insights into Ruby from Lua


#1

In my job, I have to use Lua to program some of our software. For
anything complex, I love OOP concepts. For those that don’t know, Lua
doesn’t have OOP, though it has some syntactic sugar to make basic OOP
possible.

So, I wrote a little OOP library based on Ruby’s object model. I have
an Object ‘class’, and a Class ‘class’, and Class is an instance of an
Object, and instances of Class are themselves objects that create
instance that inherit from the class on up to the Object prototype. And
so on. (Once I got my head around Lua concepts, it’s remarkable how
easy it was. Code available upon request.)

In the process, I tried to make a OOP system that was better than
Ruby’s, by changing two things that have always bothered me. In both
cases, I failed; the reasons why I failed were both “Ah-HA!” moments
for me that gave me insight (I think) into why Ruby is designed as it
is. I share them with you below.

“new” versus “initialize”

I was always a little bothered by the fact that when you call “new” on
a class, you define an “initialize” function to handle it. I
mean…wtf, why not just call what you write “new” instead? I was also
annoyed that if you return a specific value from #initialize, it gets
ignored.

I decided that I’d have the user actually write their own “new”
function for the class, and use its return value. In implementation, it
looked something like this:

Rectangle = AKClass:new( )
function Rectangle:new( height, width )
local theInstance = AKObject:new( self )
theInstance.height = height
theInstance.width = width
return theInstance
end

As I wrote code like that again and again, I realized that in every
‘new’ function I had to call AKObject:new( self ) to create the
instance, and then return it at the end. I realized that if every class
inherited it’s “new” from AKObject, then I could just delegate that
work to AKObject, and let the class just define what to do to the
instance. Suddenly, I had Ruby again, and (now that I’d seen the
alternative) I liked it:

function AKObject.new( owningClass, … )
local theInstance = {
– Lua stuff for setting up the inheritance here
}

if type( owningClass.initialize ) == ‘function’ then
owningClass.initialize( theInstance, unpack( arg ) )
– No point in using the return value here
end

return theInstance
end

function Rectangle:initialize( height, width )
self.height = height
self.width = width
end

The Trouble with First-Class Functions

I have repeatedly complained on ruby-talk about the fact that Methods
aren’t first-class functions. (The term ‘first-class functions’ as I’m
using it here is another way of saying ‘function literals’…it means
that functions are just another atomic variable type, that can be
invoked with any object as the ‘self’ scope.) Blocks and Procs and
Methods are all different? Bleah! Special-cases are the opposite of
elegant.

Lua is nothing but first-class functions, so I was happy…until I
tried to write a ‘super’ method to call the method with the same name
on the parent object. Inside a function, I have no idea what that
function is called. So I then tried to write a special-case (urgh!)
“superinit” method specifically for calling parent initializers…and
that failed. I finally got to the core of the problem this morning:

function Rectangle:initialize( width, height )
self.width = width
self.height = height
end

function Square:initialize( size )
self.class.superclass.initialize( self, size, size )
end

function UnitSquare:initialize( )
self.class.superclass.initialize( self, 1 )
end

(For those not familiar with Lua, the colon used when defining a
function means “Hey, please create an implicit first parameter named
‘self’, because I’m too lazy to type it each time.” This is why I can
pass the ‘self’ from the subclasses to the parent method, and have the
initializer operate on it instead.)

The above works just fine when I create a new Square, but I get some
infinite recursion when I try to create a new UnitSquare. Here’s an
English trace of what’s happening:

  1. AKObject creates a new instance of UnitSquare, and passes that
    instance to the UnitSquare initializer.

  2. UnitSquare’s initializer finds the class of the supplied object
    (UnitSquare), it’s parent class (Square), and calls that class’s
    initialize function, passing along the UnitSquare instance.

  3. Square’s initializer finds the class of the supplied object
    (UnitSquare), it’s parent class (Square), and…oh hell, we’re stuck in
    a loop.

The problem is that when I wrote “self.class.superclass” what I
really meant was “Hey, I want the superclass of the class that owns
this method, not the object that I happen to be operating on.”

Which means that methods need to be associated with a class.

Which means that they’re not first-class functions.

Aw fuck. Matz is smart. :slight_smile:


#2

Excellent. This post deserves to be preserved in rubygarden wiki
somewhere.

Phrogz wrote:

“new” versus “initialize”

blank objects (not initialize’d) from class#allocate, can be useful
http://whytheluckystiff.net/articles/rubyOneEightOh.html

The Trouble with First-Class Functions

I have repeatedly complained on ruby-talk about the fact that Methods
aren’t first-class functions. (The term ‘first-class functions’ as I’m
using it here is another way of saying ‘function literals’…it means
that functions are just another atomic variable type, that can be
invoked with any object as the ‘self’ scope.) Blocks and Procs and

Hmmph, so python functions aren’t 1st-clas sobjects, like they always
say

Aw fuck. Matz is smart. :slight_smile:


(-, /\ / / //

nice, too
(can’t do "e"s in ascii art)


#3

On Feb 11, 2006, at 11:18, Phrogz wrote:

In my job, I have to use Lua to program some of our software. For
anything complex, I love OOP concepts.

Bondage?

http://alt.textdrive.com/lua/19/lua-story-of-o

For those that don’t know, Lua
doesn’t have OOP, though it has some syntactic sugar to make basic OOP
possible.

Programming in Lua
Object-Oriented Programming
http://www.lua.org/pil/16.html

Cheers


#4

Yup, that chapter in PIL is exactly what I’m talking about. That
chapter describes ways to use the __index property of a metatable along
with the ‘self’ syntax sugar to build a OOP-like system. IMO that means
that Lua has OOP to the same degree that C has OOP: you can roll your
own, if you like :slight_smile:

My OOP system is rather more feature-rich than what they describe in
that chapter, although I didn’t go so far as to include multiple
inheritance or mixins. (Not because it’s not possible, but simply
because I didn’t need them for what I was creating, and because I
wanted my system to be as fast as possible.)

Anyhow, just because it’s so damn purty, here’s the diagram of the
object model for the Lua OOP system I made:
http://phrogz.net/AnarkSamples/AKClassHierarchy.png

I really hate how confusing the Ruby ASCII object model diagram is.
(Granted, it’s about as good as it can be, as pure ASCII.) Perhaps I’ll
make one of those for Ruby sometime.