Mastering JavaScript Functional Programming
上QQ阅读APP看书,第一时间看更新

Continuation Passing Style

The preceding code, in which you call a function but also pass another function that is to be executed when the input/output operation is finished, can be considered a case of CPS - Continuation Passing Style. What is this way of coding? A way of putting it, is by thinking about this question: how would you program if using the return statement was forbidden?

At first glance, this may appear to be an impossible situation. We can get out of our fix, however, if we grant this: you are allowed to pass a callback to the called function, so when that procedure is ready to return to the caller, instead of actually returning, it shall invoke the passed callback. In these terms, the callback provides the called function with the way to continue the process, and thus the name Continuation. We won't get into this now, but in Chapter 9, Designing Functions - Recursion, we will study it in depth. In particular, CPS will help to avoid an important recursion restriction, as we'll see.

Working out how to use continuations is sometimes challenging, but always possible. An interesting advantage of this way of coding, is that by specifying yourself how the process is going to continue, you can go beyond all the usual structures (if, while, return, and so on) and implement whatever mechanisms you may want. This can be very useful in some kinds of problems, where the process isn't necessarily linear. Of course, this can also lead to you inventing any kind of control structures, far worse than the possible usage of GOTO statements that you might imagine! Figure 3.1 shows the dangers of that practice!

Figure 3.1: What's the worse that could happen if you start messing with the program flow?
(Note: This XKCD comic is available online at https://xkcd.com/292/.)

You are not limited to passing a single continuation. As with promises, you can provide two or more alternate callbacks. And this, by the way, can provide a solution to another problem: how would you work with exceptions? If we simply allowed a function to throw an error, it would be an implied return to the caller -- and we don't want this. The way out is to provide an alternative callback (that is, a different continuation) to be used whenever an exception would be thrown (in Chapter 12, Building Better Containers - Functional Data Types, we'll find another solution, with Monads):

function doSomething(a, b, c, normalContinuation, errorContinuation) {
let r = 0;
// ... do some calculations involving a, b, and c,
// and store the result in r

// if an error happens, invoke:
// errorContinuation("description of the error")

// otherwise, invoke:
// normalContinuation(r)
}