Obviously sum types for errors are vastly superior to exceptions. Compare the following very readable Error variant pseudocode:
fn doSomethingFallibly() -> SameVal | Error
SomeVal x = ...;
if failed:
return Error
else
return x
fn doTheThing() -> void
match doSomethingFallibly():
SomeVal x: /* use x */
Error: /* log the error */
To the utterly unreadable exception-based implementation:
fn doSomethingFallibly() -> SameVal, except Error
SomeVal x = ...;
if failed:
raise Error
else
return x
fn doTheThing() -> void
try Someval x = doSomethingFallibly()
/* use x */
except Error: /* log the error */
This is especially egregious if you want to propagate the error. For the error variant case you can use a beautiful monadic solution
fn doTheThing() -> void|Error
SomeVal x <- doSomethingFallibly()?
While the exception based variant completely obscures the control flow:
fn doTheThing() -> void, raises Error
SomeVal x = try doSomethingFallibly()