Created
July 13, 2020 01:09
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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