An expression can appeardelayedasdo e
or 'e
,which are both the same as_ -> e
and() -> e
.Ife
has typeT
,thendo e
and 'e
both have the typeforall a. a -> T
.
Ifc
is a delayed computation, it can beforcedwith!c
,which is the same asc ()
.The expressionc
must conform to a type() -> t
for some typet
,in which case!c
has typet
.
Delayed computations are important for writing expressions that requireabilities.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 theIO
andException
abilities must be provided by a handler, seeabilities and ability handlers).Instead,program
has the typeprogram : '{IO, Exception} ()
(note the'
indicating a delayed computation). Inside a handler forIO
,this computation can be forced with!program
.
Inside the program,!readLine
has to be forced, as the type ofreadLine
isreadLine : '{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, sodo 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:
''syntacticPrecedence.x
is the same as(_ -> (_ -> x))
or(_ _ -> x)
.!!x
is the same asx () ()
.!'x
and'!x
are both the same asx
.
You can use parentheses to precisely control how'
and!
get applied.