Hacker News new | ask | show | jobs
by ribit 1771 days ago
> Neither the algorithm, nor you, need to know a priori if your predicates will throw exceptions (which are things that may be literally impossible to know upfront)

I don't think this property is desirable at all. I prefer to know whether a function can or cannot result in an error, ideally encoded within the type system. The C++ "everything can throw" paradigm yo describe here obfuscates the program logic and promotes bad coding practices. I know, C++ programers like to argue that "everything throws" is a natural property of any real world code, but somehow folks are able to work with Rust and Swift without too much hassle.

> If you're really saying you can find a strictly better solution, then we're all definitely interested in hearing... and I'll believe it when I see it.

A strictly better solution has been found long time: error sum types.

> Performance is also a big one, and C++ is designed for maximizing performance in non-exceptional executions. I don't know what error models you're thinking of, but anything along the obvious stuff I've seen (like the usual "replace T with maybe<T>/optional<T>/fancy<T>") would come with far greater performance hits n the 'happy' paths than C++ has (not to mention potential increases in memory usage, etc. in more complex cases)

This is again a very popular argument I've seen used by many in the C++ community, but the simple fact is that this argument is simply not true. Already very naive result type implementations using C++ show no measurable performance difference in the "good" path (with a non-trivial function), and using an optimized calling convention makes error sum types zero-cost on modern hardware.

For example, Swift uses a dedicated register to signal exceptional function result. On the "good" path, you have to zero this register in the callee and conditionally jump on its value in the caller. These operations are essentially free on any modern CPU with superscalar execution, register renaming and branch prediction. The only cost is a register and a few extra instructions which won't carry any performance impact. One can optimize this even further by using condition flags to signal exceptional result (frees up a register and saves an instruction).

To sum it up, using result types with optimized calling conventions gives you the same performance as the C++ exceptions on the good path, much better performance on the exception path, saves space (few bytes of extra instructions take much less space than the unwind information), radically simplifies the compiler (no long jumps, functions enter and exit regularly), radically simplifies cleanup (function exits regularly and can run destructors as usual), simplifies the control flow and so on.

In fact, the only disadvantage I see with this implementation is that exception propagation might be slower than a longjump if you have hundreds of nested functions. But I think you have much bigger problems if you call stack looks like that...