I see optional, expected, outcome and .. (monad?) as just special cases of variant. for example
template<typename T> using optional = variant
; Why is it necessary to have all these types separately implemented? Can't there be some sort of "base" type which can be used to implement all these others? Wouldn't this approach make things much simpler to review, maintain, and use? The reason is that there are different tradeoffs at work. I cover this a bit in https://ned14.github.io/outcome/history/#outcome-v2 where I explicitly mention variant2, but to summarise:
1. Outcome has a simple implementation with minimum SFINAE, a fixed ABI and aims for low impact on build times in order to maximise usefulness in public interfaces in large codebases. 2. variant2 has a less simple implementation, enabling more flexibility, better code density, but at the cost of build time impact. 3. A fully conforming Expected can be implemented easily using either Outcome or variant2. See https://ned14.github.io/outcome/faq/#how-far-away-from-the-proposed-std-expe.... 4. Optional is an interesting one. Outcome uses an internal optional implementation because reusing Optional wasn't efficient. Specifically, I needed more than a single bit discriminant, and Optional wasn't reusable for that with doing UB. Niall