Lazy Racket is available as both a language level and a module that can be used to write lazy code. To write lazy code, simply use lazy as your module’s language:
|... lazy code here...|
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.)
Mixing 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, as described in Forcing Values.