Parameter ListsDylan parameter lists support required parameters, rest parameters, keyword parameters, and sometimes a special next-method parameter. They also may include return type declarations.
Required parameters correspond to arguments 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 variable name, or a variable name specialized by a type (using the syntax variable :: type). This has the effect of declaring that supplied arguments must be general instances of that type. The singleton specializer syntax can also be used (variable == expression, which is equivalent to variable :: singleton (expression)).
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 new sequence, which is passed as the value of the rest parameter.
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 argument values to parameter variables. We say that "the arguments are keyed by symbols." These symbols are usually written in keyword syntax and so they are informally known as keywords. The arguments and parameters are matched up by keyword name. Keyword arguments can only be supplied after all required arguments are supplied.
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 keywords 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 the section on Calling More General Methods.
The following are sample parameter lists as they would appear in a method definition.(a, b, c) three required parameters (a :: <list>, b, #rest c) two required parameters and a rest parameter (#rest a) a rest parameter, no required parameters (a, b, #key foo, bar) two required and two keyword parameters (#key bar, baz, bim) three keyword and no required parameters (#rest all, #key fee, fi) a rest parameter and two keyword parameters
Argument Passing ProtocolThe argument passing protocol for a generic function or method can be described in one of the following ways, depending on its parameter list:
- It is said to accept keyword arguments if its parameter list specifies #key. The parameter list could also specify #rest.
- A function that accepts keyword arguments is said to accept all keyword arguments if its parameter list specifies #all-keys.
- It is said to accept a variable number of arguments if its parameter list specifies #rest but does not specify #key.
- It is said to require a fixed number of arguments if its parameter list does not specify either #rest or #key.
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 explicitly ignore the values.) It is possible for a method to accept keyword arguments in general, but not recognize any particular keywords.
A generic function that accepts keyword arguments may specifiy a set of keywords which must be recognized by every method added to the generic function. These are the mandatory keywords for the generic function.
A function that accepts keyword arguments is said to permit a keyword argument if the function is a method that recognizes the keyword, the function is a generic function and the keyword is recognized by any of the applicable methods, or the function accepts all keyword arguments.
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.
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 recognize, nor does it check that the arguments following the required arguments are keyword/value pairs.
A call to a function may supply the same keyword argument more than once. When this is done, the value of the leftmost occurrence is used.
Specializing Required ParametersWhen you define a generic function or method, you may specify the classes or identities of the arguments appropriate for the generic function or method. This is called specializing the generic function or method.
The following example defines a method on double which is applicable when double is called with a general instance of <number>. The instance can be a direct instance or an indirect instance of the specializer class. In the example, the argument can be an integer, a float, or any other kind of number.? define method double (thing :: <number>) thing + thing end method ? double(10) 20 ? double(4.5) 9.0Specialization restricts the arguments that may be passed as the value of the parameter. The function can be called only with arguments that match the specializers of the corresponding parameters. If a specializer is a class, the corresponding argument must be a general instance of the class. If the specializer is a singleton (used to indicate an individual object), the corresponding argument must be the singleton's object.
Providing this information offers two key benefits:
Specialized parameters are also used in method dispatch. A generic function chooses among its methods on the basis of the methods' specializers. The generic function chooses the method whose specializers most closely match the classes and identities of the actual parameters.
- It indicates to the compiler (and to anyone reading your code) that an error is signalled if arguments of the wrong type are provided.
- It allows the compiler to optimize your code and possibly improve its performance, due to the restrictions it can infer from the type information you provide.
The following are examples of parameter lists that include specializers:(a :: <window>, b, c) Three required arguments. The first must be a window. (a :: <window>, Three required arguments. b :: <character>, The first must be a window and the second a character. c) (a :: <window>, Three required arguments. b :: <character>, The first must be a window, the second a character, c :: singleton (0)) the third the integer 0. (a, b :: <string>, Two required arguments and a rest argument. #rest c) The second argument must be a string. (a :: <vector>, b, Two required and two keyword arguments. #key foo, bar) The first required argument must be a vector.Specializers are evaluated once, when a method is created. They are not reevaluated each time the method or containing generic function is called.
Specializers will usually be module-variables or singleton specializers. However, they are not restricted to these forms. A specializer can be any expression that evaluates to a type.
Dylan also allows a special syntax for singleton specializers, which is equivalent to explicitly using a singleton as a specializer:(a :: <window>, Three required arguments. b :: <character>, The first must be a window, the second a character, c == 0) the third the integer 0.
Keyword ParametersWhen defining a method that includes keyword parameters, each keyword specifier must have one of the following forms:name or name ( default ) or keyword parameter or keyword parameter ( default )In the first two forms, the name is used to indicate both the keyword and the parameter. In the last two forms, the keyword and parameter are given independently. The keyword is used when calling the method, and the parameter is used to refer to the value inside the body of the method.
The default supplies a default value for the argument. It is used when the method is called and the keyword is not supplied. The default should be an expression. It is evaluated each time the method is called and the corresponding keyword argument is not supplied. If no default is specified, the parameter corresponding to an unsupplied keyword argument is initialized to #f. The default is evaluated 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).
For example:define method percolate (#key brand (#"maxwell-house"), cups (4), strength (#"strong")) make-coffee (brand, cups, strength); end method; define method layout (widget, #key position: the-pos, 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;These methods could be called as follows:percolate (brand: #"folgers", cups: 10); percolate (strength: #"weak", brand: #"tasters-choice", cups: 1); layout (my-widget, position: point (10, 10), size: point (30, 50)); layout (my-widget, size: query-user-for-size() );The extended syntax for declaring keyword arguments (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 parameter name. If a method uses position as a parameter name, it cannot access the function stored in the module variable position. The lexical variable will shadow the module variable.
All required arguments must be supplied before any keyword arguments can be supplied.
Result ValuesParameter lists may include value declarations, which must come at the end of the parameter list and are separated from the parameters by =>. A value declaration can be of the form variable-name ::type-expression, or just variable-name if the type-expression is <object>, just like a required parameter. The result of evaluating the type-expression at the time the function is defined is a type, called a "value type." The variable-name never comes into scope, so it is just there for documentation and for syntactic consistency with parameters. It is valid for the same variable 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, for example.
The last value declaration can be preceded by #rest to indicate a variable number of values returned. 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 =>, it defaults to => #rest x :: <object>, i.e. the function can return any number of values of any type.
A function must always return the number and types of values declared in its parameter-list. More precisely:
* Each value returned by a function must be an instance of the corresponding value type, or else an error of type <type-error> will be signalled.
* If fewer values are returned by the function's body (or by the applicable method if the function is a generic function) than the number of required value declarations in the function's parameter-list, the missing values are defaulted to #f and returned. If #f is not an instance of the corresponding value type, an error of type <type-error> will be signalled.
* If a function does not have a rest value declaration, and more values are returned by the function's body (or by the applicable method if the function is a generic function) than the number of required value declarations in the function's parameter-list, the extra values are discarded and not returned.
Generic functions can have value declarations in the parameter list used in define generic. The values returned by the generic function will be instances of the value types. Rather than adding run-time checking to the generic function dispatch, the parameter list congruency rules are augmented to require each method added to the generic function to have congruent value declarations. add-method, define method, define class, etc. signal an error if this requirement is violated.
If a generic function is implicitly defined by define method or
define class, it is not given any value declarations, so it defaults to => #rest x :: <object>, which imposes no restrictions on its methods.
Parameter List CongruencyFor 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:
- They have the same number of required arguments.
- Each of the method's parameter specializers is a subtype of the corresponding parameter specializer of the generic function.
- both accept keyword arguments,
- both accept a variable number of arguments,
- or both require a fixed number of arguments.
- If the generic function accepts keyword arguments, each method must recognize the mandatory keywords of the generic function.
- If a method accepts all keyword arguments, then the generic function must accept all keyword arguments.
- In addition, the value declarations must be congruent, defined as follows:
- If the generic function's parameter list does not contain a rest value declaration, then:
- the method's parameter list must not contain a rest value declaration.
- the two parameter lists must contain the same number of required value declarations.
- each value type in the method's parameter list must be a subtype of the corresponding value type in the generic function's parameter list.
- If the generic function's parameter list contains a rest value declaration, then:
- the method's parameter list is permitted, but not required, to contain a rest value declaration.
- the method's parameter list must contain at least as many required value declarations as the generic function's parameter list.
- each value type in the method's parameter list must be a subtype of the corresponding value type in the generic function's parameter list. If the method has a rest value type, it corresponds to the generic function's rest value type. If the method has more required value types than the generic function, the extra ones correspond to the generic function's rest value type.