Yep, they are essentially sum types, but out-of-band, which is important.
In particular, you can have:
def quux():
xkcd = frob(foo(), zomg())
...lots of code...
def bar():
try
quux()
catch ExceptionX
...handle X...
def baz():
try
bar()
catch ExceptionY
...handle Y...
I assume pattern-matching can be partial without explicit default value, but (in the languages I've dealt with) it gets tricky with expressions. For instance, in C++:
auto a = foo();
auto b = bar(a, quux());
return frob(xkcd(a), b);
if foo(), bar() and quux() can throw exceptions, and you wanted to replace them with sum types - say, std::variant<F, Err1, Err2, Err3>, or for better semantic separation (more on this below), tl::expected<F, std::variant<Err1, Err2, Err3>> - you'll going to pray you have enough "monadic" methods in those classes (spoiler alert: you don't, as of C++17/current tl::expected) to rescue the spaghetti code you'll have to write to model this flow.
Recently, I've been dealing with a C++ codebase that uses tl::expected in lieu of exceptions, and while the "main flow" becomes more readable - return doThis(x, y).and_then(do_that).map(sth).map_error(logErrors); - the side effect is that the codebase is littered with trivial functions and closures to make that interface work. It's much less readable than a bunch of catch blocks every now and then. Maybe it's a limitation of C++? I feel like a syntax for partial application would remove half of the boilerplate I have to write when chaining tl::expected-returning functions.
On semantic separation, I alluded to using a "nested" sum type like (Result1 + Result2 + ...) + (Err1 + Err2 + ...) instead of Result1 + Result2 + ... + Err1 + Err2 + ... - while mathematically equivalent, they aren't syntactically equivalent in code. Grouping possible return value types separately from possible error type values improves reasoning about code - the groups tend to change independently from function to function. And if you squint, this is what exceptions give you: they separate the error sum type from return sum type, and give the former a completely different control path. This means you can think about them separately, and don't have to force-fit the success path into shape that satisfies the needs of the error path.