I'm learning about CPS with Racket, and I've managed to write up these functions:
;lift a regular single-arg function into CPS
(define (lift/k f)
(lambda (x k)
(k (f x))))
;compose two CPS functions
(define (compose/k f g)
(lambda (x k)
(g x (lambda (y)
(f y k)))))
They seem to work correctly
(define (is-two/k x k)
(k (= x 2)))
(define is-not-two/k (compose/k (lift/k not) is-two/k))
(is-not-two/k 3 display)
(is-not-two/k 2 display)
#t#f
I'm wondering if these functions are still "true CPS". Have I messed up "true" continuation-passing with these functions? Is it kosher to use function composition techniques in CPS? Is it encouraged? Or would it be considered a "compromise" to do this? Is there a more CPS-y way to do it?
Yes I know I just asked 5 questions, but the basic idea behind them all (which I'm not sure I understand correctly) is the same. Explanations in other Lisps, Haskell, Erlang, or other functional languages are fine.
The continuation-passing-style transform can be partial, or complete. You're usually working with a system where certain primitives (+, -, etc.) are stuck in non-cps-land. Fortunately, CPS works fine either way.
The steps in CPSing:
So, in your code, your 'lift/k' is essentially treating its given function as being primitive: note that the body of lift/k calls 'f' in non-tail position. If you want not to treat the lifted function as a primitive, you must go in and rewrite it explicitly.
Your 'compose' function composes two CPSed functions, but is not itself in CPS (that is, you're assuming that 'compose' is primitive. You probably want to CPS it. Note that since it just returns a value straight off, this is simple:
;compose two CPS functions
(define (compose/k f g k)
(k (lambda (x k)
(g x (lambda (y)
(f y k))))))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With