5 ms·
std::optional is a poor shadow of Option. It's what happens when C++ programmers who've seen a Maybe type in a window (years ago by the way, this isn't inspired
by tialaramex 3y ago
std::optional is a poor shadow of Option. It's what happens when C++ programmers who've seen a Maybe type in a window (years ago by the way, this isn't inspired by Rust, it was just stuck in the standardization process until C++ 17) but are starved of proper types and basic features like pattern matching try to imitate what they saw.
As a result for example std::optional<&T> doesn't exist, because to a C++ programmer it seems as though this might have assign-through semantics (!) and so WG21 decided to kick this can down the road. C++ 26 might get std::optional<&T>
- nly 3y ago[flagged]
- triblemaster 3y agoYou might now want to, but the opinion is correct.
- OtomotO 3y agoSyntax, Schmyntax... I learned too many languages over the years and can't remember every syntax from the top of my head, but does it really matter?
- nly 3y agoYeah it matters given how the whole argument falls on advanced aspects of the type system and templates. How can you speak with authority about a language you clearly aren't using day to day? I have never once missed the distance of an optional<T&>. The practical use of optional<> is that you know where T is going to be constructed and can reason about the memory layout. Using it as an alternative to T* has never even occurred to me. The ideal of bundling a presence flag and a pointer together (which would be the default underlying representation unless it was specialised to hold a T* internally) is gross and inefficient
- blegr 3y ago> The practical use of optional<> is that you know where T is going to be constructed and can reason about the memory layout. The practical use for me is making interfaces safer. Where I saw colleagues use pointers as optionals, end up mis-tracking what can be null and what can't, only checking it inconsistently, and triggering UB, I now have a clear distinction between optional and non-optional arguments/returns with an easy way to access the contained .value() without risk of UB. The type also tells me when I should handle the empty case and when I shouldn't. Most of the time, I want to pass/return a reference and the lack of `optional<&T>` makes it tiring. If only `std::reference_wrapper` had a shorter name, I could at least use that. But then I'd end up with `arg.value().get().attr` when `.value().attr` should be enough...
- frutiger 3y ago> Most of the time, I want to pass/return a reference Surprised to hear that you want to return a reference so frequently.
- mathstuf 3y agoThese kinds of discussions remind me that not everyone codes in the same domain where the same patterns dominate. I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments and instead have the imagination that others may code in different domains where different patterns dominate. Me? References get returned all the time because you want to access some state store's vector of things without copying the vector just to ask "are any of the elements X?" or adding a new method for every `std::algorithm` method for each member you might want to use it on. The benefit of `optional<T&>` over `T*` in an API is that the former communicates "you have write access to this thing which may not exist" whereas the second needs documentation for whether `nullptr` is a thing and whether the caller needs to `delete` it (or was it `delete[]` this time?).
- frutiger 3y ago> I think everyone would do well to avoid "but I don't need it, so it seems unnecessary" kinds of arguments This is an uncharitable characterisation of what I said. > References get returned all the time because you want to access some state store's vector of things without copying the vector just to ask "are any of the elements X?" This is what const references are for. Returning an optional<vector<T>&> to query if it contains an element would not be appropriate.
- harry8 3y agoIn 2 sentences this comment encapsulates everything wrong with C++ culture that has caused so many terrible errors becoming standardized forever over the years. Sometimes being nice really pays off.
- nly 3y agoSorry, but I just get sick of people pontificating about the academics of type systems and monadics. Bored me to death. std::optional<> is an extremely useful and welcome addition to C++ that improves code quality, is easy to understand, and has easy to reason about code generation. No doubt there is something that could be demonstrated in Rust with Option that is compelling, but I'd rather people showed that so a basis for comparison with modern C++ code can be made. I suspect in practice these arguments make little difference to real world code.
- kaashif 3y agoThe people criticizing std::optional are doing a very poor job. Here's the big issue: unchecked access to std::optional with operator* has undefined behavior when there's no value. This is unforgivably bad design since you can enforce exhaustive checking at compile time, but C++ isn't going in that direction. std::optional offers value() for checked access too, but that checks at runtime and throws an exception. It is possible to have an optional with no runtime check, no undefined behaviour, and guaranteed handling of both cases by checking at compile time. This is what Rust offers. Undefined behaviour being easy to invoke absolutely does matter for real world code.
- macgyverismo 3y agocan you show me how rust does this? I'm genuinely curious. I've made a toy example to show how c++ checks for undefined behavior at compile time, I am unaware of rust being able to do the same without runtime costs (however small they may be, this is a toy example after all) https://godbolt.org/z/cT9bqz8z7 https://godbolt.org/z/cT9bqz8z7
- 3y ago
- fsloth 3y agoAre you serious :D? The arbitrary syntax toenail clippings that is C++ should be handled by IDE & compiler, linters and static type checker. C++ syntax is not music or maths, it’s just more or less arbitrary gobbledygook. (I write C++ as my main job). If you can intuitively parse all that then all the power to you but it absolutely is not requirement to do proper software engineering in C++ or have opinions on the language :).
- Calavar 3y agoThe lack of support for optional<T&> is not an issue at all in my opinion. The actual issue is that std::optional is not a monadic type in the vein of Rust's Option or Haskell's Maybe. So really, what does it buy you over std::pair<T, bool>? Except being unsafe by default since it allows you to access an unconstructed T. Basic monadic operations don't arrive for std::optional until C++23, which is an unforced error. They should have been there from the beginning.
- AHTERIX5000 3y agoFunny how this just keeps happening in the C++ world. I've seen ten different promise/task frameworks successfully used in production with neat APIs but somehow std::future is still just a toy. Even std::expected was released without the usual map/then.
- deleted 3y ago[deleted]
- mgaunard 3y agostd optional is based on boost optional which was written in 2003 before any sort of lambdas made monadic operations usable. The main concern with that component was ensuring we can allocate stack storage for an object that may or may not be initialized. The reference is easily achievable by using T* so is of minimal value, but also poses some more semantic problems since a reference is not copyable while an optional is.
- tialaramex 3y agoI actually don't care that much about the monadic functions. For me the important use case is pattern matching, which C++ doesn't yet have. Pattern matching really changes how you see the entire language.
- mgaunard 3y agoC++ has pattern matching through overloading.
- tialaramex 3y agoHow do you figure?
- mgaunard 3y agoI don't understand the question. Here is the first example I found on Google if that helps you understand. std::variant<Fluid, LightItem, HeavyItem, FragileItem> package; std::visit(overload{ [](Fluid& ) { std::cout << "fluid\n"; }, [](LightItem& ) { std::cout << "light item\n"; }, [](HeavyItem& ) { std::cout << "heavy item\n"; }, [](FragileItem& ) { std::cout << "fragile\n"; } }, package);
- tialaramex 3y agoBut that's not really even a pattern match? Here's what a pattern match looks like: [This is from day 10 of last year's Advent of Code.] match (state, pipe) { (State::None, Pipe::Ground) => { if inside { n += 1; } } (State::None, Pipe::Vert) => { inside = !inside; } (State::None, Pipe::Se) => { state = State::South; } (State::None, Pipe::Ne) => { state = State::North; } // Horizontal lines make no difference to anything (State::North | State::South, Pipe::Horiz) => {} // U-turns (State::South, Pipe::Sw) | (State::North, Pipe::Nw) => { state = State::None; } // Form a vertical line (State::South, Pipe::Nw) | (State::North, Pipe::Sw) => { inside = !inside; state = State::None; } _ => { panic!("Unexpected sequence {state:?} {pipe:?}"); } }
- deleted 3y ago[deleted]
- meindnoch 3y agoEasy. When you want std::optional<T&> just use T*.