Chapter 6

Functions

Parameter Lists

The parameter list of a function describes the number and types of the arguments that the function accepts, and the number and types of the values it returns.

The parameter list of a generic function is used to define the overall protocol of the generic function. It constrains the methods that may be added to the generic function, through the parameter list congruency rules described on page 93. It may also specify that calls to the generic function may contain any keyword arguments.

The parameter list of a method specifies the types of arguments to which the method is applicable, and declares local bindings to which those arguments will be bound during the execution of the body of the method. It may also declare the return value types of the method.

Kinds of Parameters

Dylan parameter lists support required parameters, rest parameters, keyword parameters, and sometimes a next-method parameter. They also may include return value declarations.

The complete syntax of parameter lists is given in Methods on page 426.

Required parameters correspond to arguments that must be supplied when a function is called. The arguments are supplied in a fixed order and must appear before any other arguments.

Each required parameter may be a name or a name specialized by a type. Specifying a type declares that supplied argument must be a general instance of that type.

A rest parameter allows a function to accept an unlimited number of arguments.* After the required arguments of a function have been supplied, any additional arguments are collected in a sequence, which is passed as the value of the rest parameter. This sequence may be immutable, and it may or may not be freshly allocated. The types of rest parameters cannot be declared.

Keyword parameters correspond to arguments that are optional and may be given in any order. Symbols are used among the arguments to guide matching of arguments to parameters. These symbols are usually written in keyword syntax and so they are known as keywords. Keyword arguments can only be supplied after all required arguments are supplied. Keyword parameters may be specialized, restricting which values may be supplied for them. Keyword parameters may also be given default values to be used when the caller does not supply a value.

Required parameters come first in the parameter list, followed by the rest parameter, if any, and then the keyword parameters, if any. A rest parameter is indicated by the token #rest followed by the name of the parameter. Keyword parameters are indicated by the token #key followed by the keyword parameter specifiers, optionally followed by the token #all-keys.

If #rest and #key are used in the same parameter list, #rest must come first. The rest parameter will be bound to a sequence containing all the keyword arguments and their corresponding values.

A next-method parameter is indicated by the token #next, followed by the name of the parameter. It is not normally necessary to specify a next-method parameter explicitly. If a next-method parameter is not specified by the programmer, define method inserts one with the name next-method. If an explicit next-method parameter is given, it must come after the required parameters and before the rest and keyword parameters. Details of using next-method are given in Calling Less Specific Methods on page 98.

Kinds of Parameter Lists

Each function (generic function or method) has an argument passing protocol specified by its parameter list. The argument passing protocol for a method must be compatible with the argument passing protocol of any generic function to which it is added, as described in Parameter List Congruency on page 93.

The argument passing protocol of a function can be described in one of the following ways:

A method that accepts keyword arguments is said to recognize the keywords mentioned in its parameter list. (A method may, of course, mention them in the parameter list and then ignore their values. It is still said to recognize them.) It is possible for a method to accept keyword arguments in general but not recognize any particular keywords; it does this by specifying #key without any subsequent keyword parameters.

If a generic function that accepts keyword arguments mentions any specific keyword arguments in its parameter list, these are the mandatory keywords of the generic function. Every method added to the generic function must recognize these keywords.

A function may accept all keyword arguments by specifying #all-keys in its parameter list.

When a function that accepts keyword arguments is called, it is said to permit a keyword argument in the call if one of the following is true:

If a function that accepts keyword arguments is called, it will signal an error if called with a keyword argument that it does not permit, or if the arguments following the required arguments are not keyword/value pairs. This is true even if the function specifies #rest.

If a method is called via a generic function or via next-method (rather than directly), the method itself does not check whether it received any keyword arguments it does not permit, nor does it check that the arguments following the required arguments are keyword/value pairs. This check is performed by the generic function or next-method, and is made relative to the call as a whole, not relative to an individual method or the methods remaining to be called.

A call to a function may supply the same keyword argument more than once. When this is done, the leftmost keyword/value pair is used.

Specializing Required Parameters

When you define a generic function or method, you may specify the types of the arguments appropriate for the generic function or method. This is called specializing the generic function or method, or specializing the parameters of the generic function or method.

The following example defines a method specialized on <number>. The method will be applicable when double is called on a general instance of <number>.

define method double (thing :: <number>)
  thing + thing;
end method;

Specialization constrains the values that may be passed as the value of a parameter. The function can be called only with arguments that are instances of the specializers of the corresponding parameters.

Specialization is useful in three ways:

Syntactically, specializers are operands. These operands are executed once when the function is created. They are not re-executed each time the function is called. The value of the operand must be a type.

It is most common for specializers to be constant module bindings or calls to a built-in type constructor such as singleton, limited, or type-union.

There is a convenient syntax for singleton specializers, which is equivalent to explicitly calling singleton in the current lexical scope.

Keyword Parameters

The syntax of a keyword parameter is:

[ keyword ] name [ :: operand ] [ = expression ]

If keyword is not supplied, then name is used to indicate both the keyword and the name of the parameter. If the keyword and name are given independently, the keyword is used when calling the method, and the name is used as the name of the parameter inside the body of the method.

The expression supplies a default value for the parameter. It is used when the method is called and the keyword is not supplied. It is executed each time the method is called and the corresponding keyword argument is not supplied. If no expression is specified, the parameter corresponding to an unsupplied keyword argument is initialized to #f. The expression is executed in a scope that includes all the preceding parameters, including required parameters, the rest parameter (if any), the preceding keyword parameters, and the next-method parameter (if any).

