Generic Functions#

Methods in Dylan aren’t tied directly to a particular class like they are in many other languages. Instead a method belongs to a generic function (GF). Which method actually gets called is decided by the types of all of the required arguments passed to the GF. Consider the built-in add generic function:

define open generic add (seq :: <sequence>, new-element) => (new-seq :: <sequence>);

This says that any method defined on add must accept two required arguments. The first argument must be a <sequence> and the second can be anything, since no type was declared for it. The methods must return a single value which is a <sequence>.

Also:

  1. The methods on this GF may not accept any optional (keyword) arguments.

  2. The “open” adjective tells the compiler that other libraries may add methods to this GF.

If you try to define a method on add that breaks any of the above rules, you’ll get a warning from the compiler.

Note that the define generic form doesn’t contain any actual code; it merely defines an API to which all other methods with the same name must conform. We could define a very general method on add this way:

define method add (seq :: <sequence>,  new-element) => (new-seq :: <sequence>)
  concatenate-as(type-for-copy(seq), list(new-element), seq)
end;

Now, let’s say you decide it would be good to have a method specifically for <vector> because you think it will be more efficient than the default implementation, even though the default will do the right thing. You could write this:

define method add (v :: <vector>, new-element) => (new-v :: <vector>)
  let new-vector = make(<vector>, size: v.size + 1);
  map-into!(new-vector, identity, v);
  new-vector[v.size] := new-element;
  new-vector
end;

(Note that the above is just an example and isn’t intended to be super-efficient.)

Now, if you use add(#[1, 2, 3], 4) the method for <vector> will be called because the first argument is a <vector>, and if you use add(#(1, 2, 3), 4) then the first method will be called because the first argument is a <list>, which is a <sequence> but is not a <vector>.

Very often, the compiler can figure out which method to call and so no run-time overhead for dispatch is incurred in those cases. (The Open Dylan IDE has a tool to show whether method dispatch was optimized.)

Note that the second method above has a more specific return type. This can aid in compiler optimization also.