Here is a pattern that is suggested by the fact that Scheme continuations can be reused. It is the only pattern I know that exploits this property. I think that this is a pattern to be explored but avoided, an Anti-pattern. I would like to see better patterns that exploit this language feature, which may indeed be an architecture error in Scheme. (Kawa continuations expire upon use. Chez-Scheme provides a source of continuations that expire after use.) These ideas are mentioned here.
The code is divided into three sections:
The application is to plot a circle knowing its equation as y = ±√(1−x2). Depending on the argument, the square root has 0, 1 or 2 values. Conventional code that calls square root must often take awkward steps to handle these values, or the absence thereof. The conceit of this pattern is that the square root routine should return just once for each of the answers. This makes sense, the best I know, only in context of a heap of further work to do. This is not a new idea but I had not explored it.
To support the pattern we establish the heap as an unordered set of tasks which are 0 argument routines which must not return, but implemented here as a list of tasks— tskList. When a body of code, such as a task, has finished it calls yield which takes no arguments and never returns. Another pattern support is the routine to register a new task — newTsk, which does return. The third support is DoS which is a fanciful form of loop construct. (DoS n p) calls procedure p with all integers from 0 up to n, p must never return, nor does DoS return.
The routine sqrt returns each root. If there are two it registers a task which will return the 2nd root, and then returns the first. If there are no roots, it merely calls yield to ‘give up the CPU’. The routine plot takes x and y, plots the point (prints the coordinate in this toy version) and calls yield.
In the application, gr is the grain—how finely to plot the circle. None of the multiple root complexity appears in the ‘application’. This pattern fits more comfortably in the actors paradigm. It is unfortunate that the task heap implementation is global. The fault is unrelated to the pattern, I think. This obscure version protects the heap.
This small sample shows that routines that never return, always return once, or sometimes return several times may be mixed. I will not admit, however, how long it took me to debug this short piece of Scheme.
Dispensing with the blocking call disabled my intuition of the order of events so as to make a trivial program use a mysterious amount of storage. I was lucky in this case because of the order in which I found the bugs. A bane of current languages is that they encourage you to think sequentially which thwarts use of multiple CPUs, but they also informs our intuition about storage efficiency.
This use case bottoms out in the plotting of points; there is no other purpose of the code. A variation on the code would be to apply some cumulative operation on the points such as finding the area of the circle. To achieve this requires augmenting the language to say that the order of actions that would replace the plot function is not relevant. The above pattern implicitly allows reordering by (implicit) semantics of DoS and sort. In the task were to find the centroid of plotted points we would have a problem with the natural construct such as (set! xc (+ xc x)). That might be replaced by a order free functional as (xa x) where xa is a dynamically produced function that accumulates the x values. It does whatever unnatural is necessary to get the right answer in a concurrent world. Floating point is order sensitive but the order natural to the program is seldom the order that provides the best application accuracy. This is an opportunity to insert logic to improve some cumulative operations for accuracy.
I think that reusable continuations are a bad idea, but they can be exploited by at least one pattern.