Delayed computations

An expression can appeardelayedas 'e,which is the same as_ -> eand() -> e.Ifehas typeT,then 'ehas typeforall a. a -> T.

Ifcis a delayed computation, it can beforcedwith!c,which is the same asc ().The expressioncmust conform to a type() -> tfor some typet,in which case!chas typet.

Delayed computations are important for writing expressions that requireabilities.For example:

program : '{IO, Exception} ()
program = 'let
  use Text ++
  printLine "What is your name?"
  name = !console.getLine
  printLine ("Hello, " ++ name)

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 theIOandExceptionabilities must be provided by a handler, seeabilities and ability handlers).Instead,programhas the typeprogram : '{IO, Exception} ()(note the'indicating a delayed computation). Inside a handler forIO,this computation can be forced with!program.

Inside the program,!console.getLinehas to be forced, as the type ofconsole.getLineisconsole.getLine : '{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 xis the same as(_ -> f) xand!x + yis the same as(x ()) + y.

These symbols bind less tightly than keywords that introduce blocks, so'let xis the same as_ -> let xand!if b then p else qis the same as(if b then p else q) ().

Additional'and!combine in the obvious way:

  • ''syntacticPrecedence.xis the same as(_ -> (_ -> x))or(_ _ -> x).
  • !!xis the same asx () ().
  • !'xand'!xare both the same asx.

You can of course use parentheses to precisely control how'and!get applied.