Next: , Previous: , Up: Top   [Contents][Index]


11 Sub–typing with labels

Labels are types built on top of other types: we put label–types on values of a parent type to handle them locally in a special way. There are two kinds of labels:

The following syntactic bindings are exported by the library (vicare language-extensions labels). All the auxiliary syntaxes are exported by the library (vicare) and reexported by the library (vicare language-extensions labels).

Syntax: define-label-type ?type-name ?clause
Auxiliary Syntax: nongenerative
Auxiliary Syntax: parent
Auxiliary Syntax: constructor
Auxiliary Syntax: destructor
Auxiliary Syntax: type-predicate
Auxiliary Syntax: equality-predicate
Auxiliary Syntax: comparison-procedure
Auxiliary Syntax: hash-function
Auxiliary Syntax: method
Auxiliary Syntax: mixins

Define a new label type. ?type-name must be a syntactic identifier representing the label name.

The clause parent is mandatory and its single argument must be a type annotation.

The following clauses can be used in the same way they are used in define-record-type:

parent type-predicate
equality-predicate comparison-procedure hash-function
method mixins

notice that labels cannot implement interfaces.

The clause constructor may appear zero, one or more times; these clauses define a constructor function to be used with the syntax new. The constructor clause has a syntax similar to lambda/checked; it must have the format:

(constructor ?typed-formals ?body0 ?body ...)

?typed-formals must not specify a return value: the constructor returns a single value of type type-name, and its specification is automatically generated.

The clause destructor may appear zero or one time; this clause defines a destructor function to be used with the syntax delete; this destructor is not used by the garbage collector. The destructor clause has a syntax similar to lambda; it must have the format:

(destructor ?formals ?body0 ?body ...)

where ?formals must specify a single argument of type ?type-name. ?formals must not specify a return value: the destructor should return unspecified values.

The optional clause nongenerative must be used with a single argument being a symbol representing a UID associated with the label type; such symbol is used by type-unique-identifiers and so it allows multimethods to use label types.

Examples of simple labels

The following example defines a label <String> that is just a synonym for <string>:

(define-label-type <String>
  (parent <string>))

(define {O <String>}
  "ciao")

(.length O)     ⇒ 4
(hash    O)     → (string-hash O)

The following example defines a label to represent fixnums returned by comparison procedures (‘-1’, ‘0’, ‘+1’):

(define-label-type <comparison-fixnum>
  (parent (or <non-negative-fixnum> <negative-fixnum>))
  (type-predicate
    (lambda ({parent-pred <type-predicate>})
      (lambda (obj)
        (and (parent-pred obj)
             (fx<=? obj +1)
             (fx>=? obj -1))))))

(is-a? +1 <comparison-fixnum>)  ⇒ #t
(is-a? -1 <comparison-fixnum>)  ⇒ #t
(is-a?  0 <comparison-fixnum>)  ⇒ #t

(is-a? +2 <comparison-fixnum>)  ⇒ #f
(is-a? -2 <comparison-fixnum>)  ⇒ #f

Examples of hash function

Now let’s define a custom hash function (we ignore the parent hash function that gets passed as parent-hash argument):

(define-label-type <String>
  (parent <string>)
  (hash-function
    (lambda (parent-hash)
      (lambda (S)
        (if (string-empty? S)
            0
          (char-hash (string-ref S 0)))))))

(define {O <String>}
  "ciao")

(hash O)     ≡ (char-hash #\c)

Examples of methods

Let’s define a label with a method to increment a fixnum.

(define-label-type <fx>
  (parent <fixnum>)
  (method (incr)
    (fxadd1 this)))

(define {O <fx>}
  10)

(.incr O)       ⇒ 11

Now let’s define a method for adding prefixes and suffixes:

(define-label-type <String>
  (parent <string>)
  (method ({append <String>} {suff <String>})
    (string-append this suff))
  (method ({append <String>} {pref <String>} {suff <String>})
    (string-append pref this suff)))

(define {O <String>}
  "ciao")

(.append O "-suff")             ⇒ "ciao-suff"
(.append O "pref-" "-suff")     ⇒ "pref-ciao-suff"

(.length (.append O "pref-" "-suff"))
⇒ 14

Defining a constructor function

In the following example:

(define-label-type <vec>
  (parent <nevector>)
  (constructor (a b)
    (vector a b))
  (constructor (a b c)
    (vector a b c)))

(new <vec> 1 2)         ⇒ #(1 2)
(new <vec> 1 2 3)       ⇒ #(1 2 3)

we can think of the constructor clauses as generating the following functions definition:

(define/overload ({<vec>-constructor <vec>} a b)
  (vector a b))

(define/overload ({<vec>-constructor <vec>} a b c)
  (vector a b c))

notice that a type signature for the return value has been automatically inserted.

Defining a destructor function

Here we define a bogus destructor function:

(define-label-type <vec>
  (parent <vector>)
  (destructor ({O <vec>})
    `(deleted ,O)))

(define {O <vec>}
  '#(1 2))

(delete O)      ⇒ (deleted #(1 2))

we can think of the destructor clauses as generating the following function definition:

(define (<vec>-destructor {O <vec>})
  `(deleted ,O))

Example of mixin

Here we show how to include mixin clauses in a label definition:

(define-mixin-type <stuff>
  (method (pussy)
    (list 'pussy (.name this))))

(define-label-type <peluche>
  (parent (list <symbol>))
  (method (name)
    (car this))
  (mixins <stuff>))

(define {O <peluche>}
  '(cat))

(.name  O)      ⇒ cat
(.pussy O)      ⇒ (pussy cat)

Next: , Previous: , Up: Top   [Contents][Index]