Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T> 3. "Unchecked" access to the contained object, which causes an UB when performed on an uninitialized optional object. There are valid reasons why optional is defined the way it is defined. But at the same time the complaints above are also valid. This makes some people abandon Boost.Optional and use their own alternative. My idea is to provide another type wrapper, say safe_optional<T>, that would choose different tradeoffs: prefer "safety" to some flexibility and/or efficiency. It would probably be part of Boost.Optional library as the implementation can be reused - only the interface would be changed. One downside of this solution is that we would have two libraries for doing nearly the same thing, which could "scatter" the community. There is a value for standardizing certain things and making them universal. I would like to solicit your feedback. Regards, &rzej
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T> 3. "Unchecked" access to the contained object, which causes an UB when performed on an uninitialized optional object.
Number 3 is really not an issue IMO. If you're going to overload operator* then existing C++ practice dictates that you have to do if(opt) before dereferencing opt, otherwise you'll have UB.
On Sun, Nov 16, 2014 at 11:04 PM, Andrzej Krzemienski
Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T> 3. "Unchecked" access to the contained object, which causes an UB when performed on an uninitialized optional object.
There are valid reasons why optional is defined the way it is defined. But at the same time the complaints above are also valid. This makes some people abandon Boost.Optional and use their own alternative.
I don't immediately see the problem with the mixed comparisons between T and optional T... except maybe confusion regarding optional<bool> or bool-like types. Is this what you're referring to? If so, then I sort of disagree that it's a legitimate problem. If you are not talking about issues relating to bool or have some really compelling optional<bool> cases, can you point me to examples as to why this is suggested to be unsafe. I don't really care that much about the mixed comparisons, but I consider them pretty benign and if people find it useful then I won't remove it unless the current uses are fully considered. As for #2, I think I agree that conversion should probably be more explicit and is potentially a step in a positive direction. As for #3, if you are implying the alternative is to throw an exception, then I'd say that's a flat-out no. That's a logic error and we shouldn't throw an exception. The only thing that comes of that is people using the exception for basic control flow -- if they knew the optional didn't point to anything, then they wouldn't be dereferencing it. If they THOUGHT the optional pointed to something and it didn't, then they have a bug, which means the way to "handle" it is to fix the code. If they don't know at the time of the access whether there was something there or not, then they shouldn't deference it, using an exception for control flow. In that last case, they should either be explicitly branching or they should use some kind of visitation. If you're not talking about exceptions, then what exactly are you proposing? Regardless, if people come to an agreement on changes, I'm much more in favor of simply making breaking changes to the current optional rather than introducing another one. It's unfortunate for a type as fundamental and widely used as optional, but people will get over it, especially if it makes it closer to the standard proposal. -- -Matt Calabrese
2014-11-17 9:40 GMT+01:00 Matt Calabrese
On Sun, Nov 16, 2014 at 11:04 PM, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T> 3. "Unchecked" access to the contained object, which causes an UB when performed on an uninitialized optional object.
There are valid reasons why optional is defined the way it is defined. But at the same time the complaints above are also valid. This makes some people abandon Boost.Optional and use their own alternative.
I don't immediately see the problem with the mixed comparisons between T and optional T... except maybe confusion regarding optional<bool> or bool-like types. Is this what you're referring to? If so, then I sort of disagree that it's a legitimate problem. If you are not talking about issues relating to bool or have some really compelling optional<bool> cases, can you point me to examples as to why this is suggested to be unsafe. I don't really care that much about the mixed comparisons, but I consider them pretty benign and if people find it useful then I won't remove it unless the current uses are fully considered.
As for #2, I think I agree that conversion should probably be more explicit and is potentially a step in a positive direction.
As for #3, if you are implying the alternative is to throw an exception, then I'd say that's a flat-out no. That's a logic error and we shouldn't throw an exception. The only thing that comes of that is people using the exception for basic control flow -- if they knew the optional didn't point to anything, then they wouldn't be dereferencing it. If they THOUGHT the optional pointed to something and it didn't, then they have a bug, which means the way to "handle" it is to fix the code. If they don't know at the time of the access whether there was something there or not, then they shouldn't deference it, using an exception for control flow. In that last case, they should either be explicitly branching or they should use some kind of visitation. If you're not talking about exceptions, then what exactly are you proposing?
No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T> optional<int> oi = ...; 1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing 2: if (oi) doSomething(*oi); else doSomething(-1); // use default value 3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe Now we simply provide a dedicated interface for each of these three usages: 1: oi.use(&doSomething, &doSomethingElse); // or use lambdas 2: doSomething(oi.value_or(-1)); 3: doSomething(oi.value_or_eval(getIntByOtherMeans));
Regardless, if people come to an agreement on changes, I'm much more in favor of simply making breaking changes to the current optional rather than introducing another one. It's unfortunate for a type as fundamental and widely used as optional, but people will get over it, especially if it makes it closer to the standard proposal.
I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more like any value of T, plus one additional value. In this model implicit conversions make perfect sense and are n fact desirable.
On Nov 17, 2014 1:25 AM, "Andrzej Krzemienski"
No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages:
1: oi.use(&doSomething, &doSomethingElse); // or use lambdas
2: doSomething(oi.value_or(-1));
3: doSomething(oi.value_or_eval(getIntByOtherMeans));
While I agree that we should be encouraging high order functions/visitation here, my personal experience is simply that many people do not like it. When given the choice to pass a lamda or to use an "if" or a range-based-for over high order function alternatives, programmers will generally opt for not using a lambda. The fact that Bjarne thinks visitation is somehow "bad" doesn't help. I agree that it's safer and I'd be in favor of it, but be aware that not everyone will like it. That said, I'd be in favor of this. If going with this approach, though, I'd say make optional a model of a variant and/or discriminated union concept that it and variant share, along with "expected" if it were to make it into boost (I.E. allow for apply_visitor of T and none, provide a "which"). I'd still like to see the other functions you mention as well for convenience. I know that there are others (Eric?) who prefer to think of optional as a container of size 0 or 1, so it could also make sense to provide an accessor to a range. One interesting effect of this is that now range-based-for sort of becomes a "visit if." There are probably other... interesting uses of standard algorithms.
I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more like any value of T, plus one additional value. In this model implicit conversions make perfect sense and are n fact desirable.
I'm not sure I agree. These two templates aren't exactly complimentary in terms of the functionality that they provide, they really would just provide exactly the same functionality with the same internals. All that is different is the interface. Because of that, I say either it replaces optional or it doesn't exit as a part of boost. If people are convinced that this interface is less error prone, then it should be THE interface for optional.
Le 17/11/14 17:41, Matt Calabrese a écrit :
On Nov 17, 2014 1:25 AM, "Andrzej Krzemienski"
wrote: No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages: 1: oi.use(&doSomething, &doSomethingElse); // or use lambdas
2: doSomething(oi.value_or(-1));
3: doSomething(oi.value_or_eval(getIntByOtherMeans)); While I agree that we should be encouraging high order functions/visitation here, my personal experience is simply that many people do not like it. When given the choice to pass a lamda or to use an "if" or a range-based-for over high order function alternatives, programmers will generally opt for not using a lambda. The fact that Bjarne thinks visitation is somehow "bad" doesn't help. I agree that it's safer and I'd be in favor of it, but be aware that not everyone will like it. I will ad that I believe Dr BS don't like dynamic visitation, but here we are doing pattern matching on types, isn't it?
That said, I'd be in favor of this. If going with this approach, though, I'd say make optional a model of a variant and/or discriminated union concept that it and variant share, along with "expected" if it were to make it into boost (I.E. allow for apply_visitor of T and none, provide a "which"). I'd still like to see the other functions you mention as well for convenience.
Yes, all these types a sum types, so any feature in the scope of sum types can be applied to all of them. In addition, one of the optional sum types is a non-a-value, and there is a lot of common thing for such types probably valued types. expected shares a lot with optional, but it in not the only one e.g. T*. The current problem we have with our TBoost.Expected is that we don't have an implementation for C++98 compilers :( If the Boost community can accept a C++11 only library I'll be glad to work on preparing it for review. Is there any one that wants to work on a branch to port it to C++98 compilers?
I know that there are others (Eric?) who prefer to think of optional as a container of size 0 or 1, so it could also make sense to provide an accessor to a range. One interesting effect of this is that now range-based-for sort of becomes a "visit if." There are probably other... interesting uses of standard algorithms.
I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more like any value of T, plus one additional value. In this model implicit conversions make perfect sense and are n fact desirable. I'm not sure I agree. These two templates aren't exactly complimentary in terms of the functionality that they provide, they really would just provide exactly the same functionality with the same internals. All that is different is the interface. Because of that, I say either it replaces optional or it doesn't exit as a part of boost. If people are convinced that this interface is less error prone, then it should be THE interface for optional.
Andrzej, I think I see what you meant by a safe_optional. A safe optional wouldn't provide direct access to the value. (Sorry I need sometime to understand things). Couldn't safe_optional just be a wrapper of optional restricting its interface? safe_optional could be explicitly convertible to optional and optional implicitly convertible to safe_optional. Instead of safe_optional I would prefer to don't use the safe_ prefix, as optional is safe already. IIUC, safe_optional is limited to a monad interface and whatever we can build on top of it. What about monad_optional<T> or monad::optional<T>? @Matt I don't think we need to choose instead of the user. We have here two complementary interface to the same data, there is no one better than the other. Up to the user to choose the one more adapted to his needs. Best, Vicente
On Mon, Nov 17, 2014 at 10:59 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I will ad that I believe Dr BS don't like dynamic visitation, but here we are doing pattern matching on types, isn't it?
For a while I assumed exactly that, but he actually specifically voiced that he does not like visitation on variant and thinks of it as a hack. It's sad :/
@Matt I don't think we need to choose instead of the user. We have here two complementary interface to the same data, there is no one better than the other. Up to the user to choose the one more adapted to his needs.
I'm still not really sure I agree. I see no problem with things like Boost.MSM and Boost.Statechart both being in Boost, as while they solve many of the same problems they each have very clear advantages and disadvantages. I'm actually very much in favor of stuff like that. On the other hand, safe_optional and optional would basically be two templates that have identical implementation with just different interfaces. If you can say objective things about one being safer, and all else is equal, then it should be THE library and the old optional should be deprecated. I don't think it's worth having two libraries when they are so similar. -- -Matt Calabrese
On Mon, Nov 17, 2014 at 11:16 PM, Matt Calabrese
On the other hand, safe_optional and optional would basically be two templates that have identical implementation with just different interfaces. If you can say objective things about one being safer, and all else is equal, then it should be THE library and the old optional should be deprecated. I don't think it's worth having two libraries when they are so similar.
Also, to be clear, I'm not saying I believe that safe_optional is necessarily the right way to go, I just think that if it were to be decided that it is worthwhile, it should probably just replace optional. I'm not entirely convinced that it's really worth a new template, though. Ultimately I think it's probably best to simply create/evangelize high-order visitation accessors and have them work with the existing optional. Then just be clear in documentation that their use is recommended over raw access. It's the same with variant and I don't think that's a problem -- encourage visitation, but allow direct access. -- -Matt Calabrese
2014-11-18 8:16 GMT+01:00 Matt Calabrese
On Mon, Nov 17, 2014 at 10:59 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I will ad that I believe Dr BS don't like dynamic visitation, but here we are doing pattern matching on types, isn't it?
For a while I assumed exactly that, but he actually specifically voiced that he does not like visitation on variant and thinks of it as a hack. It's sad :/
@Matt I don't think we need to choose instead of the user. We have here two complementary interface to the same data, there is no one better than the other. Up to the user to choose the one more adapted to his needs.
I'm still not really sure I agree. I see no problem with things like Boost.MSM and Boost.Statechart both being in Boost, as while they solve many of the same problems they each have very clear advantages and disadvantages. I'm actually very much in favor of stuff like that.
On the other hand, safe_optional and optional would basically be two templates that have identical implementation with just different interfaces. If you can say objective things about one being safer, and all else is equal, then it should be THE library and the old optional should be deprecated. I don't think it's worth having two libraries when they are so similar.
This is something more than that. They have the same implementation but two different conceptual models with different trade-offs between performance, expressibility and UB-avoidance.
-- -Matt Calabrese
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 18 November 2014 01:16, Matt Calabrese
On Mon, Nov 17, 2014 at 10:59 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I will ad that I believe Dr BS don't like dynamic visitation, but here we are doing pattern matching on types, isn't it?
For a while I assumed exactly that, but he actually specifically voiced that he does not like visitation on variant and thinks of it as a hack. It's sad :/
FWIW: I think he is (a) correct, but (b) we have nothing better to replace it, so we still need it.
On the other hand, safe_optional and optional would basically be two templates that have identical implementation with just different interfaces. If you can say objective things about one being safer, and all else is equal, then it should be THE library and the old optional should be deprecated. I don't think it's worth having two libraries when they are so similar.
+1. Optional is a vocabulary type; it shouldn't have two different spellings and work in two different ways. -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
On Tue, Nov 18, 2014 at 12:24 PM, Nevin Liber
On 18 November 2014 01:16, Matt Calabrese
wrote: On Mon, Nov 17, 2014 at 10:59 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I will ad that I believe Dr BS don't like dynamic visitation, but
here
we are doing pattern matching on types, isn't it?
For a while I assumed exactly that, but he actually specifically voiced that he does not like visitation on variant and thinks of it as a hack. It's sad :/
FWIW: I think he is (a) correct, but (b) we have nothing better to replace it, so we still need it.
We should really start a thread about this. I'm very curious to see an actual objective rationale both for why visitation over a closed set of types known at compile time is in any way a "hack," along with a realistic alternative. It really is a fundamental operation of a discriminated union. As someone who uses variants pretty much as the "default" for run-time polymorphism in day-to-day coding, I have never seen an actual explanation for what someone might consider bad about variant visitation, neither from Bjarne nor anyone else.
On Tue, Nov 18, 2014 at 2:16 AM, Matt Calabrese
On Mon, Nov 17, 2014 at 10:59 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I will ad that I believe Dr BS don't like dynamic visitation, but here we are doing pattern matching on types, isn't it?
For a while I assumed exactly that, but he actually specifically voiced that he does not like visitation on variant and thinks of it as a hack. It's sad :/
He presented an interesting switch-like matching (both values and types) mechanism at the last standards committee. But until we get it, we need *something*. Tony
2014-11-18 7:59 GMT+01:00 Vicente J. Botet Escriba : Le 17/11/14 17:41, Matt Calabrese a écrit : On Nov 17, 2014 1:25 AM, "Andrzej Krzemienski" wrote: No, no exceptions.
The solution is based on the observation that there is a limited number
of
things you can do with optional<T> optional<int> oi = ...; 1:
if (oi) doSomething(*oi);
else doSomethingElse(); // or nothing 2:
if (oi) doSomething(*oi);
else doSomething(-1); // use default value 3:
if (!oi) oi = (int)getIntByOtherMeans();
doSomething(*oi); // now it is safe Now we simply provide a dedicated interface for each of these three usages: 1:
oi.use(&doSomething, &doSomethingElse); // or use lambdas 2:
doSomething(oi.value_or(-1)); 3:
doSomething(oi.value_or_eval(getIntByOtherMeans)); While I agree that we should be encouraging high order
functions/visitation
here, my personal experience is simply that many people do not like it.
When given the choice to pass a lamda or to use an "if" or a
range-based-for over high order function alternatives, programmers will
generally opt for not using a lambda. The fact that Bjarne thinks
visitation is somehow "bad" doesn't help. I agree that it's safer and I'd
be in favor of it, but be aware that not everyone will like it. I will ad that I believe Dr BS don't like dynamic visitation, but here we
are doing pattern matching on types, isn't it? That said, I'd be in favor of this. If going with this approach, though,
I'd say make optional a model of a variant and/or discriminated union
concept that it and variant share, along with "expected" if it were to
make
it into boost (I.E. allow for apply_visitor of T and none, provide a
"which"). I'd still like to see the other functions you mention as well
for
convenience. Yes, all these types a sum types, so any feature in the scope of sum types
can be applied to all of them.
In addition, one of the optional sum types is a non-a-value, and there is
a lot of common thing for such types probably valued types. expected shares
a lot with optional, but it in not the only one e.g. T*. The current problem we have with our TBoost.Expected is that we don't have
an implementation for C++98 compilers :(
If the Boost community can accept a C++11 only library I'll be glad to
work on preparing it for review.
Is there any one that wants to work on a branch to port it to C++98
compilers? I know that there are others (Eric?) who prefer to think of optional as a
container of size 0 or 1, so it could also make sense to provide an
accessor to a range. One interesting effect of this is that now
range-based-for sort of becomes a "visit if." There are probably other...
interesting uses of standard algorithms. I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more
like any value of T, plus one additional value. In this model implicit
conversions make perfect sense and are n fact desirable. I'm not sure I agree. These two templates aren't exactly complimentary in
terms of the functionality that they provide, they really would just
provide exactly the same functionality with the same internals. All that
is
different is the interface. Because of that, I say either it replaces
optional or it doesn't exit as a part of boost. If people are convinced
that this interface is less error prone, then it should be THE interface
for optional. Andrzej, I think I see what you meant by a safe_optional. A safe optional wouldn't provide direct access to the value. (Sorry I need
sometime to understand things). Couldn't safe_optional just be a wrapper of optional restricting its
interface? safe_optional could be explicitly convertible to optional and
optional implicitly convertible to safe_optional. Instead of safe_optional I would prefer to don't use the safe_ prefix, as
optional is safe already. IIUC, safe_optional is limited to a monad
interface and whatever we can build on top of it. What about
monad_optional<T> or monad::optional<T>? I agree with these suggestions. I do not want to explore the possibilities
in detail just yet. As the first step, I want to check if there exists
consensus on having two types like this, offering different trade-offs. @Matt I don't think we need to choose instead of the user. We have here
two complementary interface to the same data, there is no one better than
the other. Up to the user to choose the one more adapted to his needs. Best,
Vicente _______________________________________________
Unsubscribe & other changes: http://lists.boost.org/
mailman/listinfo.cgi/boost
On 18 Nov 2014 at 7:59, Vicente J. Botet Escriba wrote:
The current problem we have with our TBoost.Expected is that we don't have an implementation for C++98 compilers :( If the Boost community can accept a C++11 only library I'll be glad to work on preparing it for review. Is there any one that wants to work on a branch to port it to C++98 compilers?
FYI Vicente I should, before Christmas, port Expected to my BindLib platform which is rapidly maturing into a very viable lightweight base for a C++ 11 only Boost 2.0 - it can now do compiler feature detection and emulation of Boost.Test, thus eliminating the need for Boost.Core and Boost.Test. Then Expected can function as a std::experimental and a boost edition, indeed both builds can coexist in the same translation unit. I hope to submit BindLib into the Boost review queue early 2015. These last few weeks have been consumed with porting AFIO to BindLib as the first real test of the viability of the new platform, and getting regular Jenkins CI unit testing working which has been a challenge given the heavy preprocessor usage to implement psuedo-Modules. It was a great moment last week when proposed Boost.AFIO completed a full unit test cycle using nothing but BindLib and the VS2015 STL without a single line of Boost code anywhere in there, and seconds later it completed a full unit test cycle using Boost exclusively for everything. BindLib doesn't care about C++ 98, but the bindings are inevitably a hard requirement for template aliasing support in the compiler. On a 98 platform, bindings could be synthesised using namespace mounts, indeed due to lack of two phase lookup on MSVC one already needs to monkey patch MSVC's broken lookup using namespace mounts. To be honest the challenge in getting Expected onto a 98 platform will be all the C++ 11 features Expected uses. Even my unrestricted unions emulation for VS2013 only works because I took advantage of VS2013's very relaxed parser. That emulation won't compile on an AST based compiler as it's illegal C++. Those are the kind of headaches someone porting Expected to 98 would have to face - I could imagine a brand new rewrite from scratch might actually be faster. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 11/18/2014 07:59 AM, Vicente J. Botet Escriba wrote:
The current problem we have with our TBoost.Expected is that we don't have an implementation for C++98 compilers :( If the Boost community can accept a C++11 only library I'll be glad to work on preparing it for review.
Yes please. The consensus is that C++11 only libraries are acceptable.
Andrzej Krzemienski
No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Just to throw some idea in, there's an altenative based on the following approach: let's assume we have a function f such as f(T1,...,T2)->ret and we have arg1,...argn where the type of argi optional<T1>. To call f we'd have to do something like auto ret= arg1&&arg2&&...&&argn? f(arg1.get(),...,argn.get()): none; In fact, we could *lift* f so that we write this instead: auto ret=lift<f>(arg1,...,argn); with the same semantics. This is a case of *monadic lifting* as explained (and implemented) at http://bannalia.blogspot.com/2014/03/monadic-lifting-in-c.html Lifting can be applied to regular functions as well as operator such as the arithmetic ones (relational operators don't follow the asme behavior). For the same price, lift<f> can accept any combination of optional and non-optional args. Not sure this is a case of interface augmentation that we want to apply to boost::optional, but I felt like mentioning it at least. Joaquin M López Muñoz Telefónica
Le 17/11/14 18:14, Joaquin M Lopez Munoz a écrit :
Andrzej Krzemienski
writes: No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe Just to throw some idea in, there's an altenative based on the following approach: let's assume we have a function f such as
f(T1,...,T2)->ret
and we have arg1,...argn where the type of argi optional<T1>. To call f we'd have to do something like
auto ret= arg1&&arg2&&...&&argn? f(arg1.get(),...,argn.get()): none;
In fact, we could *lift* f so that we write this instead:
auto ret=lift<f>(arg1,...,argn);
with the same semantics. This is a case of *monadic lifting* as explained (and implemented) at
http://bannalia.blogspot.com/2014/03/monadic-lifting-in-c.html Hi Joaquin, I missed your post.
I like this, of course :)
If I understood your blog, this should be something like
auto ret = lift
Lifting can be applied to regular functions as well as operator such as the arithmetic ones (relational operators don't follow the asme behavior). For the same price, lift<f> can accept any combination of optional and non-optional args. Not sure this is a case of interface augmentation that we want to apply to boost::optional, but I felt like mentioning it at least.
This lift operation is a good thing when you have a function. The problem is that people don't have always a function to call, they have just code to execute. The closer we have are lambdas auto res = fmap(o1, ..., on , [](a1, ..., an) { // the code }); Thanks for raising this alternative. Vicente
Vicente J. Botet Escriba
Le 17/11/14 18:14, Joaquin M Lopez Munoz a écrit :
Just to throw some idea in, there's an altenative based on the following approach:[...] we could *lift* f so that we write this instead:
auto ret=lift<f>(arg1,...,argn);
with the same semantics. This is a case of *monadic lifting* as explained (and implemented) at
http://bannalia.blogspot.com/2014/03/monadic-lifting-in-c.html
Hi Joaquin, I missed your post.
I like this, of course :)
If I understood your blog, this should be something like
auto ret = lift
(a1, ..., an); Or could the optional template be deduced?
It would be more like lift<optional>(f)(a1,...,an) but in any case the particular monad being lifted to (here optional) must be specified.
doesn't scales to expected
. Instead of a template <class> parameter, lift should take a type constructor, In Boost.Expected we would do this as auto ret = lift
, f>(a1, ..., an); where _holder is a holder type that makes expected<_holder, error_code> to behave as a type constructor.
Absolutely, additional template parameters like in expected would call for a type constructor.
This lift operation is a good thing when you have a function. The problem is that people don't have always a function to call, they have just code to execute. The closer we have are lambdas
auto res = fmap(o1, ..., on , [](a1, ..., an) { // the code });
You can lift lambdas as well: auto res=lift<optional>([](int x,int y){ return ...; })(o1,o2); Joaquín M López Muñoz Telefónica
Le 27/11/14 12:51, Joaquin M Lopez Munoz a écrit :
Vicente J. Botet Escriba
writes: Le 17/11/14 18:14, Joaquin M Lopez Munoz a écrit :
Just to throw some idea in, there's an altenative based on the following approach:[...] we could *lift* f so that we write this instead:
auto ret=lift<f>(arg1,...,argn);
with the same semantics. This is a case of *monadic lifting* as explained (and implemented) at
http://bannalia.blogspot.com/2014/03/monadic-lifting-in-c.html Hi Joaquin, I missed your post.
I like this, of course :)
If I understood your blog, this should be something like
auto ret = lift
(a1, ..., an); Or could the optional template be deduced? It would be more like
lift<optional>(f)(a1,...,an)
but in any case the particular monad being lifted to (here optional) must be specified. In TBoost.Expected we have
auto x = fmap(f, a1, ..., an);
that do the same and the type of the result is deduced from f and ai.
The lift you propose can not deduce the result type directly as it
doesn't knows yet ai, but couldn't this be done at the call of the lifter?
template
This lift operation is a good thing when you have a function. The problem is that people don't have always a function to call, they have just code to execute. The closer we have are lambdas
auto res = fmap(o1, ..., on , [](a1, ..., an) { // the code }); You can lift lambdas as well:
auto res=lift<optional>([](int x,int y){ return ...; })(o1,o2);
Of course. Thanks for recalling it to me. I wanted to share this (from http://en.wikipedia.org/wiki/Nullable_type) Nullable references were invented by C.A.R. Hoare http://en.wikipedia.org/wiki/C.A.R._Hoare in 1965 as part of the Algol W http://en.wikipedia.org/wiki/Algol_W language. Hoare later described their invention as a "billion dollar mistake".^[4] http://en.wikipedia.org/wiki/Nullable_type#cite_note-4 This is because object pointers that can be NULL require the user to check the pointer before using it and require specific code to handle the case when the object pointer is NULL. [4] Tony Hoare (2009). "Null References: The Billion Dollar Mistake" http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+.... # QCon London.http://developer.apple.com/library/ios/documentation/cocoa/conceptual/Progra... Sorry the link doesn't works. I would appreciate a lot a valid link. An abstract could be found here https://www.linkedin.com/pulse/article/20141126171912-7082046-tony-hoare-inv... Best, Vicente
2014-11-17 9:40 GMT+01:00 Matt Calabrese
: On Sun, Nov 16, 2014 at 11:04 PM, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T> 3. "Unchecked" access to the contained object, which causes an UB when performed on an uninitialized optional object.
There are valid reasons why optional is defined the way it is defined. But at the same time the complaints above are also valid. This makes some people abandon Boost.Optional and use their own alternative.
I don't immediately see the problem with the mixed comparisons between T and optional T... except maybe confusion regarding optional<bool> or bool-like types. Is this what you're referring to? If so, then I sort of disagree that it's a legitimate problem. If you are not talking about issues relating to bool or have some really compelling optional<bool> cases, can you point me to examples as to why this is suggested to be unsafe. I don't really care that much about the mixed comparisons, but I consider them pretty benign and if people find it useful then I won't remove it unless the current uses are fully considered.
As for #2, I think I agree that conversion should probably be more explicit and is potentially a step in a positive direction.
As for #3, if you are implying the alternative is to throw an exception, then I'd say that's a flat-out no. That's a logic error and we shouldn't throw an exception. The only thing that comes of that is people using the exception for basic control flow -- if they knew the optional didn't point to anything, then they wouldn't be dereferencing it. If they THOUGHT the optional pointed to something and it didn't, then they have a bug, which means the way to "handle" it is to fix the code. If they don't know at the time of the access whether there was something there or not, then they shouldn't deference it, using an exception for control flow. In that last case, they should either be explicitly branching or they should use some kind of visitation. If you're not talking about exceptions, then what exactly are you proposing?
No, no exceptions. The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages:
1: oi.use(&doSomething, &doSomethingElse); // or use lambdas This is the idea of visitation and try-catch behind this function. What would be the result of this use function? Should both functions return
Le 17/11/14 10:24, Andrzej Krzemienski a écrit : the same type? I would suggest to call this accept or match. In addition this could be a free function auto x = match(oi, [] (int i) {}, [] () {} ); The function could take several optional auto x = match(make_tuple(oi, os), [] (int i, string const& s) {}, [] (...) {} );
2: doSomething(oi.value_or(-1));
I believed this was already part of the interface.
3: doSomething(oi.value_or_eval(getIntByOtherMeans));
Why do we need a new safe_optional class to add these features? Best, Vicente
2014-11-18 7:22 GMT+01:00 Vicente J. Botet Escriba : Le 17/11/14 10:24, Andrzej Krzemienski a écrit : 2014-11-17 9:40 GMT+01:00 Matt Calabrese akrzemi1@gmail.com>
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a
recurring complaint about Boost.Optional that it allows you to do "unsafe" things:
1. Inadvertent mixed comparisons between T and optional<T>
2. Unintended conversion from T to optional<T>
3. "Unchecked" access to the contained object, which causes an UB when
performed on an uninitialized optional object. There are valid reasons why optional is defined the way it is defined. But at the same time the complaints above are also valid. This makes some
people abandon Boost.Optional and use their own alternative. I don't immediately see the problem with the mixed comparisons between
T
and optional T... except maybe confusion regarding optional<bool> or
bool-like types. Is this what you're referring to? If so, then I sort of
disagree that it's a legitimate problem. If you are not talking about
issues relating to bool or have some really compelling optional<bool>
cases, can you point me to examples as to why this is suggested to be
unsafe. I don't really care that much about the mixed comparisons, but I
consider them pretty benign and if people find it useful then I won't
remove it unless the current uses are fully considered. As for #2, I think I agree that conversion should probably be more
explicit
and is potentially a step in a positive direction. As for #3, if you are implying the alternative is to throw an exception,
then I'd say that's a flat-out no. That's a logic error and we shouldn't
throw an exception. The only thing that comes of that is people using the
exception for basic control flow -- if they knew the optional didn't
point
to anything, then they wouldn't be dereferencing it. If they THOUGHT the
optional pointed to something and it didn't, then they have a bug, which
means the way to "handle" it is to fix the code. If they don't know at
the
time of the access whether there was something there or not, then they
shouldn't deference it, using an exception for control flow. In that last
case, they should either be explicitly branching or they should use some
kind of visitation. If you're not talking about exceptions, then what
exactly are you proposing? No, no exceptions.
The solution is based on the observation that there is a limited number of On Sun, Nov 16, 2014 at 11:04 PM, Andrzej Krzemienski <
things you can do with optional<T> optional<int> oi = ...; 1:
if (oi) doSomething(*oi);
else doSomethingElse(); // or nothing 2:
if (oi) doSomething(*oi);
else doSomething(-1); // use default value 3:
if (!oi) oi = (int)getIntByOtherMeans();
doSomething(*oi); // now it is safe Now we simply provide a dedicated interface for each of these three
usages: 1:
oi.use(&doSomething, &doSomethingElse); // or use lambdas This is the idea of visitation and try-catch behind this function. What
would be the result of this use function? Should both functions return the
same type? I would suggest to call this accept or match. In addition this could be a
free function auto x = match(oi,
[] (int i) {},
[] () {}
);
The function could take several optional auto x = match(make_tuple(oi, os),
[] (int i, string const& s) {},
[] (...) {}
); 2:
doSomething(oi.value_or(-1)); I believed this was already part of the interface. 3:
doSomething(oi.value_or_eval(getIntByOtherMeans)); Why do we need a new safe_optional class to add these features? Because once we have the three above (2 and 3 already present in
boost::optional) we can remove all other interface that has a potential to
cause confusion or UB. Best,
Vicente _______________________________________________
Unsubscribe & other changes: http://lists.boost.org/
mailman/listinfo.cgi/boost
On Mon, Nov 17, 2014 at 12:24 PM, Andrzej Krzemienski
The solution is based on the observation that there is a limited number of things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages:
1: oi.use(&doSomething, &doSomethingElse); // or use lambdas
I'd call it visit(). Also, a one argument visit() could be useful (the visitor would be passed boost::none if the value is absent).
2: doSomething(oi.value_or(-1));
We already have that.
3: doSomething(oi.value_or_eval(getIntByOtherMeans));
So, basically, the proposal is to add visitation API, am I correct? In that case why not add it to the regular optional? IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
Regardless, if people come to an agreement on changes, I'm much more in favor of simply making breaking changes to the current optional rather than introducing another one. It's unfortunate for a type as fundamental and widely used as optional, but people will get over it, especially if it makes it closer to the standard proposal.
I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more like any value of T, plus one additional value. In this model implicit conversions make perfect sense and are n fact desirable.
Exactly. Vladimir's example is an illustration of this.
2014-11-18 8:00 GMT+01:00 Andrey Semashev
On Mon, Nov 17, 2014 at 12:24 PM, Andrzej Krzemienski
wrote: The solution is based on the observation that there is a limited number
of
things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages:
1: oi.use(&doSomething, &doSomethingElse); // or use lambdas
I'd call it visit(). Also, a one argument visit() could be useful (the visitor would be passed boost::none if the value is absent).
2: doSomething(oi.value_or(-1));
We already have that.
3: doSomething(oi.value_or_eval(getIntByOtherMeans));
So, basically, the proposal is to add visitation API, am I correct?
And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
In that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
Regardless, if people come to an agreement on changes, I'm much more in favor of simply making breaking changes to the current optional rather than introducing another one. It's unfortunate for a type as fundamental and widely used as optional, but people will get over it, especially if it makes it closer to the standard proposal.
I would rather not go that way. boost::optional is not unsafe. It is simply something else than what some people think. Its conceptual model is more like any value of T, plus one additional value. In this model implicit conversions make perfect sense and are n fact desirable.
Exactly. Vladimir's example is an illustration of this.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Tue, Nov 18, 2014 at 12:23 PM, Andrzej Krzemienski
2014-11-18 8:00 GMT+01:00 Andrey Semashev
: So, basically, the proposal is to add visitation API, am I correct?
And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
In that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
I see. In this case I agree in that the original optional should remain the same. However, I don't see much point in such a restricted subset of the current optional. Just imagining myself using functions/lambdas instead of get() and similar code makes me dizzy. IMO, visitation API could be useful for generic code (i.e. which already uses boost::visit or similar facility and is as such compatible with boost::variant). It's not a direct replacement for extraction API (i.e. getters), it's just too cumbersome. I had realized this when I was working on Boost.Log and ended up with both interfaces to attribute values - each has its strong and weak sides and is more useful than the other in some cases.
2014-11-18 10:36 GMT+01:00 Andrey Semashev
On Tue, Nov 18, 2014 at 12:23 PM, Andrzej Krzemienski
wrote: 2014-11-18 8:00 GMT+01:00 Andrey Semashev
: So, basically, the proposal is to add visitation API, am I correct?
And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
In that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
I see. In this case I agree in that the original optional should remain the same. However, I don't see much point in such a restricted subset of the current optional. Just imagining myself using functions/lambdas instead of get() and similar code makes me dizzy.
I sympathize with your opinion. Personally, I also prefer the current way of accessing the value. Yet, I see people complain that it is unsafe. It is my hypothesis that there exist a portion of users that just like the monadic interfaces along with the inconvenience that comes with it. This post is to confirm my hypothesis, and if it is the case, to satisfy the demand.
IMO, visitation API could be useful for generic code (i.e. which already uses boost::visit or similar facility and is as such compatible with boost::variant). It's not a direct replacement for extraction API (i.e. getters), it's just too cumbersome. I had realized this when I was working on Boost.Log and ended up with both interfaces to attribute values - each has its strong and weak sides and is more useful than the other in some cases.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I see. In this case I agree in that the original optional should remain the same. However, I don't see much point in such a restricted subset of the current optional. Just imagining myself using functions/lambdas instead of get() and similar code makes me dizzy.
I sympathize with your opinion. Personally, I also prefer the current way of accessing the value. Yet, I see people complain that it is unsafe. It is my hypothesis that there exist a portion of users that just like the monadic interfaces along with the inconvenience that comes with it. This post is to confirm my hypothesis, and if it is the case, to satisfy the demand.
Another option I would like to see is for `optional` to implement a range. So instead of using a lambda, a `for` scope can be used. Paul -- View this message in context: http://boost.2283326.n4.nabble.com/optional-Safe-optional-tp4669110p4669184.... Sent from the Boost - Dev mailing list archive at Nabble.com.
On 18 November 2014 07:30, pfultz2
I see. In this case I agree in that the original optional should remain the same. However, I don't see much point in such a restricted subset of the current optional. Just imagining myself using functions/lambdas instead of get() and similar code makes me dizzy.
I sympathize with your opinion. Personally, I also prefer the current way of accessing the value. Yet, I see people complain that it is unsafe. It is my hypothesis that there exist a portion of users that just like the monadic interfaces along with the inconvenience that comes with it. This post is to confirm my hypothesis, and if it is the case, to satisfy the demand.
Another option I would like to see is for `optional` to implement a range.
+1. Note: To make this happen in std::experimental::optional might be a bit of a battle, as there are at least a few committee members who don't like this idea when I've floated it by them, but I'd certainly be willing to help push it along. Nevin :-) -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
On Tue, Nov 18, 2014 at 3:32 PM, Nevin Liber
On 18 November 2014 07:30, pfultz2
wrote: I see. In this case I agree in that the original optional should remain the same. However, I don't see much point in such a restricted subset of the current optional. Just imagining myself using functions/lambdas instead of get() and similar code makes me dizzy.
I sympathize with your opinion. Personally, I also prefer the current way of accessing the value. Yet, I see people complain that it is unsafe. It is my hypothesis that there exist a portion of users that just like the monadic interfaces along with the inconvenience that comes with it. This post is to confirm my hypothesis, and if it is the case, to satisfy the demand.
Another option I would like to see is for `optional` to implement a range.
+1.
Why? It doesn't make a very useful/interesting range, does it? (Unless you agree with Sean that everything should be range-able, ie an int denotes a range of one int. Agreeing with Sean _is_ a strong argument :-) I think the range-for-loop lacks clarity here. I worry that optional having "multi-paradigm" is harmful - since it can't do all of them without compromise. Tony
On 18 November 2014 03:48, Andrzej Krzemienski
I sympathize with your opinion. Personally, I also prefer the current way of accessing the value. Yet, I see people complain that it is unsafe.
People *always* complain.
It is my hypothesis that there exist a portion of users that just like the monadic interfaces along with the inconvenience that comes with it. This post is to confirm my hypothesis, and if it is the case, to satisfy the demand.
It is your time to implement and maintain of course, but it doesn't seem like "making everybody happy" is a tractable problem. Some interfaces just aren't good and should not be encouraged, even if it satisfies a few. -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
Andrzej, On 11/18/2014 08:23 PM, Andrzej Krzemienski wrote:
... And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
1. I might be open to the idea of unifying/cleaning the access interface and settling on one... but taking them *all* out?.. Or you want to keep operator->()? Then, I still can do optional<T> ot; T* t = ot.operator->(); You might say, it's crazy. My point is that if you want to save me (the user) from myself with "safe optional", you are most likely wasting your effort -- an uneducated idiot will always find a way to hurt himself. 2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
In that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
I have to admit I find it somewhat of a concern hearing from the "optional" maintainer and someone leading "optional" for standardization that he does not seem to share/agree with the current "optional" design to the point where you are keen on forking a "seriously incompatible" variant. Going ahead with the latter will be a lot of effort implementing, promoting, educating, defending... IMO that'll be the effort 1) not necessarily successful; 2) fragmenting the user-base; 3) most importantly, it'll be the effort taken *away* from the current "optional"... which seems so close (hopefully) to standardization. If you have concerns (you would not initiate the conversation if you did not), let's discuss them within the "optional" boundaries and document/justify the decisions made. Do I worry too much?
Le 18/11/14 22:35, Vladimir Batov a écrit :
Andrzej,
On 11/18/2014 08:23 PM, Andrzej Krzemienski wrote:
... And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
1. I might be open to the idea of unifying/cleaning the access interface and settling on one... but taking them *all* out?.. Or you want to keep operator->()? Then, I still can do
optional<T> ot; T* t = ot.operator->();
You might say, it's crazy. My point is that if you want to save me (the user) from myself with "safe optional", you are most likely wasting your effort -- an uneducated idiot will always find a way to hurt himself.
IIUC the goal of a safe_optional is to give access to the underlying value only via functions/lambdas. I see it as a black box. Of course, conversion from safe_optional<T> to optional<T> and vice-versa are IMO mandatory,
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
It is about the pre-conditions. Before doing a conversion you need to ensure that there is a value. This give an if-the-else style that doesn't scale when you have several optionals in the game.
In that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
I have to admit I find it somewhat of a concern hearing from the "optional" maintainer and someone leading "optional" for standardization that he does not seem to share/agree with the current "optional" design to the point where you are keen on forking a "seriously incompatible" variant. Going ahead with the latter will be a lot of effort implementing, promoting, educating, defending... IMO that'll be the effort 1) not necessarily successful; 2) fragmenting the user-base; 3) most importantly, it'll be the effort taken *away* from the current "optional"... which seems so close (hopefully) to standardization. If you have concerns (you would not initiate the conversation if you did not), let's discuss them within the "optional" boundaries and document/justify the decisions made. Do I worry too much?
Things are moving in C++ to the functional paradigm very quick. People would need to learn, soon or late, functional programming and monads. I'm sorry, they are viral !!! Best, Vicente
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Le 18/11/14 22:35, Vladimir Batov a écrit : ...
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
It is about the pre-conditions. Before doing a conversion you need to ensure that there is a value. This give an if-the-else style that doesn't scale when you have several optionals in the game.
Hmm, aren't we talking about different conversions? I was referring to "T to optional<T>". I have the feeling that you are referring to "optional<T> to T". I've never been advocating the implicit latter.
... I have to admit I find it somewhat of a concern hearing from the "optional" maintainer and someone leading "optional" for standardization that he does not seem to share/agree with the current "optional" design to the point where you are keen on forking a "seriously incompatible" variant. Going ahead with the latter will be a lot of effort implementing, promoting, educating, defending... IMO that'll be the effort 1) not necessarily successful; 2) fragmenting the user-base; 3) most importantly, it'll be the effort taken *away* from the current "optional"... which seems so close (hopefully) to standardization. If you have concerns (you would not initiate the conversation if you did not), let's discuss them within the "optional" boundaries and document/justify the decisions made. Do I worry too much?
Things are moving in C++ to the functional paradigm very quick. People would need to learn, soon or late, functional programming and monads. I'm sorry, they are viral !!!
You may well be right... Still, I would not be that quick and decisive underestimating people's inertia. After all, functional programming languages have been around much longer than C++. I do not see them occupying minds of the programming majority. Even using primitive functional programming in C++ (say, std::for_each instead of ol' and trusty for loop) has not been exactly "viral". :-)
On 19/11/2014 13:55, Vladimir Batov wrote:
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Things are moving in C++ to the functional paradigm very quick. People would need to learn, soon or late, functional programming and monads. I'm sorry, they are viral !!!
You may well be right... Still, I would not be that quick and decisive underestimating people's inertia. After all, functional programming languages have been around much longer than C++. I do not see them occupying minds of the programming majority. Even using primitive functional programming in C++ (say, std::for_each instead of ol' and trusty for loop) has not been exactly "viral". :-)
A lot of that has been the lack of proper lambdas and closures until "recently" (even now, many people are still using pre-C++11 compilers). While it's certainly *possible* to write functional-ish code without these, I don't think anyone would disagree that "bind" and friends uglify the code and often require contortions to provide required inputs and outputs.
On 11/19/2014 12:14 PM, Gavin Lambert wrote:
On 19/11/2014 13:55, Vladimir Batov wrote:
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Things are moving in C++ to the functional paradigm very quick. People would need to learn, soon or late, functional programming and monads. I'm sorry, they are viral !!!
You may well be right... Still, I would not be that quick and decisive underestimating people's inertia. After all, functional programming languages have been around much longer than C++. I do not see them occupying minds of the programming majority. Even using primitive functional programming in C++ (say, std::for_each instead of ol' and trusty for loop) has not been exactly "viral". :-)
A lot of that has been the lack of proper lambdas and closures until "recently" (even now, many people are still using pre-C++11 compilers).
While it's certainly *possible* to write functional-ish code without these, I don't think anyone would disagree that "bind" and friends uglify the code and often require contortions to provide required inputs and outputs.
While I agree with your point I suspect there might be something more fundamental why functional programming in C++ and functional programming in general has been on the fringes of and hardly present in mainstream commercial s/w development (not academia).
On 19/11/2014 14:32, Vladimir Batov wrote:
While I agree with your point I suspect there might be something more fundamental why functional programming in C++ and functional programming in general has been on the fringes of and hardly present in mainstream commercial s/w development (not academia).
The main problem I think is just that functional programming is more restrictive. At least in pure FP, your functions can only see their own inputs and provide values by their direct outputs. Most "real" code is messier than this -- you end up with containers of things and operations on those things that may have side effects and working state, and often end up hanging things off singletons. Admittedly steering closer to functional ideals can lead to cleaner and more-modular/less-coupled designs, but they're still a harder sell. IoC frameworks usually try to do something similar at the object level rather than the function level, and there are lots of people who don't like IoC for much the same reason. I also think that people are just more naturally wired to think in terms of instruction sequences or recipes (imperative design) rather than formulae (functional design). Still, it's going to vary a lot by developer. Some people struggle with SQL queries, which are also mostly a kind of FP, while others are masters.
On Tue, Nov 18, 2014 at 7:37 PM, Gavin Lambert
On 19/11/2014 14:32, Vladimir Batov wrote:
While I agree with your point I suspect there might be something more fundamental why functional programming in C++ and functional programming in general has been on the fringes of and hardly present in mainstream commercial s/w development (not academia).
The main problem I think is just that functional programming is more restrictive. At least in pure FP, your functions can only see their own inputs and provide values by their direct outputs. Most "real" code is messier than this -- you end up with containers of things and operations on those things that may have side effects and working state, and often end up hanging things off singletons.
I feel like it's a little bit different from that. I say this because "functional" algorithms in C++ aren't usually functional in the sense of purity anyway -- they still generally operate via side-effects. Using things like the standard algorithms do not preclude any of those "real" scenarios. What we do have plenty of in C++ are high-order functions. Pure functional programming aside, people just seem to not like high order functions in general, regardless of whether or not there are side-effects. My personal interpretation of this is that people don't like them because they add another level of abstraction, or more specifically, a level of abstraction that is /visible/ in their code. Even though we are software engineers and should embrace abstraction, many programmers don't like it because abstraction introduces one more level between what they write and what they get. This is often interpreted as additional complexity as opposed to a tool that helps isolate it. In my opinion, that view of things is a common misconception that sits in the mind of a lot of programmers out there. My overall thought is that the general lack of acceptance of high-order functions and abstraction is mostly just a knee-jerk reaction to that perceived complexity, and this is something that somehow needs to change in the programming community. The following is obviously anecdotal, but in my personal experience, people will hate on something as simple as a high-order for_each function to no end for being too abstract or for obfuscating things, but as soon as it becomes a built-in language feature, such as range-based-for, even if it does essentially the same thing, people immediately embrace it. Maybe it's because it's syntactically "cleaner," but I suspect that's not really it. So what is it then? Again, I don't think it has anything to do with purity since a library-based for_each isn't even generally pure. Again, you can still operate via side-effects. Purity isn't the problem. As far as I can tell it's that if the for-each algorithm is clearly visible as a /function/ and not simply some built-in part of the language, people perceive the notion as somehow more complicated, even though both facilities may do exactly the same thing and with the same amount of abstraction. The avoidance of the library-form stems from a programmer's [correct] fear of complexity that they now erroneously associate with a library-level algorithm even though they wouldn't associate it with a language-level version of that very same, simple algorithm. People ultimately want a solution to be as simple as it can be, for good reason, but I think it's easy to confuse complexity with abstraction and so people immediately look at an abstract, high-order function as intrinsically complex. The difference in perception of the complexity and abstraction between the language-level construct and the high-order library form of the same construct, in my opinion, purely psychological. People just need to recognize this and get past it, though in practice that seems like a difficult step to take and many programmers sadly don't ever take it. I see this as a huge problem because as software engineers, while we should always avoid complexity, abstraction is one tool that helps /remove/ complexity in high-level code by isolating invariants and by representing common patterns as individual high order functions. When people turn away from high-order functions, I generally see it as turning away from abstraction, and that is no way to build large-scale software. Anyway, I have a bad habit of typing a lot of words, but I really feel strongly that this is all very important in "real" development. High-order functions aren't merely academic, they are important for isolating complexity in real-world problems. -- -Matt Calabrese
Le 19/11/14 01:55, Vladimir Batov a écrit :
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Le 18/11/14 22:35, Vladimir Batov a écrit : ...
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
It is about the pre-conditions. Before doing a conversion you need to ensure that there is a value. This give an if-the-else style that doesn't scale when you have several optionals in the game.
Hmm, aren't we talking about different conversions? I was referring to "T to optional<T>". I have the feeling that you are referring to "optional<T> to T". I've never been advocating the implicit latter.
You are right. I was referring to optional<T> to T conversions. The T to optional<T> conversion suffer from overload resolution ambiguity. void f(int); void f(optional<int>); f(1); // ambiguous. but this is a different problem. Removing the conversion will make the following code correct f(1); f(make_optional(1)); I really think that these make_ factories make the code much more readable. let see how optional, any, expected default conversion work void f(int); void f(optional<int>); void f(expected<int>); void f(any);
... I have to admit I find it somewhat of a concern hearing from the "optional" maintainer and someone leading "optional" for standardization that he does not seem to share/agree with the current "optional" design to the point where you are keen on forking a "seriously incompatible" variant. Going ahead with the latter will be a lot of effort implementing, promoting, educating, defending... IMO that'll be the effort 1) not necessarily successful; 2) fragmenting the user-base; 3) most importantly, it'll be the effort taken *away* from the current "optional"... which seems so close (hopefully) to standardization. If you have concerns (you would not initiate the conversation if you did not), let's discuss them within the "optional" boundaries and document/justify the decisions made. Do I worry too much?
Things are moving in C++ to the functional paradigm very quick. People would need to learn, soon or late, functional programming and monads. I'm sorry, they are viral !!!
You may well be right... Still, I would not be that quick and decisive underestimating people's inertia. After all, functional programming languages have been around much longer than C++. I do not see them occupying minds of the programming majority. Even using primitive functional programming in C++ (say, std::for_each instead of ol' and trusty for loop) has not been exactly "viral". :-)
I think the main reason has been that people needed to define some function object elsewhere. With lambdas, this is no more an issue. Of course, most of the mainstreams projects are not yet using a C++11/c++14 compiler. For those that have not yet see the Seasoning talk from Seam Parent I suggest you take a look at the no-raw-loops part The other are also interesting, of course, but this part is really related to what we are discussing. I agree with you that in general people don't even know all the std algorithms are there (and that they can create their own). Let me recall a devise I have heard a lot recently. LESS CODE, MORE SOFTWARE Best, Vicente [1] http://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning
On Wed, Nov 19, 2014 at 9:58 AM, Vicente J. Botet Escriba
Le 19/11/14 01:55, Vladimir Batov a écrit :
Hmm, aren't we talking about different conversions? I was referring to "T to optional<T>". I have the feeling that you are referring to "optional<T> to T". I've never been advocating the implicit latter.
You are right. I was referring to optional<T> to T conversions. The T to optional<T> conversion suffer from overload resolution ambiguity.
void f(int); void f(optional<int>);
f(1); // ambiguous.
No, it's not. The second f() requires user-defined conversion and the first one does not.
On 11/19/2014 05:58 PM, Vicente J. Botet Escriba wrote:
Le 19/11/14 01:55, Vladimir Batov a écrit :
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Le 18/11/14 22:35, Vladimir Batov a écrit : ...
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
It is about the pre-conditions. Before doing a conversion you need to ensure that there is a value. This give an if-the-else style that doesn't scale when you have several optionals in the game.
Hmm, aren't we talking about different conversions? I was referring to "T to optional<T>". I have the feeling that you are referring to "optional<T> to T". I've never been advocating the implicit latter.
You are right. I was referring to optional<T> to T conversions. The T to optional<T> conversion suffer from overload resolution ambiguity.
void f(int); void f(optional<int>);
f(1); // ambiguous.
As Andrey already pointed out (while I was asleep :-) it is not ambiguous.
but this is a different problem.
Andrey, I and GCC insist that there is not problem here. :-)
Removing the conversion will make the following code correct
f(1); f(make_optional(1));
Again, (1) the code is already correct as-is. (2) asking the library user to call "f(make_optional(1))" where he conceptually deals with "ints" is cruel and wrong. The user needs to to program and deal with his domains concepts ('int' in this particular case) not optional<int>. Logically, from the user perspectives, the no-int condition is not part of the range. It's special case. Technically, for implementation purposes, however, it is. That's where "optional" steps in and bridges the gap between logical/human view and implementation. Contorting the human view to cater for implementation has always been wrong.
I really think that these make_ factories make the code much more readable.
let see how optional, any, expected default conversion work
void f(int); void f(optional<int>); void f(expected<int>); void f(any);
My current focus is "optional" and on that level "void f(int)" conveys what it does.
...
For those that have not yet see the Seasoning talk from Seam Parent I suggest you take a look at the no-raw-loops part The other are also interesting, of course, but this part is really related to what we are discussing.
I agree with you that in general people don't even know all the std algorithms are there (and that they can create their own).
I've been in s/w longer that I can remember and to be honest those std::for_each and the ilk do not grow on me... unless they provide *real* functionality (like sorting, etc.)... and I honestly tried to love them... The reason is simple -- readability. I jump back to the old code and immediately see/understand what it does. Lambdas *might* address that... Can't judge... C++0x compilers throughout.
Le 19/11/14 21:46, Vladimir Batov a écrit :
On 11/19/2014 05:58 PM, Vicente J. Botet Escriba wrote:
Le 19/11/14 01:55, Vladimir Batov a écrit :
On 11/19/2014 10:52 AM, Vicente J. Botet Escriba wrote:
Le 18/11/14 22:35, Vladimir Batov a écrit : ...
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
It is about the pre-conditions. Before doing a conversion you need to ensure that there is a value. This give an if-the-else style that doesn't scale when you have several optionals in the game.
Hmm, aren't we talking about different conversions? I was referring to "T to optional<T>". I have the feeling that you are referring to "optional<T> to T". I've never been advocating the implicit latter.
You are right. I was referring to optional<T> to T conversions. The T to optional<T> conversion suffer from overload resolution ambiguity.
void f(int); void f(optional<int>);
f(1); // ambiguous.
As Andrey already pointed out (while I was asleep :-) it is not ambiguous.
but this is a different problem.
Andrey, I and GCC insist that there is not problem here. :-)
void f(long);
Removing the conversion will make the following code correct
f(1); f(make_optional(1));
Again, (1) the code is already correct as-is. (2) asking the library user to call "f(make_optional(1))" where he conceptually deals with "ints" is cruel and wrong. The user needs to to program and deal with his domains concepts ('int' in this particular case) not optional<int>. Logically, from the user perspectives, the no-int condition is not part of the range. It's special case. Technically, for implementation purposes, however, it is. That's where "optional" steps in and bridges the gap between logical/human view and implementation. Contorting the human view to cater for implementation has always been wrong.
My examples are not the best. Andrzej one in this thread is much more appropriated. return ac.weight() > ac.max_weight(); The safe_optional would just not accept this expression. I understand people that could prefer this security. ...
...
For those that have not yet see the Seasoning talk from Seam Parent I suggest you take a look at the no-raw-loops part The other are also interesting, of course, but this part is really related to what we are discussing.
I agree with you that in general people don't even know all the std algorithms are there (and that they can create their own).
I've been in s/w longer that I can remember and to be honest those std::for_each and the ilk do not grow on me... unless they provide *real* functionality (like sorting, etc.)... and I honestly tried to love them... The reason is simple -- readability. I jump back to the old code and immediately see/understand what it does. Lambdas *might* address that... Can't judge... C++0x compilers throughout. I can understand you if you are using a C++98 compiler. The for_each example has already been replaced by range-based for, but there is much more than for_each.
do-notation result in code more readable. All we need in C++ is to add some syntactical sugar to cover the common cases. operator await would be a major step in this sens. await propagates the not-a-value cases, and return converts explicitly to the result type. But the conversion from T to M<T> is not implicit. We need some kind of make factory that results in something that is implicitly convertible to M<T>. what do you think of f( explicit 1 ); to state that we want an explicit conversion to a M<int>? Vicente
On November 19, 2014 5:05:57 PM EST, "Vicente J. Botet Escriba"
We need some kind of make factory that results in something that is implicitly convertible to M<T>.
what do you think of
f( explicit 1 );
to state that we want an explicit conversion to a M<int>?
-1 A change in the overload set can change the meaning of such an expression or make it ambiguous, so it is better to state explicitly the intended conversion. ___ Rob (Sent from my portable computation engine)
2014-11-18 22:35 GMT+01:00 Vladimir Batov
In
that case why not add it to the regular optional?
IMHO, in order to introduce an alternative component, there should be significant and incompatible design and interface differences between the two. So far I don't see the need for such differences.
As explained above: serious backwards incompatibility. Unacceptable by many, including myself.
I have to admit I find it somewhat of a concern hearing from the "optional" maintainer and someone leading "optional" for standardization that he does not seem to share/agree with the current "optional" design to the point where you are keen on forking a "seriously incompatible" variant. Going ahead with the latter will be a lot of effort implementing, promoting, educating, defending... IMO that'll be the effort 1) not necessarily successful; 2) fragmenting the user-base; 3) most importantly, it'll be the effort taken *away* from the current "optional"... which seems so close (hopefully) to standardization. If you have concerns (you would not initiate the conversation if you did not), let's discuss them within the "optional" boundaries and document/justify the decisions made. Do I worry too much?
Ok, this concern deserves a clarification from my part. I am more than satisfied with the current design of both boost::optional and std::optional. i find the decisions right. The direct (unchecked) access to the contained value is essential. The conversion from T may not be critically essential but is very useful. My motivation is NOT the dissatisfaction with the current interface. You are right that by starting this thread I might have been sending the opposite message. Therefore I am declaring my position and motivation here explicitly. It is simply that I observe and acknowledge the expectations of a portion of the community to have a different interface. I also acknowledge that having two interfaces for nearly the same thing may be a bad idea in itself. This is why I am simply discussing (talking) and not taking any actions. I want to hear others' input. And I find yours particularly valuable. Note that the interface I am proposing is not just fool-proof. It is designed in a way that forces you to express your intentions rather that just giving orders. The safety is not achieved by just prohibiting things but by forcing you to rethink what you are doing. To some extent, I consider Boost an incubator of ideas rather than "the standard way of doing things". I would agree that having two optionals in STD would be a bad message. This is less so for Boost. We would have people experiment with an alternative and decide which they find better, and they could deliver their opinion to the Standardization committee. Andrzej,
On 11/18/2014 08:23 PM, Andrzej Krzemienski wrote:
... And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
1. I might be open to the idea of unifying/cleaning the access interface and settling on one... but taking them *all* out?.. Or you want to keep operator->()? Then, I still can do
optional<T> ot; T* t = ot.operator->();
You might say, it's crazy. My point is that if you want to save me (the user) from myself with "safe optional", you are most likely wasting your effort -- an uneducated idiot will always find a way to hurt himself.
Note that my intention is not to remove these from boost::optional, but to provide a second type that does not have them. This is not affecting you: you will stick to boost::optional interface which remains unchanged. Yes, in the new hypothetical I would be also removing operator-> No unchecked access. Yes, I do acknowledge that direct unchecked access is necessary (e.g., for performance reasons). It is only for the people that are determined to use the alternate interface and are willing to pay the cost.
2. I find it troubling that you keep bringing "remove implicit conversion from T " up. That indicates that the property is close to the top of your "grievances" list. On my side, I find that implicit conversion essential for sane interactions of users with "optional". Back a while, when we were discussing "optional" (was it ISO forum?), I remember Fernando Cacciola expressing the same opinion (and as strongly). I'd greatly appreciate if you could please provide practical usage/deployment examples where implicit conversion from T causes a problem.
optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; } double Aircraft::max_weight() { return 10000.0; } bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug } This example only tells me that there exist some use cases where conversion from T is dangerous/unintended/surprising. I DO NOT conclude that it should have never been added. Regards, &rzej
On 19 November 2014 02:37, Andrzej Krzemienski
We would have people experiment with an alternative and decide which they find better, and they could deliver their opinion to the Standardization committee.
This can be taken to mean that you, the proposer of optional for the standard (and hence a domain expert), do not believe the design space has been sufficiently explored and the committee might be better off waiting and not putting std::experimental::optional into C++17. -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
On Wed, Nov 19, 2014 at 10:02 PM, Nevin Liber
On 19 November 2014 02:37, Andrzej Krzemienski
wrote: We would have people experiment with an alternative and decide which they find better, and they could deliver their opinion to the Standardization committee.
This can be taken to mean that you, the proposer of optional for the standard (and hence a domain expert), do not believe the design space has been sufficiently explored and the committee might be better off waiting and not putting std::experimental::optional into C++17.
I think you're taking it too negatively. It's very good that Andrzej is open to new ideas, especially since he's behind std::optional proposal. Exploring new approaches doesn't mean that the current optional is not well designed or not useful.
On 19 November 2014 13:42, Andrey Semashev
I think you're taking it too negatively.
Perhaps. Maybe it's just the scars from trying to get optional into C++14. Whenever expected or variant gets discussed in committee, the question of "Do we still need optional?" gets raised. The answer in favor of optional is yes because it has a much better interface. If the interface is in flux, well, that argument isn't very compelling. While I will continue to vote strongly in favor of adding optional to C++17, I might not participate in the battles to help make that happen, since I am far more interested in forward progress than churn for churn's sake. But that is just me. -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
Le 19/11/14 22:08, Nevin Liber a écrit :
On 19 November 2014 13:42, Andrey Semashev
wrote: I think you're taking it too negatively.
Perhaps. Maybe it's just the scars from trying to get optional into C++14.
Whenever expected or variant gets discussed in committee, the question of "Do we still need optional?" gets raised. The answer in favor of optional is yes because it has a much better interface. If the interface is in flux, well, that argument isn't very compelling.
While I will continue to vote strongly in favor of adding optional to C++17, I might not participate in the battles to help make that happen, since I am far more interested in forward progress than churn for churn's sake. But that is just me.
I don't master English, what do you mean by "churn for churn's sake "? I believe that all of us think that it is better to have the current std::experimental::optional in C++17 than not having it at all. You seam to state that experimenting with other interfaces goes against this primary goal. I just hope you are not right, but who knows ... Vicente
On 19 November 2014 16:32, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Le 19/11/14 22:08, Nevin Liber a écrit :
On 19 November 2014 13:42, Andrey Semashev
wrote:
I think you're taking it too negatively.
Perhaps. Maybe it's just the scars from trying to get optional into C++14.
Whenever expected or variant gets discussed in committee, the question of "Do we still need optional?" gets raised. The answer in favor of optional is yes because it has a much better interface. If the interface is in flux, well, that argument isn't very compelling.
While I will continue to vote strongly in favor of adding optional to C++17, I might not participate in the battles to help make that happen, since I am far more interested in forward progress than churn for churn's sake. But that is just me.
I don't master English, what do you mean by "churn for churn's sake "?
Change for the sake of change, vs. change to make something better. Andrzej has stated that he believes the current interface is better than the new one, so this change isn't for the sake of making a better interface. optional was accepted into Boost over a decade ago, so this change isn't to gain consensus amongst Boost reviewers. Could a second version of optional could pass an independent review, as opposed to the library maintainer just adding it? (Note: I am not disputing that he can just add it if he wants; that is his right.) std::experimental::optional was accepted into Library Fundamentals; there was consensus amongst committee members for the current interface.
I believe that all of us think that it is better to have the current std::experimental::optional in C++17 than not having it at all.
But that is not a done deal. I would really like to be able to spell it as std::optional. Did the committee recommend that the author provide alternatives to the interface in std::experimental::optional? AFAIK, no (but I was not present in the room for all the various discussions). Does having a second optional type in Boost by the person who proposed optional to the committee help or hinder the chances of consensus being reached for moving std::experimental::optional into C++17? He has already stated in this thread that he is doing so partially for others to present an alternative to the committee, if they should so choose. I see three scenarios: (1) Having two different interfaces reduces consensus, possibly to the point of having various different optionals remain in std::experimental indefinitely. (2) The new interface is ignored. (3) The new interface is so horrible that it increases consensus putting optional in C++17. IMO, (1) is the far more likely scenario.
You seam to state that experimenting with other interfaces goes against this primary goal. I just hope you are not right, but who knows ...
The committee is unpredictable; much of it depends on who is in the room when it gets discussed. Just my opinion, I don't speak for the committee, etc., etc. Nevin :-) -- Nevin ":-)" Liber mailto:nevin@eviloverlord.com (847) 691-1404
On Wed, Nov 19, 2014 at 6:40 PM, Nevin Liber
I see three scenarios:
(1) Having two different interfaces reduces consensus, possibly to the point of having various different optionals remain in std::experimental indefinitely.
(2) The new interface is ignored.
(3) The new interface is so horrible that it increases consensus putting optional in C++17.
IMO, (1) is the far more likely scenario.
You missed (4) The new interface is so awesome it is accepted immediately and replaces the other interface. or (5) the new and old interfaces are combined into awesomeness. I see (4) as unlikely, and agree that (1) is more likely. (5) would mean more churn, and running out of time. So the decision to weigh is whether the new interface is likely to result in awesomeness, and is it worth the risk. Tony
2014-11-19 22:08 GMT+01:00 Nevin Liber
On 19 November 2014 13:42, Andrey Semashev
wrote: I think you're taking it too negatively.
Perhaps. Maybe it's just the scars from trying to get optional into C++14.
Whenever expected or variant gets discussed in committee, the question of "Do we still need optional?" gets raised. The answer in favor of optional is yes because it has a much better interface. If the interface is in flux, well, that argument isn't very compelling.
While I will continue to vote strongly in favor of adding optional to C++17, I might not participate in the battles to help make that happen, since I am far more interested in forward progress than churn for churn's sake. But that is just me.
Hi Nevin. I really appreciate what you are saying. You made me realize one thing I was not aware before. That an ordinary programmer expressing his ideas and opinions on a forum, can affect what gets and what does not get into the standard. I am not being sarcastic. Proposing Optional was a pain even for me, even though I am not exposed to the process. I appreciate your efforts in helping Optional get into the standard. I can also see why this thread can be seen as undermining the effort. I am pretty sure Optional will make it into the standard. Its advantage over Variant or Expected is that it is already there in a TS and that it proved useful for more than a decade. A working solution is better than a projected future solution. Back in around 2009, I was wondering why they are adding type traits when at the same time all the primitive concepts like CopyConstructible were being added. It looked like an unnecessary duplication. But their advantage was that they were real, unlike concepts. Best regards, &rzej
On Wed, Nov 19, 2014 at 2:42 PM, Andrey Semashev
On Wed, Nov 19, 2014 at 10:02 PM, Nevin Liber
wrote: On 19 November 2014 02:37, Andrzej Krzemienski
wrote: We would have people experiment with an alternative and decide which they find better, and they could deliver their opinion to the Standardization committee.
This can be taken to mean that you, the proposer of optional for the standard (and hence a domain expert), do not believe the design space has been sufficiently explored and the committee might be better off waiting and not putting std::experimental::optional into C++17.
I think you're taking it too negatively. It's very good that Andrzej is open to new ideas, especially since he's behind std::optional proposal. Exploring new approaches doesn't mean that the current optional is not well designed or not useful.
The thing with optional is that it is the first in a series of "wrapper" types: optional, expected, variant, any, clone_ptr, ... It would be wonderful to get it as "right" as the container + algorithm approach of STL. If it was just optional, I wouldn't care so much. But it is a much bigger class of, uh, classes. So, yes, someone, particularly a proposer, saying "maybe this is a better way", is a good thing, but does exactly say the design space has not been sufficiently explored. Stepanov had years, and a number of tries (with a number of languages) to get STL right. Tony
On 11/19/2014 07:37 PM, Andrzej Krzemienski wrote:
... Ok, this concern deserves a clarification from my part. I am more than satisfied with the current design of both boost::optional and std::optional. i find the decisions right. The direct (unchecked) access to the contained value is essential. The conversion from T may not be critically essential but is very useful. My motivation is NOT the dissatisfaction with the current interface.
You are right that by starting this thread I might have been sending the opposite message. Therefore I am declaring my position and motivation here explicitly.
It is simply that I observe and acknowledge the expectations of a portion of the community to have a different interface.
People will have different expectations no matter what. You provide 2 interfaces then there'll be a group wanting third. If satisfying that "special community" is *truly* the driving force behind your alternative initiative, then it's ill-conceived IMO as it sends the wrong message to the "main community", sends the wrong message to the standardization committee, fragments the user-base... you see the picture. Instead, I'd see documentation of the design and deployment patterns, education to be the right way to go... unless you are in that "other community"... that's when alarm bells start ringing for me with regard to the current "optional".
I also acknowledge that having two interfaces for nearly the same thing may be a bad idea in itself. This is why I am simply discussing (talking) and not taking any actions. I want to hear others' input. And I find yours particularly valuable.
Note that the interface I am proposing is not just fool-proof. It is designed in a way that forces you to express your intentions rather that just giving orders. The safety is not achieved by just prohibiting things but by forcing you to rethink what you are doing.
To some extent, I consider Boost an incubator of ideas rather than "the standard way of doing things". I have to disagree. IMO Boost has outgrown that phase long ago and as it is *now* to the programming community at large Boost is not an incubator. It's THE "almost standard" extension of the Std. library. Consequently, one needs to think twice offering something as confusing as an incompatible alternative to something as fundamental and entrenched and ready for standardization as "optional". If it was something spanking new and unknown, it might be very different.
I would agree that having two optionals in STD would be a bad message. This is less so for Boost. We would have people experiment with an alternative and decide which they find better, and they could deliver their opinion to the Standardization committee.
I suspect the committee does not need/want those 2 opinions. They need a solid library with established user-base... nothing less... I can't speak for them though and can be easily wrong.
... optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug }
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
This example only tells me that there exist some use cases where conversion from T is dangerous/unintended/surprising. I DO NOT conclude that it should have never been added.
This only tells me :-) that you seem to suffer from a common s/w programmer syndrome -- you are thinking as a machine rather than a human. Pls do not get me wrong. It's not an insult. I catch myself doing the same and have to force myself to step back and think as a human :-) and code for human rather than for a machine. I hope you see what I mean.
On 11/20/2014 08:22 AM, Vladimir Batov wrote:
On 11/19/2014 07:37 PM, Andrzej Krzemienski wrote:
... optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug }
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then? Say, bool operator<(T const&, optional<T> const&) { BOOST_STATIC_ASSERT(); }
Le 19/11/14 22:36, Vladimir Batov a écrit :
On 11/20/2014 08:22 AM, Vladimir Batov wrote:
On 11/19/2014 07:37 PM, Andrzej Krzemienski wrote:
... optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug }
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then? Say,
bool operator<(T const&, optional<T> const&) { BOOST_STATIC_ASSERT(); }
I don't think this is a solution. User defining its own function void f(optional<T>, optional<T>); would need to add the following? void f(T, optional<T>) { BOOST_STATIC_ASSERT(); } void f(optional<T>, T) { BOOST_STATIC_ASSERT(); } What if there are 3 optional parameters? We can not say to the user that they need to program this way. Vicente
On 11/20/2014 09:12 AM, Vicente J. Botet Escriba wrote:
Le 19/11/14 22:36, Vladimir Batov a écrit :
... On the second thought I might probably agree that op<() might be questionable... Can we address that differently then? Say,
bool operator<(T const&, optional<T> const&) { BOOST_STATIC_ASSERT(); }
I don't think this is a solution. User defining its own function
void f(optional<T>, optional<T>);
would need to add the following?
void f(T, optional<T>) { BOOST_STATIC_ASSERT(); } void f(optional<T>, T) { BOOST_STATIC_ASSERT(); }
What if there are 3 optional parameters? We can not say to the user that they need to program this way.
That's immensely different... and it should not be prohibited... T to optional<T> is natural propagation/conversion... as 'int' to 'double'. That should happen automatically and seamlessly... as 'int' to 'double'. In the other direction -- optional<T> to T -- like double to int, needs to be explicit... and it is.
2014-11-19 22:36 GMT+01:00 Vladimir Batov
On 11/20/2014 08:22 AM, Vladimir Batov wrote:
On 11/19/2014 07:37 PM, Andrzej Krzemienski wrote:
...
optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug }
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then?
No. Allowing this comparison to work is the right thing to do. It is a
natural consequence of Optional's conceptual model. You should look at
optional<T> as a T plus one additional value, less than any other value.
No-one stops you from adopting any other model (like container of size
0-or-1), but then you are risking that you will be surprised by the result.
Optional is not a container of size 0-or-1. You do not expect an element to
be implicitly converted to its container type.
The source of the confusion in this example above is the wrong expectation
that the compiler will warn you about any place where optional<T> is
confused with T. They are supposed and expected to be confused and mixed.
That's the idea behind implicit conversions.
Yet, many people make this invalid expectation, because what they are
really looking for is something different: something that will detect as
many potential programmer errors as possible (including false positives).
Note that the proposed expected
On Thu, Nov 20, 2014 at 10:29 AM, Andrzej Krzemienski
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then?
No. Allowing this comparison to work is the right thing to do. It is a natural consequence of Optional's conceptual model. You should look at optional<T> as a T plus one additional value, less than any other value. No-one stops you from adopting any other model (like container of size 0-or-1), but then you are risking that you will be surprised by the result.
Optional is not a container of size 0-or-1. You do not expect an element to be implicitly converted to its container type.
The source of the confusion in this example above is the wrong expectation that the compiler will warn you about any place where optional<T> is confused with T. They are supposed and expected to be confused and mixed. That's the idea behind implicit conversions.
Yet, many people make this invalid expectation, because what they are really looking for is something different: something that will detect as many potential programmer errors as possible (including false positives).
Note that the proposed expected
has the same "identity crisis":
It'll probably face the same resistance. Comparing no value and a value simply doesn't make sense and code like shown is a real bug. What's wrong with being explicit with what you want, especially in ambiguous situations like those mixed comparisons? -- Olaf
2014-11-20 10:49 GMT+01:00 Olaf van der Spek
On Thu, Nov 20, 2014 at 10:29 AM, Andrzej Krzemienski
wrote: On the second thought I might probably agree that op<() might be questionable... Can we address that differently then?
No. Allowing this comparison to work is the right thing to do. It is a natural consequence of Optional's conceptual model. You should look at optional<T> as a T plus one additional value, less than any other value. No-one stops you from adopting any other model (like container of size 0-or-1), but then you are risking that you will be surprised by the result.
Optional is not a container of size 0-or-1. You do not expect an element to be implicitly converted to its container type.
The source of the confusion in this example above is the wrong expectation that the compiler will warn you about any place where optional<T> is confused with T. They are supposed and expected to be confused and mixed. That's the idea behind implicit conversions.
Yet, many people make this invalid expectation, because what they are really looking for is something different: something that will detect as many potential programmer errors as possible (including false positives).
Note that the proposed expected
has the same "identity crisis": It'll probably face the same resistance. Comparing no value and a value simply doesn't make sense and code like shown is a real bug.
I disagree with this statement. In case of optional<T> having no value of T
is deliberately treated as a yet another value. This has a well defined
purpose. It is covered in the documentation here:
http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
the docs also recommends NOT to use optional<T> to signal a potential
error/difficulty in computing T:
http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/t...
.
This is the choice made for optional<T>. The authors of expected
What's wrong with being explicit with what you want, especially in ambiguous situations like those mixed comparisons?
Nothing wrong with that. It is just not the job for optional. Perhaps a job
for expected
On Thu, Nov 20, 2014 at 11:00 AM, Andrzej Krzemienski
Note that the proposed expected
has the same "identity crisis": It'll probably face the same resistance. Comparing no value and a value simply doesn't make sense and code like shown is a real bug.
I disagree with this statement. In case of optional<T> having no value of T is deliberately treated as a yet another value. This has a well defined purpose. It is covered in the documentation here: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
By containers you specifically mean map, right? Unordered map for example would not require operator< Containers (maps) always come up as the primary rationale for operator< However, the user could also define his own comparison function for this purpose. Perhaps other use cases of operator< could be presented.
the docs also recommends NOT to use optional<T> to signal a potential error/difficulty in computing T: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/t... .
This is the choice made for optional<T>. The authors of expected
may want to choose a different model. What's wrong with being explicit with what you want, especially in ambiguous situations like those mixed comparisons?
Nothing wrong with that. It is just not the job for optional. Perhaps a job for expected
.
But is it what people have problems with isn't it? -- Olaf
2014-11-20 11:20 GMT+01:00 Olaf van der Spek
On Thu, Nov 20, 2014 at 11:00 AM, Andrzej Krzemienski
wrote: Note that the proposed expected
has the same "identity crisis": It'll probably face the same resistance. Comparing no value and a value simply doesn't make sense and code like shown is a real bug.
I disagree with this statement. In case of optional<T> having no value of T is deliberately treated as a yet another value. This has a well defined purpose. It is covered in the documentation here:
http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
By containers you specifically mean map, right? Unordered map for example would not require operator<
Containers (maps) always come up as the primary rationale for operator< However, the user could also define his own comparison function for this purpose.
Perhaps other use cases of operator< could be presented.
I was trying to illustrate how it is useful to think of optional<T> as extending the domain of T (rather than representing "either T or error"). Not to illustrate the storage in containers. The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions: optional<int>() != 2; The above is logical and useful, even outside of the containers.
the docs also recommends NOT to use optional<T> to signal a potential error/difficulty in computing T:
http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/t...
.
This is the choice made for optional<T>. The authors of expected
may want to choose a different model. What's wrong with being explicit with what you want, especially in ambiguous situations like those mixed comparisons?
Nothing wrong with that. It is just not the job for optional. Perhaps a job for expected
. But is it what people have problems with isn't it?
Some people are surprised by the implicit conversion, because they expect it would not be there. the have this expectation because they do not understand the conceptual model behind optional<T>. True, they have a problem. I claim, the root of the problem is not understanding the tool they have.
On Thursday, November 20, 2014 11:49:42 AM UTC+1, Andrzej Krzemienski wrote:
Perhaps other use cases of operator< could be presented.
I was trying to illustrate how it is useful to think of optional<T> as extending the domain of T (rather than representing "either T or error"). Not to illustrate the storage in containers.
The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
my 2 cents (as a user of boost::optional): Is optional<int>() < 2 true or false? And 2 < optional<int>()? To me the problem is how the extended domain is sorted, i.e. how optional<T>() is sorted vs all the possible values of T. If the ordering is not clear, then no comparison operators should be provided. That said, equality (and inequality) are meaningful and should be provided. Regards
On Thursday, November 20, 2014 4:04:25 PM UTC+1, dar...@gmail.com wrote:
On Thursday, November 20, 2014 11:49:42 AM UTC+1, Andrzej Krzemienski wrote:
Perhaps other use cases of operator< could be presented.
I was trying to illustrate how it is useful to think of optional<T> as extending the domain of T (rather than representing "either T or
error").
Not to illustrate the storage in containers.
The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
my 2 cents (as a user of boost::optional):
Is optional<int>() < 2 true or false? And 2 < optional<int>()?
To me the problem is how the extended domain is sorted, i.e. how optional<T>() is sorted vs all the possible values of T. If the ordering is not clear, then no comparison operators should be provided.
That said, equality (and inequality) are meaningful and should be provided.
Regards
Andrzej has kindly pointed me to the most up-to-date docs, where this is clarified in two places: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q... http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/t... I think a link from the reference to the clarification would be very useful. Now I agree it is clear how optional<T>() is sorted wrt to T and it is up to the user to know the tool. FWIW, now I don't see the need for safe_optional.
On 11/20/2014 11:49 AM, Andrzej Krzemienski wrote:
The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
As optional is a nullable type, it could be useful to let the comparison operators return a tri-bool instead of a bool.
On Nov 20, 2014 at 16:13, Bjorn Reese wrote:
On 11/20/2014 11:49 AM, Andrzej Krzemienski wrote:
The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
As optional is a nullable type, it could be useful to let the comparison operators return a tri-bool instead of a bool.
There are a number of well-trodden paths for these kind of comparisons... optional(none) could be 'bottom' as it is now, or follow the example of IEEE floating point NaN, or follow three value logic like NULL in SQL. I think those are all differently sensible, and there are probably more... Best regards, Gareth ************************************************************************ The information contained in this message or any of its attachments may be confidential and is intended for the exclusive use of the addressee(s). Any disclosure, reproduction, distribution or other dissemination or use of this communication is strictly prohibited without the express permission of the sender. The views expressed in this email are those of the individual and not necessarily those of Sony or Sony affiliated companies. Sony email is for business use only. This email and any response may be monitored by Sony to be in compliance with Sony's global policies and standards
On Thu, Nov 20, 2014 at 8:13 AM, Bjorn Reese
On 11/20/2014 11:49 AM, Andrzej Krzemienski wrote:
The user-provided int can be any value of int plus the situation where
user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
As optional is a nullable type, it could be useful to let the comparison operators return a tri-bool instead of a bool.
I don't think I agree. none doesn't mean "indeterminate." If two things are none then they are equal, much like how two null pointers are equal. If we have a desire for a generalization of tribool, I.E. "indeterminable<T>" or something along those lines, then it should really be its own type. In practice, though, I find such types really difficult to work with as they are irregular and I'm not convinced such types are worth existing (admittedly I think I might be in the minority here). Having comparison operators return something other than bool, especially something like tribool, or if those operators simply behave as tribool comparisons do, makes it difficult to use such types in generic code and can cause things to break in weird ways without a compile error. This is because == in such a case is not actually an equivalence relation and the relational operators are not a partial order, which goes against assumptions in [some] generic code, not to mention against most people's intuitions when they see such operators in use. Perhaps it's an assumption that people shouldn't make, but at the same time, I see no tangible benefit to having the operators behave that way over just having a separate named function, if that. I've never really been convinced that floating point (with respect to NaN) and tribool behavior regarding comparisons is a good idea, but again, I think I'm in the minority there. As a side note, the old concepts proposal even made this assumption. When operator == is defined for a type and it returns something convertible to bool, it is assumed to be a model of an equivalence relation (rather than requiring someone to explicitly state that they model the concept). This is also the general assumption that most people make as well, so I don't think we should break that assumption. -- -Matt Calabrese
On Thu, Nov 20, 2014 at 11:49 AM, Andrzej Krzemienski
http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
By containers you specifically mean map, right? Unordered map for example would not require operator<
Containers (maps) always come up as the primary rationale for operator< However, the user could also define his own comparison function for this purpose.
Perhaps other use cases of operator< could be presented.
I was trying to illustrate how it is useful to think of optional<T> as extending the domain of T (rather than representing "either T or error"). Not to illustrate the storage in containers.
Optional really is either T or none (no value) isn't it? What do other languages do? Do they have something like optional? How are comparisons defined?
The user-provided int can be any value of int plus the situation where user decides not to type any input. the latter is a valid and useful information. E.g. when an int represents some threshold, having boost::none means "go with no threshold". boost::none is just a value. this in turn implies the implicit conversions:
optional<int>() != 2;
The above is logical and useful, even outside of the containers.
Right, but if there's no threshold, would 2 be above or below this non-existant threshold? And if none means unknown or not yet available, what does the answer become?
But is it what people have problems with isn't it?
Some people are surprised by the implicit conversion, because they expect it would not be there. the have this expectation because they do not understand the conceptual model behind optional<T>.
True, they have a problem. I claim, the root of the problem is not understanding the tool they have.
Should the tool match common user expectations (including novice users) or do you expect users to bend their models to the tools? ;) -- Olaf
Le 20/11/14 23:42, Olaf van der Spek a écrit :
On Thu, Nov 20, 2014 at 11:49 AM, Andrzej Krzemienski
wrote: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
By containers you specifically mean map, right? Unordered map for example would not require operator<
Containers (maps) always come up as the primary rationale for operator< However, the user could also define his own comparison function for this purpose.
Perhaps other use cases of operator< could be presented.
I was trying to illustrate how it is useful to think of optional<T> as extending the domain of T (rather than representing "either T or error"). Not to illustrate the storage in containers. Optional really is either T or none (no value) isn't it?
What do other languages do? Do they have something like optional? How are comparisons defined? In Haskell data Maybe = Just T | Nothing
You can not convert a maybe implicitly from a T (the concept doesn't
exist in the language as AFAIK). You need to use the constructor Just
x = Just 1
y = Nothing
This is equivalent to our
auto x = make_optional(1);
auto y = none; // nullopt
You can extract the value of a Maybe by pattern matching. IIUC, that
means that you need to consider all the cases, as if we have a switch on
an enum and the compiler forced us to have the case for all the enum
literals.
zeroAsDefault:: Maybe Int -> Int
zeroAsDefault mx= case mxof
Nothing-> 0
Just x-> x
We don't have yet pattern matching in C++. This pattern matching is not
equivalent to the optional::value function.
We could have a match function that do this in C++14 (maybe C++11)
// zeroAsDefault:: Maybe Int -> Int
auto zeroAsDefault(optional<int> mx){
return match(mx,
[] (pattern
But is it what people have problems with isn't it?
Some people are surprised by the implicit conversion, because they expect it would not be there. the have this expectation because they do not understand the conceptual model behind optional<T>.
True, they have a problem. I claim, the root of the problem is not understanding the tool they have.
Should the tool match common user expectations (including novice users) or do you expect users to bend their models to the tools? ;)
Maybe the user needs a different tool. Boost or the standard can provide it if there is a real need. Of course having too much tools makes things more complex, but tools that are less suitable to the users are used less or just not used. Boost is a laboratory where tools can be experimented. std::experimental also, but the cost to get accepted a proposal is very high. As Nevin has said, if for the time to move something from std::experimental to std we have two tools that do almost the same thing, but with different interface, and we have no probes that one is better than the other, there is a high risk that none goes to std:: Should the discussion wait until we have a std::optional in C++17 and then propose a std::experimental::maybe (or whatever is called)? I don't think we can wait. Some people is demanding now for these alternative interfaces. If they are not in Boost or in the standard, they will build its own libraries with minimal different flavors (there already a lot of them). Reassuming, I think that providing these alternative (less imperative safe types) as safe_optional or maybe must be done together with a good functional library including Functor, Applicative, Alternative, Monad. This will take some time but I really think it is worth working on it, at least in Boost. C++14 is giving a lot of different design opportunities to those available in C++98. People is experimenting just now. I'm not sure we can have an acceptable solution with C++98. C++14 allows a good experimentation, but I think that the complete solution will be really there when C++ include sum types and pattern matching. Tis could not be done for C++17 as there is not a proposal yet. Best, Vicente
On 21/11/2014 11:42, Olaf van der Spek wrote:
What do other languages do? Do they have something like optional? How are comparisons defined?
In .NET, Nullable<T>: - has implicit T-to-Nullable<T> conversion & construction - has explicit Nullable<T>-to-T conversion (which throws an exception if it has no value) - can either access "HasValue" and "Value" properties directly or use a "value_or" style method. - supports equality testing (two nulls are equal); due to the way that default language conversions work this applies to both T and Nullable<T>. - supports "getting a suitable hash value" as an operation, similar to std::hash. - does NOT support comparisons other than equality directly, although there is an explicit comparison method Nullable.Compare<T> that can be called to do so; it operates only on two Nullable<T>s but implicit conversion will allow a T to be passed in unadorned.
On Thu, Nov 20, 2014 at 5:20 AM, Olaf van der Spek
On Thu, Nov 20, 2014 at 11:00 AM, Andrzej Krzemienski
wrote: Note that the proposed expected
has the same "identity crisis": It'll probably face the same resistance. Comparing no value and a value simply doesn't make sense and code like shown is a real bug.
I disagree with this statement. In case of optional<T> having no value of T is deliberately treated as a yet another value. This has a well defined purpose. It is covered in the documentation here: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/q...
By containers you specifically mean map, right? Unordered map for example would not require operator<
Containers (maps) always come up as the primary rationale for operator< However, the user could also define his own comparison function for this purpose.
std::experimental::optional specializes std::less for exactly this purpose. It could retain this specialization while at the same time not support operator<. (Or, to save std::less, introduce std::order, but that is another conversation) Tony
On Thu, Nov 20, 2014 at 5:00 AM, Andrzej Krzemienski
the docs also recommends NOT to use optional<T> to signal a potential error/difficulty in computing T: http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/t... .
This is the choice made for optional<T>. The authors of expected
may want to choose a different model.
expected
On 11/20/2014 08:29 PM, Andrzej Krzemienski wrote:
2014-11-19 22:36 GMT+01:00 Vladimir Batov
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns. On the second thought I might probably agree that op<() might be questionable... Can we address that differently then? No. Allowing this comparison to work is the right thing to do. It is a natural consequence of Optional's conceptual model. You should look at optional<T> as a T plus one additional value, less than any other value. No-one stops you from adopting any other model (like container of size 0-or-1), but then you are risking that you will be surprised by the result.
Optional is not a container of size 0-or-1. You do not expect an element to be implicitly converted to its container type.
1. I am not (and never have been) advocating that "optional" is a container. I personally find it wrong... maybe interesting for curiosity sake but ultimately misleading and distracting. 2. I do understand and do agree with the decision made regarding "optional" sorting -- no-value is less than "any" value. Seems sensible and easily "documentable" and makes "optional" easily usable in ass. containers without hassle. 3. What I have doubts about (and I stress -- doubts -- as indicated by my previous conflicting posts) is op<(T, optional<T>). Yes, *mechanically*, one can say -- T is implicitly propagated to optional<T> and then op<(optional<T>, optional<T>) is applied. Easy-peasy. I wish life was that straightforward. *It feels to me* that, when T is compared to optional<T>, it is quite likely a bug -- it's far too open for misuse and (mis)interpretation. Based on that feeling I tend to suggest banning op<(T, optional<T>). With that we "kill two birds" -- (a) we address the safety concern you raised and (b) we achieve that within the "optional" framework without losing any existing functionality.
The source of the confusion in this example above is the wrong expectation that the compiler will warn you about any place where optional<T> is confused with T. They are supposed and expected to be confused and mixed. That's the idea behind implicit conversions.
Here with all due respect I have to cautiously disagree... probably due to too broad a brush you are using to paint the picture. I have no issue with T to optional<T> implicit conversion. I think it's essential. What I am cautious about is when and how liberally that conversion is applied. "Always" just does not sit well with me. The idea that you are seemingly advocating seems unduly mechanical -- when we need to do anything with a T and optional<T> pair, then propagate T to optional<T> and then apply the "optional natural model". The problem (as I see it) is that, when we are in the T land, we apply T rules, when we are in the optional<T> land, we apply optional<T> rules. However, when we have T *and* optional<T>, we are right on the border. You say, "apply optional<T> rules". I say "I do not know" and, therefore, as a library writer I want to leave that decision to the user -- i.e. force him to be explicit, i.e. "t < *ot" or "optional<T>(t) < ot". Does it make sense?
Yet, many people make this invalid expectation,
Hmm, maybe those expectations are not as invalid as you are trying to present them. I am hoping the paragraph above clarifies my feeling about it.
because what they are really looking for is something different: something that will detect as many potential programmer errors as possible (including false positives). ...
2014-11-20 21:57 GMT+01:00 Vladimir Batov
On 11/20/2014 08:29 PM, Andrzej Krzemienski wrote:
2014-11-19 22:36 GMT+01:00 Vladimir Batov
There is no bug here! Not from the human perspectives. You are comparing
two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then?
No. Allowing this comparison to work is the right thing to do. It is a natural consequence of Optional's conceptual model. You should look at optional<T> as a T plus one additional value, less than any other value. No-one stops you from adopting any other model (like container of size 0-or-1), but then you are risking that you will be surprised by the result.
Optional is not a container of size 0-or-1. You do not expect an element to be implicitly converted to its container type.
1. I am not (and never have been) advocating that "optional" is a container. I personally find it wrong... maybe interesting for curiosity sake but ultimately misleading and distracting.
2. I do understand and do agree with the decision made regarding "optional" sorting -- no-value is less than "any" value. Seems sensible and easily "documentable" and makes "optional" easily usable in ass. containers without hassle.
3. What I have doubts about (and I stress -- doubts -- as indicated by my previous conflicting posts) is op<(T, optional<T>). Yes, *mechanically*, one can say -- T is implicitly propagated to optional<T> and then op<(optional<T>, optional<T>) is applied. Easy-peasy. I wish life was that straightforward.
*It feels to me* that, when T is compared to optional<T>, it is quite likely a bug -- it's far too open for misuse and (mis)interpretation. Based on that feeling I tend to suggest banning op<(T, optional<T>). With that we "kill two birds" -- (a) we address the safety concern you raised and (b) we achieve that within the "optional" framework without losing any existing functionality.
The source of the confusion in this example above is the wrong expectation
that the compiler will warn you about any place where optional<T> is confused with T. They are supposed and expected to be confused and mixed. That's the idea behind implicit conversions.
Here with all due respect I have to cautiously disagree... probably due to too broad a brush you are using to paint the picture. I have no issue with T to optional<T> implicit conversion. I think it's essential. What I am cautious about is when and how liberally that conversion is applied. "Always" just does not sit well with me. The idea that you are seemingly advocating seems unduly mechanical -- when we need to do anything with a T and optional<T> pair, then propagate T to optional<T> and then apply the "optional natural model". The problem (as I see it) is that, when we are in the T land, we apply T rules, when we are in the optional<T> land, we apply optional<T> rules. However, when we have T *and* optional<T>, we are right on the border. You say, "apply optional<T> rules". I say "I do not know" and, therefore, as a library writer I want to leave that decision to the user -- i.e. force him to be explicit, i.e. "t < *ot" or "optional<T>(t) < ot". Does it make sense?
It does. You are also correct to observe that we have started talking more about feelings than anything that can be objectively asserted. I am forced to admit I might be tempted to apply a too simplistic view. But that is still too little for me to get convinced that we should allow the converting constructor and poison the mixed operator less at the same time. I agree that there is no solution here that would be considered satisfactory by everyone, so we are forced to select the least of two evils, and it seams that there is no objective way of even measuring the two evils. The usage of converting constructor in optional only shows to me that we have not established a good criteria for telling when having a converting constructor is a good idea. I always used to think that if only you are not loosing any information, it is safe. But in the case of optional I can see that even then it can have some negative consequences. Perhaps it is the way the language is designed that is wrong: that you either allow this conversion in every possible situation or never at all.
Yet, many people make this invalid expectation,
Hmm, maybe those expectations are not as invalid as you are trying to present them. I am hoping the paragraph above clarifies my feeling about it.
Ok, that was too blunt. For one, I admit I fell into this trap myself at least once. But then I am trying to remain sane. In the end, I acknowledge the concern about potential pitfalls. Still, I find a value in that I am able to tell what optional IS and then reason from that what operations it will have and with what semantics. With poisoned mixed comparison optional would be a set of safety patches that do not amount to a coherent meaning. It would be safer, but it would be a headache, because you would never know what it is.
On 11/21/2014 09:13 AM, Andrzej Krzemienski wrote:
2014-11-20 21:57 GMT+01:00 Vladimir Batov
... Does it make sense? It does. You are also correct to observe that we have started talking more about feelings than anything that can be objectively asserted.
:-) Actually it was *me* talking about "feelings"... and it was only a figure of speech to highlight the fact that I am open for discussion rather that heading for confrontation. The behavior I am advocating (or have feelings for ;-) is fairly straightforward IMO (of course). I can sum them up as follows. 1) When we are bringing T into the optional<T> land, we apply implicit constructor. For example, we provide T to a function taking optional<T>. Here, the direction of the conversion is unambiguous. Namely, T to optional<T>. So, we apply it. 2) When the direction of conversion is not as clear, we refuse applying it and leave it to the user. op<(T, optional<T>) would be one such example.
I am forced to admit I might be tempted to apply a too simplistic view. But that is still too little for me to get convinced that we should allow the converting constructor and poison the mixed operator less at the same time.
Well, trying to advocate an idea is always an up-hill battle. Unfortunately, it's all I have. If it does not convince you, so be it. I just thought that solution would fix many potential mis-uses of "optional" without any sacrifice in functionality of forking off.
I agree that there is no solution here that would be considered satisfactory by everyone, so we are forced to select the least of two evils, and it seams that there is no objective way of even measuring the two evils.
I am not sure I agree. What I find attractive in "my" (for simplicity sake) suggestion is that it provides explicitness (i.e. safety) when we need it *without* sacrificing any existing functionality or interface. I.e. win-win situation. So to speak, wolves are fed and the sheep are safe. :-)
The usage of converting constructor in optional only shows to me that we have not established a good criteria for telling when having a converting constructor is a good idea. I always used to think that if only you are not loosing any information, it is safe. But in the case of optional I can see that even then it can have some negative consequences. Perhaps it is the way the language is designed that is wrong: that you either allow this conversion in every possible situation or never at all.
Oh, c'mon. Cheer up. Things are not as gloomy, are they. :-) Here the subtlety (IMO) lies in how reasonable it is to add information. Namely, as I indicated, if we bring T into the optional<T> fold, then we apply optional<T> rules to T, i.e. apply t to optional<T> conversion. Say, we have a mandarin and an orange. When we bring the mandarin to an orange factory, then mandarin-to-orange "conversion" is applied and for all purposes mandarin is a "small orange". Outside the factory, when we compare mandarin to orange, it is not immediately clear what should be treated as what. If we apply mandarin-to-orange "conversion", then it'll be "small orange vs. big orange". If instead we apply orange-to-mandarin "conversion", then it'll be "sweet mandarin vs. sour mandarin". Given the library writer does not know, which it is, we ban it. Still not convinced? I've spent all munition I had. :-)
The usage of converting constructor in optional only shows to me that we
have not established a good criteria for telling when having a converting constructor is a good idea. I always used to think that if only you are not loosing any information, it is safe. But in the case of optional I can see that even then it can have some negative consequences. Perhaps it is the way the language is designed that is wrong: that you either allow this conversion in every possible situation or never at all.
Oh, c'mon. Cheer up. Things are not as gloomy, are they. :-) Here the subtlety (IMO) lies in how reasonable it is to add information. Namely, as I indicated, if we bring T into the optional<T> fold, then we apply optional<T> rules to T, i.e. apply t to optional<T> conversion. Say, we have a mandarin and an orange. When we bring the mandarin to an orange factory, then mandarin-to-orange "conversion" is applied and for all purposes mandarin is a "small orange". Outside the factory, when we compare mandarin to orange, it is not immediately clear what should be treated as what. If we apply mandarin-to-orange "conversion", then it'll be "small orange vs. big orange". If instead we apply orange-to-mandarin "conversion", then it'll be "sweet mandarin vs. sour mandarin". Given the library writer does not know, which it is, we ban it. Still not convinced? I've spent all munition I had. :-)
I responded to this in another thread. We diverged too much from the original issue.
On 21/11/2014 09:57, Vladimir Batov wrote:
3. What I have doubts about (and I stress -- doubts -- as indicated by my previous conflicting posts) is op<(T, optional<T>). Yes, *mechanically*, one can say -- T is implicitly propagated to optional<T> and then op<(optional<T>, optional<T>) is applied. Easy-peasy. I wish life was that straightforward.
*It feels to me* that, when T is compared to optional<T>, it is quite likely a bug -- it's far too open for misuse and (mis)interpretation. Based on that feeling I tend to suggest banning op<(T, optional<T>). With that we "kill two birds" -- (a) we address the safety concern you raised and (b) we achieve that within the "optional" framework without losing any existing functionality.
+1. Implicit T -> optional<T> conversion is good and essential. operator==(optional<T>, optional<T>) and operator<(optional<T>, optional<T>) are both good and essential. operator==(T, optional<T>) and operator==(optional<T>, T) are also good and convenient (but not essential). operator<(T, optional<T>) and operator<(optional<T>, T) are more dubious; it is probably better to require explicit conversion here, even though *technically* they should be implicit given the above. While granted the current definitions of this fit the model that "none" is just an extra value that is less than any other value, I feel like this case has more potential for error, especially for those people using optional<T> to represent an undefined value (though as you said, perhaps there should be a separate type for that concept -- though I've never liked any type that tries to claim none != none). Having said that, apart from a few initial surprises with optional<bool> that I quickly learned the quirks of (and it was less surprising than tribool, at that), I've never had any personal problems with the current interface of optional<T>.
On Thu, Nov 20, 2014 at 4:29 AM, Andrzej Krzemienski
2014-11-19 22:36 GMT+01:00 Vladimir Batov
: On 11/20/2014 08:22 AM, Vladimir Batov wrote:
On 11/19/2014 07:37 PM, Andrzej Krzemienski wrote:
...
optional<double> Aircraft::weight() { if (Impl* impl = get_impl()) return impl->compute_weight(); else return none; }
double Aircraft::max_weight() { return 10000.0; }
bool too_heavy(Aircraft& ac) { return ac.weight() > ac.max_weight(); // in "safe" optional compiler would spot the bug }
There is no bug here! Not from the human perspectives. You are comparing two extremely closely related types! As long as we agree on how "none" is treated, then all kosher. We do compare "ints" with "doubles", don't we? No "safety" concerns.
On the second thought I might probably agree that op<() might be questionable... Can we address that differently then?
No. Allowing this comparison to work is the right thing to do. It is a natural consequence of Optional's conceptual model. You should look at optional<T> as a T plus one additional value, less than any other value. No-one stops you from adopting any other model (like container of size 0-or-1), but then you are risking that you will be surprised by the result.
The "correct" model is actually initialized-or-not. For non-Regular types (&T or others), anything else is surprising. In particular, the nature of optional assignment is construct-or-assign. For Regular types, there is no real difference, for custom types there might be. Its behaviour of assignment is, to me, the fundamental essence of optional. Seeing it as may-be-uninitialized would suggest that less-than-comparison doesn't make much sense. (Equality still makes sense, IMO) Tony
On Tue, Nov 18, 2014 at 4:23 AM, Andrzej Krzemienski
2014-11-18 8:00 GMT+01:00 Andrey Semashev
: On Mon, Nov 17, 2014 at 12:24 PM, Andrzej Krzemienski
wrote: The solution is based on the observation that there is a limited number
of
things you can do with optional<T>
optional<int> oi = ...;
1: if (oi) doSomething(*oi); else doSomethingElse(); // or nothing
2: if (oi) doSomething(*oi); else doSomething(-1); // use default value
3: if (!oi) oi = (int)getIntByOtherMeans(); doSomething(*oi); // now it is safe
Now we simply provide a dedicated interface for each of these three usages:
1: oi.use(&doSomething, &doSomethingElse); // or use lambdas
I'd call it visit(). Also, a one argument visit() could be useful (the visitor would be passed boost::none if the value is absent).
2: doSomething(oi.value_or(-1));
We already have that.
3: doSomething(oi.value_or_eval(getIntByOtherMeans));
So, basically, the proposal is to add visitation API, am I correct?
And to remove operator* and get() and value() and get_pointer() - anything that could cause UB. And remove implicit conversion from T
value() throws, not UB. (At least in std::experimental) I could see a "safer" or at least "more explicit" optional that didn't use operator*, but still allowed access via named functions. And maybe did less implicit conversion / comparison. Not exactly safer, but more explicit, which helps safety. Tony
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
The problem with optional is that it tries to be a drop-in replacement
proxy for its underlying type, and, unfortunately, it can't fully do that.
So it ends up with an identity crisis. IMO, optional should be treated as
a first class object, not a thin wrapper around T, that means no implicit
conversions to/from T, no implicit comparisons with T, etc... Last time I
looked at this, that will solve the reference rebinding gotcha. That is,
you'll have one behavior for optional<T> and optional
On 11/17/2014 09:12 PM, Mostafa wrote:
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
The problem with optional is that it tries to be a drop-in replacement proxy for its underlying type, and, unfortunately, it can't fully do that. So it ends up with an identity crisis. IMO, optional should be treated as a first class object, not a thin wrapper around T, that means no implicit conversions to/from T, no implicit comparisons with T, etc... Last time I looked at this, that will solve the reference rebinding gotcha. That is, you'll have one behavior for optional<T> and optional
....
1. With all due respect I do not feel I can agree with the above... and I do not believe "optional" has a "problem"... especially of the described magnitude. IMO the result of applying the conceptual/design changes described above to the existing "optional" won't be "optional" as we know it... not even close. And after using "optional" quite a bit I can say I personally won't be very happy. IMO "optional" has been born as a practical solution to a real problem and IMO it solves it quite well. Yes, it has certain behavior that the user needs to be aware of... but what class does not impose restrictions of that kind? Any potential functional/behavioral change has to be looked at individually. For example, I do agree that that there should not be implicit optional<T> to T conversion. I was not even aware there was such. However, implicit T to optional<T> conversion has a very practical purpose. For example, int value(optional<int> =optional<int>()); allows me to shrink the API as value getter and setter are merged into one function. Namely, int k = value(); // Get the value value(22); // Set the value. Implicit conversion of T to optional<T> Instead, asking the user to call explicitly value(optional<22>) is a professional suicide. 2. As for the separate/additional "safe" optional, I am personally not that thrilled by the idea as of now. IMO that'll result in user-base fragmentation, incompatibilities and inevitably more confusion in the end. Just my 2c.
On Mon, 17 Nov 2014 13:12:56 -0800, Vladimir Batov
On 11/17/2014 09:12 PM, Mostafa wrote:
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
The problem with optional is that it tries to be a drop-in replacement proxy for its underlying type, and, unfortunately, it can't fully do that. So it ends up with an identity crisis. IMO, optional should be treated as a first class object, not a thin wrapper around T, that means no implicit conversions to/from T, no implicit comparisons with T, etc... Last time I looked at this, that will solve the reference rebinding gotcha. That is, you'll have one behavior for optional<T> and optional
.... 1. With all due respect I do not feel I can agree with the above... and I do not believe "optional" has a "problem"... especially of the described magnitude. IMO the result of applying the conceptual/design changes described above to the existing "optional" won't be "optional" as we know it... not even close.
Just to clarify, since the discussion is in the context of an "alternative optional", what I wrote about how the existing library should behave just applies to the alternative one.
And after using "optional" quite a bit I can say I personally won't be very happy. IMO "optional" has been born as a practical solution to a real problem and IMO it solves it quite well. Yes, it has certain behavior that the user needs to be aware of... but what class does not impose restrictions of that kind?
Not all classes have gotchas. And, IMO, C++ has too many gotchas as it is.
Any potential functional/behavioral change has to be looked at individually.
For example, I do agree that that there should not be implicit optional<T> to T conversion. I was not even aware there was such.
However, implicit T to optional<T> conversion has a very practical purpose.
And that leads to the intractable rebinding gotcha for optional
For example,
int value(optional<int> =optional<int>());
allows me to shrink the API as value getter and setter are merged into one function. Namely,
int k = value(); // Get the value value(22); // Set the value. Implicit conversion of T to optional<T> Instead, asking the user to call explicitly
value(optional<22>)
is a professional suicide.
Templates are neither new nor unknown in C++. If you find yourself typing too much, just use a typedef.
2. As for the separate/additional "safe" optional, I am personally not that thrilled by the idea as of now. IMO that'll result in user-base fragmentation, incompatibilities and inevitably more confusion in the end.
If there are multiple valid yet incompatible design choices, then IMO there is nothing wrong with providing alternatives.
On 11/18/2014 11:25 AM, Mostafa wrote:
On Mon, 17 Nov 2014 13:12:56 -0800, Vladimir Batov
wrote: On 11/17/2014 09:12 PM, Mostafa wrote:
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
... And after using "optional" quite a bit I can say I personally won't be very happy. IMO "optional" has been born as a practical solution to a real problem and IMO it solves it quite well. Yes, it has certain behavior that the user needs to be aware of... but what class does not impose restrictions of that kind?
Not all classes have gotchas. And, IMO, C++ has too many gotchas as it is.
Well, that's far too broad a statement to be argued over... still, my logic behind restrictions-related statement is that, say, a class provides a service, nothing is free, so to make use of the service the user needs to give something back... be that adhering to certain deployment pattern or API or something.
Any potential functional/behavioral change has to be looked at individually.
For example, I do agree that that there should not be implicit optional<T> to T conversion. I was not even aware there was such.
However, implicit T to optional<T> conversion has a very practical purpose.
And that leads to the intractable rebinding gotcha for optional
.
Yes, I've heard of that "beast". In all honesty to me it looks quite
esoteric and beyond the realm of practical every-day programming. I
suspect if it's taken out altogether, 95% of the users will not even
notice. I can be wrong though and prepared to be convinced otherwise...
if you show me a practical example where it's a must-have... Without
such I fully understand/support the decision to get rid of optional
For example,
int value(optional<int> =optional<int>());
allows me to shrink the API as value getter and setter are merged into one function. Namely,
int k = value(); // Get the value value(22); // Set the value. Implicit conversion of T to optional<T> Instead, asking the user to call explicitly
value(optional<22>)
is a professional suicide.
Templates are neither new nor unknown in C++. If you find yourself typing too much, just use a typedef.
Hmm, not sure I follow what templates have to do with it... and the amount of typing is irrelevant. The point was if conceptually user expects to use/deal with "int", then the requirement to explicitly specify optional<int> looks unreasonably heavy and unnatural. It exposes too much underlying infrastructure where it does not need to. All IMO of course.
2. As for the separate/additional "safe" optional, I am personally not that thrilled by the idea as of now. IMO that'll result in user-base fragmentation, incompatibilities and inevitably more confusion in the end.
If there are multiple valid yet incompatible design choices, then IMO there is nothing wrong with providing alternatives.
I do not believe it's that simple. If, when referring to "incompatible design choices", we are talking about differences like "car vs. truck", then I'd argue that those are not alternatives as they serve quite distinct purposes and, therefore, adhere to different designs. Otherwise, having "incompatible design choices" like a toothbrush and a "safe" toothbrush and a "whistling" toothbrush are distracting and confusing and IMO counter-productive. I had an impression that "safe" optional suggestion was more in the latter camp as it still very much wanted to be "optional"... but a tiny bit different. It might well be that I misunderstood.
On Mon, 17 Nov 2014 19:57:08 -0800, Vladimir Batov
On 11/18/2014 11:25 AM, Mostafa wrote:
On Mon, 17 Nov 2014 13:12:56 -0800, Vladimir Batov
wrote: On 11/17/2014 09:12 PM, Mostafa wrote:
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
... And after using "optional" quite a bit I can say I personally won't be very happy. IMO "optional" has been born as a practical solution to a real problem and IMO it solves it quite well. Yes, it has certain behavior that the user needs to be aware of... but what class does not impose restrictions of that kind?
Not all classes have gotchas. And, IMO, C++ has too many gotchas as it is.
Well, that's far too broad a statement to be argued over... still, my logic behind restrictions-related statement is that, say, a class provides a service, nothing is free, so to make use of the service the user needs to give something back... be that adhering to certain deployment pattern or API or something.
I'm in agreement with the last sentence. What I'm saying is that the current optional usage pattern, IMHO, is just too inconsistent.
Any potential functional/behavioral change has to be looked at individually.
For example, I do agree that that there should not be implicit optional<T> to T conversion. I was not even aware there was such.
However, implicit T to optional<T> conversion has a very practical purpose.
And that leads to the intractable rebinding gotcha for optional
. Yes, I've heard of that "beast". In all honesty to me it looks quite esoteric and beyond the realm of practical every-day programming. I suspect if it's taken out altogether, 95% of the users will not even notice. I can be wrong though and prepared to be convinced otherwise... if you show me a practical example where it's a must-have... Without such I fully understand/support the decision to get rid of optional
for std::tr2::optional proposal. So, re-phrasing J. Stalin's "no man, no problem" -- "no "feature", no problem.
And that exacerbates the problem doesn't it? If optional
For example,
int value(optional<int> =optional<int>());
allows me to shrink the API as value getter and setter are merged into one function. Namely,
int k = value(); // Get the value value(22); // Set the value. Implicit conversion of T to optional<T> Instead, asking the user to call explicitly
value(optional<22>)
is a professional suicide.
Templates are neither new nor unknown in C++. If you find yourself typing too much, just use a typedef.
Hmm, not sure I follow what templates have to do with it... and the amount of typing is irrelevant. The point was if conceptually user expects to use/deal with "int", then the requirement to explicitly specify optional<int> looks unreasonably heavy and unnatural.
Then why specify in the interface in the first place?
2. As for the separate/additional "safe" optional, I am personally not that thrilled by the idea as of now. IMO that'll result in user-base fragmentation, incompatibilities and inevitably more confusion in the end.
If there are multiple valid yet incompatible design choices, then IMO there is nothing wrong with providing alternatives.
I do not believe it's that simple. If, when referring to "incompatible design choices", we are talking about differences like "car vs. truck", then I'd argue that those are not alternatives as they serve quite distinct purposes and, therefore, adhere to different designs. Otherwise, having "incompatible design choices" like a toothbrush and a "safe" toothbrush and a "whistling" toothbrush are distracting and confusing and IMO counter-productive. I had an impression that "safe" optional suggestion was more in the latter camp as it still very much wanted to be "optional"... but a tiny bit different. It might well be that I misunderstood.
They will be competing libraries. But I don't see that as a bad thing. It will only be confusing if the rationale for their difference is not well documented.
Le 17/11/14 22:12, Vladimir Batov a écrit :
On 11/17/2014 09:12 PM, Mostafa wrote:
On Sun, 16 Nov 2014 23:04:43 -0800, Andrzej Krzemienski
wrote: Hi Everyone, I would like to run an idea through everyone in this list. There is a recurring complaint about Boost.Optional that it allows you to do "unsafe" things: 1. Inadvertent mixed comparisons between T and optional<T> 2. Unintended conversion from T to optional<T>
The problem with optional is that it tries to be a drop-in replacement proxy for its underlying type, and, unfortunately, it can't fully do that. So it ends up with an identity crisis. IMO, optional should be treated as a first class object, not a thin wrapper around T, that means no implicit conversions to/from T, no implicit comparisons with T, etc... Last time I looked at this, that will solve the reference rebinding gotcha. That is, you'll have one behavior for optional<T> and optional
.... 1. With all due respect I do not feel I can agree with the above... and I do not believe "optional" has a "problem"... especially of the described magnitude. IMO the result of applying the conceptual/design changes described above to the existing "optional" won't be "optional" as we know it... not even close. And after using "optional" quite a bit I can say I personally won't be very happy. IMO "optional" has been born as a practical solution to a real problem and IMO it solves it quite well. Yes, it has certain behavior that the user needs to be aware of... but what class does not impose restrictions of that kind? Any potential functional/behavioral change has to be looked at individually.
For example, I do agree that that there should not be implicit optional<T> to T conversion. I was not even aware there was such. This conversion must be explicit if any.
However, implicit T to optional<T> conversion has a very practical purpose. For example,
int value(optional<int> =optional<int>());
allows me to shrink the API as value getter and setter are merged into one function. Namely,
int k = value(); // Get the value value(22); // Set the value. Implicit conversion of T to optional<T>
Please don't do that. Use just overload.
Instead, asking the user to call explicitly
value(optional<22>)
is a professional suicide.
2. As for the separate/additional "safe" optional, I am personally not that thrilled by the idea as of now. IMO that'll result in user-base fragmentation, incompatibilities and inevitably more confusion in the end. Anything useful for safe_optional is useful for optional. So I see safe_optional as a view of a restricted interface. This doesn't mean that conversions from one to the other are not possible.
Vicente
Andrzej Krzemienski wrote
My idea is to provide another type wrapper, say safe_optional <T> , that would choose different tradeoffs: prefer "safety" to some flexibility and/or efficiency. It would probably be part of Boost.Optional library as the implementation can be reused - only the interface would be changed.
One downside of this solution is that we would have two libraries for doing nearly the same thing, which could "scatter" the community. There is a value for standardizing certain things and making them universal.
Can't this concern be addressed by making this an addition to the optional library. So that safe_optional<T> would be an wrapper around optional<T> with extra error checking? Robert Ramey -- View this message in context: http://boost.2283326.n4.nabble.com/optional-Safe-optional-tp4669110p4669135.... Sent from the Boost - Dev mailing list archive at Nabble.com.
2014-11-17 18:01 GMT+01:00 Robert Ramey
Andrzej Krzemienski wrote
My idea is to provide another type wrapper, say safe_optional <T> , that would choose different tradeoffs: prefer "safety" to some flexibility and/or efficiency. It would probably be part of Boost.Optional library as the implementation can be reused - only the interface would be changed.
One downside of this solution is that we would have two libraries for doing nearly the same thing, which could "scatter" the community. There is a value for standardizing certain things and making them universal.
Can't this concern be addressed by making this an addition to the optional library. So that safe_optional<T> would be an wrapper around optional<T> with extra error checking?
In fact, Boost.Optional already comes with two interfaces: 1. optional_base<T> (with minimum set of operations) 2. optional<T> that derives from the former. Although the former one is not advertized in the documentation, the intent was that it is used by people who are not happy with the latter interface, so they can use it to build whatever interface they like. I was planning to use optional_base as a base for the new interface. First, I want to see peoples' reaction. Regards, &rzej
participants (18)
-
Andrey Semashev
-
Andrzej Krzemienski
-
Bjorn Reese
-
dariomt@gmail.com
-
Gavin Lambert
-
Gottlob Frege
-
Joaquin M Lopez Munoz
-
Matt Calabrese
-
Mostafa
-
Nevin Liber
-
Niall Douglas
-
Olaf van der Spek
-
pfultz2
-
Rob Stewart
-
Robert Ramey
-
Sylvester-Bradley, Gareth
-
Vicente J. Botet Escriba
-
Vladimir Batov