I'm just learning CPS and I'm trying to pass the following program to this style
mirror Void = Void
mirror (Node x left right) = Node x (mirror right) (mirror left)
From what I understand I have to pass the continuation the base case and in the recursive case build the construction with lambdas. for the case of a sum it would be
cpsadd n 0 k = k n
cpsadd n succ(m) k = cpsadd n m (\v -> k succ(v))
--where k is the continuation, i.e, the stack.
Another example with list
mult [] k = k 1
mult (x:xs) k = mult xs (\v -> k (x*v))
In that sense I had the idea the first idea
mirror Void k = k Void
mirror (Node x l r) k = Node x (\v -> k r v) (\w -> k v r)
But immediately I realized that I am building the tree without passing the continuation k. So I had the second idea
mirror Void k = k Void
mirror (Node x l r) k = mirror Node x (\v -> k r v l)
Now I do pass the continuation, but when I test it (by hand) I don't get to the base case, so it didn't work either. And it confuses me that I have to call the recursive function twice and flip them to make the mirror.
Any idea? Thankss!
CodePudding user response:
One basic transformation you need to perform often to get to CPS is turning
f x y = g (h x) y -- non-CPS
into
f' x y k = h' x (\r -> g' r y) -- CPS
That is, anytime you need to call a function in the middle of an expression, you instead call the CPS version of that function, and give it as continuation a lambda which finishes the expression. So let's start with your definition for mirror
, and work towards a CPS implementation.
mirror Void = Void
mirror (Node x left right) = Node x (mirror right) (mirror left)
I'll write [e]
to denote that the expression e
needs to be converted to CPS form, and just e
if it has already been transformed. First let's add the k
argument, and wrap the implementations in brackets to indicate they need to be transformed:
mirror Void k = [Void]
mirror (Node x left right) k = [Node x (mirror right) (mirror left)]
Transforming the Void case is easy: you did that already.
mirror Void k = k Void
mirror (Node x left right) k = [Node x (mirror right) (mirror left)]
Now we need to address the first recursive call to mirror right
. We call it immediately, and then give it a lambda (which isn't fully converted yet):
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = [Node x right' (mirror left)]
Now the body of r
has a call to mirror left
that needs to be lifted out:
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = mirror left l
where l left' = [Node x right' left']
Now the body of l
has no recursive calls left, and has exactly the value you wanted to pass to k
to begin with, so the final transformation is easy: just call k
.
mirror Void k = k Void
mirror (Node x left right) k = mirror right r
where r right' = mirror left l
where l left' = k $ Node x right' left'
If you like, you can write that with lambdas instead of where
clauses, but the principle is the same.