This is exactly what I think too. Sum types are so powerful, I feel a lot safer in Python + mypy with sum types (`from typing import Union`) than anything with C++ [1] even though C++ has a significantly more complex type system (it's type system is TC) and Python is as type-unsafe as a language can get. C++ made this odd choice as if any complex type system is better than a simple type system. When I was a younger software engineer, I agreed with this choice but now I completely disagree. I think next generation of software engineers will see that Turing completeness is a bug, and that the whole thing about software safety is finding weak features that are just powerful enough to be useful. If you don't believe me, go look at a language like Agda. You do not need Turing completeness: function application, sum types, primitive/structural recursion/corecursion are just about everything you need as a programmer and everything else that gives you a Turing complete language is extraneous.
[1] unless you liberally use `union`s everywhere which is very unidiomatic C++, but can be done if desired.
You can just make your python modules out of another language. I am not sure why code has to be big. Break it down and make it executive, not very friendly in terms of threads but if you're writing an API you are writing it for Linux which is vastly safer for threads than Microsoft or Mac.
- nobody uses it in the ecosystem. As outlined in the article, a lot of value of Option/Result is derived from their pervasiveness in the Rust ecosystem. C++ is far from this
- the ergonomics of it are terrible: no pattern matching, structural variants instead of named variants (yes you can emulate that with wrapper types but meh), lambda-oriented matching means you cannot as easily do things like early returns, statement-oriented language limits the usefulness anyway, lack of combinators, and for error handling specifically, lack of `?` operator
- performance is dubious. I had very steep and unexpected performance cliffs when lambda inlining started to fail for some reason. Having sum types be a language construct guarantees we're not relying on things like lambda optimisation here.
Variant types where one has to use objects as types don’t come up that often in API design or data structures in my experience with mobile and system programming on e.g. Linux. I think I’ve genuinely had to use them only a few times.
Error types are probably the most popular incarnation of that. There’s several libraries available and they will be part of the C++ standard.
This is a case of the Rust community overselling a minor feature as a game-changing novelty.
> Variant types where one has to use objects as types don’t come up that often in API design or data structures in my experience with mobile and system programming on e.g. Linux
You can't use what you don't have, so you adapt to the tools you do have. In my C++ time, the team would often write types that logically held several variants. However they were expressed as product types, so with space overhead and error-prone, unergonomic use.
Sum types are game-changing. Look at any Rust project you'll find enums with data everywhere. They're just a building block to model problems, like product types are. A language that miss them is as strange too me as a language without product types. After 10 years of mostly C++11 and C++14 I would never go back to it for this reason alone (although as outlined in the article there are other reasons too).
> Error types are probably the most popular incarnation of that. There’s several libraries available and they will be part of the C++ standard.
I just checked the old Rust classic “ripgrep” on github.
In matcher.rs, the enum is used as poor man’s OOP. I saw several match expressions which then call the same function on each matched type.
searcher/mod.rs is an error definition.
glob.rs contains a sort of policy enum which can be implemented once again with OOP or as a policy template.
json.rs usage can be modeled as a single class.
core/app.rs is more involved, but can be modeled as a series of structs with a map from enum -> any. Or as am std::variant. Or using OOP.
I looked at all instances and didn’t see anything game-changing. It’s a nice syntax and it should have really good performance, but such idioms are way too low-level to change any game.
This is a straw man. There is nobody on this Earth claiming that sum types are necessary to solve a problem. Product types aren't necessary either. You could just write everything in Assembly. Or maybe raw bytecode if you want.
As should be patently fucking obvious to anyone who has been commenting on a technology web site for as long as you have, sum types are a tool. They are a tool for expressing clearly and concisely the idea that a value can be exactly one of several possible options. That tool then interacts with the rest of the language based on that invariant, sometimes providing things like exhaustiveness checking and pattern matching. Put all this together, and you have a very succinct and very clear way of representing certain kinds of values in a program.
Your comment might as well go through ripgrep and talk about how functions aren't needed. "They could have just used goto here and there."
> I looked at all instances and didn’t see anything game-changing. It’s a nice syntax and it should have really good performance, but such idioms are way too low-level to change any game.
Sum types were game changing to me when I learned about them over a decade ago. Since then, they have been a significant factor in how I think about and structure data in programs.
I have zero interest in trying to convince someone like you that you should think it's game changing. That's not the point. Maybe you could do some perspective taking and realize that others might just think differently than you.
not to diminish value of sum types (those are not specific to Rust of course) but I think that product types belong to a database / config rather than being hardcoded.
> This is a case of the Rust community overselling a minor feature as a game-changing novelty.
Sum types reify control flow into an object from which said control flow can be retrieved. Compiler checked sum types remove the possibility of retrieving inconsistent control flow.
The transform is equivalent to callback to future.
It's one of the things that looks unimportant until you use it. After that, the absence is repeatedly experienced when working with C++. We don't use tagged unions much because the ergonomics are terrible.
You clearly haven't tried using variant types properly then. Sure, `std::variant` is pretty unergonomic, but they really are game changing. Most of my types are variant types
Most of your types? Well that certainly deserves congratulations and I hope someone will take advantage of the opportunity.
I think you misunderstood me though if you say I haven’t used them properly. I just didn’t need them or find them that useful because it rarely happens that I want to model a type which has several states.
Specific variant types like std::optional or the Result-equivalents are useful, but not that game changing either. They’re nice I suppose.
I wonder what kind of software you write that such low-level coding idioms make a big difference to the end result. Or what do you mean by game changing?
So, so many ways in which this is defective compared to actual sum types, some of them were already listed, but to me the most crucial, even if mostly about theory rather than practice, is valueless_by_exception.
The choice to provide exceptions everywhere as a error handling means C++ is obliged to admit that your std::variant may not have a value at all. Which blows up all of your type safety. In Rust I can say that this Pet is either a Dog or a Cat, and it cannot be neither, but in C++ std::variant of a Cat and a Dog might nevertheless be valueless_by_exception anyway. What can you do about that? I guess you could throw an exception...
In Rust if X is either A or B, and Y is either C or D, and Z is either E or F, then a structure with X, Y and Z has only eight possible states. In C++ this structure has 27 possible states because of exceptions.
I get the "theoretical" point, but the issues I encountered in practice are closer to the ones I listed in my response than to valueless_by_exception (which I never encountered, I think?).
Wrt type safety we had more pressing issues with C++ (use after move, implicit conversions, ...)
Exceptions are an optional feature. Also, if sufficient amount of constructors are marked noexcept, then the variant can never be valueless. Implementations are optimized accordingly.
You also don't have to handle the exception, in which case you won't access the variant again anyway. Or it gets handled where the variant is teared down. It's very unlikely that it gets handled where the variant is constructed or assigned to, making it a non-issue.
I meant exceptions as a user of the language. You can decide not to throw. You can mark functions noexcept.
Granted, it's more awkward when you consume 3rd party libraries, but you can still wrap them in noexcept interfaces and be fine with terminating when an exception is actually thrown or do something else. Not much different to a panic.
[1] unless you liberally use `union`s everywhere which is very unidiomatic C++, but can be done if desired.