My only concern is a boolean-typed keyword where #t is the sensible default.
> I'm thinking that instead of #f a singleton should be used instead that has
> no other purpose.
Gambit internally uses a (macro-absent-obj) for situations like this.
Its default value for keyword arguments if #f though, as is Kawa's.
Gauche's default is #<undef>, and Sagittarius' is #<unspecified>.
Based on my experience with Common Lisp, #f (nil in CL) is the best
default. Keyword args are generally used for optional arguments, and one
usually wants to do things like
(define foo
(lambda/kw (:key this? that? other)
(let ((really? (and this? that?))
(other (or other 123)))
...)))
This is complicated a lot if we need to test for a special
(undef-object?) every time. Argument passing in wrappers is also
complicated if we need to pass (undef-object) instead of #f.
Common Lisp supports "supplied-p parameters"; boolean variables that say
whether a particular argument was given by the caller or not. I
gradually stopped using them altogether, and just used a nil default
value for everything. I also stopped using CL's custom-default-value
support for optional/kw args because the lambda-lists quickly became
unreadable and wrapper functions were a pain (you can't just pass #f for
things you don't need; you have to check each and every opt/kw arg in
the wrapped function's lambda-list to see what the default value is).
IIRC CL even allows the default values to be expressions; replicating
those expressions in wrapper functions, keeping in mind that they can be
verbose and rely on the lexical context of the wrapped function which
you don't have handy at the wrapper, really sets your hair on fire. As
you say, I say it's spinach :)
It's nice that Scheme uses (eof-object) and (eof-object?) instead of #f
for ports, since (read) can return #f. But in the case of lambda/kw the
person writing the procedure can design its arguments to work around
limitations like that. In the case of (read) you can't work around
limitations in the same way, since it has to accept arbitrary input
forms at any point in the stream.