[optional] operator<(optional<T>, T) -- is it wrong?
I moved this discussion from the other thread.
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(); // this compiles and does the wrong thing }
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.
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. :-)
When you are talking about oranges and mandarins outside the context of C++, I am convinced. the problem I have is with mapping it onto C++ and the design of Optional. (1) In C++ we have the converting constructor. This appears close to what you call "bringing T into the optional<T> land", but I claim it is not the same. And the consequences of these nuance differences result in the problem in question. Perhaps the notion of "bringing T into the optional<T> land" would be better reflected by an explicit constructor, or function make_optional(). I am not saying that the converting constructor is wrong here. I am just saying that the motivation is *slightly* different than "bringing T into the optional<T> land", it is more for syntactic convenience, which is almost the same, but not same. (2) Your philosophy "when the direction of conversion is not as clear, we refuse applying it" -- there is no way to apply it within the definition of optional. We can apply it by poisoning every operation in the world that takes T or optional<T>. But that looks impractical. Back to Vicente's concern: 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.
I do not think it has been addressed.
On 11/21/2014 09:29 PM, Andrzej Krzemienski wrote:
...
(1) In C++ we have the converting constructor. This appears close to what you call "bringing T into the optional<T> land", but I claim it is not the same.
They are obviously not the same -- "bringing" is an action, "converting constructor" is the tool to execute that action... when needed. If we have the tool (the constructor) it does not mean we swing it indiscriminately and apply it everywhere it fits. There has to be logical justification to apply that constructor... not just mechanical ability to do that. The converter is a powerful tool. However, with that "hammer" we should not fall into the trap of seeing everything like a nail.
And the consequences of these nuance differences result in the problem in question.
To me the difference's not that "nuanced". "Action" and "tool" are like "walking" and "shoes", "singing" and "microphone".
Perhaps the notion of "bringing T into the optional<T> land" would be better reflected by an explicit constructor, or function make_optional().
Mechanically, yes. Logically I am sure there are places where the action (of T to optional<T>) is unequivocal. If my API requests optional<T>, then I explicitly say -- I deal with optional<T> and apply optional<T> rules. I.e. my API is in the optional<T> land. So, when you provide T instead, it's propagated to optional<T> at the point of entering my API. The same as feed 'int' to func(double). It's not controversial, is it? How func(optional<T>) is different?
I am not saying that the converting constructor is wrong here. I am just saying that the motivation is *slightly* different than "bringing T into the optional<T> land", it is more for syntactic convenience, which is almost the same, but not same. (2) Your philosophy "when the direction of conversion is not as clear, we refuse applying it" -- there is no way to apply it within the definition of optional. We can apply it by poisoning every operation in the world that takes T or optional<T>. But that looks impractical. Back to Vicente's concern: 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.
IMO no need complicating the matter with number of parameters. Just one will do. Are you implying that for every void f(optional<T>); we need to have poisoned void f(T) { BOOST_STATIC_ASSERT(); } As I tried to describe above, if I specify "void f(optional<T>, optional<T>);" as my API, then I explicitly say that it plays by optional<T> rules. It's no different from "void f(double, double);" called with "ints". If playing be optional<T> rules might be different for T rules or potentially confusing, I suggest we poison it. op<(T, optional<T>) is an excellent example because it implements interaction of its two arguments and that interaction s different if 1) both args are T; 2) both args are optional<T>; 3) args are of different types. As they are different, we should have 3 different implementations... #3 just happens to be no-implementation. I might well fail to understand your concern as those examples above seem far too hypothetical. If you can come up with a realistic example, that'd probably help me to see what you see.
I do not think it has been addressed.
Andrzej Krzemienski wrote
... 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.
I do not think it has been addressed.
The problem here (as I can see it) is that you supply an abstract fun(T, optional<T>) with no context whatsoever and then ask -- how it can be addressed -- promote T to optional<T> or poison... or something else?.. Obviously, no matter what the choice is it'll be correct for one set of real functions and wrong for others. So, the answer is "it depends on the context". Granted, it's very tempting to have one absolute truth, one simple answer to all fun(T, optional<T>) under the sun... Say, "shall not kill" sounds like a good rule to live by... unless one is attacked... then "kill in defence" might sound like a reasonable approach... unless one is at war... then "kill plenty" sounds like an expectation... I guess, all I am trying to convey with all that killing and mandarins and oranges is that we need to look at things in context and address those in context so that is, in the end, beneficial to the user. From that perspective (usefulness, user-friendliness, rather than some hypothetical library purity) there cannot possibly be any doubts about sensible/unquestionable implicit promotion of T to optional<T>, about what is more natural, better for the user: foo->set_time("11:55PM") or foo->set_time(boost::optional<string>("11:55PM")) As for op<(T, optional<T>), then the library writer'd like to be as helpful as possible and to provide as much functionality as possible. Unfortunately, there is just not enough context to go one way or the other. So, stating that by prohibiting the operator is the safest and most honest solution. -- View this message in context: http://boost.2283326.n4.nabble.com/optional-operator-optional-T-T-is-it-wron... Sent from the Boost - Dev mailing list archive at Nabble.com.
2014-11-24 7:42 GMT+01:00 Vladimir Batov
Andrzej Krzemienski wrote
... 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.
I do not think it has been addressed.
The problem here (as I can see it) is that you supply an abstract fun(T, optional<T>) with no context whatsoever and then ask -- how it can be addressed -- promote T to optional<T> or poison... or something else?.. Obviously, no matter what the choice is it'll be correct for one set of real functions and wrong for others. So, the answer is "it depends on the context". Granted, it's very tempting to have one absolute truth, one simple answer to all fun(T, optional<T>) under the sun... Say, "shall not kill" sounds like a good rule to live by... unless one is attacked... then "kill in defence" might sound like a reasonable approach... unless one is at war... then "kill plenty" sounds like an expectation... I guess, all I am trying to convey with all that killing and mandarins and oranges is that we need to look at things in context and address those in context so that is, in the end, beneficial to the user. From that perspective (usefulness, user-friendliness, rather than some hypothetical library purity) there cannot possibly be any doubts about sensible/unquestionable implicit promotion of T to optional<T>, about what is more natural, better for the user:
foo->set_time("11:55PM") or foo->set_time(boost::optional<string>("11:55PM"))
As for op<(T, optional<T>), then the library writer'd like to be as helpful as possible and to provide as much functionality as possible. Unfortunately, there is just not enough context to go one way or the other. So, stating that by prohibiting the operator is the safest and most honest solution.
I think at this point I had better pause for some time and rethink, whether it is just that I fail to deliver my point of view correctly, or whether I am just too hang out to my own solution. I get your arguments. It is just that they do not let me conclude that op<(T, optional<T>) should be banned. Either I am too fixed on my point of view, or there does exist an objective argument that I perceive, but cannot put into words. In general, my argument is not about being pure, but more about "can the concept of optional<T> be easily explained and taught?". I sense that they are different things, although I admit that when I try to explain it, the explanations look the same in either case. I will need to take some time and rethink.
2014-11-24 9:34 GMT+01:00 Andrzej Krzemienski
2014-11-24 7:42 GMT+01:00 Vladimir Batov
: ... 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
Andrzej Krzemienski wrote that
they need to program this way.
I do not think it has been addressed.
The problem here (as I can see it) is that you supply an abstract fun(T, optional<T>) with no context whatsoever and then ask -- how it can be addressed -- promote T to optional<T> or poison... or something else?.. Obviously, no matter what the choice is it'll be correct for one set of real functions and wrong for others. So, the answer is "it depends on the context". Granted, it's very tempting to have one absolute truth, one simple answer to all fun(T, optional<T>) under the sun... Say, "shall not kill" sounds like a good rule to live by... unless one is attacked... then "kill in defence" might sound like a reasonable approach... unless one is at war... then "kill plenty" sounds like an expectation... I guess, all I am trying to convey with all that killing and mandarins and oranges is that we need to look at things in context and address those in context so that is, in the end, beneficial to the user. From that perspective (usefulness, user-friendliness, rather than some hypothetical library purity) there cannot possibly be any doubts about sensible/unquestionable implicit promotion of T to optional<T>, about what is more natural, better for the user:
foo->set_time("11:55PM") or foo->set_time(boost::optional<string>("11:55PM"))
As for op<(T, optional<T>), then the library writer'd like to be as helpful as possible and to provide as much functionality as possible. Unfortunately, there is just not enough context to go one way or the other. So, stating that by prohibiting the operator is the safest and most honest solution.
I think at this point I had better pause for some time and rethink, whether it is just that I fail to deliver my point of view correctly, or whether I am just too hang out to my own solution.
I get your arguments. It is just that they do not let me conclude that op<(T, optional<T>) should be banned. Either I am too fixed on my point of view, or there does exist an objective argument that I perceive, but cannot put into words.
In general, my argument is not about being pure, but more about "can the concept of optional<T> be easily explained and taught?". I sense that they are different things, although I admit that when I try to explain it, the explanations look the same in either case.
I will need to take some time and rethink.
Anyone else? Would you be affected if operator<(optional<T>, T) is poisoned? (but operator==(optional<T>, T) remains working)
Andrzej Krzemienski
Anyone else? Would you be affected if operator<(optional<T>, T) is poisoned? (but operator==(optional<T>, T) remains working)
I actually had a small bug in production code a few years ago after changing an integral type to an optional because I hadn't expected this behavior, so I think that would be good.
On 11/24/2014 09:07 PM, Andrzej Krzemienski wrote:
Anyone else? Would you be affected if operator<(optional<T>, T) is poisoned? (but operator==(optional<T>, T) remains working)
Another *pragmatic* point (probably more relevant for the std::optional variant) is if op<(T, optional<T>) is prohibited now and then later it is decided that decision was wrong (even though I can't possibly see how as it does not take *any* functionality away), then adding it back will not cause any issues. On the contrary, it op<() is allowed to stay now and later it's decided that decision was wrong and op<() is better taken out, then it won't be possible to do... well, much harder anyway as it's be a breaking change.
Le 25/11/14 21:13, Vladimir Batov a écrit :
On 11/24/2014 09:07 PM, Andrzej Krzemienski wrote:
Anyone else? Would you be affected if operator<(optional<T>, T) is poisoned? (but operator==(optional<T>, T) remains working)
Another *pragmatic* point (probably more relevant for the std::optional variant) is if op<(T, optional<T>) is prohibited now and then later it is decided that decision was wrong (even though I can't possibly see how as it does not take *any* functionality away), then adding it back will not cause any issues. On the contrary, it op<() is allowed to stay now and later it's decided that decision was wrong and op<() is better taken out, then it won't be possible to do... well, much harder anyway as it's be a breaking change.
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better. Best, Vicente
On 11/25/2014 7:18 PM, Vicente J. Botet Escriba wrote:
Le 25/11/14 21:13, Vladimir Batov a écrit :
On 11/24/2014 09:07 PM, Andrzej Krzemienski wrote:
Anyone else? Would you be affected if operator<(optional<T>, T) is poisoned? (but operator==(optional<T>, T) remains working)
Another *pragmatic* point (probably more relevant for the std::optional variant) is if op<(T, optional<T>) is prohibited now and then later it is decided that decision was wrong (even though I can't possibly see how as it does not take *any* functionality away), then adding it back will not cause any issues. On the contrary, it op<() is allowed to stay now and later it's decided that decision was wrong and op<() is better taken out, then it won't be possible to do... well, much harder anyway as it's be a breaking change.
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better.
I do not understand why anyone feels that the less than operator for boost::optional<T> is wrong.
On Tue, Nov 25, 2014 at 5:27 PM, Edward Diener
On 11/25/2014 7:18 PM, Vicente J. Botet Escriba wrote:
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better.
I do not understand why anyone feels that the less than operator for boost::optional<T> is wrong.
I can MAYBE see not having them as long as std::less and family are still specialized accordingly (specializing std::less is [correctly, IMO] in for the proposed standard optional now anyway, thanks to Tony Van Eerd). Ultimately, though, I really don't think there's a problem with also having the operators overloaded. I simply don't buy the "gotchas." -- -Matt Calabrese
On 11/25/2014 10:20 PM, Matt Calabrese wrote:
On Tue, Nov 25, 2014 at 5:27 PM, Edward Diener
wrote: On 11/25/2014 7:18 PM, Vicente J. Botet Escriba wrote:
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better.
I do not understand why anyone feels that the less than operator for boost::optional<T> is wrong.
I can MAYBE see not having them as long as std::less and family are still specialized accordingly (specializing std::less is [correctly, IMO] in for the proposed standard optional now anyway, thanks to Tony Van Eerd). Ultimately, though, I really don't think there's a problem with also having the operators overloaded. I simply don't buy the "gotchas."
If the C++ standard says that associative container of std::optionals is fine but some programmers believe that manually comparing them via the less than operator is not fine, I think we have a real conceptual problem here, even outside of the mere non-orthogonality of the situation.
On 11/26/2014 02:33 PM, Edward Diener wrote:
On 11/25/2014 10:20 PM, Matt Calabrese wrote:
On Tue, Nov 25, 2014 at 5:27 PM, Edward Diener
wrote: On 11/25/2014 7:18 PM, Vicente J. Botet Escriba wrote:
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better.
I do not understand why anyone feels that the less than operator for boost::optional<T> is wrong.
I can MAYBE see not having them as long as std::less and family are still specialized accordingly (specializing std::less is [correctly, IMO] in for the proposed standard optional now anyway, thanks to Tony Van Eerd). Ultimately, though, I really don't think there's a problem with also having the operators overloaded. I simply don't buy the "gotchas."
If the C++ standard says that associative container of std::optionals is fine but some programmers believe that manually comparing them via the less than operator is not fine, I think we have a real conceptual problem here, even outside of the mere non-orthogonality of the situation.
The discussion is not about op<(optional<T>, optional<T>) but rather op<(T, optional<T>)
On 11/25/2014 10:44 PM, Vladimir Batov wrote:
On 11/26/2014 02:33 PM, Edward Diener wrote:
On 11/25/2014 10:20 PM, Matt Calabrese wrote:
On Tue, Nov 25, 2014 at 5:27 PM, Edward Diener
wrote: On 11/25/2014 7:18 PM, Vicente J. Botet Escriba wrote:
Following your reasoning, I will suggest to remove the implicit construction from T to optional<T> and/or remove the operator<(optional<T>, optional<>). If we can not live without them, we could always try to do whatever is better.
I do not understand why anyone feels that the less than operator for boost::optional<T> is wrong.
I can MAYBE see not having them as long as std::less and family are still specialized accordingly (specializing std::less is [correctly, IMO] in for the proposed standard optional now anyway, thanks to Tony Van Eerd). Ultimately, though, I really don't think there's a problem with also having the operators overloaded. I simply don't buy the "gotchas."
If the C++ standard says that associative container of std::optionals is fine but some programmers believe that manually comparing them via the less than operator is not fine, I think we have a real conceptual problem here, even outside of the mere non-orthogonality of the situation.
The discussion is not about
op<(optional<T>, optional<T>)
but rather
op<(T, optional<T>)
You have types A, B. There is an implicit conversion from A to B. op<(A,A) is defined. op<(B,B) is defined. but you object if op<(A,B) or op<(B,A) is defined. I am sorry but this is too clever for me and I do not get the point. If it is purely to protect the programmer from his own error in not understanding the relation between A and B I do not agree with this. There must be a strong, other valid reason. If in this particular case you document the ordering of values in A and B there is no strong case that I can see. Misuse of a class by a programmer too lazy or unaware to understand how it works is not a valid reason to remove functionality for everybody else. If you want coddling, use some other language ( like Java for instance ). In C++ we either know what we are doing or we don't use what we don't know. It is the same whether it is the language or a library. I am certainly not against an alternative class related to optional if that is what others want to develop. But please don't call it 'safe_optional'. And leave optional itself alone in this respect. It is fine the way it is for me.
On Tue, Nov 25, 2014 at 7:33 PM, Edward Diener
If the C++ standard says that associative container of std::optionals is fine but some programmers believe that manually comparing them via the less than operator is not fine, I think we have a real conceptual problem here, even outside of the mere non-orthogonality of the situation.
We already have this kind of thing in the standard. Consider pointer types. You can use std::less with arbitrary pointers, but this is not the case with operator< (though operator< is well defined when the objects pointed to are in the same array). Again, I'm not advocating leaving out operator< for optional, but if the main reason why people want it is because it makes it easy to work with associative containers, we already have precedent in the standard to allow this and not operator<. -- -Matt Calabrese
Vicente Botet wrote
... Following your reasoning, I will suggest to remove the implicit construction from T to optional <T> and/or remove the operator<(optional <T> , optional<>). If we can not live without them, we could always try to do whatever is better.
Vicente, you are not serious, right?.. It is just that you did not like/agree with my "reasoning" and, so you suggested something unreasonable (IMO) and presented it as a "consequence" of my "reasoning" just to indicate how ridiculous mine was... Was that the idea?... Or I am misunderstanding you post? I cannot live without implicit T to optional<T> and prohibiting it will break tonnes of my code and project code and Fernando Cacciola's code (if I remember our similar discussions years ago)... and it'll break all that code *now*... no need to wait and see... and "selling" optional<T> without implicit T to optional<T> will be real hard... in my neck of the woods anyway. -- View this message in context: http://boost.2283326.n4.nabble.com/optional-operator-optional-T-T-is-it-wron... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 26/11/14 06:26, Vladimir Batov a écrit :
... Following your reasoning, I will suggest to remove the implicit construction from T to optional <T> and/or remove the operator<(optional <T> , optional<>). If we can not live without them, we could always try to do whatever is better. Vicente, you are not serious, right?.. It is just that you did not
Vicente Botet wrote like/agree with my "reasoning" and, so you suggested something unreasonable (IMO) and presented it as a "consequence" of my "reasoning" just to indicate how ridiculous mine was... Was that the idea?... Or I am misunderstanding you post? Vladimir, yes and not. Were talking of the possibility of a new safe_optional. I would like we explore how we would like to have this new type. I cannot live without implicit T to optional<T> and prohibiting it will break tonnes of my code and project code and Fernando Cacciola's code (if I remember our similar discussions years ago)... and it'll break all that code *now*... no need to wait and see... and "selling" optional<T> without implicit T to optional<T> will be real hard... in my neck of the woods anyway.
I understand your backward compatibility concern? I'm not suggesting at
all to change boost::optional.
I'm looking on how all these conversions can be made safer.
You surely remember my Boost.Conversion proposal that was rejected. I
believe that there is something interesting behind this idea.
The idea is to state explicitly that you want a explicit conversion but
don't state to what. The what is deduced from the context.
Boost.Conversion was a library proposal. We could rethink it as a
language proposal. Let me represent by
[ x ]
this explicit conversion to the context.
With this we could be able to
optional<int> f(int x) {
if (x<0)
return nullopt ;
}
return [ x ];
}
[ x ] is longer than x but shorter than make_optional(x).
The meaning of
return [ x ]
on the context of a function returning M would be given by
return M(x);
We could also add an indirection and translate it to
return make_explicitly(type<M>{}, x);
or
return type<M>{}[x];
where the type<M> instance is used only to be able to overload on M.
template <class T>
struct type {};
The default implementation could then be a call to the explicit
constructor of the context from the parameter
// default to explicit constructor
template
On November 26, 2014 2:12:56 AM EST, "Vicente J. Botet Escriba"
Le 26/11/14 06:26, Vladimir Batov a écrit :
Vicente Botet wrote
... Following your reasoning, I will suggest to remove the implicit construction from T to optional <T> and/or remove the operator<(optional <T> , optional<>).
Vicente, you are not serious, right?..
Vladimir, yes and not. Were talking of the possibility of a new safe_optional. I would like we explore how we would like to have this new type.
[snip]
I'm looking on how all these conversions can be made safer.
[snip]
The idea is to state explicitly that you want a explicit conversion but don't state to what. The what is deduced from the context.
That idea is rather odd to me. One is hardly being explicit if one is relying on context to determine the target type.
Let me represent by
[ x ]
this explicit conversion to the context. With this we could be able to
optional<int> f(int x) { if (x<0) return nullopt ; } return [ x ]; }
[ x ] is longer than x but shorter than make_optional(x).
The meaning of
return [ x ]
The target type is reasonably deduced to be the function return type, so this is a means to avoid repeating the type. auto would work as well: return auto(x);. That's on keeping with auto's purpose.
on the context of a function returning M would be given by
return M(x);
We could also add an indirection and translate it to
return make_explicitly(type<M>{}, x);
or
return type<M>{}[x];
where the type<M> instance is used only to be able to overload on M.
template <class T> struct type {};
The default implementation could then be a call to the explicit constructor of the context from the parameter
// default to explicit constructor template
M make_explicitly(type<M>, X && x) { return M(std::forward<T>(x)); }
Using auto would be simpler.
We could stop here or we could use [ x ] not only on a return statement
but also in any expression as in
optional<int> oi; int x; ... if ( oi < [x] ) ....
Note that we don't need an implicit conversion from T to optional<T> then but only an explicit one, avoiding the more verbose
if ( oi < std::make_optional(x) )
If anything in the expression or calling context changes, the target type can change. How is that explicit? It's no better than an implicit conversion. Indeed, it's worse: it bypasses the control implied by marking a constructor explicit.
This can be applied also on the context of the await proposal to separate the concerns. await will take care of the the continuation part and {{}} will take care explicit conversion transformation.
future<int> f(int x) { if (x > 10) return await something_long(x); else return [ x + 1 ]; // make_ready_future }
Use auto for the return statement.
This operator could be used also to mean the empty set, as in
set<int> f {
... return []; };
This is just default construction. How about auto() or auto{}?
Another example
variant< int, string> f(int x) { if (x > 10) return [ std::string("___") ]; else return [ x + 1 ]; }
These are return statements, so just use auto here, too. ___ Rob (Sent from my portable computation engine)
Le 26/11/14 11:08, Rob Stewart a écrit :
On November 26, 2014 2:12:56 AM EST, "Vicente J. Botet Escriba"
wrote: Le 26/11/14 06:26, Vladimir Batov a écrit :
Vicente Botet wrote
... Following your reasoning, I will suggest to remove the implicit construction from T to optional <T> and/or remove the operator<(optional <T> , optional<>). Vicente, you are not serious, right?.. Vladimir, yes and not. Were talking of the possibility of a new safe_optional. I would like we explore how we would like to have this new type. [snip]
I'm looking on how all these conversions can be made safer. [snip]
The idea is to state explicitly that you want a explicit conversion but don't state to what. The what is deduced from the context. That idea is rather odd to me. One is hardly being explicit if one is relying on context to determine the target type. Well, you state explicitly that you allow the compiler to convert to the target type, without repeating the target type. The compiler know already it.
Let me represent by
[ x ]
this explicit conversion to the context. With this we could be able to
optional<int> f(int x) { if (x<0) return nullopt ; } return [ x ]; }
[ x ] is longer than x but shorter than make_optional(x).
The meaning of
return [ x ] The target type is reasonably deduced to be the function return type, so this is a means to avoid repeating the type. auto would work as well: return auto(x);. That's on keeping with auto's purpose.
Glad to see you are for this use case. I'm not against return auto(x) and the syntax, while important, is less important than th esemantic. Has this been proposed/accepted/adopted already in the C++ standard?
on the context of a function returning M would be given by
return M(x);
We could also add an indirection and translate it to
return make_explicitly(type<M>{}, x);
or
return type<M>{}[x];
where the type<M> instance is used only to be able to overload on M.
template <class T> struct type {};
The default implementation could then be a call to the explicit constructor of the context from the parameter
// default to explicit constructor template
M make_explicitly(type<M>, X && x) { return M(std::forward<T>(x)); } Using auto would be simpler.
I have no problem with the form.
We could stop here or we could use [ x ] not only on a return statement
but also in any expression as in
optional<int> oi; int x; ... if ( oi < [x] ) ....
Note that we don't need an implicit conversion from T to optional<T> then but only an explicit one, avoiding the more verbose
if ( oi < std::make_optional(x) ) If anything in the expression or calling context changes, the target type can change. How is that explicit? It's no better than an implicit conversion. Indeed, it's worse: it bypasses the control implied by marking a constructor explicit.
Yes and not. If the expression oi < [x] has only a sens when [x] is optional<int>, then what [x] is requesting is an explicit conversion from x to optional<int>. If the expression oi < [x] could be valid for more than one type T, the expression is malformed. If you don't want to depend on the context just be more explicit and use oi < std::make_optional(x) There are a lot of situations, as is the case for the return type, on which the context is unequivocally identified. In all these cases, the use of [x] or auto(x) is useful.
This can be applied also on the context of the await proposal to separate the concerns. await will take care of the the continuation part and {{}} will take care explicit conversion transformation.
future<int> f(int x) { if (x > 10) return await something_long(x); else return [ x + 1 ]; // make_ready_future } Use auto for the return statement.
Would a possible auto(x+1) let me translate it to make_ready_future(x)? Hopping my comments help to clarify what I'm locking for. Vicente
Vicente Botet wrote
... Were talking of the possibility of a new safe_optional. I would like we explore how we would like to have this new type.
I am not against any new types which prove to be useful... just do not call it *_optional. Let your class stand on its own feet and be judged on its own merit... instead of piggy-backing on "optional"... and confusing and fragmenting "optional" user-base.
... I'm looking on how all these conversions can be made safer. ...
OK, Vicente, my most humble apologies for snipping all the stuff you typed that flew right over my head and made me feel stupid. I have to be brutally honest I did not understand your "idea to state explicitly that you want a explicit conversion but don't state to what" and your "implicit explicit conversion thing". You obviously put a lot of thought into it but I personally did not get *what problem* exactly you are trying to solve. I personally do not feel there is a conversion-related problem that needs fixing... On the other hand I do not know Haskell, so it may well be that I do not know what I am missing. :-) I just hope that other people you'll present your idea to will be smarter than me to appreciate it. Do you have any links to slow-pace tutorial/explanation/justification of what you are trying to propose that I could peruse without hurry? -- View this message in context: http://boost.2283326.n4.nabble.com/optional-operator-optional-T-T-is-it-wron... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 26/11/14 12:00, Vladimir Batov a écrit :
Vicente Botet wrote
... Were talking of the possibility of a new safe_optional. I would like we explore how we would like to have this new type. I am not against any new types which prove to be useful... just do not call it *_optional. Let your class stand on its own feet and be judged on its own merit... instead of piggy-backing on "optional"... and confusing and fragmenting "optional" user-base. I will stop here, so that we can preserve boost::optional intact ;-)
... I'm looking on how all these conversions can be made safer. ... OK, Vicente, my most humble apologies for snipping all the stuff you typed that flew right over my head and made me feel stupid. I have to be brutally honest I did not understand your "idea to state explicitly that you want a explicit conversion but don't state to what" and your "implicit explicit conversion thing". You obviously put a lot of thought into it but I personally did not get *what problem* exactly you are trying to solve. I personally do not feel there is a conversion-related problem that needs fixing... On the other hand I do not know Haskell, so it may well be that I do not know what I am missing. :-) I just hope that other people you'll present your idea to will be smarter than me to appreciate it. Do you have any links to slow-pace tutorial/explanation/justification of what you are trying to propose that I could peruse without hurry?
No problem, I recognize that my description is not well ordered. I will try again in a new thread. Best, Vicente
I have been bitten in the past by operator<. I was converting code that used unsigned with a 'magic value' to represent nullness to boost::optionalstd::uint32_t. However, the maximum value it used was std::numeric_limitsstd::uint32_t:max(). A simple find and replace for this magic constant (which was referred to everywhere as DWORD_NULL) with boost::none and fixing compile errors led to some erroneous code. We had code that assumed an 'uninitialized' std::uint32_t was greater than any valid value. The concept of "value not present" is not inherently less-than or greater-than any value, but because of operator<, code compiled that shouldn't have. I suppose this is really an argument against any operator< overload, not just the mixed-type comparison. I don't expect this to be a major problem for new code, but there is a lot of code out there that uses a magic value, and that is what optional is supposed to replace.
participants (9)
-
Andrzej Krzemienski
-
David Stone
-
Edward Diener
-
Marcel Raad
-
Matt Calabrese
-
Rob Stewart
-
Vicente J. Botet Escriba
-
Vladimir Batov
-
Vladimir Batov