Ejectors & Escape Expressions¶
Ejectors can be hard to explain with words alone, so we will start with code:
# 42
escape ej { 42 }
# 42
escape ej { ej(42) }
# null
escape ej { ej() }
An escape expression creates an ejector, which is an
ordinary-looking object, and then evaluates its body. Calling .run()
on an
ejector will change the return value from the body’s return value to whatever
is passed, or null
by default.
We can also optionally catch the value and manipulate it. However, any
catch
clause will only be run if the ejector is called:
# 42
escape ej { 42 } catch p { 5 }
# 5
escape ej { ej() } catch p { 5 }
# 7
escape ej { ej(42) } catch p { p // 6 }
Ejector-based Control Flow¶
The first major use for ejectors is in implementing several common kinds of control flow. By themselves, ejectors can be used to prematurely end or ‘short-circuit’ a computation; calling an ejector prevents any future computation:
# 42, no exception
escape ej { ej(42); 5 // 0 }
Ejectors even work when called by other objects:
# 6
def f(x, ej):
return ej(x) * 7
escape ej { f(6, ej) }
Conditional Definitions¶
# 0
escape ej {
def x :Int exit ej := "five"
x
} catch problem { 0 }
throw.eject
¶
Often we might want to ensure that the object we are calling will actually
alter control flow. We will see many motivating examples shortly. In these
cases, we can use throw.eject/2
to ensure that we will not continue
computation:
if (weAreFinished):
throw.eject(ej, "finished")
launchMissiles<-()
This is equivalent to ej("finished")
but will only launch missiles
conditionally. We might imagine a simple implementation of this method:
def throwEject(ej, problem):
ej(problem)
throw(problem)