Since the first reason can also be done using the values procedure and the second using case-lambda I'm not clear the advantages of using continuation passing style.
...except that the definition of values
specifies that it calls its continuation with multiple arguments.
My favorite example of a problem where continuation passing style is helpful is writing pattern matchers. This is a kind of macro that's like case
on steroids; it takes a value and tries to match its structure against a sequence of patterns built from pairs, symbols (standing for variables) and non-symbol atoms (standing for values). If a match succeeds, then it binds the identifiers in the pattern to the corresponding subparts of the value, and executes a consequent for that pattern. If it fails, then it tries the next pattern.
It's pretty straightforward to write this sort of macro in a form of continuation passing style, using two different continuations to represent "what to do if a match succeeds" (the success continuation) and "what to do if a match fails" (the failure continuations).
Take this (simplified) fragment of a pattern matching macro I once wrote (I apologize if you don't know syntax-case or syntax-rules; and since I adapted it on the fly, I sure hope it works too!). I'm going to focus on the rule that matches a pair pattern. This is a pattern that consists of a pair of two patterns, a head pattern and a tail pattern; it matches pairs whose head matches the head pattern and whose tail matches the tail-pattern.
;;;
;;; Outer "driver" macro; the meat is in pmatch-expand-pattern.
;;;
(define-syntax pmatch
(syntax-rules ()
((pmatch value-expr (pattern . exprs) . clauses)
(let* ((value value-expr)
(try-next-clause
(lambda () (pmatch value . clauses))))
(pmatch-expand-pattern pattern
value
;; success-k
(begin . exprs)
;; failure-k
(try-next-clause))))))
(define-syntax pmatch-expand-pattern
(lambda (stx)
(syntax-case stx ()
;; Cases for constants and quoted symbols omitted, but they're trivial.
;; Match a pair pattern. Note that failure-k is expanded three times;
;; that's why pmatch encapsulates its expansion inside a thunk!
((pmatch-expand-pattern (head-pat . tail-pat) value success-k failure-k)
(syntax
(if (pair? value)
(pmatch-expand-pattern head-pat
(car value)
;; If we successfully match the head, then
;; the success continuation is a recursive
;; attempt to match the tail...
(pmatch-expand-pattern tail-pat
(cdr value)
success-k
failure-k)
failure-k))
failure-k))
;; Match an identifier pattern. Always succeeds, binds identifier
;; to value
((pmatch-expand-pattern identifier value success-k failure-k)
(identifier? (syntax identifier))
(syntax (let ((identifier value)) success-k)))
)))
Note the success-k and failure-k subforms in the pmatch-expand-pattern
macro expressions. These represent expressions that are being used as the "continuation," in a slightly loose term, for the pattern matcher. The success continuation is used when the pattern under consideration matches the value under consideration; the failure continuation is used when it doesn't. The success continuation is, depending on whether we've matched all of the current top-level pattern yet, either an expression that will match the rest of the pattern, or the consequent that we execute when a pattern is done matching. The failure continuations are used when a pattern fails to match, in order to backtrack to the next choice point.
As I mentioned, the term "continuation" is being used a bit loosely in the code above, since we're using expressions as continuations. But this is just a detail about how to implement this as a macro—the algorithm could be implemented purely at runtime with actual procedures as the continuations. (Also, the try-next-clause
procedures are continuations in the literal sense.)