6 Functions
Functions accept zero or more arguments, and return zero or more values. The parameter list of the function describes the number and types of the arguments which the function accepts, and the number and types of the values it returns.
There are two kinds of function, methods and generic functions. Both are invoked in the same way. The caller does not need to know whether the function it is calling is a method or a generic function.
A method is the basic unit of executable code. A method accepts a number of arguments, creates local bindings for them, executes an implicit body in the scope of these bindings, and then returns a number of values.
A generic function contains a number of methods. When a generic function is called, it compares the arguments it received with the parameter lists of the methods it contains. It selects the most appropriate method, and invokes it on the arguments. This technique of method dispatch is the basic mechanism of polymorphism in Dylan.
All Dylan functions are objects, instances of <function>
. Generic functions are instances of <generic-function>
and methods are instances of <method>
.
define generic
or by calling make
on the class <generic-function>
. They are most often created with define generic
.
Generic functions may also be created implicitly by define method
or by slot specifications in class definitions.
A generic function definition includes a parameter list, which constrains the methods that can be added to the generic function; some aspects of the parameter must be matched by any method added. In addition, a generic function parameter list may specify that all keyword arguments are permitted in a call to the generic function.
Parameter list congruency is described on page 91. The complete syntax of define generic
is given on page 362.
The following definition defines a generic function which accepts a single required argument. All methods added to this generic function must also accept a single required argument.
define generic double (thing)The following definition defines a generic function which accepts two arguments of type
<number>
. All methods added to the generic function must accept two required arguments of type <number>
or subtype of <number>
.
define generic average (n1 :: <number>, n2 :: <number>)Generic functions created with
define generic
may be sealed or open. For details of this option, see "Declaring Characteristics of Generic Functions" on page 133.define method
, local
, and method
program constituents. define method
is used to define a method and add it to a generic function in a module binding. local
is used to create local bindings that contain self-recursive and mutually-recursive methods. method
is used to create and return methods for immediate application, for use as function arguments, or for storage in a variable or other data structure. Methods are also created for slot getters and setters when a class is created.
Methods cannot be created with make
.
The parameters and return values of a method are described in its parameter list. The specializers in the parameter list declare the types of the arguments 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 "Parameter Lists" on page 82.
When the method is invoked, it executes its implicit body. Statements in the implicit body are executed in order, in an environment which contains the parameters bound to the arguments.
Methods may be invoked directly (used as functions), or indirectly through the invocation of a generic function.
define method
creates a method and adds it to a generic function in a module variable. If the module variable indicated 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.
The following method accepts a single argument of type <number>
, and returns the number doubled. The method will be added to the generic function in the module binding double
.
define method double (thing :: <number>)
=> nother-thing :: <number>;
thing + thing;
end method;
define method
allows the programmer to control aspects of the sealing of the generic function to which the method is added. For more details, see "Abbreviations for Define Inert Domain" on page 136.
A generic function with no required parameters can contain a single method. Adding a new method has the effect of replacing the existing method.
The complete syntax of define method
is given on page 363.
local
is used for creating methods in local bindings. A single local
declaration may create one or more such methods. These methods may be self-recursive and they may be mutually-recursive with other methods created by the same local
declaration.
local
is similar to let
in that it creates local bindings in the current body. The parameters and the bodies of the methods are within the scope of the bindings. In this way, the methods can refer to themselves and to other methods created by the same local
declaration.
The complete syntax of local
is given on page 377.
define method newtons-sqrt (x :: <number>) local method sqrt1 (guess) // note call to other local method if (close-enough? (guess)) guess else sqrt1 (improve (guess)) // note self-recursive call end if end sqrt1, method close-enough? (guess) abs (guess * guess - x) < .0001 end close-enough?, method improve (guess) (guess + (x / guess)) / 2 end improve; sqrt1 (1) end method newtons-sqrt;
method
statement.Methods created directly can be stored in module variables, passed as arguments to generic functions, stored in data structures, or immediately invoked.
The following example creates a method and stores it in the module variable square. It is appropriate to define a method in this way (rather than with define method) when the protocol of the function being defined does not require multiple methods.
define constant square = method (n :: <number>) n * n; end method;It is sometimes useful to create a method inline and pass it directly to another function which accepts a method as an argument, as in the following example.
// sort accepts a test argument, which defaults to \< sort(person-list, test: method(person1, person2) person1.age < person2.age end method)Methods created directly with the
method
statement may be called directly or they may be added to generic functions. Usually, however, when you want to add a method to a generic function, you create and add the method in a single declarative step, with define method
. method
or local
can be passed to functions and returned from functions. In both cases, the methods retain access to the lexical context in which they were created. Such methods are called closures.
The following example defines a function which returns score-card methods. The method which is returned is closed over the score
parameter. Each time this method is called, it updates the score
parameter and returns its new value.
define method make-score (points :: <number>) method (increase :: <number>) points := points + increase; end method; end method make-score; define constant score-david = make-score(100) define constant score-diane = make-score(400) score-david(0)Each invocation ofÞ 100 score-david(10)
Þ 110 score-david(10)
Þ 120 score-diane(10)
Þ 410 score-david(0)
Þ 120
make-score
creates a new binding for score
, so each closure returned by make-score
refers to a different binding. In this way, assignments to the variable made by one closure do not affect the value of the variable visible to other closures.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 same arguments both times. The method that is returned is closed over the function which was passed in as an argument.
define method double (internal-method :: <function>) method (#rest args) apply (internal-method, args); apply (internal-method, args); #f end method end method;define constant double-dave = double(score-david);
score-david(0)
Þ120 double-david(10)
Þ140 score-david(0)
Þ140
Generated with Harlequin WebMaker