I spent the last week working on KLOS and I’m happy to see that one of examples now run on Kawa, so success!
KLOS is a port of TinyCLOS to Kawa with some changes:
In TinyCLOS, the instance structure is stored as a vector, and there’s a mapping of closures to instances in order to support entities (generic functions). Because such a mapping already exists, instances are also then stored as closures that return the vector, so that access to both instances and entities is unified via the same lambda interface. This was beneficial also because there is no built-in support of printing circular structures, so the opaqueness of lambdas come in handy for both instances and entities.
In KLOS, I opted to use Records to store instances (same as R7RS-TinyCLOS aka Virgo). Without funcallable objects in Scheme, however, the mapping of closures to instances is still necessary. What this means that instances and entities have separate accessors: instance-ref
and instance-set!
reads into the slots vector inside the instance
record; and entity-ref
/entity-set!
need to first looks up the record using the entity closure.
In Swindle (TinyCLOS for Racket), because Racket has callable structs, everything is actually represented the same way, but for some reason (that Eli has forgotten and can’t quite explain to me), the distinction of allocate-instance
and allocate-entity
still exists in the source code.
In all existing TinyCLOS variants, init arguments are handled using a helper function called getl
which is used to lookup values by key. In KLOS, running under Kawa with its built-in support for DSSSL keywords, I have replaced all the usages of getl
with #!key
in the lambda list directly.
;; Instead of this:
{define {foo #!rest initargs)
(let ((a (getl initargs 'a #f))
...))
;; Write it directly like this:
(define (foo #!key a) ...)
I thought this ought to be a simple and straight-forward change, but progress came slower than expected because now I’ve had to learn more about how keywords are handled, specifically the handling of #!key
and #!rest
together, and how to pass keywords arguments through a series of functions. Turns out Kawa has special syntax supporting the merging of keywords arguments, but I also learned later on (via testing on Gambit Scheme) that (apply func args)
works without using non-portable syntax.
(define (bar #!rest rest #!key a b) (list a b rest))
;; When passing the argument directly, the keywords passed to FOO1 do not get processed by BAR
(define (foo1 #!rest rest) (bar rest))
(foo1 a: 1 b: 2)
> (#f #f ((a: 1 b: 2)))
;; Using Kawa @: syntax, the two arglists are spliced into one
(define (foo2 #!rest rest) (bar @:rest))
(foo2 a: 1 b: 2)
> (1 2 (a: 1 b: 2))
;;APPLY does the same thing portably
;;
(define (foo3 #!rest rest) (apply bar rest))
(foo3 a: 1 b: 2)
> (1 2 (a: 1 b: 2))
Additionally, now I have a better sense of the differences between Dylan, DSSSL and Racket’s handling of keywords:
Lambdas are defined like this: (define (foo a (b 'b) #:c c . d) (list a b c d))
Racket supports optional arguments, keyword arguments and rest arguments.
Its basic syntax does not allow arbitrary keyword arguments.
Features are marked using #!optional
, #!rest
and #!key
.
The order of #!key
and #!rest
is flexible and yields different results depending on the order.
It’s the same as DSSSL, except #key
must come after #rest
and there’s no support for #optional
.
For KLOS, I’ve chosen to stick to Dylan semantics while using the DSSSL system, which means not using #!optional
.
I hope to add support for initargs
so that the code snippet from Dylan Design Note #3: Make Class Specification (Addition) runs.
If time permits, I’d also like to add syntax support so we can write define-class
and define-method
.
I’d also like to port bps.dylan
to Scheme as a typing exercise to get a better feel for what’s actual in use and required for the port.
Common Lisp has &allow-other-keys
and Infix Dylan has #all-keys
. Guile also supports :allow-other-key
. Will it be sufficient to just write #!rest r #!key a b c
, because neither Kawa nor Gambit support such a marker.