A safe interface to the delimited continuation primops.
A delimited control operator is defined only beneath a corresponding delimiter. The core idea of native-cont is to hence achieve safety by representing the scope of a computation at the type level.
type Cont :: Scope -> Type -> TypeA delimiter then delimits a subscope from its immediate superscope
type Limit :: Scope -> Scope -> Type -> Typeand every new Limit creates a corresponding subscope.
newLimit :: (forall q. q <| r => Limit q r a -> Cont r b) -> Cont r bTo actually enter that subscope, the Limit must be imposed.
impose :: Limit q r a -> Cont q a -> Cont r aWithin, it's safe to sunder the continuation at the Limit.
sunder
:: p <| q
=> Limit q r a
-> ((Cont p b -> Cont q a) -> Cont r a)
-> Cont p bThis type signature shows the payoff of our scoping discipline. Read in English:
- In any subscope
pofq, we maysunderthe continuation at the juncture of scopesqandr. - Doing so captures the continuation up to scope
qand throws us into scoper.
You can run Cont in the RealWorld, but it requires IO:
runContIO :: Cont RealWorld a -> IO aThis in turn allows IO within:
runIO :: p <| RealWorld => IO a -> Cont p aTo run Cont in a pure context we must instead isolate it to its own universe:
runCont :: (forall u. Cont u a) -> aThere are a lot of similar delimited control operators that do subtly different things with the delimiter. Too subtle, in fact, to be distinguished by type in prior art (to my knowledge). In native-cont however, it is not so—behold!
control0 :: p <| q => Limit q r a -> ((Cont p b -> Cont q a) -> Cont r a) -> Cont p b
shift0 :: p <| q => Limit q r a -> ((Cont p b -> Cont r a) -> Cont r a) -> Cont p b
control :: p <| q => Limit q r a -> ((Cont p b -> Cont q a) -> Cont q a) -> Cont p b
shift :: p <| q => Limit q r a -> ((Cont p b -> Cont r a) -> Cont q a) -> Cont p bThe differences in their behaviour can be read from the type signatures! Further, they can no longer be confused—if you accidentally use the wrong one, the type checker will set you straight.
GHC.Exts |
NativeCont |
Control.Continuation.* |
|---|---|---|
PromptTag# a |
Limit# _ _ a |
Limit _ _ a |
newPromptTag# |
newLimit# |
newLimit |
samePromptTag# |
sameLimit# |
sameLimit |
prompt# |
impose# |
impose |
control0# |
sunder# |
sunder |
Beyond the core library, this package provides several public sublibraries:
- native-cont:loop
- Loops with
breakandcontinue.
- Loops with
- native-cont:exception
- Checked exceptions.
- native-cont:handler
yielding values to handlersinstalled atLimits.
- native-cont:coroutine
- Coroutines.
- native-cont:algebraic
- Algebraic effects.
The purpose of these libraries is, in order:
- To test the interface of the core library.
- To be usage examples for the core library.
- To provide additional functionality compatible with the core offerings.