TIL: Don't use C++ for new projects

Today I learned that the new C++ standard (C++17) has std::variant - but no pattern matching.

Variants and how they work

Variants or “tagged unions” are a special form of data type. They describe an abstract thing which could be one concrete thing or another concrete thing. Variants are available in a lot of languages, including and not limited to ML, Haskell, Algol 68, Pascal, Ada, Rust and even C.

With C++17, the C++ standard was extended to have the std::variant generic type, which is basically a “C union on steroids”.

(To the C++ greybeard programmers reading this: I know this is technically not right, how it is implemented and how this works is very different from an actual C union, but what it is used for is basically the same, so please don’t yell at me for this statement.)

A std::variant can be used to express, for example, a return value of a function which might have failed. Maybe you think “Hey, that’s what exceptions are for” - no, that’s not what exceptions are for, though a lot of programmer use them like this. Exceptions are for unexpected behaviour of your program. If you’re writing a terminal calculator, an invalid input is an expected failure. An unexpected failure would be that you cannot read from stdin!

In cases where expected failures happen, a std::variant can be used to return a “I failed doing my job”-value.

Why you need pattern matching

Pattern matching can be used to destructure variants efficiently (as in code, readability, extendability and thus maintainability) to access their inner values.

For example, in Haskell, one can destructure a variant like this:

data Result = Ok | Err

explainresult :: Result -> [Char]
explainresult Ok  = "All right"
explainresult Err = "Ouch"

Here, we define the variant Result and a function explainresult which destructures the variant and returns a string based on the passed variant. Of course, variants can hold data and this data can be accessed then.

As a matter of fact, variants are incredible useful in everyday programming and so is pattern matching for destructuring them.

What C++17 offers instead of pattern matching

C++17 does not offer pattern matching. Why? I don’t know. But it really should have, because the way this issue is solved in C++17 is painful: std::visit is used for imitate pattern matching.

On the first glance, the following code looks rather okayish (well, it is still C++, right?):

std::vector<std::variant<char, long, float, int, double, long long>>
           vec = {5, '2', 5.4, 100ll, 2011l, 3.5f, 2017};

// display each value
for (auto& v: vec){
  std::visit([](auto&& e){std::cout << e << " ";}, v);
}

But this is simple. What if we want to actually do something with the element based on which type it has?

Well, not so nice: We have to design our own visitor and equip it with the functionality:

struct Visitor {
    void operator()(const string& s) const {
      // ...
    }

    void operator()(const int n) const {
      // ...
    }
};

While having a dedicated visitor which can be passed around is rather nice, having it as the only option for pattern matching is hell.

Also, the standard library does not provide a helper for building a visitor object from a list of lambdas!

All in all: Is the whole thing a step into the right direction? No. It is rather a stumble into the rightish direction. Nothing more.

A friendly advice from a non-Cpp programmer

Take a grain of salt. I’m not a (C++) professional, hence this might not reflect the opinions of the broader community. Also, the following might get some people really angry. But I don’t care.

Don’t use C++ for new projects!

I know C++ is a heavy target and the standard is backwards compatible and a lot of people count on the standards committee to get this right and yaddayaddayadda. From my point of view, C++ tries to catch up. This is, of course, nice. But at that speed, other languages will innovate much faster and better and C++ will slowly die. That’s one reason I would never ever use C++ for a new project.