OOP flavours - was Python vs. Ruby

From: james_b [mailto:james_b@neurogami.com]
I’ve been doing this sort of thing in my code, and after a while it
struck me as common sense, not purist theory. I find it goes a long
way towards cleaning up or avoiding procedural code where I
want OO, and
I find it easier to read.

I like this style too, and Ruby allows one to do it well via Mixin Oriented
Programming (MOP?). The best library based example I’ve seen is in Amrita,
where you extend your model class with Amrita::ExpandByMember, so amrita can
render the object correctly.

Similarly, you don’t need to expand classes to predict there uses - for the
example of IO printing flying around, you can extend on the fly:
module STDOUTPrinter
def print
STDOUT.print self.to_str
end
end

str = “helloworld”
str.extend STDOUTPrinter
str.print

Its a fairly useless example in this case, but extend this to template based
GUIs along the amrita lines, and mixin oriented programming shows its real
strength.

Not that I do it much in practice right now, but this message captures a lot
about how I’ve been thinking in Ruby lately. Although I still don’t
understand it, I really like the ideas of “side-inheritance” as used by
Matju:
http://www.ruby-talk.org/13827

David.

> I like this style too, and Ruby allows one to do it well via Mixin Oriented > Programming (MOP?). The best library based example I've seen is in Amrita, > where you extend your model class with Amrita::ExpandByMember, so amrita can > render the object correctly. > > Similarly, you don't need to expand classes to predict there uses - for the > example of IO printing flying around, you can extend on the fly: > module STDOUTPrinter > def print > STDOUT.print self.to_str > end > end > > str = "helloworld" > str.extend STDOUTPrinter > str.print > > Its a fairly useless example in this case, but extend this to template based > GUIs along the amrita lines, and mixin oriented programming shows its real > strength.

Actually this isn’t a very good example. This whole argument about
"print should be a member of an object" is demonstrably inelegant and
wrong, for a number of reasons (some of which have been noted already):

*  The action of "printing" presupposes a few things:

   -  A device to print on
   -  A format in which to print
   -  Content to print

*  A String specifies no device on which to print

*  A String provides content and, to some extent, format

*  The physical act of printing is device-dependent, and thus in
   OOP should be encaspulated within the device representation object

*  While it is possible, as in the above example, to provide a
   method which calls on a device to print, this couples String with
   the concept of printing, which may not be meaningful in all
   contexts.

*  This does not scale well, as it requires N methods for N classes,
   as opposed to one method for N classes.

There’s an important OOP concept that’s somewhat unspoken, but should be
heeded rigorously: containership. This means that if you look at a
given thing, actions or attributes that are not contained within that
thing should not be a part of that thing. Following this principle
avoids two problems:

*  Confusion: When telling an object to do something, you can be
   sure it's the object doing it, and not another object doing the
   action on "self", as in a previous example in this thread:

       foo.eat(person)         # The food is eating the person?
       person.eat(food)        # No, the person eats the food

       food.eaten_by(person)   # Good reaction method, also clear;
                               # should be called by Person#eat

*  Undesired coupling: If you store outside data or define outside
   actions, you will end up in a situation where your methods
   require outside context which isn't there:

      w  = World.new
      sp = Sphere.new(w, 0, 0, 5) # Bad coupling, the location and
                                  # world should not matter here

      :                           # elsewhere

      sp = Sphere.new(??.., 5)    # We might not have a World here,
                                  # we just want a sphere

      sp.circumference            # Outside information not relavent
                                  # to the sphere itself


      # Someone else implements it like this:

      w  = World.new
      sp = Sphere.new(5)

      w.add(sp, 0, 0)             # Good, no coupling

      :

      sp = Sphere.new(5)          # No ties to worry about
      sp.circumference