In the following example, all three keyword parameters have default values, and all three use the same name for the keyword and the parameter.

define method percolate (#key brand = #"maxwell-house",
                              cups = 4,
                              strength = #"strong")
  make-coffee (brand, cups, strength);
end method;

The caller can choose which keyword arguments to supply and what order to supply them in:

percolate (brand: #"java", cups: 10);
percolate (strength: #"strong",
           brand: #"starbucks",
            cups: 1);

The following method has two keyword parameters. In each, the name of the keyword and the name of the parameter is specified separately. The first keyword parameter has a default value, the second does not.

define method layout (widget, #key position: the-pos = 0,
                                   size: the-size)
  let the-sibling = sibling (widget);
  unless (the-pos = position (the-sibling))
    align-objects (widget, the-sibling, the-pos, the-size);
end method;
layout(my-widget, position: 100, size: 500);
layout(my-widget, size: query-user-for-size() );

The keyword parameter syntax in which the keyword name and parameter name are given separately is needed to allow keyword names such as position: without forcing the method to use position as a local binding. If a method uses position as a local binding, it cannot access the module binding position (which contains a function). The local binding would shadow the module binding.

All required arguments must be supplied before any keyword arguments can be supplied. The following call to layout will signal an error:

layout(position: 100, size: 500);

Types for Keyword Parameters

When a type is indicated for a keyword parameter in a method, it is the same as establishing a type for a local binding. Specifically, the types of any keyword parameters are not used for method dispatch. Keyword parameter types are not allowed in generic function definitions, and do not figure into parameter list congruency.

The following two method definitions are equivalent:

method (#key X :: <integer>)
  ... X ...
end method;

method (#key X)
  let X :: <integer> = X;
  ... X ...
end method;

If a keyword parameter is given a type, if #f is not an instance of that type, and if the keyword parameter is not given a default value, then the keyword parameter is essentially required. An error of type <type-error> will be signaled if a call to the method does not include the keyword.

The following examples include keyword parameters that include both a type and a default value.

define method find-happiness (#key hint :: <symbol> =  #"here")
  ...
end method find-happiness;

define method find-food (#key hint :: <restaurant>
                              =  lookup-default-restaurant())
  ...
end method find-food;

Return Value Declarations

Parameter lists may include value declarations. Value declarations come at the end of the parameter list and are separated from the parameters by =>. For each return value, a value declaration can specify a name and an operand or just a name if the type is <object>. The complete syntax of value declarations is given in Methods on page 426.

The result of executing the operand at the time the function is defined is a type, called a value type. The name never comes into scope. It is included for documentation and for syntactic consistency with parameters. It is valid for the same name to be used in both one parameter and one value declaration in the same parameter list; this is useful as documentation that a function returns one of its arguments.

The last value declaration can be preceded by #rest to indicate a variable number of return values. A value declaration preceded by #rest is called a rest value declaration. A value declaration not preceded by #rest is called a required value declaration. The value type in a rest value declaration is the type of each one of the remaining individual values, not the type of a conceptual sequence of multiple values.

If a parameter-list does not contain a value declaration, it defaults to => #rest x :: <object>. That is, the function can return any number of values of any type.

A function will always return the number and types of values declared in its parameter-list. More precisely:

Because of the parameter list congruency rules for value declarations (see Parameter List Congruency on page 93) the values returned by a generic function do not have to be checked by the generic function. The check inside a method will always be enough to verify that the return values are valid for the generic function.

define method average (x :: <number>, y :: <number>)
 => mean :: <number>;
 (x + y) / 2
end method;

// Returning multiple values
define method limits (center :: <number>, radius :: <number>)
 => (min :: <number>, max :: <number>);
 values(center - radius, center + radius);
end method;

// The same name used both as a parameter and as a value type
define method rotate (image :: <picture>, rotation-angle :: <number>)

 => (image :: <picture>);
 …
end method;

// This method can return one, two, or three values
define method family (kid :: <person>)
  => (kid :: <person>, #rest parents);
  let mom = kid.mother;
  let dad = kid.father;
  case
    mom & dad => values(kid, mom, dad);
    mom => values(kid, mom);
    dad => values(kid, dad);
    otherwise => kid;
  end case
end method family;

Note that the following example does not declare a return value of type <number>. It declares a return value of type <object>. To specify a type, both the name and the type must be specified. If only one is given, it is taken as the name.

define method average (x :: <number>, y :: <number>)
 => <number>;
 truncate/((x + y), 2);
end method;

Errata: In the published book, the return value declaration is incorrect: => avg :: <number>;

Parameter List Congruency

For any given generic function, the generic function and all methods for that function must have congruent parameter lists. Two parameter lists are congruent if they satisfy the following conditions:

In addition, the value declarations must be congruent, defined as follows:

Parameter Lists of Implicitly Defined Generic Functions

As a general principle, the parameter list of an implicitly defined generic function will impose as few constraints as possible on the methods that may be added. If a more constrained generic function definition is desired, an explicit definition should be used.

The parameter list of an implicitly defined generic function is determined by its method definitions. These method definitions include both methods defined using define method and slot getter and setter methods defined using define class.


* In practice, an implementation may place a reasonable limit on the number of arguments that may be passed to any function.