Hacker News new | ask | show | jobs
by jgavris 1402 days ago
Eventually you'll be able to use std::expected in C++23!

https://en.cppreference.com/w/cpp/header/expected

Don't throw exceptions, require the caller to handle errors and propagate them up the stack (everything returns an expected) if they cannot be handled. You are forced to model the error domains instead of just throwing an exception and assuming the caller knows to catch it and do something with it.

Java has checked exceptions, but, Kotlin decided to abandon them.

The nice codebases I have worked on stick to the Result<T,E> type in Swift or Kotlin. And thus you are forced to 'translate' errors (exceptions?) as described in Alan Griffith's 'Exceptional Java'.

https://accu.org/journals/overload/10/48/griffiths_406/

"If a checked exception is thrown (to indicate an operation failure) by a method in one package it is not to be propagated by a calling method in a second package. Instead the exception is caught and "translated". Translation converts the exception into: an appropriate return status for the method, a checked exception appropriate to the calling package or an unchecked exception recognised by the system. (Translation to another exception type frequently involves "wrapping".)"

If you can't wait for C++23, there's a single header implementation here.

https://github.com/TartanLlama/expected

3 comments

I last used C++ extensively in the 00s but my general impression of modern C++ is that as it becomes more and more complex, it tackles it's complexity... by adding more complexity.
Pretty debatable. C++ was always a pretty big language. Maintaining a decades-old project is going to require either ongoing modernization or you’ll most likely end up touching a lot of it, but you can, and the “start over” option proposed so frequently around here is not for organizations with tens of thousands of engineer years in their codebase.

New stuff that doesn’t need your decade-plus of C++ libraries or hit a few other corner cases should probably be in Rust, but you’re back to, that’s mostly greenfield stuff.

If you are beholden to not starting from scratch, modern C++ heavily subsets the language and is rather pleasant, certainly the tooling is best-in-class (which is often overlooked).

I consider it a good thing that C++ still compiles code from decades ago, and it’s frankly astonishing how well it’s aged when you consider the domains it targets.

Ditching everything every few years is nice work if you can get it, and this being HN that’s going to be a vocal bloc, but usually you get to greenfield everything because something else failed.

That's exactly how it is, every few years there is so much added without taking almost nothing back that it feels a different language, with so many styles out there it's challenging, to say the least.
Just like any other language, Python 3 will be a study case for generations for what happens when one takes features away.
Does C++23 have something like Rust's ? for std::expected?
It’s extremely non-obvious how to do the Maybe and Either type classes in C++ in a truly ergonomic way.

Rust wiring in syntax for this (which is what ?-notation is, it’s special-case do-notation) is both a blessing and a curse: it’s extremely useful today and a breath of fresh air relative to C++ or Java or whatever but this scenario has been seen before: GHC had do-notation and return, which are dramatically more flexible than ?-notation, wired in pretty deep when people started realizing that you want Functor => Applicative => Monad with pure === return and ApplicativeDo. They’re still trying to get the migration done today and they started years and years ago.

Time will tell how Rust will age as it inevitably continues adding algebraic power, but superficially ?-notation seems doomed to end up a kind of dangling appendage that will be obsoleted by some more general notation for algebraic effects.

That's interesting. Having learned Haskell long before Rust (and having been programming in C++ for a long time) I found Rust's ?-notation to be annoyingly limited due to the reasons you gave, and also annoying syntactically. I saw in many Rust sources it's often tucked away at the end of lines, flush against the last character, a single character which changes the meaning of the line, well, the whole function, but is often lost to me visually, and I have to scan the line to see it. But I guess people got used to it or just don't bother parsing all characters in a line of code like I do.

When you say "They’re still trying to get the migration done today... " you mean Rust's core developers, right? Is there anywhere I can read up on it?

Ah no the migration I was referring to is `Applicative`: I just upgraded our "primary" Nix `devShell` to use `9.2.4` (`RecordDotSyntax` is finally here, may the thousand-year reign of the Glorious Glasgow Haskell Compiler commence! /s) and it's hitting me with deprecation notices about `return = ...` rather than `return = pure` and/or omitting and explicit `return`. Which makes sense, the rationale is at https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/monad-of....

I remember when I was working on `Haxl` at FB in like, 2015 or 2016, it needed `ApplicativeDo` and that was like, quite the novelty in industrial Haskell (I think it was already a no-brainer to the research/OSS/etc. crowd). So mainstreaming `Applicative` into the effects hierarchy has been going on for 6 or 7 years that I know about, and I bet a lot longer than that (the paper introducing `Applicative` is from 2008: https://www.staff.city.ac.uk/~ross/papers/Applicative.html).

There's an (admittedly oversimplified) sales deck for algebraic effects that I have found really effective at selling this stuff to open-minded non-FP hackers: https://philipnilsson.github.io/Badness10k/posts/2017-05-07-....

At the risk of inviting an old-fashioned HN brigade gang-tackle: I think it indirectly illustrates why Rust is going to end up going all-in and ditching the ?-notation. Please don't hurt me.

There is ongoing work to generalise ? operator in Rust with traits[1]. If it succeeds, current code is not going to be obsoleted by the new solution, only extended.

[1]: <https://doc.rust-lang.org/std/ops/trait.Try.html>

If compound statements every get standardized then you could write a TRY macro that worked pretty similarly.

https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

Yes we've been using this. Unfortunately it is not standard and iiuc msvc has no equivalent?
Exceptions.
Exceptions are nothing like Rust's Result.

- exceptions don't appear in the signature of a function

- you don't have to handle exceptions explicitly, while you cannot access the value of a Result without explicit error handling

- Exceptions are implemented like Rust's panic. The error path in exception is more costly, while the nominal path is supposedly less so, due to less register and branch pressure than the result type that at least has a discriminant to check.

Exceptions are such a different way or handling errors from Result that my team finds itself reimplementing Result types in C++ again and again. Alas, it is neither standard nor ergonomic in C++

There is also Boost Outcome