Stacktraces
This example illustrates user defined stack traces.
import list
import option
type Nothing {}
record SourcePosition(filepath: String, line: Int, column: Int)
record TraceFrame(position: Option[SourcePosition], description: String)
type Stacktrace = List[TraceFrame]
interface Exception[E] {
def raise(exception: E, msg: String, trace: Stacktrace): Nothing
}
record RuntimeError()
def raise[A](msg: String): A / Exception[RuntimeError] = do raise(RuntimeError(), msg, Nil()) match {}
def raise[A, E](exception: E, msg: String): A / Exception[E] = do raise(exception, msg, Nil()) match {}
// converts exceptions of (static) type E to an uncatchable panic that aborts the program
def panicOn[E] { prog: => Unit / Exception[E] }: Unit =
try { prog() } with Exception[E] { def raise(exception, msg, trace) = panic(msg) }
// reports exceptions of (static) type E to the console
def report[E] { prog: => Unit / Exception[E] }: Unit =
try { prog() } with Exception[E] { def raise(exception, msg, trace) = println(msg) }
// ignores exceptions of (static) type E
def ignoring[E] { prog: => Unit / Exception[E] }: Unit =
try { prog() } with Exception[E] { def raise(exception, msg, trace) = () }
def trace[R, E](description: String) { prog: => R / Exception[E] } =
try { prog() }
with Exception[E] {
def raise(exception, msg, trace) =
do raise(exception, msg, Cons(TraceFrame(None(), description), trace)) match {}
}
def printStacktrace(trace: Stacktrace): Unit = trace match {
case Nil() => ()
case Cons(TraceFrame(pos, desc), rest) =>
println(" " ++ desc);
printStacktrace(rest)
}
def bar(n: Int): Int / Exception[RuntimeError] = {
with trace[Int, RuntimeError]("function bar()");
if (n == 0) raise("boom!") else bar(n - 1)
}
def foo() = {
with trace[Int, RuntimeError]("function foo()");
bar(5)
}
def main() =
try {
foo(); ()
} with Exception[RuntimeError] {
def raise(_, msg, trace) = {
println("Runtime Exception: " ++ msg);
printStacktrace(trace)
}
}
main()