[plt-scheme] What is the role of promise and force?

From: Richard Cobbe (cobbe at ccs.neu.edu)
Date: Sun May 6 19:15:42 EDT 2007

On Sun, May 06, 2007 at 05:12:07PM -0500, Grant Rettke wrote:
>  What is the role of promise and force?
>  This weekend I read the R5RS spec. Before then I hadn't heard anything
>  about promise and force, in other words, they don't get blogged about
>  or written up much. Why not? Are they important? For what kinds of
>  things are they used?
>  It is suggested that they allow for you to write programs in a true
>  functional style. Are they similar to the goal of Haskell monads in
>  that they let you "do it all functionally?"

Promises get you a limited degree of laziness.  Danny pointed you to
streams, which is one important application of laziness, but it's not the
only one.

As another example, consider a program that needs to construct and process
a complex directed graph.  Each node has a bunch of properties that are
read from disk when the node is created, so node creation isn't cheap.
Further, while the graph isn't complete, it's pretty close.  So, if we
construct the graph in the naive way (for Scheme), creating the first
node would have to pull in a whole bunch of others, for a significant
startup cost.

If we make the edges promises instead of direct references, though, then we
can defer the cost of loading the node on the other end of an edge until we
traverse that edge.  Further, promises cache their results, so once we load
a node, we never have to do it again (if we're a little clever about how we
create our promises).

Now, laziness and side-effects don't always interact very nicely.  While
Haskell solves this problem by sequestering side effects in monads, which
force an order on your computation, Scheme leaves this up to you to solve.
This isn't such an issue if you structure your program carefully.  In the
above example, for instance, we can define all the graph types and
low-level graph manipulation functions in a single module, and then design
that module's interface so that the laziness is completely hidden from the
client.  So we know exactly where the laziness is, and we don't have to
worry about side-effects from the rest of the program and laziness from our
graph interacting in unexpected ways.

(I'm actually doing something like this a program that processes Java
source code.  Nodes are types in the Java program, and edges are
statically-identifiable references from one type to another: supertype,
field type, method argument type, etc.)


Posted on the users mailing list.