On 28/05/2017 10:46, Niall Douglas wrote:
I would agree. But well, we were outvoted. And that probably means rejection of this library, as the presented library does not implement what the majority want (yet).
Just to clarify the meaning, since I'm not especially fluent in standardese: by "narrow contract" you mean "has UB if you don't include external checks", correct? I really don't like that even being an option in a type intended to improve error handling.
I personally think I'll have very little use for the narrow contract varieties, but I've been convinced by the non-empty-capable vs empty-capable distinction because it means that my public API functions can specify via the type they return whether an empty return is possible or not, thus more accurately specifying their public contract. That, and the fact I can still use a receiving empty-capable variety for detecting when loops haven't found what they were looking for etc I find very compelling.
Doesn't that make things more complicated, though? Library A uses one flavour of it and library B uses a different flavour; how do they interoperate? (I know both can coexist due to namespacing and other things, but still if one method calls the other there has to be some kind of handover of the return value.) For that matter, what happens when a method in library A (using the guaranteed-never-empty flavour) calls a method in library B (using the empty-by-default flavour)? Can they put the return value into one of their never-empty outcomes (but then what if it's empty?) or do they have to work with the might-be-empty outcome type that's foreign to their own code and way they want to do things, and thus more likely to be used incorrectly? Typedefs can sort out the differences to a certain extent, but then you need to remember to use the right one with each library (either getting unexpected behaviour or the compiler yelling at you when incorrect, depending on implementation, neither of which is particularly desirable), and there's possible interoperability issues for storing the results from multiple libraries into a single vector, for example. I know this entire discussion has proved that it's hard to find consensus on the One True Way™, but I'm inclined to believe that it may still be important to do so when trying to define a vocabulary type.
People are getting hung up on the multitude of types. They're just API personalities onto an identical implementation. They're just different ways of speaking to the same variant storage, so basically we are using the C++ type system like a pair of glasses onto the exact same view, with well defined rules for changing which glasses you're wearing as demand presents.
Perhaps this answers some of my questions above with "it just works", but perhaps also these sorts of things should be clarified in the docs? What about interop between a v1 outcome and a v2 outcome? That's no longer an identical implementation.