One thing that catches me all the time is that when writing examples in a functional language, the continuation of call/cc is on the left of the call to call/cc.
Rewriting your example as imperative statements might be more understandable to non-functional[1] programmers like me:
x = call/cc ...
print x ;; <- this is the continuation
Of course this does not work well with your nice reduction based explanation, but can be made to work if you go for a CPS transformation based explanation.
For sure! Like I said, this is very similar to fork(), and you've basically called out why :)
pid = fork();
if (pid != 0) {
printf("parent!");
} else {
printf("child!");
}
// Displays, in principle, though in any order:
// parent!
// child!
`fork` captures "the rest of the program" -- the part after the `;` on `fork()`, although confusingly including the `which = _` -- and causes it to be run twice, each with a different return value (which really means "passed to the continuation").
> the continuation of call/cc is on the left of the call to call/cc.
Kiiind of. It's really around the call to call/cc -- the "101" and "5" in this example are to the right, while the "+" is to the left:
(+ (call/cc (\k. k 42)) 101 5)
= (\k. k 42) (\x. (+ x 101 5))
= (\x. (+ x 101 5)) 42
= (+ 42 101 5)
In terms of a syntax tree, the continuation is above the call to call/cc. The left/right distinction only really shows for super-simple examples. But, conversely, the argument for function application is always below the application sense, so there's a very reliable duality here.
(Another, slightly twisted perspective is that a syntax tree is, after all, a graph; and following the parent of a node leads to a subgraph just as well as following a child. The fact that this subgraph is "upside-down" is just an extra detail ;), but you can use this subgraph during reduction just like you would a child subgraph. In this way, call/cc is literally just function application, but applying the parent subgraph instead of a normal child.)
((There's a third, incredibly boring member of this family... we can pass one child to another with `apply`, and pass the context to a child with `call/cc`. What lets us pass a child to the context? `id`, or the no-op operator!))
One thing that catches me all the time is that when writing examples in a functional language, the continuation of call/cc is on the left of the call to call/cc.
Rewriting your example as imperative statements might be more understandable to non-functional[1] programmers like me:
Of course this does not work well with your nice reduction based explanation, but can be made to work if you go for a CPS transformation based explanation.[1] pun fully intended: