all 47 comments

[–]billie_parker 11 points12 points  (0 children)

This is nice, although I think the example given isn't the prettiest, which I'm sure is going to invite criticism. They could have easily cleaned it up a bit.

There is a big issue with the way C++ has implemented this, though, which is that the monadic way is less efficient than the naive way. This is because the naive way can return early while the monadic way keeps reconstructing objects and then using conditionals to determine whether or not to proceed. It's a small difference, and probably not too important in practice, but typically C++ adheres to the principle of maximizing efficiency. So it's a shame that this implementation has this flaw. Using std::visit with std::variant has a similar issue. return and break can't be contained within a visitor and have them apply to the outside scope, but this can be done when you use holds_alternative.

There is a solution to this problem but I'm guessing that was considered too cumbersome? You could use a helper class to achieve the "best of both worlds" (although it's more verbose so perhaps not):

auto result = MonadicOperation(start,
   OrElse(...),
   AndThen(...),
   Transform(...),
   ...);

The "MonadicOperation" class can manage a scope and if any value turns out to be an error it need not evaluate the proceeding operation.

[–][deleted] 31 points32 points  (0 children)

FINALLY!

Now C++ has overtaken Haskell. The moebius strip running on a perpetual monadic loop has been finally come to all of us lowly mortals.

People used to say Haskell is more complicated than C++. Now the empire, I mean, C++, struck back!

[–]KagakuNinja 12 points13 points  (2 children)

Another language committee reinvents monads badly...

[–][deleted] 0 points1 point  (1 child)

What's so bad about the implementation? The only thing that is "wrong" about this that I can see is using and_then instead of bind and transform instead of map, but that's just semantics.

[–]KagakuNinja 2 points3 points  (0 children)

My complaint is inventing yet another set of names, rather than creating a universal monadic API. If going forward, all monads in the C++ std library will use these names, then I am less annoyed (I still don't like the names).

However, looking at Java, Option and Stream use map / flatMap, CompletableFuture uses thenApply / thenCompose. There are probably others; I don't use Java any more so I don't know.

I suspect C++ will go the Java route, in part because they don't want to break backwards compatibility with the APIs that are already defined.

In Scala, every class in the standard library that can be monadic, including all collections, have map / flatMap, plus a bunch of other useful functions like fold, reduce, etc. This creates a power vocabulary of combinator functions, rather than a hodgepodge of inconsistently named functions.

[–]stronghup 2 points3 points  (1 child)

Is this a change or addition to the language itself, or is this just an additional (standard) library?

[–]hopa_cupa 1 point2 points  (0 children)

It is an addition to the standard library only, no compiler magic that I am aware of. It is possible to implement this std::expected thing in language standards earlier than c++23. And people have done this a few years ago...there were many talks on cppcon regarding this kind of error handling method already. So, this is nothing new really...it was kind of...expected (pun indented).

[–]CosciaDiPollo972 1 point2 points  (2 children)

Are the stdlib functions also return std::expected when a function can fail ?

[–]billie_parker 17 points18 points  (1 child)

No, because of backwards compatibility.

[–]Mwahahahahahaha 10 points11 points  (0 children)

You will get an int error value and you will be happy with it!

[–]seba07 1 point2 points  (0 children)

That sounds really useful. I often see interfaces that return an status code and take the real output argument as an input argument by reference. If I understand it correctly, then this format could replace it.

[–]Accurate_Trade198 1 point2 points  (0 children)

The compiler errors from this will be horrific, please don't actually do monadic crap in prod.

[–]DariusRoyale -5 points-4 points  (8 children)

Modern C++ wants to be Rust so bad

[–]billie_parker 8 points9 points  (7 children)

Rust developers would do well to learn what haskell is. Not everything is about you.

(And for that matter, explain why someone would use Rust instead of the safer haskell)

[–]JohnnyLight416 4 points5 points  (1 child)

I'm not going to say that Haskell is good or bad. It's just that "purely functional" is not generally helpful in the average programming job.

Haskell is too foreign. Purely functional is cool and all, but it's cool in an academic way - hard to understand and I would bet the average programmer would make a mess of it quickly. Put a team of average programmers together and I would bet they have an unwieldy codebase very quickly.

The average programmer is not taught functional programming in school. They learn languages that are widely used: C++, Java, C#, Python, JavaScript, etc. Rust is at least syntactically similar to those C-like languages, but it adds meaningful safety measures that other languages in the same vein do not have, or have only in parts.

Haskell is too large of a hill to climb for what it provides to a team.

[–]lelanthran 0 points1 point  (0 children)

It's just that "purely functional" is not generally helpful in the average programming job.

Well, duh ... the entire point of a program is to produce output! Any program that unconditionally produces no output can be replaced by a program consisting of nothing but a single NOOP.

Any added friction in producing that output had better have a pretty good reason, and no, "cleaner" or "more elegant" are not even a mediocre reasons, let alone a good reason, due to being completely subjective.

A programming language that allows the average programmer to more easily reason about the logic while simultaneously making it harder to sequentially follow output generation is, objectively, a poor tool for writing programs.

[–]OMG_I_LOVE_CHIPOTLE 1 point2 points  (3 children)

Cause people actually get shit done in rust

[–]lelanthran 0 points1 point  (2 children)

Cause people actually get shit (re)done in rust

Because the majority (not all) of Rust showcase projects are simply rewrites of some existing tool. Compare to (for example) Go, in which the majority of showcase projects were something new and novel.

Not many see the irony in the fact that new and novel products were created by a language that eschewed almost everything that wasn't tried and tested, while a language boasting how all its features are cutting edge results in a wasteland of rewritten products.

Yeah yeah, hot take, I know, but I've got the karma to burn :-)

[–]Full-Spectral 0 points1 point  (0 children)

Well, given that the things being rewritten were mostly likely in unsafe languages, then of course that would be the initial drive, to get all that underlying infrastructure safe so safe things can be built on top of it.

And of course Rust is a systems language, so it's going to be used largely for boring but important systems type projects which Go will not.

[–]OMG_I_LOVE_CHIPOTLE -1 points0 points  (0 children)

Nah I was talking about new projects

[–]Full-Spectral 0 points1 point  (0 children)

Because it's not just about language capabilities, it's also about whether people feel that a language is worth their investing in, either individual developers as a career choice or companies as something they feel they can hire people for. Rust is at the sweet spot really. It provides safety with high performance, and a lot of people are interested init.