These get even worse as you start trying to tie objects and methods with
less relation together, like String and IO. Would you consider a
String#display function that takes a GraphicsDevice? A GD and
coordinates? colors? fonts? transformations? Things get out of hand
quickly. What if you’re working with multiple interfaces? String#print
is an identical concept, except it’s merely displaying on an inferior
rendering device. And it won’t work for ncurses, or without a console.
Could you use mixin to abstract #print to different devices? Yes.
Could you abstract the formatting complexities? Maybe. Can you
ensure the concept of printing even makes sense at the target? No.

Thus, tying these sorts of things is not elegant, and not a good idea.

Now, if you want a good example of mixins, think of creating a class
which is multifaceted—but beware of violating containership, and of
the distinction between is-A and has-A. For instance, if you were
constructing a SpaceVessel, the following would be good:

class MyVessel < SpaceVessel
    include Battleship
    include Cargoship
end

The following would not, of course:

class MyVessel < SpaceVessel
    include SuperLuminalEngines
    include Weapons
end

Anyhow, this message is getting far too long, so that’s all.

···

On Wed, 3 Sep 2003 09:33:02 +0900 David Naseby david.naseby@eonesolutions.com.au wrote:


Ryan Pavlik rpav@mephle.com

“Let me tell you, I find your math to be highly suspect.” - 8BT

I’m surprised nobody has mentioned the built-in method Object#display. It
is analogous to String#print, and gives the lie to the notion that you
need to also implement Fixnum#print, Range#print, etc.

--------------------------------------------------------- Object#display
obj.display( port=$> ) → nil

···

On Wed, 3 Sep 2003 09:33:02 +0900 > David Naseby david.naseby@eonesolutions.com.au wrote:

> I like this style too, and Ruby allows one to do it well via Mixin > Oriented Programming (MOP?). The best library based example I've seen > is in Amrita, where you extend your model class with > Amrita::ExpandByMember, so amrita can render the object correctly. > > Similarly, you don't need to expand classes to predict there uses - > for the example of IO printing flying around, you can extend on the > fly: > module STDOUTPrinter > def print > STDOUT.print self.to_str > end > end > > str = "helloworld" > str.extend STDOUTPrinter > str.print > > Its a fairly useless example in this case, but extend this to template > based GUIs along the amrita lines, and mixin oriented programming > shows its real strength.

Actually this isn’t a very good example. This whole argument about
“print should be a member of an object” is demonstrably inelegant and
wrong, for a number of reasons (some of which have been noted already):

[…]


 Prints obj on the given port (default $>). Equivalent to:
    def display(port=$>)
      port.write self
    end
 For example:
    1.display
    "cat".display
    [ 4, 5, 6 ].display
    puts
 produces:
    1cat456

Personally I never use it, for all the reasons that have been given, and
one more: I prefer #puts to #write.

Gavin

This has been a great thread. Lots of interesting concepts have been
argued and many interesting points of view have been shown. It’s
taking me a while to absorb it and I’m nowhere close to being
finished. I’m attaching to Ryan’s post as I found it especially
thought-provoking.

Ryan Pavlik rpav@mephle.com writes:

Actually this isn’t a very good example. This whole argument about
“print should be a member of an object” is demonstrably inelegant and
wrong, for a number of reasons (some of which have been noted already):

*  The action of "printing" presupposes a few things:

   -  A device to print on
   -  A format in which to print
   -  Content to print

*  A String specifies no device on which to print

*  A String provides content and, to some extent, format

*  The physical act of printing is device-dependent, and thus in
   OOP should be encaspulated within the device representation object

One of the tough spots I’ve bumped into, and which has been touched in
this thread, too, is the difference (sometimes dissonance) between a
model that is easy to understand and a model that is easy to
develop/maintain/extend easily, or in other words an intuitive model
and a good working model.

The former seems to be about how much the model is close to
real world experience and common sense, the latter about how much it
follows certain practices and ways that programming experience has
shown to be effective (i.e. to enable to develop/maintain/extend
easily).

