You are looking at historical revision 37330 of this page. It may differ significantly from its current revision.

messages

Description

An implementation of algebraic, abstract and object types. Algebraic types are discriminated variant records, as advocated in the classic "Essentials of Programming Languages" by Friedman, Wand and Haynes and named datatype there. They were ported to Chicken by Felix Winkelmann. Here, they are implemented via messages, i.e functional vectors tagged with a type- and a variant-key, and accessed via case-variant, a small wrapper around bind-case from the bindings module.

Note that the arguments of variant constructors can accept zero or multiple predicates.

Abstract types are based on algebraic types, but hide the variant constructors and export constructor and accessor procedures instead.

Object types export message handlers as well as the messages understood by that handler, the latter being created by constructors of algebraic types. The message handlers are procedures closed over some state, so they can be identified with the objects itself.

All three types implement some sort of inheritance via delegation. To avoid different names for different levels of inheritance, the constructors and the exported routines of abstract types are curried and tagged with keywords. For example, if Foo is a type and #:bar is the tag of some variant, the actual constructor is (Foo #:bar). This has the drawback to be heavy on the fingers but the advantage, that different types may have the same symbol discriminator.

This documentation uses special ellipses meaning

functional vectors

functional-vectors

[procedure] (functional-vectors sym ..)

documentation procedure

fvector

<procedure (fvector . args)</procedure> type constructor

fvector?

[procedure] (fvector? xpr)

type predicate

fvector-ref

<procedure (fvector-ref fv k)</procedure> accessor

fvector-tail

[procedure] (fvector-tail fv k)

accessor

fvector-data

[procedure] (fvector-data fv)

transforms a (possibly nested) fvector into a (possibly nested) list

messages

make-message

[procedure] (make-message type-key key . args)

type constructor

message?

[procedure] (message? xpr)

type predicate

message-of?

[procedure] (message-of? type-key)

returns a predicate which checks, if its only argument is a message of the given type-key

message-key

[procedure] (message-key msg)

returns the key of a message

message-type

[procedure] (message-type msg)

returns the type-key of a message

message-data

[procedure] (message-data msg)

returns the data vector of the message

messages

[procedure] (messages sym ..)

shows the list of available exported symbols of the module when called without argument or the signature of that very argument.

case-variant

[syntax] (case-variant type obj variant ....)

where each variant is either of the form

or as last variant

This one macro replaces in algebraic messages the many accessor routines by destructuring the variants via pattern matching. It destructures obj of type depending on its variants, i.e. matches the variant-key and the argument-list (variant-key (arg ...)) in sequence and invokes the body .... of the first matching pair. The else clause serves as catch-all.

define-algebraic-type

[syntax] (define-algebraic-type Child Parent .. variant ....)

where each variant is either of the form

define-abstract-type

