Function applications are delayed, and promises are automatically forced. The language provides bindings that are equivalent to most of the mzscheme and scheme/list libraries. Primitives are strict in the expected places; struct constructors are lazy; if, and, or etc. are plain (lazy) functions. Strict functionality is provided as-is: begin, I/O, mutation, parameterization, etc. To have your code make sense, you should chain side effects in begins, which will sequence things properly. (Note: This is similar to threading monads through your code – only use begin where order matters.)
Mizing lazy and strict code is simple: you just write the lazy code in the lazy language, and strict code as usual. The lazy language treats imported functions (those that were not defined in the lazy language) as strict, and on the strict side you only need to force (possibly recursively) through promises.
A few side-effect bindings are provided as-is. For example, read and printf do the obvious thing – but note that the language is a call-by-need, and you need to be aware when promises are forced. There are also bindings for begin (delays a computation that forces all sub-expressions), when, unless, etc. There are, however, less reliable and might change (or be dropped) in the future.
There are a few additional bindings, the important ones are special forms that force strict behaviour – there are several of these that are useful in forcing different parts of a value in different ways.
The bindings of lazy/force are re-provided by .
expr : any/c
Evaluates expr strictly. The result is always forced, over and over until it gets a non-promise value.
expr : any/c
Similar to !, but recursively forces a structure (e.g: lists).
expr : any/c
Similar to !!, but also wraps procedures that if finds so their outputs are forced (so they are useful in a strict world).
Forces the expr which is expected to be a list, and forces the cdrs recursively to expose a proper list structure.
To avoid dealing with multiple values, they are treated as a single tuple in the lazy language. This is implemented as a multiple-values struct, with a values slot.
x : multiple-values?
Used to split such a tuple to actual multiple values. (This may change in the future.)
Forces expr and uses split-values on the result.
Similar to !values, but forces each of the values recursively.
The lazy/promise module implements lazy promises as implicitly used by the language.
Note: this module implements a new kind of promises. MzScheme’s promises are therefore treated as other values – which means that they are not forced by this module’s force.
lazy implements a new kind of promise. When used with expressions, it behaves like delay. However, when lazy is used with an expression that already evaluates to a promise, it combines with it such that force will go through both promises. In other words, (lazy expr) is equivalent to (lazy (lazy expr)). The main feature of this implementation of promises is that lazy is safe-for-space (see SRFI-45 for details) – this is crucial for tail-recursion in Lazy Scheme.
To summarize, a sequence of lazys is forced with a single use of force, and each additional delay requires an additional force – for example, (lazy (delay (lazy (delay (lazy expr))))) requires three forces to evaluate expr.
Note: lazy cannot be used with an expression that evaluates to multiple values. delay, however, is fine with multiple values. (This is for efficiency in the lazy language, where multiple values are avoided.)
As mentioned above, using delay and force is as in Scheme, except for two differences. The first is a technicality – force is an identity for non-promise values. This makes it more convenient in implementing the lazy language, where there is no difference between a value and a promise.
The second difference is that circular (re-entrant) promises are not permitted (i.e., when a promise is being forced, trying to force it in the process will raise an error). For example, the following code (see srfi-45 for additional examples):
(let ([count 5])
returns 0 with Scheme’s delay/force, but aborts with an error with this module’s promises. This restriction leads to faster code (see a SRFI-45 discussion post for some additional details), while preventing diverging code (the only reasonable way to use circular promises is using mutation as above).
Similar in functionality to Scheme’s delay
Creates a “lazy” promise. See above for details.
x : any/c
x : any/c
A predicate for promise values.
The lazy/mz-without-promises module simply provides all of mzscheme except for promise-related functionality: delay, force, and promise?. This is because lazy/promise defines and provides the same names. It is intended as a helper, but you can use it together with lazy/promise to get a mzscheme-like language where promises are implemented by lazy/promise.