Contributed by Scott Anderson
Dylan polymorphism is significantly different from that of other OO languages. Classes do use the interfaces of other classes and inherit slots (members). However, methods are defined independently of classes. Classes are strictly structures holding type information and variables.
Dylan methods are polymorphic. Each method belongs to a generic function of the same name, and the family of methods implements the function for each class. In other words, if you define a function family named "double", you can be assured that "double" probably means the same thing for each class for which a double method has been implemented. As such, methods exist in a global namespace (or within a module or library).
When you call a method, Dylan looks for the most specific definition for that method that matches the class(es) passed as the argument(s). In the shapes example, if there were a method "draw(shape :: )", when calling "draw(some-var)" Dylan would first look for "draw(var :: )". If it was not defined, Dylan then walks up the class inheritance tree for some-var until it finds a match.
In this way, Dylan can support multiple dispatch. If you define a method "foo(t1 :: , t2 :: )", and another "foo(t1 :: , t2 :: )", then calls to foo will perform the same specificity search as with single dispatch.
Internally to a method, you can call "next-method()", which will return the next more-general match of the method based on the argument types.
Dylan itself is a pain in the ass. The documentation is incomplete, links are broken everywhere, and some of the documentation even disagrees with other documentation. Dylan is not suitable for production work, as there are no commercial-quality compilers and the existing compiler is fairly difficult to use. Each program needs at least three files, defining compiler arguments, library imports and exports, and source. The compiler is also god-awful slow.
library: shapes executable: shapes entry-point: shapes:%main shapes-exports.dylan shapes.dylan
module: dylan-user define library shapes use dylan; use format-out; use format; end library; define module shapes use dylan; use extensions; use format-out; use format; end module;
module: shapes synopsis: Dylan "shapes" example author: Scott Anderson <email@example.com> copyright: Copyright 1999, Scott Anderson Permission granted to distribute freely. define class <shape> (<object>) // initialize a shape slot x :: <integer>, required-init-keyword: x:; slot y :: <integer>, required-init-keyword: y:; end class <shape>; // absolute move origin to a newX and newY for shapes define method move-to(shape :: <shape>, new-x :: <integer>, new-y :: <integer>) => (); shape.x := new-x; shape.y := new-y; end method move-to; // relative move origin by delX and delY for shapes define method r-move-to(shape :: <shape>, del-x :: <integer>, del-y :: <integer>) => (); shape.x := del-x + x(shape); shape.y := del-y + y(shape); end method r-move-to; // rectangle class define class <rectangle> (<shape>) // initialize a rectangle slot width :: <integer>, required-init-keyword: width:; slot height :: <integer>, required-init-keyword: height:; end class <rectangle>; // draw the rectangle define method draw( rec :: <rectangle>) => (); format-out("Drawing rectangle at: (%D,%D), width: %D, height %D\n", x(rec), y(rec), width(rec), height(rec) ) end method draw; // circle class define class <circle> (<shape>) // initialize a circle slot radius :: <integer>, required-init-keyword: radius:; end class <circle>; // draw a circle define method draw( circ :: <circle>) => (); format-out("Drawing circle at: (%D,%D), radius: %D\n", x(circ), y(circ), radius(circ) ) end method draw; define method main(appname, #rest arguments) // throw the shapes in a sequence let scribble = make(<vector>, of: <shape>, size: 2); scribble := make(<rectangle>, x: 10, y: 20, width: 5, height: 6); scribble := make(<circle>, x: 15, y: 25, radius: 8); // loop through and display the magic of polymorphism for (s in scribble) draw(s); r-move-to(s, 100, 100); draw(s); end; // call a rectangle-specific function let r = make(<rectangle>, x: 0, y: 0, width: 15, height: 15); // note the use of the 'setter' syntax // "r.width := 30" and "width(r) := 30" are both equivalent width-setter(30, r); draw(r); exit(exit-code: 0); end method main;
Drawing rectangle at: (10,20), width: 5, height 6 Drawing rectangle at: (110,120), width: 5, height 6 Drawing circle at: (15,25), radius: 8 Drawing circle at: (115,125), radius: 8 Drawing rectangle at: (0,0), width: 30, height 15