Performance myths like this are really interesting. Usage of features is usually not inherently responsible for bad performance, but the way they are used. Usage of these features can amplify the effects of bad software architecture. Some features might just be elected as the culprit while there is really a different problem.
If a little boxing and unboxing in a functional language takes 7 times as long as the rest of the code, something must be very very wrong. It's hard to believe that.
In this concrete case: a data intensive workflow should really have no or only few error situations. The performance of exceptions should not matter at all. I would guess that reworking the code to avoid exceptions also included reworking the program structure.
I don't believe the boxing/unboxing performance claim either. I built a large project in this style (in F# specificially) and the efficiency is not a problem. It's possible said person built his custom implementation that had issues but if you use ILSpy to decompile how union types end up compiled you'll see it comes down to a boolean check against an integer followed by a pointer dereference.
OCaml has very fast exception throwing/handling mechanism which apparently is why some of the applications built in OCaml make heavy use of exceptions for control flow (which as been mentioned by others, if you push too far is a nightmare to to reason about). The appropriate strategy to port such code to F# for example (which is a highly similar language to OCaml) is to switch to unions in the return type.
The approach advocated by Scott is a general pattern with which you build an entire application around. It does marvel when building a system surrounded by others that you cannot trust and database with brittle consistency (as most 15+ years line of business database eventually become).
As for some of the other suggestion advocated in the comments, you have to note that composability becomes a factor. If you are dealing with a collection of entries each of which might fail or succeed, the railway oriented programming approach scales to handling collection of errors/successes gracefully which so many error handling strategies fail to meet.
I have tried designing an application around Haskell's error monads in the past, and it was an absolute disaster. There was so much typing. The types became really hard to understand and the program structure became really rigid. I do not know that it can't be done better, but I've heard prominent Haskell guys say that combining errors (or any monads at at all) sucks in practice.
And I've got zero problems with just handling errors procedurally. I realize that errors simply combine very badly: If there are many possible error kinds the best you can do in the end is usually to just quit. That's not what they promised on the tin :-)
So in the end I just write procedural code and I'm careful not to get into situations where many different kinds of errors can happen. I select a few error cases that I care enough about to handle. Because handling even a single kind of error means a lot of complexity on top of a software project.
Yes I agree it can become unwieldy if it is used throughout an application. I think the sweet spot is too keep the pattern at the application boundary. You can use business objects the way Scott describes them for the internals without threading Either/Results monads throughout your application. Moving calls to services and databases to the application boundary also helps keeps the internals (where most of the business logic will reside) clean of Either/Results. For the few things left that could fail in the internals, stick to exceptions mechanism and avoid catching. I'm a big believer bugs should make the application crash.
If a little boxing and unboxing in a functional language takes 7 times as long as the rest of the code, something must be very very wrong. It's hard to believe that.
In this concrete case: a data intensive workflow should really have no or only few error situations. The performance of exceptions should not matter at all. I would guess that reworking the code to avoid exceptions also included reworking the program structure.