Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created July 13, 2020 01:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnynek/2f4df1dba266ec6b9c224310038a28b7 to your computer and use it in GitHub Desktop.
Save johnynek/2f4df1dba266ec6b9c224310038a28b7 to your computer and use it in GitHub Desktop.
A short sketch of how we might bring java's checked exceptions to scala using union types.
/*
* Scala 3 is getting union types, so it occurs to me we could use these to model checked exceptions
* on the JVM.
* This is a sketch of how this could work
*/
// on the JVM, any operation could through some Error (OOM for instance)
// so annotating should be some kind of Exception, not any Throwable
type Throws[E <: Exception, +A]
// if can't throw, that is like saying you can only throw Nothing
// but the compiler should know that `A =:= Throws[Nothing, A]`
type NoThrow[+A] = Throws[Nothing, A]
def div(x: Int, y: Int): Throws[IllegalArgumentException, Int] =
if (y == 0) throw new IllegalArgumentException("cannot divide by 0")
else x / y
// so, we can think of `throw new E(...)` as having type Throws[E, Nothing]
// then try/catch can remove branches of a union Throws
def foo(): Throws[E1 | E2, Int] = ???
// to compute the return type here, we would basically
// do a difference operation: if foo(): Throws[E, Int]
// then callFoo returns Throws[E - E1, Int],
// so if E = E1 | E2, we return Throws[E2, Int]
def callFoo(): Throws[E2, Int] =
try foo()
catch {
case (e: E1) => -1
}
// We could also define:
trait ThrowingFn[A, E, B] {
def apply(a: A): Throws[E, B]
}
// lastly, if we have some language support so that we can
// treat `Throws[E, A] as an A but we propagate all the
// possible throws into the value type:
def foo1(): Throws[E1, Foo1] = ???
def foo2(): Throws[E2, Foo2] = ???
def foo3(a: Foo1, b: Foo2): Throws[E3, Foo3] = ???
val x = foo3(foo1(), foo2()) // returns Throws[E1 | E2 | E3, Foo3]
// The fact that java allows anything to return RuntimeException without tracking it is
// an additional complication that the above has ignored.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment