Obviously exceptions are vastly superior to sum types for errors. Compare the following very readable exception based-pseudocode:
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 */
To the utterly unreadable error variant based implementation that uses obscure functional stuff like patter matching: 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 */
This is especially egregious if you want to propagate the error. The exception based variant can focus on the success control flow: fn doTheThing() -> void, raises Error
SomeVal x = try doSomethingFallibly()
While for the error variant case you have to use even more obscure monadic code: fn doTheThing() -> void|Error
SomeVal x <- doSomethingFallibly()?