Previous section: Magnitude Comparisons

Dylan reference manual -- Function Defining Forms

6. Functions

Function Defining Forms

Generic Functions and G. F. Methods

The basic tools for defining generic functions and methods are the defining forms define generic and define method.

define generic is used to declare information about the generic function as a whole. It is not strictly necessary to use define generic; generic functions can be created with define method alone.

define [ adjectives ] generic name  parameter-list  	[Definition]
define generic creates a new generic function object. name is a variable name, and is defined as a read-only variable in the current module, containing the new generic function.

The adjectives are words separated by spaces. The allowed adjectives are open and sealed. The adjectives control the level of dynamism of the generic function. See the Controlling Dynamism chapter for more details.

The parameter-list specifies what arguments are acceptable to the generic function, and constrains which methods may be added to the generic function. In its simplest form, the parameter-list is a list of variable names, separated by commas, which represent the required parameters of the generic function. Methods added to the generic function must have parameter lists that are congruent with the generic function's parameter list.

A generic function with no required parameters can contain a single method. Adding a method has the effect of replacing the old method. A complete description of parameter lists and congruency is given in the Function Applicability section. define method is used to add methods to a generic function. It can also be used to create a new generic function that does not already exist.

define [ adjectives ] method name   parameter-list 	[Definition]
       implicit-body  
end [method [ name ]] define method creates a method and adds it to the generic function in name. If the module variable name is not already defined, it is defined as with define generic. Thus, define method will create a new generic function or extend an old one, as needed. Methods added to a generic function must have parameter lists that are congruent with the generic function's parameter list. A complete description of congruency is given in the Function Applicability section.

The adjectives are words separated by spaces. The allowed adjectives are open and sealed. The adjectives control the level of dynamism of the named generic function. See the Controlling Dynamism chapter for more details. The parameters of the method--and the specializers of the parameters--are described in the parameter-list. The specializers declare what kinds of arguments are acceptable to the method. The method can be called only with arguments that match the specializers of the parameters. A complete description of parameter lists is given in the Function Applicability section.

When the method is invoked, it executes the implicit-body. Statements in the implicit-body are executed in order. The method parameters are bound as lexical variables over the scope of the implicit-body.

The following example shows how a generic function double could be defined to work with some built-in classes.

You can define a method for double that is applicable when double is called on a number.

? define method double (thing :: <number>)
    thing + thing;
  end method;
double
? double
{the generic function double}
? double(10)
20
The generic function double is now defined, but because it has a method only for the class <number>, it can be called only on instances of <number>. If you try to call double with another class of argument, you will get an error.
? double("the rain in Spain.")
error: no method for {the generic function double} was found
       for the arguments ("the rain in Spain.")
To operate on a second class of arguments, you have to add another method to double.
? define method double (thing :: <sequence>)
    concatenate (thing, thing);
  end method;
double
? double("the rain in Spain.")
"the rain in Spain.the rain in Spain."

Bare Methods

In Dylan, methods can also be created and used directly: The basic tool for creating methods is the special form method.
method parameter-list 	[Special Form]
  implicit-body
end [method]
	=>  method
method creates and returns method. method is a method that accepts the arguments described by parameter-list and then executes the implicit-body. Statements in the implicit-body are executed in order. The parameter-list describes the parameters for method. A complete description of parameter lists is given in the Function Applicability section.

Here is an example:

? method(num1, num2)
    num1 + num2
  end method
{an anonymous method}
;the second argument to SORT is the test function
? sort(person-list,
       method(person1, person2)
          person1.age < person2.age
       end method)
? begin
    let double = method(number) number + number end;
    double(double(10))
  end
40
The following example defines a method for double that works on functions. When you double a function, you get back a method that accepts arguments and calls the function twice, passing the arguments both times.
? define method double (my-method :: <function>)
    method (#rest args)
      apply (my-method, args);
      apply (my-method, args);
      #f
    end method
  end method;
double
? define constant print-twice = double(print)
print-twice
? print-twice
{an anonymous method}
? print-twice("The rain in Spain. . .")
The rain in Spain. . .The rain in Spain. . .
#f
? print-twice(55)
5555
#f
Bare methods may be called directly or they may be added to generic functions.[15] Usually, however, when you want to add a method to a generic function, you create and add the method in a single step, with define method.
local method-spec1 , method-spec2 , ...;	[Special Form]
local can be used for creating self- and mutually-recursive methods.

Each name is bound to a method specified by the corresponding method-spec . Each method-spec has the same syntax as the method form, except that the word method is optional, and a variable name precedes the parameter list.

local is similar to let, in that it binds variables within the current scope (usually the immediately enclosing begin...end block). The scope of the names also includes the parameter lists and the bodies of the methods. This means that the methods can refer to themselves and to the other methods created by the local form.

? define method root-mean-square (s :: <sequence>)
     local method average (nums)
             reduce1 (\+, nums) / size (nums)
           end average,
           method square (n)
             n * n 
           end square;
     sqrt (average (map (square, s)))
  end method root-mean-square

? root-mean-square (#(5, 6, 6, 7 ,4))
5.692099788303083
? define method newtons-sqrt (x)
     local method sqrt1 (guess)
             if (close? (guess))
                guess
             else
                sqrt1 (improve (guess))
             end if
           end sqrt1,
           method close? (guess)
             abs (guess * guess - x) < .0001
           end close?,
           method improve (guess)
             (guess + (x / guess)) / 2
           end improve;
      sqrt1 (1)
   end method newtons-sqrt

? newtons-sqrt (25)
5.000000000053723

Next section: Parameter Lists