citizen428.blog()

Try to learn something about everything

Tom and Michael vs Prototypical Inheritance

Tom and I read another paper, this time on prototypical inheritance: Organizing Programs Without Classes. You can find his post here and I have to admit that he also did almost the entire summary this time whereas I only added some minor things and code examples.

Summary

OOP is a paradigm which uses “objects” to encapsulate behavior and state in a programming language. This can be accomplished in a few ways, two of the more popular being class based and prototype based object orientation. In class based object models (e.g. Java) a class will be written to define the behavior and state that the object will embody. A class can be inherited from and extended to provide new functionality. In prototype based object models (e.g. ECMAScript, Io), there are no classes, only objects which can be cloned and altered to provide new features. Here’s a quick comparison of the two approaches in Ruby and ECMAScript:

Ruby:

1
2
3
4
5
6
7
8
9
10
11
# define a class
class Hello
   def hello
     puts "Hello"
   end
end #=> nil
# create an object instance
h = Hello.new #=> #<Hello:0x00000100a2f520>
# call a method
h.hello #=> nil
Hello

ECMAScript:

1
2
3
4
5
6
7
8
// create object using a function
function Hello() { this.hello = function() { alert("Hello!") } };
// create instance
h = new Hello();
h.hello(); // pops up an alert
// alternatively we can use an object literal
h = {hello: function() { alert("Hello!") } };
h.hello();

Class based inheritance purports many organizational benefits. Do prototype based languages share them? The paper looks at each benefit in turn and shows approaches to add these features into classless languages or even improve upon them.

Behaviour Sharing:
In class based programming languages code re-use is easy, you can inherit from a class and you’ll get access to that class’s methods and member variables.

In prototype based languages a clone/copy method is written on a prototypical object which can define which data members and methods are copied to the new object. This idea can be refined to an object whose sole purpose is to supply behavior, a trait. This can be a parent object of the new object to provide its functionality. As this trait object is shared amongst all clones, any changes to it affect the clones, just as with the class and subclass of class based programming. Here’s an example on behavior sharing in a prototype based language (Io):

1
2
3
4
5

1
2
3
4
5
<span class='line'><span class="nx">A</span> <span class="o">:=</span> <span class="nb">Object</span> <span class="nx">clone</span>
</span><span class='line'><span class="nx">A</span> <span class="nx">m</span> <span class="o">:=</span> <span class="nx">method</span><span class="p">(</span><span class="nx">write</span><span class="p">(</span><span class="s2">&quot;in A\n&quot;</span><span class="p">))</span>
</span><span class='line'><span class="nx">A</span> <span class="nx">m</span>
</span><span class='line'><span class="nx">B</span> <span class="o">:=</span> <span class="nx">A</span> <span class="nx">clone</span>
</span><span class='line'><span class="nx">B</span> <span class="nx">m</span>
</span>

This will print:

1
2
in A
in B

Representation Sharing:
In class based languages, subclassing gives the child class access to the member variables of the parent, its representation. In prototype based languages, a trait object provides only behavior, not data. This job is performed by a prototype instance of the object, one every other object will be cloned from. Its clone method will copy across its data slots (member variables) with any default values the programmer has provided. This is called a “Data Parent”.

In Ruby:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A
  def initialize
    @member = "fred"
  end
 def method_a
   puts "in A with #{@member}"
 end
end

class B < A
  def method_b
    puts "in B with #{@member}"
  end
end

A.new.method_a
B.new.method_b

#in A with fred
#in B with fred

In IO:

1
2
3
4
5
6
7
8
9
Food := Object clone

pizza := Food clone
pizza name := "Meat Feast"
pizza price := 10

pasta := pizza clone
pasta name = "Macaroni Cheese"
pasta price = 5

Dynamic Behaviour Changes – Changing an instance’s class and dynamic inheritance:
Tom: This was a section of particular interest, mainly because I think it’s not quite as skewed as they portray it. The idea is that because in a prototype based model the reference to the parent object (traits, data) is simply a reference it can be changed very easily depending on the state the object is in. This provides new functionality as the object goes through its life. In a class based model, changing an instance’s class is difficult at best and not a good idea, however the idea of changing behavior based on state is still a useful concept. The authors however seem to miss the concept of composition and delegation, it’s next to trivial to change the behavior of an object simply by changing the object that is providing a function, just take a look at the Strategy Pattern for an example.
Michael: This paper is from 1991, the Gang of Four book on Design Patterns from 1994, so I guess you’re blaming the authors for not knowing about something that hasn’t been published when they wrote their paper.
Tom: Fair point! I wonder when the concept of delegation was first coined?
Michael: According to Wikipedia in a paper from 1986, “Using Prototypical Objects to Implement Shared Behavior in Object-Oriented Systems”.

In ECMAScript:

1
2
3
4
5
6
function circle(){}
circle.prototype
#circle
circle.prototype = function ellipse(){}
circle.prototype
#ellipse

Changing the prototype can entirely change the behavior of an object.

Naming and Categorising:
This feature is vital in any nontrivial system, without being able to find code to serve its purpose the programmer would be constantly rewriting the same code over and over. Class based languages tend to provide a globally accessible name for a set of functions. They also tend to provide namespaces to prevent naming collisions or categorisation. Prototype based languages need to be able to access the prototype anywhere in the system to be cloned, but as they are just regular objects, they have no internal names. The authors of the paper propose that namespace objects can be used to fill this need. Essentially a globally accessible hash map, which can contain nested namespace objects to provide categorisation. This later emerged as a fairly common pattern in ECMAScript.

In Ruby:

1
2
3
class A; end
A
#A

A is a globally defined constant which points to an Object.

In ECMAScript:

1
2
3
4
5
6
TopLevel = window.TopLevel || {}
TopLevel.SecondLevel = function(){
  var member = null;
  function memberMethod(){
  }
}

Note that TopLevel is simply an object literal.

The naming of these type of systems is highly dependent on the structure of the system, this type of naming is called Extensional naming. Intensional naming is the term given to systems where the name of classes is defined by the programmer and not necessarily related to the structure of the system. The authors claim that Extensional naming has a few key advantages:

1) It’s free with a classless language; no other constructs are required for this behaviour to work.
2) The names are also expressions, and as this is code it can have additional interpretations.
3) Intensional naming can become inconsistent quite easily.

Tom’s not convinced any of these reasons are actually advantages: 1) just because a system comes with a feature doesn’t mean you should use it if there is a better alternative available – prototype based models don’t seem to have much of an alternative. 2) because it is code and not a simple lookup this is more complicated and has more potential for error. 3) this is probably valid. Changing a class name does require it to be updated everywhere in the system, but with refactoring tools this is mitigated.

Conclusion:
This was a pretty interesting paper. Personally I’ve always been drawn to Io, but never got past the “playing around” stage with it. Checking out Self also is on my todo list. I think there’s a certain elegance to prototype based object orientation and I’d like to research it a bit more.

Further reading:

Comments

Copyright © 2016 - Michael Kohl - Powered by Octopress