Subject: bug#70510: Small documentation error for case-lambda*

From: jeremy <[email-masked]>
Date: Sun, 21 Apr 2024 12:08:38 -0400

Hi,
I've been familiarizing myself with scheme and guile by haphazardly reading documentation and experimenting, and I believe I have found an error in the docs here: https://www.gnu.org/software/guile/manual/html_node/Case_002dlambda.html

The example at the bottom of the page reads as follows:
(define f
  (case-lambda*
    ((a #:optional b) 'clause-1
    ((a #:optional b #:key c) 'clause-2)
    ((a #:key d) 'clause-3)
    ((#:key e #:rest f) 'clause-4)))

(f) ⇒ clause-4
(f 1) ⇒ clause-1
(f) ⇒ clause-4
(f #:e 10) clause-1
(f 1 #:foo) clause-1
(f 1 #:c 2) clause-2
(f #:a #:b #:c #:d #:e) clause-4

;; clause-2 will match anything that clause-3 would match.
(f 1 #:d 2) ⇒ error: bad keyword args in clause 2

However, the penultimate example `(f #:a #:b #:c #:d #:e)` does not appear to match clause 4 at all when I try it at the guile REPL, and I'm not sure how it ever could have. I believe it is matching `clause-2`, binding `a` to `#:a`, `b` to `#f`, and treating the last 4 parameters as keyword arguments matching #:b and #:d - I get an "Unrecognized keyword: #:b" error.

Perhaps a more useful example definition would be:

(define f
  (case-lambda*
    ((a #:optional b) (list 'clause-1 a b))
    ((a #:optional b #:key c) (list 'clause-2 a b c))
    ((a #:key d) (list 'clause-3 a d))
    ((#:key e #:rest f) (list 'clause-4 e f))))

as it reveals not only which clause is matched but what the bindings end up as, making it more clear _how_ it matched. For example, the last example alludes to the fact that interaction between #:optional and #:key arguments can be surprising, but it's much clearer with a working example:

(f) ⇒ (clause-4 #f ())
(f 1) ⇒ (clause-1 1 #f)
(f #:e 10) ⇒ (clause-1 #:e 10)
(f 1 #:foo) ⇒ (clause-1 1 #:foo)
(f 1 #:c 2) ⇒ (clause-2 1 #f 2)
(f #:a 2 #:c #;d)  ⇒ (clause-2 #:a 2 #:d)
(f 1 2 3 #:e 4) ⇒ (clause-4 5 (1 2 3 #:e 4))

;; clause-2 will match anything that clause-3 would match.
(f 1 #:d 2) ⇒ error: unrecognized keyword #:d in clause 2

;; clause-2 will also match many things that you might expect clause-4 to match
(f #:e 1 2 3 4) ⇒ (clause-4 1 (#:e 1 2 3 4))
(f 1 #:e 2 3 4) ⇒ Unrecognized keyword: #:e
(f 1 2 #:e 3 4) ⇒ Unrecognized keyword: #:e
(f 1 2 3 #:e 4) ⇒ (clause-4 4 (1 2 3 #:e 4))

Anyway, hope this is helpful! I'm finding the docs very good in general.

Thanks,
Jeremy