The dissonance lies in the unsurprising fact that an intuitive model
and a good working model don’t necessarily coincide. (Yeah, you are
authorized to think `truism’ here. ;-))

Sometimes a very natural model is a very uncomfortable one. The
example has been pointed out of drawing something on a sheet of paper,
with various combinations and orders of pen', paper’, ink', figure’, and as many suggestions as to who does the `drawing’ and
therefore who shall be the receiver of the message and who shall be
the parameter.

What surprised me lies in what was not suggested: that it is a
painter who might know how to do the drawing, rather than a pen or a
sheet of paper. Now allow yourself to be a little surprised as well,
if you will, before reading on. :slight_smile:

Thinking from a real world perspective, it would seem only natural
that a painter knew his tools, had access to them and could use them.

Thinking from a programming perspective, though, this smells of
passive data and procedural thinking (which isn’t necessarily bad).
Yes, I’m thinking exactly about the memory area that stands for the
sheet of paper and the procedure lighting up some bits here and there
to simulate a trace of ink.

Yet, still thinking from a programming perspective but this time
wearing our Object Oriented Wizard’s clothes, it suddenly becomes
natural to think of pen, paper and ink as living and active things.
Wizard says to pen: Pen, fly to the sheet of paper and draw!' and the pen does so. Or, to get back to the string and printing device example, wizard says to string: String, lay thyself on the device
surface!’ or `Device, receive this string and make it appear on your
surface!'.

Interestingly, much was discussed about the proper combination of pen,
paper, ink and figure, but it seemed somewhat more quietly accepted
that it is a device that does the printing of a string. The paper was
perceived passive, the device active.

But the device acts no more than the paper, and no more than the pen
or string; it’s the painter that moves the pen and draws the figure on
the paper, and it’s the device driver that moves the virtual pen and
draws the virtual figure on the device’s virtual paper.

The `why’ of our tendency to consider some things passive and some
things active would lots of thinking in itself. Who knows, maybe it’s
just that, as technology goes on, we’re surrounded more and more by
reacting and acting objects, and it makes sense to assign this
property even to those things who were not born with such initiative,
when their representations live somewhere inside the most active of
real objects, the computer. [1]

Or it might just be that, doing so, some things work better, and we’re
back to the dissonance between easy to understand vs. good working
systems. And/or to an unsettling question: `In a world where we speak
to pens and they fly and write, do we really want to stay attached to
models resembling real world? How many excellent and exciting models
have we chosen to not explore and discover just because they appear so
little natural and real world?’

[1] It was only when re-reading that sentence that I noticed `…those
things who were…‘’

*  Undesired coupling: If you store outside data or define outside
   actions, you will end up in a situation where your methods
   require outside context which isn't there:

      w  = World.new
      sp = Sphere.new(w, 0, 0, 5) # Bad coupling, the location and
                                  # world should not matter here

      :                           # elsewhere

      sp = Sphere.new(??.., 5)    # We might not have a World here,
                                  # we just want a sphere

      sp.circumference            # Outside information not relavent
                                  # to the sphere itself


      # Someone else implements it like this:

      w  = World.new
      sp = Sphere.new(5)

      w.add(sp, 0, 0)             # Good, no coupling

      :

      sp = Sphere.new(5)          # No ties to worry about
      sp.circumference

This was what most caught my attention, because in the meanwhile I was
working with GTK and teaching AWT on one side (where you do things
like window.add(widget) rather than Widget.new(window)) and was toying
with a little LOGO implementation in C/Ruby (where I had done
something like g = Graphics.new; t = Tortoise.new(g)).

From a real world (sort of) perspective on the model: are coordinates
a property of an object or not? That’s a good question, and I’m
terribly envious the certainty of your answer, Ryan. :slight_smile:

Michael Benedikt, architect, wrote about the dimensionality of
Cyberspace [2] and about intrinsic and extrinsic dimensions of
entities in a – real or perceived – space. Intrinsic dimensions are
e.g. color, texture, temperature, etc. Extrinsic dimensions are x, y,
z. As an operative definition of extrinsic vs. intrinsic, I assume
that in a moving object extrinsic dimensions change while intrinsic
dimensions stay the same, because ext-rinsic dimensions depend on a
relation with an ext-ernal context.

(Now of course this is limited; intrinsic dimensions are measured on
some scale and once you take that as the external context, you’ve
turned intrinsic into extrinsic. But my head is exploding already.)

Now the question: who owns/knows the values of extrinsic dimensions?

Is it the responsibility of the space to know where every sphere it
contains is placed, or is it the responsibility of each sphere to know
where it is, while the space’s only job is to contain them?

That’s a natural question to hear from a programmer, but then again,
from a real world perspective I don’t think it’s the right the
question.

Because, of course, it’s us observers that realize the relation
between a context and the things it contains. And coordinates in the
space is precisely a matter of relation among entities, something that
is in between' the space and the sphere, rather than of’ the space
or `of’ the sphere. Just like the act of drawing is what lies between
a pen and a sheet of paper, not really belonging to either, maybe only
belonging to the painter’s mind or hand. (Attempt at a martial arts
or Japanese oriented joke: Hey, somebody please invent ma-oriented
programming! :-))

It’s this kind of concepts that I find difficult to model in an
object-oriented world, rather than in a procedural world (can I say
observer-oriented world? Einstein has been insistently knocking
behind my left ear since I’ve started talking about space and
dimensions. ;-)).

And this leads me to…

The programmer’s perspective on the model. Because in the end we
build programs based not (only) on the rules and concepts that seem
most natural, but on the rules and concepts that work best.

For example: in a CAD we will often want to know the coordinates of
the sphere according to a default coordinate reference (world
coordinates). When we have this setting:

      # Someone else implements it like this:

      w  = World.new
      sp = Sphere.new(5)

      w.add(sp, 0, 0)             # Good, no coupling

How would one get to know the coordinates of sp? He would have to
travel (iterate) the world along the contained objects' dimension looking for the coordinates structure that refers to sp’, maybe
something like:

coords = world.object_coordinates.find {|id, x, y, z| sp.id == id}

(And note that `id’ is kind of an extrinsic dimension related to
another context – memory – here held in the object, not in the
context!)

