(lispkit type) provides a simple, lightweight type abstraction mechanism. It allows for creating new types at runtime that are disjoint from all existing types. The API for type creation and management is purely procedural. There is no explicit representation of types.
New types are created with function
make-type accepts one argument, which is a type label. The type label is an arbitrary value that is only used for debugging purposes. Typically, symbols are used as type labels.
The following line introduces a new type for intervals:
(define-values (new-interval interval? interval-ref make-interval-subtype) (make-type 'interval))
(make-type 'interval) returns four functions:
new-intervalis a procedure which takes one argument, the internal representation of the interval, and returns a new object of the new interval type
interval?is a type test predicate which accepts one argument and returns
#tif the argument is of the new interval type, and
interval-reftakes one object of the new interval type and returns its internal representation.
interval-refis the inverse operation of
make-interval-subtypeis a type generator (similar to
make-type), a function that takes a type label and returns four functions representing a new subtype of the interval type.
Now it is possible to implement a constructor
make-interval for intervals:
(define (make-interval lo hi) (if (and (real? lo) (real? hi) (<= lo hi)) (new-interval (cons (inexact lo) (inexact hi))) (error "make-interval: illegal arguments" lo hi)))
make-interval first checks that the constructor arguments are valid and then calls
new-interval to create a new interval object. Interval objects are represented via pairs whose car is the lower bound, and cdr is the upper bound. Nevertheless, pairs and interval objects are distinct values as the following code shows:
(define interval-obj (make-interval 1.0 9.5)) (define pair-obj (cons 1.0 9.5)) (interval? interval-obj) ⟾ #t (interval? pair-obj) ⟾ #f (equal? interval-obj pair-obj) ⟾ #f
The type is displayed along with the representation in the textual representation of interval objects:
#interval:(1.0 . 9.5).
Below are a few functions for interval objects. They all use
interval-ref to extract the internal representation from an interval object and then operate on the internal representation.
(define (interval-length interval) (let ((bounds (interval-ref interval))) (- (cdr bounds) (car bounds)))) (define (interval-empty? interval) (zero? (interval-length interval)))
The following function calls show that
interval-ref fails with a type error if its argument is not an interval object.
(interval-length interval-obj) ⟾ 8.5 (interval-empty? '(1.0 . 1.0)) ⟾ [error] not an instance of type interval: (1.0 . 1.0)
(make-type type-label) [syntax]
Creates a new, unique type, and returns four procedures dealing with this new type:
- The first procedure takes one argument returning a new object of the new type wrapping the argument
- The second procedure is a type test predicate which accepts one argument and returns
#tif the argument is of the new type, and
- The third procedure takes one object of the new type and returns its internal representation (what was passed to the first procedure).
- The fourth procedure is a type generator (similar to
make-type), a function that takes a type label and returns four functions representing a new subtype of the new type.
type-label is only used for debugging purposes. It is shown when an object's textual representation is used. In particular, calling the third procedure (the type de-referencing function) will result in an error message exposing the type label if the argument is of a different type than expected.