Section 2 – Global Capabilities
interface Exc { def throw(msg: String): Unit }
interface Console { def println[A](a: A): Unit }
interface Time { def now(): Int }
// console and time are "global" -- that is, module wide capabilities
def globalCapabilities { console: Console } { time: Time }: Unit {
def sayTime() {
console.println("The current time is: " ++ show(time.now()))
}
// other blocks closing only over console or time
def sayHello() { console.println("Hello!") }
def currentTime() { time.now() }
def repeat(n: Int) { f: () => Unit }: Unit {
if (n == 0) { () } else { f(); repeat(n - 1) {f} }
}
repeat(3) { () => console.println("Hello!") };
repeat(3) { () => sayTime() };
def parallelWrong { f: () => Unit } { g: () => Unit }: Unit { () }
// this wrong definition admits the following call:
parallelWrong
{ () => console.println("Hello, ") }
{ () => console.println("world!") };
// "correct" specification
def parallel(f: () => Unit at {}, g: () => Unit at {}): Unit { () }
// try commenting out the below call to see type errors
// parallel(
// box { () => console.println("Hello, ") },
// box { () => console.println("world!") });
// same here, this is illtyped:
// val firstClassWrong = box {} { () => console.println("Hello, ") };
val firstClassInfer = box { () => sayTime() };
def sayTimeThrice(): Unit { repeat(3) { () => sayTime() } }
// 2.1.4 Capability Polymorphism
def repeater { f: () => Unit }: (Int) => Unit at { f }
{ return box { (n: Int) => repeat(n) {f} } }
val repeatTime = repeater { sayTime };
// some more variants illustrating closure and capability sets
val b = sayTime;
val n = repeater { () => sayTime() };
val n1 = repeater { sayTime };
val b2 : () => Unit at {console, time} = box { () => (unbox n)(3) };
val b3 = n(3);
val b4 = { () => n(3) };
val r = repeater { b };
()
}
Support Code for Running Examples
def run[T] { prog : {Console} {Time} => T }: T {
try {
prog {console} {time}
} with console: Console {
def println[A](msg: A) { println(msg); resume(()) }
} with time: Time {
def now() { resume(timestamp()) }
}
}
We can run the above examples
run {globalCapabilities}
Section 2 – Local Capabilities and Effect Handlers
interface Stopwatch { def elapsed(): Int }
def localCapabilities { console: Console } { time: Time }: Unit {
try { console.println("hello"); exc.throw("world"); console.println("done") }
with exc: Exc {
def throw(msg: String) { console.println(msg ++ "!") }
};
// uncomment the following example to see a type error:
// try { return (box exc) } with exc: Exc { def throw(msg: String) { () } };
()
}
Again, we can run the above examples:
run {localCapabilities}
Example 2.1
interface FileHandle { def readByte(pos: Int): Int }
def withFile[T](path: String) { prog: {FileHandle} => T }: T {
try { prog {fh} } with fh: FileHandle {
def readByte(pos: Int) { resume(pos + 42) }
}
}
def fileExample() {
withFile("A.txt") { {fileA: FileHandle} =>
val offsetReader : Int => Int at {fileA} =
withFile("B.txt") { {fileB: FileHandle} =>
val offset = fileB.readByte(0);
return box { (pos: Int) => fileA.readByte(pos + offset) }
};
(unbox offsetReader)(10)
}
}
Running the example will print 92 (0 + 42 + 42 + 10).
fileExample()
Effect Handlers
def effectHandlers { console: Console } { time: Time }: Unit {
val before = time.now();
try {
def report(t: Int) { console.println(show(t) ++ "ms") }
report(watch.elapsed());
report(watch.elapsed());
report(watch.elapsed())
} with watch: Stopwatch {
def elapsed() {
// we can observe the capture of `resume` by boxing it:
val k = box resume;
resume(time.now() - before)
}
}
}
run {effectHandlers}