A generic function represents zero or more
similar methods. Every method created by means of define
method is automatically contained
within the generic function of the same name. For example, a
programmer could define three methods named display
, each of which acted on a different data type:
define method display(i :: <integer>)
do-display-integer(i);
end;
define method display(s :: <string>)
do-display-string(s);
end;
define method display(f :: <float>)
do-display-float(f);
end;
When a program calls display, Dylan examines
all three methods. Depending on the number and type of arguments to
display, Dylan invokes one of the above methods.
If no methods match the actual parameters, an error occurs.
In C++, this process occurs only at compile time. (It's called
operator overloading.) In Dylan, calls to display
may be resolved either at compile time or while the program is actually
executing. This makes it possible to define methods like:
define method display(c :: <collection>)
for (item in c)
display(item); // runtime dispatch
end;
end;
This method extracts objects of unknown type from a collection,
and attempts to invoke the generic function display
on each of them. Since there's no way for the compiler
to know what type of objects the collection actually contains, it
must generate code to identify and invoke the proper method at
runtime. If no applicable method can be found, the Dylan runtime
environment throws an exception.
Generic functions may also be declared explicity, allowing the
programmer to exercise control over what sort of methods get added.
For example, the following declaration limits all display
methods to single parameter and no return value:
define generic display(thing :: <object>) => ()
Generic functions are explained in greater detail in the chapter on multiple dispatch.