Up: API
Compose procedures with the ability to control the flow of execution.
Here is the simplest example of composing with continuations, to demonstrate what’s the role of continuations here:
(define proc (compose-cont (lambda (val cont)
;; passing value to the next procedure
;; returning its result
(cont val))
(lambda (val cont)
;; returning any value, as a result of
;; the whole `proc`
'second-step-return)
(lambda (val cont)
;; not reached, as the previous procedure
;; has returned without calling `cont`
(throw 'never-mind))))
(proc 'the-value)
;; => second-step-return
The composed procedure can be made of any arity. The rule here is: if the composed procedure needs to accept N arguments, then each procedure of the composition must accept N+1 arguments, where the N+1’th argument is the continuation procedure. The continuation procedure itself accepts N arguments, and passes them as first arguments to the next procedure of the composition.
Procedures in a composition may return any amount of values - that would be the result of the whole composition. The neat thing is that each procedure is able to receive the result of the rest of the composition, do something with it or return its own result.
The order the procedures are applied is from top to bottom. What if
the last procedure of a composition calls the continuation? In
that case the built-in current-compose-end-handler is called,
which just returns #f by default.
Surely, the current-compose-end-handler can be customized, as it
is a Guile
parameter.
It needs to be a procedure of the same arity as the whole composed
procedure. That means, the end handler doesn’t receive the continuation
procedure. Here is an example of how it could be customized:
(define proc (compose-cont
(lambda (val cont)
(cont val))
(lambda (val cont)
;; the last procedure calling the continuation
;; which applies the current-compose-end-handler
(cont val))))
(proc 'the-value)
;; => #f
;; the default end handler in action
(parameterize ((current-compose-end-handler (lambda (val)
(format #f "Unprocessed value: ~a" val))))
(proc 'the-value))
;; => "Unprocessed value: the-value"
;; the customized end handler in action
Compose procedures, applying them from top to bottom.
For those uncomfortable with the procedure ordering of the Guile’s
native compose procedure, there is a macro compose-top,
which just lets the procedures to be executed from top to bottom.
The flexibility of compose procedure is preserved by
compose-top. Procedures of a composition aren’t expected to be
of the same arity - just the outputs must be matched with the inputs.
Here is an example of compose-top composing procedures of
different arity:
(define proc (compose-top
(lambda (val)
(values val (format #f "New value of: ~a" val)))
(lambda (val1 val2)
val2)))
(proc 'the-value)
;; => "New value of: the-value"
Default value:
#<procedure 7fc498b77ea0 at ice-9/boot-9.scm:809:2 _>
Up: API