[REBOL] Re: Rebol Framework suggestion
From: doublec:acc at: 11-Sep-2001 14:17
> Usually in OOP, the Class defines the methods and properties of an
> object and the objects will implements their own properties based on
> the class...
Not always. What you define above is class based OOP. There is also generic function
based oop as used by languages like Common Lisp and Dylan. Warning, longish discussion
on differences between the different ways of doing OOP below.
In generic function based OOP the methods do not belong to a class. All arguments passed
to a method are used in resolving what method to call. This is known as multiple dispatch.
It's similar to C++ overloading, but determined by run time types rather than compile
time types. So for example, in C++:
class Window {};
class Event {};
class ApplicationWindow1 : public Window {};
class LeftClickEvent : public Event {};
void HandleEvent(Window* window, Event* event);
void HandleEvent(ApplicationWindow1* window, LeftClickEvent* event);
Event* event = new LeftClickEvent;
Window* window = new ApplicationWindow1;
HandleEvent(window, event);
This calls HandleEvent(Window*, Event*) rather than HandleEvent(ApplicationWindow1*,LeftClickEvent*)
as you might want. This is because the dispatch is based on the compile time type of
the arguments.
Now you could make HandleEvent a virtual function to get run time dispatch. But then
the dispatch occurs only on the first argument, the Window type. How to also dispatch
on the second argument? For example, in C++ with virtual functions:
class Event {};
class LeftClickEvent : public Event {};
class Window
{
public:
virtual void HandleEvent(Event* event);
virtual void HandleEvent(LeftClickEvent* event);
};
class ApplicationWindow1 : public Window
{
public:
virtual void HandleEvent(Event* event);
virtual void HandleEvent(LeftClickEvent* event);
};
Event* event = new LeftClickEvent;
Window* window = new ApplicationWindow1;
window->HandleEvent(event);
Again, you might expect HandleEvent(LeftClickEvent*) of the ApplicationWindow1 class
to be called but this does not happen. Instead HandleEvent(Event*) of the ApplicationWindow1
class is called. Why? Because the static type of the second argument is an Event*. The
run time type is a LeftClickEvent*. Method dispatch on arguments for C++ methods are
for the static type. The other problem occurs with every Window class needing to know
about every Event class. When a new Event class is created you must go back and modify
every existing Window class to add a handler for it.
Note that Java and Smalltalk have similar problems as C++ here. This is the main reason
for the existance of the Visitor pattern - workarounds for the lack of multi method dispatch.
A generic function based OOP system dispatches all arguments on the run time type of
the arguments, and allow extending the methods of a class without having to go back and
re-edit the class. This is because methods do not 'belong' to a class. They belong to
'generic functions' which contain collections of methods. When a generic function is
called all the methods that belong to that generic are examined and the method that takes
the most specific arguments that are passed is called. So in Dylan, the following example
works as expected:
define class <window> (<object>) end;
define class <application-window1> (<window>) end;
define class <event> (<object>) end;
define class <left-click-event> (<event>) end;
define generic handle-event(window :: <window>, event :: <event>);
// Methods look stand-alone but they are implicitly belong to the generic
// function of the same name.
define method handle-event(window :: <application-window1>, event :: <left-click-event>)
// do something
end;
define method handle-event(window :: <window>, event :: <event>)
// do something else
end;
let event :: <event> = make(<left-click-event>);
let window :: <window> = make(<application-window1>);
handle-event(window, event);
Here the handle-event method called is the one that has the <application-window1> and
<left-click-event> arguments. Or any other method that fits into the inheritance hierarchy.
I believe this form of OO, as used by Dylan and CLOS, is easier to use than the C++/Java
form of class based OO. Extension without modifying existing classes is easier, and multi-method
dispatch is something you wonder how you managed to live without when you get used to
it. Another language that uses multi-method dispatch is Cecil , and I'm sure there are
others.
If you are thinking of implementing OO ideas on top of Rebol, you might like to consider
some of the alternative OO systems. Here's some links:
Dylan:
http://www.double.co.nz/dylan
Cecil:
http://www.cs.washington.edu/research/projects/cecil/www/cecil-home.html
Common Lisp (CLOS):
http://ww.telnet.net/cliki
Please note that I include these links and the examples above not as advertisements for
using other languages. I include them so you can get a feel for how different OO systems
exist, possibly easier to use than standard class based OO, and how you might like to
include some of the ideas in Rebol if you are looking to create an OO system for Rebol.
Chris.