Dylan Design Notes

#6: Limited Types	(Addition)

Version 1, March 1993
Copyright (c) 1993-1994, Apple Computer

This design note introduces a new generic function, limited, for 
constructing limited types, and specific methods for creating limited 
integer types and limited collection types.   For example, (limited 
<integer> min: 0 max: 255) and (limited <array> of: <single-float>) 
are useful types.

Limited types are not classes.  See Dylan Design Note #5: 
Regularization of the Type System for the general framework allowing 
types that are not classes.

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

1. Limited Type Constructor

Add a new function, limited, with the following definition:

limited class   #key  => type	[Generic Function]
The function limited returns a limited subtype of class.   The 
available keyword arguments depend on the class.  Not all classes 
support limited; the methods for limited are documented individually.

2. Limited Integer Types

Specify a method for creating limited subtypes of <integer>:

limited (singleton <integer>) #key  min max => type	[G. F. Method]
Returns a limited integer type, which is a subtype of <integer> whose 
instances are integers greater than or equal to min (if min: is 
specified) and less than or equal to max (if max: is specified).  If 
no keyword arguments are specified, the result type is equivalent to 
<integer>.  Limited integer types are not instantiable.

;accepts integers between -1000 and 1000 inclusive.
(define-method f ((x (limited <integer> min: -1000 max: 1000)) ...) 
...)

;accepts all strictly positive integers.
(define-method f ((x (limited <integer> min: 1) ...) ...)

If w, x, y, and z are integers, the following equivalences hold:

(instance? x (limited <integer> min: y max: z))
   iff (and (instance? x <integer>) (<= y x z))

(instance? x (limited <integer> min: y))
   iff (and (instance? x <integer>) (<= y x))

(instance? x (limited <integer> max: z))
   iff (and (instance? x <integer>) (<= x z))

(subtype? (limited <integer> min: w max: x) 
          (limited <integer> min: y max: z))
   iff (and (>= w y) (<= x z))

(subtype? (limited <integer> min: w ...) (limited <integer> min: y))
   iff (>= w y)

(subtype? (limited <integer> ... max: x) (limited <integer> max: z))
   iff (<= x z)


3. Limited Collection Types

Specify a method for creating a limited subtype of a collection 
class:

limited collection-class  #key  of  size  => type	[G. F. Method]
Returns a limited collection type: a subtype of collection-class  
whose instances are limited to have elements of a particular type 
and/or limited to a particular number of elements.  A limited 
collection type is instantiable and accepts the same keyword 
arguments to make as the collection-class from which it is derived.

collection-class  must be instantiable and not sealed, thus among the 
predefined collection classes, the following are acceptable: <table>, 
<array>, <vector>, <stretchy-vector>, <string>, <deque>, and <range>.

If  of: type  is specified, each element must be an instance of type.  
Fetching an element of the collection is guaranteed to return an 
instance of type.    Setting or initializing an element will check 
that the new element is an instance of type  and signal an error of 
type <type-error> if it is not.

If  size: integer  is specified, there must be exactly integer  
elements.  This keyword argument is not accepted if collection-class  
is stretchy (e.g. <table>, <stretchy-vector>, or <deque>).  For 
<array>, the argument can be a sequence of integers.

Here are some example expressions that return useful types:

(limited <array> of: <single-float>)
(limited <vector> of: (limited <integer> min: 0 max: 255))
(limited <stretchy-vector> of: <window>)
(limited <vector> of: <single-float> size: 100)
(limited <table> of: <symbol>)

Two limited collection types are disjoint unless they match exactly, 
because any operation that works on an instance of a supertype must 
work on an instance of a subtype.

 If c1 and c2 are instantiable, unsealed subclasses of <collection>, 
t1 and t2 are types, t3 is a type other than a limited collection 
type, and n1 and n2 are integers, the following equivalences hold:

(subtype? (limited c1 of: t1 size: n1) (limited c2 of: t2 size: n2))
   iff (and (id? c1 c2) (subtype? t1 t2) (subtype? t2 t1) (id? n1 
n2))

(subtype? (limited c1 of: t1) (limited c2 of: t2))
   iff (and (id? c1 c2) (subtype? t1 t2) (subtype? t2 t1))

(subtype? (limited c1 size: n1) (limited c2 size: n2))
   iff (and (id? c1 c2) (id? n1 n2))

(subtype? (limited c1 ...) t3) iff (subtype? c1 t3)


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

Notes:

We needed to specify that two limited collection types are disjoint 
unless they match exactly, because any operation that works on an 
instance of a supertype must work on an instance of a subtype.  Only 
if the element types are identical can the element and element-setter 
operations on the supertype also work on the subtype.  Otherwise 
either element on the subtype could return values that a caller of 
element on the supertype would not expect, or else element-setter on 
the subtype could fail to accept values that element-setter on the 
supertype accepts.

Implementation Notes:

Like any type, limited subtypes must be supported by the generic 
function dispatch mechanism.  If the usual approach is used, where 
the generic function effectively calls object-class and uses that 
class as a key for a table lookup that finds the applicable methods, 
then when the class has limited subtypes used as method specializers, 
an additional dispatch is required.  This is similar to the way 
singletons are usually handled, so the implementation should be 
straightforward.  Perhaps the real cost is implementing the compiler 
optimizations that are made possible by this, which are not required 
by the language but which nearly everyone will want to do.

Type-limited collections can be used to hoist type checking from the 
time when an element is extracted from a collection, back to the time 
when the element is put into the collection, which could be useful 
with any user-defined element type.  Often the compiler can prove the 
type check in (setter element) is unnecessary and optimize it out.  
Limiting the size of a collection allows the compiler to optimize out 
array bounds checking.