[syntax] (define-abstract-type Child Parent .. variant .... (with clause ....)

defines a hidden algebraic type with variant .... and exports two procedures, the type predicate Child? and a parametrized procedure Child, which, when called with key #:? returns the type predicate, wenn called with another key argument returns the corresponding procedure (Child key) ... , when called without argument returns documentation of all those procedures.

All the procedures are specified in each clause .... either as

or

Note that the first list in each clause looks like a variant in the algebraic type.

Note also that the exported objects in the with clause have access to the hidden algebraic type, which can be processed via algebraic-case

[syntax] (define-object-type Child Parent .. state (msg . code) ....)

where state is either (state ((a a? ...) ...) inv ....) or (state ((a a? ...) ... as as? ...) inv ....) and code is the body of the procedure to be called in case msg is matched. Defines a constructor (Child-instance a ...) or (Child-instance a ... . as) checked with predicate Child-instance?, as well as an algebraic-type Child, which is used to manipulate the instance's state. The constructor's arguments are checked by a? ... and as? respectively.

Dependencies

bindings, checks, symbol-utils

Examples


;; options
(define-algebraic-type Option
  (#:none)
  (#:some (n number?)))

(define (qux opt)
  (case-variant Option opt
    (#:none () #f)
    (#:some (arg) arg)))

(qux ((Option #:some) 5)) ; -> 5
(qux ((Option #:none))) ; -> #f

;; immutable typed lists
(define (0<= x) (and (number? x) (not (negative? x))))
(define-abstract-type List
  (#:null)
  (#:cons (first number?) (rest (List #:?)))
  (with
    ((#:maker args number?)
     (let loop ((args args))
       (if (null? args)
         ((List #:null))
         ((List #:cons) (car args)
                        (loop (cdr args))))))
    ((#:null? (xs (List #:?)))
     (case-variant List xs
       (#:null () #t)
       (else #f)))
    ((#:ref (xs (List #:?))
            (k 0<=))
     (let loop ((xs xs) (k k))
       (case-variant List xs
         (#:null () (error '(List #:ref)))
         (#:cons (a as) (if (zero? k)
                          a
                          (loop as (- k 1)))))))
    ((#:tail (xs (List #:?))
             (k 0<=))
     (let loop ((xs xs) (k k))
       (case-variant List xs
         (#:null () xs)
         (#:cons (a as) (if (zero? k)
                          xs
                          (loop as (- k 1)))))))
    ))

(define as0123 ((List #:maker) 0 1 2 3)) 

((List #:ref) as0123 2) ; -> 2
((List #:null?) ((List #:tail) as0123 4)) ; -> #t

;; objects
(define-object-type Rect
  (state ((x% (cell-of? number?))
          (y% (cell-of? number?))
          (w% (cell-of? number?))
          (h% (cell-of? number?)))
    #t)
  ((#:x) (x%))
  ((#:y) (y%))
  ((#:w) (w%))
  ((#:h) (h%))
  ((#:x! (x number?)) (x% x))
  ((#:y! (y number?)) (y% y))
  ((#:w! (w number?)) (w% w))
  ((#:h! (h number?)) (h% h))
  ((#:move! (dx number?) (dy number?))
   (let ((x (x%)) (y (y%)))
     (x% (+ dx x)) 
     (y% (+ dy y))
     (list x y)))
  ((#:scale! (r number?))
   (let ((w (w%)) (h (h%)))
     (w% (* r w))
     (h% (* r h))
     (list w h)))
  )

(define rect (Rect-instance (cell 0) (cell 0) (cell 1) (cell 1)))

(define-object-type Square Rect
  (state ((parent Rect-instance?))
    (= (parent ((Rect #:w))) (parent ((Rect #:h)))))
  ((#:parent)  parent)
  ((#:w! (w number?))
   (let ((old (parent ((Rect #:w)))))
     (parent ((Rect #:w!) w))
     (parent ((Rect #:h!) w))
     old))
  ((#:h! (h number?))
   (let ((old (parent ((Rect #:h)))))
     (parent ((Rect #:w!) h))
     (parent ((Rect #:h!) h))
     old))
  ((#:scale! (r number?))
   (let ((old-w (parent ((Rect #:w))))
         (old-h (parent ((Rect #:h)))))
     (parent ((Rect #:scale!) r))
     (list old-w old-h)))
  )

(define square
  (Square-instance
    (Rect-instance (cell 0) (cell 0) (cell 1) (cell 1))))

(square ((Square #:w))) ; -> 1
(square ((Square #:w!) 50)) ; -> 1
(square ((Square #:w))) ; -> 50
(square ((Square #:h))) ; -> 50

(define-object-type Baz
  (state ((a number?) as number?) #t)
  ((#:x) a)
  ((#:xs) as))

(define baz (Baz-instance 0 1 2 3))

(baz ((Baz #:x))) ; -> 0
(baz ((Baz #:xs))) ; -> '(1 2 3)
(baz ((Baz #:invariant?))) ;-> #t

Last update

Mar 01, 2019

Author

Juergen Lorenz

License

Copyright (c) 2015-2019, Juergen Lorenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission. 
  
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Version History

0.4
dependency on simple-cells removed and added in test-dependencies
0.3
dependency on simple-cells added
0.2
dependency on symbol-utils added
0.1
initial import