On the other hand, querying sp' about its coordinates is trivial if extrinsic dimensions are stored in sp’ itself:

print sp.x
print sp.y
print sp.z

This is more convenient. But now the sphere is coupled to a specific
context, and we cannot extend the context (for example introducing a
fourth dimension) without making the contained objects aware of it.
Which I suppose is one of the consequences Ryan was getting at.

The sphere also spits out data that are not collected from its own
point of view, instead from that of an external observer, which could
be counted as one more (albeit subtler) break of encapsulation.

[2] Cyberspace - First Steps, Massachusetts Institute of Technology,
1991

These get even worse as you start trying to tie objects and methods with
less relation together, like String and IO. Would you consider a
String#display function that takes a GraphicsDevice? A GD and
coordinates? colors? fonts? transformations? Things get out of hand
quickly. What if you’re working with multiple interfaces? String#print
is an identical concept, except it’s merely displaying on an inferior
rendering device. And it won’t work for ncurses, or without a console.
Could you use mixin to abstract #print to different devices? Yes.
Could you abstract the formatting complexities? Maybe. Can you
ensure the concept of printing even makes sense at the target? No.

Somehow, the content has to arrive on the rendering device. Somehow,
the device must know additional data about the representation of the
content (I don’t say that this additional data `must arrive’ because
the rendering device might as well make them up based on its own
reasoning.)

Now, the string is just content. How does it get to the device?

Since we have accepted that objects can be active when it makes things
work better (ok, I have accepted that ;-)), I see nothing wrong
with:

s = "Hello, world!"
s.transmit_to(io_device)

…provided that there is some agreed convention that says that
io_devices get content through a well defined interface like:

class IODevice
  def receive_content(content)
    ...
  end
end

…so that String#transmit_to knows how to transfer itself to the
IODevice. The requirement of String to know this interface is the
consequence of making it so intelligent that there must not be an
outside painter or observer that picks the letters up and carries them
to an io_device, thereby knowing both the string and io_device.

What I see something wrong with, and again what I think Ryan was
getting at in the post and in the quote above specifically, is when
string knows more than its content, i.e. knows that additional data
needed for representing a string on a device; data which, yet, is
device-dependent.

So rather than String#display_to or String#print_to, a more balanced
(less prone to abuses or consequences) way to let the string have some
initiative and yet not stomp on others’ objects feet could be:

s = "Hello, World!"
s.transmit_to(io_device)
io_device.set_color('blue')
io_device.set_cursor(100, 70)
io_device.print_content

Or just:

s = "Hello, World!"
s.transmit_to(io_device)

Which reads: IO device listens to the data space and is willing to
display the data that requests it, but at a time and in a way
depending on its policies, not the data’s.

So io_device decides whether to automatically assign a presentation to
the content and display it right away, or wait until someone provides
presentation data, or schedule its presentation at regular intervals
in another thread, and so on. Which, come to think about it, it’s
just what could already be happening with SomeObject#display, if the
method name didn’t make us assume a communication of content and a
subsequent presentation, but just a communication of content.

Anyhow, this message is getting far too long, so that’s all.

I can sense people regretting the same thought didn’t come to my mind
sooner. O:-)

Massimiliano

I overlooked that, but … I’m still sticking by what I wrote above. :wink:

In fact, while I usually define #inspect or #to_s for simple debugging,
I like to keep even the format of things separate from above, in a
sortof MVC-ish design.

···

On Wed, 3 Sep 2003 10:41:05 +0900 “Gavin Sinclair” gsinclair@soyabean.com.au wrote:

I’m surprised nobody has mentioned the built-in method Object#display. It
is analogous to String#print, and gives the lie to the notion that you
need to also implement Fixnum#print, Range#print, etc.

Personally I never use it, for all the reasons that have been given, and
one more: I prefer #puts to #write.


Ryan Pavlik rpav@mephle.com

“Let me tell you, I find your math to be highly suspect.” - 8BT

A painter object is a useful model. Consider mixing in style modules
Picasso, Jackson_Pollock, or Rolf_Harris: given the same subjects
and the same tools, the results would quite different.

    Hugh
···

On Sun, 7 Sep 2003, Massimiliano Mirra - bard wrote:

What surprised me lies in what was not suggested: that it is a
painter who might know how to do the drawing, rather than a pen or a
sheet of paper. Now allow yourself to be a little surprised as well,
if you will, before reading on. :slight_smile:

One of the tough spots I’ve bumped into, and which has been touched in
this thread, too, is the difference (sometimes dissonance) between a
model that is easy to understand and a model that is easy to
develop/maintain/extend easily, or in other words an intuitive model
and a good working model.

Great point. I think I personally have almost always put all my effort
toward the good working model and very little toward an intuitive model.
When they do coincide, great… if not, tough.

That is probably not a very “rubyist” view and is almost certainly a
reflection of my Java background, where complex but extremely flexible API’s
are the norm rather than the exception. The rationale is often that you can
always add the intuitive model later by adding a facade.

Yet after working with Ruby a little bit I’ve found most of the libraries
I’ve encountered to be very intuitive and have not yet been left wanting for
more flexibility. I’ll be more conscious of both sides of the equation next
time I design a class library, I’ll be interested to see how exposure to
Ruby influences my designs…

You can always use the Facade pattern and produce an intuitive
interface to a complex object model. I think its important to get the
model technically right before you make it accessible.

···

Great point. I think I personally have almost always put all my effort
toward the good working model and very little toward an intuitive model.
When they do coincide, great… if not, tough.