Dylan Design Notes

#21: Result Type Declarations	(Addition)
Copyright (c) 1993-1994, Apple Computer

Version 1, March 1993

This design note introduces a way to declare the result types of 
methods and generic functions.  This addition is intended to make 
code more self-documenting and allow for better compiler 
optimization.  Type declarations will be checked at run time unless 
they can be proven at compile time to be satisfied always.

-------------------------------------------------------------------

Parameter lists are extended to include value declarations, which 
must come at the end of the parameter list and are separated from the 
parameters by #values.  Value declarations are of the form {variable-
name | (variable-name type-expression)}.

At the time a function is defined, each value declarations type-
expression is evaluated to produce a value type.   If type-expression  
is not specified for a particular value declaration, the value type 
defaults to <object>.  When the function is invoked, each value 
returned must be an instance of the corresponding value type, or else 
an error of type <type-error> will be signaled.

? (define-method plus (x y #values (total <integer>)) (+ x y))
? (plus 22 3)
25
? (plus 1.5 7)
;type-error

The variable-name never comes into scope; it exists only 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.

(define-method always (object #values function)
   (method (#rest ignored 
            #values (object (object-type object)))
      object))

The last value declaration can be preceded by #rest to indicate a 
variable number of values returned.  A rest value declaration is a 
value declaration preceded by #rest.  A required value declaration is 
a value declaration not preceded by #rest.  

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 signaled.

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.

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.

(define numbers (method ((start <integer>) (count <integer>)
                         #values (end <integer>)
                                 #rest (number <integer>))
  (bind ((end (+ start count)))
    (apply values end (range from: start through: (- end 1))))))

If a parameter list does not contain #values, it defaults to #values 
#rest (x <object>), i.e. the function can return any number of values 
of any type.

Generic functions can have value declarations in the parameter list 
used in define-generic-function.  The values returned by the generic 
function will be instances of the value types.  

(define-generic-function floor/ ((numerator <real>) 
                                 (denominator <real>)
                                 #values (quotient <integer>)
                                         (remainder <real>)))

(define-method floor/ ((numerator <small-integer>)
                       (denominator <small-integer>)
                       #values  (quotient <integer>)                 
                                (remainder <small-integer>))
  ...)

Rather than adding run-time checking to the gene ric function 
dispatch, the parameter list congruency rules are augmented to 
require each method added to the generic function to have congruent 
value declarations, defined as follows.  add-method, define-method, 
define-class, etc. signal an error if this requirement is violated.

If the generic function's parameter list does not contain a rest 
value declaration:
	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:
	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.

If a generic function is implicitly defined by define-method or  
define-class, its value declarations default to #values #rest (x 
<object>) which imposes no restrictions on its methods.

-------------------------------------------------------------------

The two reasons for the change are to make more compiler optimization 
possible by giving the compiler more information about types, and to 
make code more readable, again by supplying more information about 
types.

We chose the syntax to be as consistent as possible with the rest of 
Dylan.  We included variable names in the value declarations, rather 
than using type-expressions alone, because names are useful to reduce 
confusion when there is more than one value, and because it makes the 
syntax consistent between the parameters and values portions of a 
parameter-list.  In any case these variable names have no effect on 
the runtime.  We are considering changes to this syntax in the 
future.

The type in a value declaration is called a value type rather than 
a value specializer so no one thinks it somehow affects method 
dispatch.

The semantics are incontrovertible, except for the question of 
whether returning a different number of values than declared should 
do what is proposed, return exactly that many values, or signal an 
error.  Returning the declared number of values, as proposed, 
potentially allows a more efficient implementation because the caller 
does not have to check the number of values and the callee does not 
have to return a value count.

The parameter list congruency rules ensure that the number and types 
of values returned by a method are valid for the generic function.  A 
method can have stronger constraints on its values than the generic 
function as a whole, if desired, and can return additional values 
specific to that method if the generic function allows it with a rest 
value declaration (which is the default).

If a generic function is implicitly defined by define-method,  
define-class, or define-slot, it is not given any value declarations.  
A programmer who wants value declarations on a generic function can 
write an explicit define-generic-function.  In any case it is 
important for all implicit definitions of generic functions to be 
consistent in this respect.

Because of the way the values declaration defaults, this proposal 
does not change the behavior of any program that could be written in 
Dylan before this feature was added.

