An expression can appear delayed as do e or 'e, which are both the same as _ -> e and () -> e. If e has type T, then do e and 'e both have the type forall a. a -> T.
If c is a delayed computation, it can be forced with c() or !c. The expression c must conform to a type () -> t for some type t, in which case !c has type t.
Delayed computations are important for writing expressions that require abilities. For example:
This example defines a small I/O program. The type {IO, Exception} () by itself is not allowed as the type of a top-level definition, since the IO and Exception abilities must be provided by a handler, see abilities and ability handlers. Instead, program has the type program : '{IO, Exception} () (note the ' indicating a delayed computation). Inside a handler for IO, this computation can be forced with !program.
Inside the program, !readLine has to be forced, as the type of readLine is readLine : '{IO, Exception} Text, a delayed computation which, when forced, reads a line from standard input.
Syntactic precedence
The reserved symbols ' and ! bind more tightly than function application, So 'f x is the same as (_ -> f) x and !x + y is the same as (x ()) + y.
These symbols bind less tightly than keywords that introduce blocks, so do x and 'let x are both the same as _ -> let x and !if b then p else q is the same as (if b then p else q) ().
Additional ' and ! combine in the following way:
do do syntacticPrecedence.xis the same as(_ -> (_ -> x))or(_ _ -> x).!!xis the same asx () ().!'xand'!xare both the same asx.
You can use parentheses to precisely control how ' and ! get applied.