[erlang-questions] OOP in Erlang

Richard O'Keefe ok@REDACTED
Thu Aug 12 08:09:14 CEST 2010


On Aug 12, 2010, at 3:35 PM, Tony Arcieri wrote:

> On Wed, Aug 11, 2010 at 8:49 PM, Richard O'Keefe <ok@REDACTED> wrote:
> 
>> I was recently reading a collection of essays by Kent Beck, one of the
>> big names in the Smalltalk and Patterns worlds.  Oddly enough, he said
>> that he didn't think inheritance was all that big a deal.
> 
> 
> I must respectfully disagree here, especially in an everything-is-an-onject
> language like Smalltalk.

It wasn't me.  It was a great big Smalltalk guru saying that.
> 
> With everything-is-an-object and inheritance, all of your objects speak a
> common object protocol,

No they don't.  Oh sure, there are a bunch of methods in Object.
VisualWorks 7.5 has 253 methods available in Object.
Squeak-3.8Full  has 441 methods available in Object.
My own Smalltalk has 98 methods available in Object,
and that's despite trying to keep the number down.
(It's about 80 if you exclude private error reporting methods.)

Of course, *without* inheritance, all my objects would
*still* support all these methods.

There is nothing in the definitions of "OO" that have
been bandied around to this point that implies "everything is
an object" or "has a single root".

I repeat: inheritance can be a great burden.

Imagine me adding a new class to Squeak 3.8.
"What should my object do for each of these 441 methods?"
I don't even know what most of them *are*, and when I look
at them, the documentation usually isn't there so that I
don't really understand what the methods do for *any* object.

Take just one example, which I had never looked at before.

	actAsExecutor
	    "Prepare the receiver to act as executor for
	     any resources associated with it."
	    self breakDependents

What should that do for MY object?  Hmm, what does everyone
else do?  Oh, only StandardFileStream overrides it, and that
just calls super and then sets the name to nil.
Who sends it?

	executor
	    "Return an object which can act as executor for
	     finalization of the receiver."
	    ^self shallowCopy actAsExecutor

I am beginning to get a glimmer here.  If _this_ object is
going to be finalised, then the message about it is going
to be sent to some *other* object which has a copy of this
object's internal state.  Let's see what breakDependents
does.

	breakDependents
	    "Remove all of the receiver's dependents."
	    self myDependents: nil

Got that.  Oh dear.  I haven't done anything about #myDependents:.
That's OK, turns out I don't need to.  Looking further,
#executor is called in WeakRegistry, where

	add: anObject
	    "Add anObject to the receiver.
	     Store the object as well as the associated executor."
	    |executor|
	    executor := anObject executor.
	    self protected: [
		valueDictionary at: anObject put: executor].
	    ^anObject

I see.  #actAsExecutor is something I have to worry about if and
only if my object is ever added to a WeakRegistry.  Whoops!  I
don't and can't know if that will ever happen, so I had better
worry about it for ALL classes.  I don't understand how things
are supposed to work if my object has a reference to something,
an executor for it is made (which holds onto that reference),
and then my object replaces that reference by a different one.
Won't the executor use the wrong object?

Now I am thoroughly confused and worried (and happier than ever
to have my own Smalltalk).

Multiply this by about 100 to discover how it feels to
be inherited at by something with so many methods.

Now suppose you want to make something that is a subclass of
OrderedCollection.  Oh my: there are 719 methods.  I have to
make sure I don't break -any- of them.

I have actually done this, and BOY was it a pain.

Being inherited upon from a great height is no fun at all.
The more there is to inherit, the harder it is.

I'm afraid the Smalltalk tradition seems to be "check that
the methods I currently want to use work and the H**L with
the rest of them".  Case in point:  RunArray.  When I came
to implement RunArray, I found it difficult to implement
certain methods that the ANSI standard requires sequences to
have.  So I wondered how other Smalltalks had done them.
Turned out they hadn't bothered.

> which can provide all sorts of things to the end
> user by default for every object,

It isn't inheritance that makes all sorts of things available
to the user, it is conformance to common interfaces.
It's wonderful for whoever gets to use the stuff.

It's painful for whoever has to implement it.
Amongst other things, just because you didn't override a method
doesn't mean you have no need to test it.  Any time you make a
new class, you need to test *ALL* its methods, and if you
inherited 441 of them, that's 441 methods needing several test
cases each.  You *might* be able to inherit some test cases,
but it's practically certain you will have to write a heck of a
lot of tests for things you personally aren't responsible for
and wish weren't there.

> including powerful introspection methods
> which let you peruse and probe a particular object's state at runtime.
> 
> Is this possible without inheritance? Sure. But I find it to be particularly
> powerful and elegant in languages like Ruby.

And Ruby copied practically all of it from Smalltalk.
Yes, introspection is possible without inheritance.
For example, my Smalltalk can find
	- the class of an object
	- the name of the class
	- the comment of the class (if there is one)
	- the methods defined in that class
	- the methods available in that class
and can	- call any of those methods
and as it happens, the way this works makes no use of inheritance.
ALL of the key methods are in Object or Class and are not
overridden.
It so happens that I don't provide
	X the names of the instance variables
	X access to the instance variables of another object
but an object can
	- access its own instance variables in an array-like way
so that	- the persistence machinery can do this.

Persistence *does* make use of inheritance, but if inheritance had
not been available it could have been done another way.


I'm not saying that inheritance is always a bad thing.
Just that it has fairly massive costs as well as benefits,
so that it isn't always an overall win.




More information about the erlang-questions mailing list