[variant] Maintainer

Hi, I've been keeping an eye on Boost.Varinat for a few years. Since then multiple issues were closed; support for rvalues and variadic templates was added; other good things happened. Could I be promoted to an official maintainer of Variant? This will help me to react on issues faster (email notifications) which is essential. I'm also planning to add missing features from N4450 without affecting the current never-empty behaviour and without breaking existing code. -- Best regards, Antony Polukhin

On 6/25/2015 5:18 PM, Antony Polukhin wrote:
Hi,
I've been keeping an eye on Boost.Varinat for a few years. Since then multiple issues were closed; support for rvalues and variadic templates was added; other good things happened.
Could I be promoted to an official maintainer of Variant?
Have you tried contacting either of the library developers to see if they are still interested in maintaining the library ? I see an e-mail for one of them, Eric Friedman, in the libs/maintainers.txt list. If there is no interest by them in maintaining the library I don't personally see why you should not be the official maintainer of the library. But I do not know who actually decides on such things.
This will help me to react on issues faster (email notifications) which is essential. I'm also planning to add missing features from N4450 without affecting the current never-empty behaviour and without breaking existing code.

2015-06-26 1:12 GMT+03:00 Edward Diener <eldiener@tropicsoft.com>:
Have you tried contacting either of the library developers to see if they are still interested in maintaining the library ? I see an e-mail for one of them, Eric Friedman, in the libs/maintainers.txt list. If there is no interest by them in maintaining the library I don't personally see why you should not be the official maintainer of the library. But I do not know who actually decides on such things.
I've written a letter to Eric Friedman and awaiting response. Thanks for support! -- Best regards, Antony Polukhin

Le 25/06/15 23:18, Antony Polukhin a écrit :
Hi,
I've been keeping an eye on Boost.Varinat for a few years. Since then multiple issues were closed; support for rvalues and variadic templates was added; other good things happened.
Could I be promoted to an official maintainer of Variant?
This will help me to react on issues faster (email notifications) which is essential. I'm also planning to add missing features from N4450 without affecting the current never-empty behaviour and without breaking existing code.
Hi Anthony, I would suggest you to have two separated classes, one the existing one and the other that can be used to experiment and move towards whatever the experimental standard variant would become. Note that the proposal is moving a lot and there is not yet enough consensus on its semantic. Best, Vicente

2015-06-26 9:10 GMT+03:00 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr
:
Hi Anthony,
I would suggest you to have two separated classes, one the existing one and the other that can be used to experiment and move towards whatever the experimental standard variant would become. Note that the proposal is moving a lot and there is not yet enough consensus on its semantic.
This seems like an overkill in this particular case. There's an Eggs.Variant that matches proposal better. Totally rewriting Boost.Variant to be close to it seems wrong. Instead I'll focus on adding missing metaprogramming features (tuple_size, tuple_element) and improving free functions (get<Index>(variant), comparisons). Such changes do not require separate class. -- Best regards, Antony Polukhin

Hi Antony, Thanks for stepping up to the plate for being a maintainer of variant. I, and I'm sure many others as well, really appreciate your willingness to take on maintenance! On Fri, Jun 26, 2015 at 12:59 AM, Antony Polukhin <antoshkka@gmail.com> wrote:
2015-06-26 9:10 GMT+03:00 Vicente J. Botet Escriba < vicente.botet@wanadoo.fr
:
I would suggest you to have two separated classes, one the existing one and the other that can be used to experiment and move towards whatever the experimental standard variant would become. Note that the proposal is moving a lot and there is not yet enough consensus on its semantic.
This seems like an overkill in this particular case.
There's an Eggs.Variant that matches proposal better. Totally rewriting Boost.Variant to be close to it seems wrong. Instead I'll focus on adding missing metaprogramming features (tuple_size, tuple_element) and improving free functions (get<Index>(variant), comparisons). Such changes do not require separate class.
I do want to echo Vicente's sentiment though. What you see in N4450 may be nothing like what we get in the standard. Although there was agreement at the Lenexa meeting, everything is being discussed still. We wouldn't want to get into the unfortunate situation where Boost.Variant models an early proposal that ended up being discarded. If that were the case, Boost.Variant would still have to stick with the older proposal in order to maintain backwards compatibility. Also, I think you probably want to look at N4516 which is the latest version of the proposal and was what was accepted by WG21's LEWG ( http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4516.pdf). But, like I said, we seem to be back at the drawing board so I'd be against making any changes to Boost.Variant based on papers at this point. Well, at least on issues that are contentious (like get<index>). -- David Sankel

On 6/27/15 6:09 AM, David Sankel wrote:
Hi Antony,
Thanks for stepping up to the plate for being a maintainer of variant. I, and I'm sure many others as well, really appreciate your willingness to take on maintenance!
+1. I really appreciate what you are doing! Thank you very much! Regards, -- Joel de Guzman http://www.ciere.com http://boost-spirit.com http://www.cycfi.com/

2015-06-26 9:10 GMT+03:00 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr
: Hi Anthony,
I would suggest you to have two separated classes, one the existing one and the other that can be used to experiment and move towards whatever the experimental standard variant would become. Note that the proposal is moving a lot and there is not yet enough consensus on its semantic.
This seems like an overkill in this particular case.
There's an Eggs.Variant that matches proposal better. Totally rewriting Boost.Variant to be close to it seems wrong. I'm not talking of rewriting anything, but of writing :) I have a bad experience adding new functionalities in Boost.Thread. Now I believe that I should had created a new and separated version instead. Boost.Variant is quite big compared to e.g. Eggs.Variant. Making use of
Le 26/06/15 08:59, Antony Polukhin a écrit : the new C++features make it easier and more maintainable. My bad, I expressed my intent incorrectly. I don't think that Boost must follows the experimental proposals. What I believe is that Boost could have some experimental libraries that are signaled as such and that can have an interface that is not fixed yet. I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course. In the Boost.Variant doc there is a section on future directions, about the possibility to have a variant with a policy based design. I'm not sure this is the good way. However I believe that we need several kind of variant types that have different characteristics. We cannot have a variant type that fits all the shoes. I'm sure that there are some application for which the double storage will be the best choice, as there is no heap allocation. Other applications could prefer that the operations that can not be implemented ensuring the strong exception guaranties are just not provided if providing them would mean a lost in performances for the other operations. Others are requesting/suggesting an implicit possibly empty variant. boost::variant behaves almost already like that when the is a boost::blanc type. This possibly empty variant would inherit from some of the optional interfaces. BTW, is boost::variant<T,T> v; well formed? if yes, what does get<T>(v) returns? I don't know the Boost.Variant implementation but I have a possible improvement just from what is documented. I believe that there is yet a possible optimization to Temporary heap backup design, consisting in reusing the unused memory (if big enough). Given variant<Ts...> the storage has size max<alignof(Ts)...>. If we have T stored and we want to assign U and sizeof alignof(T)+alignof(U) <= max<alignof(Ts)...> and U is nothrow move constructible we can use the unused memory as double storage and avoid the heap allocation/deallocation. Is that optimization implemented already?
Instead I'll focus on adding missing metaprogramming features (tuple_size, tuple_element) and improving free functions (get<Index>(variant), comparisons). Such changes do not require separate class.
I don't see any conflicts on current discussions index access. Other functions that could help if you introduce the index access are the emplace functions. However I don't see why the proposal is specializing tuple_size, tuple_element. I will prefer that variant provide a way to get the meta tuple of its alternatives, so that you can get its size and the element type. A meta function to create a variant type from a meta tuple could be useful. Best, Vicente

On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course.
Vicente, while this is really flattering, I feel compelled to clarify a few things. First of, as I have already said before, Eggs.Variant is an experiment. As such it is highly unstable, and I reserve the right to change things in any way I see fit, with no regards for backwards compatibility, maintenance, or support. I have made these kinds of changes in the past, and I have more planned for the near future. I think it would be unwise to make it a part of Boost, even as an experimental library, until the design has fully hatched ("Eggs", get it?). Second, Eggs.Variant is not tracking the standard proposal, which it predates, nor it looks like it's going to converge with it. For instance, the visible empty state is not going anywhere. As a fundamental building block, I cannot afford to pay the cost of double buffering, heap allocation, restrictions to nothrow-move-constructible alternative types, etc. The abstractions I built on top of it might, since they are the ones who attach meaning to the variant, but it is up to them to make those choices. Likewise, I'm not considering implementing support for void and reference types as alternatives. To sum it up, Eggs.Variant is its own thing, but it is way too young for me to know what that thing is yet. Regards, -- Agustín K-ballo Bergé.- http://talesofcpp.fusionfenix.com

On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course.
Vicente, while this is really flattering, I feel compelled to clarify a few things.
First of, as I have already said before, Eggs.Variant is an experiment. As such it is highly unstable, and I reserve the right to change things in any way I see fit, with no regards for backwards compatibility, maintenance, or support. I have made these kinds of changes in the past, and I have more planned for the near future. I think it would be unwise to make it a part of Boost, even as an experimental library, until the design has fully hatched ("Eggs", get it?). Agustin, this is exactly the kind of constraints of an experimental
Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : library. When the design is fully hatched then it moves to non experimental.
Second, Eggs.Variant is not tracking the standard proposal, which it predates, nor it looks like it's going to converge with it.
I know that, and this is why I want libraries as yours to be part of Boost[.Experimental].
For instance, the visible empty state is not going anywhere. As a fundamental building block, I cannot afford to pay the cost of double buffering, heap allocation, restrictions to nothrow-move-constructible alternative types, etc. I know that your variant is possibly empty and the C++ standard proposal is never-empty, and this makes them different from the user point of view. Bjarne S. and Anthony W. are pushing towards a possible empty variant, we don't know yet what the std::experimental::variant will be and less yet what std::variant will be in C++17.
boost::variant combined with boost::blanc gives this kind of possibly empty variant, but IMHO this is a quite different type. template <class ...Ts> using optional_variant = boost::variant<boost::blanc, Ts...>; // +/- I suggest to name them as variant<Ts...> : never-empty optionals<Ts...> : possibly empty I believe that the usage of these classes is quite different. There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C). While I understand the efficiency trade-offs, this difference in behavior merits IMHO a different name (well if we have several variants ;-). The current C++ proposal design has a hidden error state that IMO is viral and should be fixed. The problem is that with the current language there is no know implementation without using extra memory (as boost::variant does). I would like to see the language modified so that the boost::variant original implementation [1] is well-formed. This implementation would still be less efficient than an possible empty variant that mix empty and error, but the guaranties would be different.
The abstractions I built on top of it might, since they are the ones who attach meaning to the variant, but it is up to them to make those choices. Likewise, I'm not considering implementing support for void and reference types as alternatives. Could you tell us more about the rationale? it is because some wrappers as void_t and reference_wrapper<T> can be used instead?
BTW, how eggs::variant<T,T> behaves?
To sum it up, Eggs.Variant is its own thing, but it is way too young for me to know what that thing is yet.
Regards,
No problem, I'm not requesting you to submit your library to Boost. It is just an example of the kind of experimental libraries I would like to see in Boost.[Experimental]. I believe that we need more experimental and basic libraries as yours in Boost. Best, Vicente [1] http://live.boost.org/doc/libs/1_58_0/doc/html/variant/design.html#variant.d...

On 28 Jun 2015 at 10:38, Vicente J. Botet Escriba wrote:
There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C). While I understand the efficiency trade-offs, this difference in behavior merits IMHO a different name (well if we have several variants ;-).
You can see then why I deliberately made my lightweight monad & future fixed variant. It makes this issue (which was talked about frequently at C++ Now) go away. BTW for those interested lightweight future-promise will be a minimum of 2x faster than STL future-promise as I got the first benchmarks late last night. I think 50 CPU cycle promise => future overhead might just be doable.
The current C++ proposal design has a hidden error state that IMO is viral and should be fixed. The problem is that with the current language there is no know implementation without using extra memory (as boost::variant does). I would like to see the language modified so that the boost::variant original implementation [1] is well-formed. This implementation would still be less efficient than an possible empty variant that mix empty and error, but the guaranties would be different.
I think adding ternary logic support to the language would make solving this sort of problem much more elegant. Charley Bay did a good talk "Your CPU is Binary" at C++ Now which convinced me to add tribool support to lightweight monad + future such that: Empty => False Value => True Errored/Excepted => Indeterminate Ternary logic then has the truth tables from https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_log ics. The extension to the C++ language, if I understood what was proposed by Charley, is minimal: variant<...> v; tribool t(v); // from explicit operator tribool() if(t) { /* has value */ } else if(!t) { /* is empty */ } else { /* is errored */ } // Failure to provide this branch is a compiler error In other words, the only relaxation of the language spec is that: 1. Logic operators permit tribool in the standard. 2. Where choosing operator tribool and operator bool are ambiguous, we always choose operator tribool. (3. I'd personally like if strongly typed enums permitted constexpr only member functions such as operator tribool(), see below). I have a constexpr tribool for C++ 11 at https://github.com/ned14/boost.spinlock/blob/master/include/boost/spin lock/tribool.hpp for anyone interested. Its most annoying feature is requiring users to use the free functions: true_(tribool) false_(tribool) unknown(tribool) ... because enum class doesn't allow constexpr member functions :(
To sum it up, Eggs.Variant is its own thing, but it is way too young for me to know what that thing is yet.
Regards, No problem, I'm not requesting you to submit your library to Boost. It is just an example of the kind of experimental libraries I would like to see in Boost.[Experimental].
I believe that we need more experimental and basic libraries as yours in Boost.
+100. Far too much interesting C++ isn't coming to Boost any more. We need Boost.Sandbox back, and Vicente's Boost.Experimental looks like a great replacement. To get in a library merely needs to look a bit like a Boost library e.g. naming conventions. Other than that, it's open season. If such a thing were set up, can we have a Google Summer of Code section in it for student projects please? Right now they get their own github org for each year, but I think the students would just love if their GSoC was closer to Boost. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

Le 28/06/15 15:15, Niall Douglas a écrit :
On 28 Jun 2015 at 10:38, Vicente J. Botet Escriba wrote:
The current C++ proposal design has a hidden error state that IMO is viral and should be fixed. The problem is that with the current language there is no know implementation without using extra memory (as boost::variant does). I would like to see the language modified so that the boost::variant original implementation [1] is well-formed. This implementation would still be less efficient than an possible empty variant that mix empty and error, but the guaranties would be different. I think adding ternary logic support to the language would make solving this sort of problem much more elegant. Charley Bay did a good talk "Your CPU is Binary" at C++ Now which convinced me to add tribool support to lightweight monad + future such that:
Empty => False Value => True Errored/Excepted => Indeterminate
Ternary logic then has the truth tables from https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_log ics.
The extension to the C++ language, if I understood what was proposed by Charley, is minimal:
variant<...> v; tribool t(v); // from explicit operator tribool() if(t) { /* has value */ } else if(!t) { /* is empty */ } else { /* is errored */ } // Failure to provide this branch is a compiler error Sorry Nial, but I don't want to program like that.
Vicente

On 28 Jun 2015 at 18:03, Bjorn Reese wrote:
Empty => False Value => True Errored/Excepted => Indeterminate
It seems more intuitive to me if Empty returns Indeterminate and Errored returns False.
In a Kleene logic, false < unknown < true for AND, so for variant if you wanted error && empty to equal empty, you'd choose the scheme above. If you wanted error && empty to equal error, you'd choose your scheme. For lightweight monad and future, I've gone for empty being dominant. This was not an easy choice to make, and I am still unsure if it is correct. For variant it's that much harder again. I am currently totally undecided personally on that one. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

<vicente.botet@wanadoo.fr> wrote:
Le 28/06/15 15:15, Niall Douglas a écrit :
On 28 Jun 2015 at 10:38, Vicente J. Botet Escriba wrote:
The current C++ proposal design has a hidden error state that IMO is
viral and should be fixed. The problem is that with the current language there is no know implementation without using extra memory (as boost::variant does). I would like to see the language modified so that the boost::variant original implementation [1] is well-formed. This implementation would still be less efficient than an possible empty variant that mix empty and error, but the guaranties would be different.
I think adding ternary logic support to the language would make solving this sort of problem much more elegant. Charley Bay did a good talk "Your CPU is Binary" at C++ Now which convinced me to add tribool support to lightweight monad + future such that:
Empty => False Value => True Errored/Excepted => Indeterminate
Ternary logic then has the truth tables from https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_log ics.
The extension to the C++ language, if I understood what was proposed by Charley, is minimal:
variant<...> v; tribool t(v); // from explicit operator tribool() if(t) { /* has value */ } else if(!t) { /* is empty */ } else { /* is errored */ } // Failure to provide this branch is a compiler error
My proposed language change would be something like: if(...) { ... } else { ... } unknown { ... } ...Where the "trailing-unknown" clause would be a non-breaking C++ language addition. It implies "true" and "false" are different from "unknown". As an accident, this happens to behave similarly to a "try/catch" clause when you have extended if/else chains (because an "unknown" never navigates into an "if/else", but can only navigate into an "unknown"). On Sun, Jun 28, 2015 at 7:38 AM, Vicente J. Botet Escriba
Sorry Nial, but I don't want to program like that.
Fair point. It's different. The assertion is that our code is littered with "false-choices" and badly-handled ambiguities. The assertion is that Boole's conclusions are based largely on false assumptions that manifest in unfortunate ways when constructing real-world systems. Under an alternative model, the common construct would be something like: void foo(...) { return switch_(...) { ... } } ... and you manage your true/false/unknown cases explicitly. Works great for asynchronous and distributed, and for interfacing-with-hardware. --charley

On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Under an alternative model, the common construct would be something like:
void foo(...) { return switch_(...) { ... } }
... and you manage your true/false/unknown cases explicitly. Works great for asynchronous and distributed, and for interfacing-with-hardware.
That's exactly what Rust does. In fact, you'll find yourself either writing match x { patterns ... } or using monadic programming. A lot, because there is little other choice. Out of interest, what do you think of my free function ternary logic programming: tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ } Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On Wed, Jul 1, 2015 at 8:49 PM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Under an alternative model, the common construct would be something like:
void foo(...) { return switch_(...) { ... } }
... and you manage your true/false/unknown cases explicitly. Works great for asynchronous and distributed, and for interfacing-with-hardware.
That's exactly what Rust does. In fact, you'll find yourself either writing match x { patterns ... } or using monadic programming. A lot, because there is little other choice.
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
What if you combined a c++11 enum class with an implicit conversion operator? The implicit conversion can take place in a switch statement, but not if() or int() expressions since the enum class is not implicitly convertible to those types. enum class status { complete, error, waiting }; class niall_monad { public: constexpr niall_monad(const niall_monad&) = default; constexpr niall_monad(status result) : result_(result) {} constexpr operator status() const { return result_; } private: status result_; }; constexpr bool is_complete(const niall_monad value) { switch(value) { case status::complete: return true; default: case status::error: case status::waiting: return false; } } int main() { static_assert(!is_complete(niall_monad(status::waiting)), ""); static_assert(!is_complete(niall_monad(status::error)), ""); static_assert(is_complete(niall_monad(status::complete)), ""); if (niall_monad(status::complete)) { // fails return 1; } int foo = niall_monad(status::complete); // fails return 0; } With errors: niall.cpp:33:7: error: no viable conversion from 'niall_monad' to 'bool' if (niall_monad(status::complete)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ niall.cpp:9:13: note: candidate function constexpr operator status() const { ^ niall.cpp:37:7: error: no viable conversion from 'niall_monad' to 'int' int foo = niall_monad(status::complete); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ niall.cpp:9:13: note: candidate function constexpr operator status() const { ^ 2 errors generated. Remove the bad conversions, and you've got a compiled program. Are there other situations that an enum class can implicit convert to something else? A static case will force things obviously. I'd have to read the specs to know. Lee

On 2/07/2015 12:49, Niall Douglas wrote:
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
FWIW, when working with boost::tribool most code that I've seen/written deals with it this way: if (t == true) { /* true */ } else if (t == false) { /* false */ } else { /* indeterminate */ } which is basically the same as yours. Although explicit comparison to true/false tends to irritate many people (for good reasons, when only bool is involved). In some cases this pattern is used, though, which seems to annoy fewer people: if (boost::indeterminate(t)) { /* indeterminate */ } else { /* true/false -- can be passed to bool api or used if (t) */ }

On Wed, Jul 1, 2015 at 6:49 PM, Niall Douglas
On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Under an alternative model, the common construct would be something like:
void foo(...) { return switch_(...) { ... } }
... and you manage your true/false/unknown cases explicitly. Works great for asynchronous and distributed, and for interfacing-with-hardware.
That's exactly what Rust does. In fact, you'll find yourself either writing match x { patterns ... } or using monadic programming. A lot, because there is little other choice.
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
I'm uncertain it's possible to reform if/else (a boolean construct) to do ternary or MVL things. Or more likely, I'm uncertain it's possible to reform programmers that are trained in if/else as a binary construct. It's a tricky problem. I think your syntax is the best we can do with if/else, and the boost::indeterminate() similarly "inverts" the separation of "unknown" from the "true/false" values. So, after having looked hard for alternatives, I actually like your syntax (because I have a hard time finding a more obvious way to do that with if/else). You made a comment in a previous email that your "tribool" approach was "paranoid" in addressing "unknown" -- YES, TOTALLY AGREE! That's the only way to do this, or you are totally screwed with the original problem (of a false assumption expressed through mistaken logic that was not caught by the compiler). But, I also agree with other comments in this thread that it annoys most programmers to demand explicit-comparison-testing-with-bool. We expect implicit-bool-conversion, and frankly, it is quite awkward to do a three-way-division using a boolean if/else construct. IMHO, this is the key problem: if/else is just not very composable. Like the classic book, "The Art of Software Testing" discusses, refactoring if/else is amazingly tough (and branch analysis is a combinatorial explosion). It's hard to express and reason about, and refactoring usually means a rewrite. We can set up an "if/else-ladder" to otherwise do what a switch would do, but in the end, switch ends up being more clear-and-expressive. Easier to support and extend. I *really* like that you are pursuing it, though. Like pornography, we should mostly be able to recognize elegance and expressiveness when somebody demonstrates an elegant way to do it. --charley

On Wed, Jul 1, 2015 at 11:07 PM charleyb123 . <charleyb123@gmail.com> wrote:
On Wed, Jul 1, 2015 at 6:49 PM, Niall Douglas
On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Under an alternative model, the common construct would be something like:
void foo(...) { return switch_(...) { ... } }
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
So, after having looked hard for alternatives, I actually like your syntax (because I have a hard time finding a more obvious way to do that with if/else).
IMHO, this is the key problem: if/else is just not very composable. Like the classic book, "The Art of Software Testing" discusses, refactoring if/else is amazingly tough (and branch analysis is a combinatorial explosion). It's hard to express and reason about, and refactoring usually means a rewrite.
We can set up an "if/else-ladder" to otherwise do what a switch would do, but in the end, switch ends up being more clear-and-expressive. Easier to support and extend.
(Sorry so many snips, but I wanted to focus on something specific). It probably isn't elegant, but maybe we could explore something like: tribool t; boost::if_( t, [&]( /*true condition's code*/), [&](/*false conditions code*/), [&](/* indeterminate conditions code*/) ); Although there has to be a better way to clean it up... maybe with tagging the arguments or something. - Trey

On 2 Jul 2015 at 9:36, Joseph Van Riper wrote:
(Sorry so many snips, but I wanted to focus on something specific).
It probably isn't elegant, but maybe we could explore something like:
tribool t;
boost::if_( t, [&]( /*true condition's code*/), [&](/*false conditions code*/), [&](/* indeterminate conditions code*/) );
Although there has to be a better way to clean it up... maybe with tagging the arguments or something.
Lightweight monad already has that: monad<std::string> a("niall"); // Does bind work with chains of value, error, exception and empty? auto x( a >> [ec](std::string){return ec;} >> [](std::error_code){return std::make_exception_ptr(5);} >> [](std::exception_ptr){return;} >> [](monad<std::string>::empty_type){return std::string("douglas");} ); BOOST_CHECK(x.get()=="douglas"); // Does monad type permutation from bind work? auto y( a >> [ec](std::string) -> monad<int> {return ec;} >> [](std::error_code){return std::make_exception_ptr(5);} >> [](std::exception_ptr){return;} >> [](monad<int>::empty_type){return 5;} ); BOOST_CHECK(y.get()==5); Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 1 Jul 2015 at 21:07, charleyb123 . wrote:
So, after having looked hard for alternatives, I actually like your syntax (because I have a hard time finding a more obvious way to do that with if/else).
I ended up a similar place of not being able to think of anything better than meets the criteria.
But, I also agree with other comments in this thread that it annoys most programmers to demand explicit-comparison-testing-with-bool. We expect implicit-bool-conversion, and frankly, it is quite awkward to do a three-way-division using a boolean if/else construct.
Ah, I suppose I didn't mention my solution to that yet. My tribool won't convert implicitly to bool, but anything converting to a tribool also converts to a bool. So, for monad<T>: //! \brief Same as `true_(tribool(*this))` constexpr explicit operator bool() const noexcept; //! \brief True if monad contains a value_type, false if monad is empty, else unknown. constexpr operator tribool::tribool() const noexcept; So, if we have: monad<int> a; We can do via the explicit bool conversion: if(a) { execute if a has value } if(!a) { execute if a does not have value i.e. is empty or errored/excepted } This is natural to program against. But if we want the ternary logic we can also do: if(true_(a)) { execute if a has value } if(false_(a)) { execute if a is empty } if(unknown(a)) { execute if a is errored/excepted } This is another appeal of the free function based ternary logic design. You push the implicit bool conversion onto the thing with tribool state. The library user gets both boolean and ternary idioms according to their wont. Does this appeal to the list more? I realise I didn't explain myself well. As usual.
I *really* like that you are pursuing it, though. Like pornography, we should mostly be able to recognize elegance and expressiveness when somebody demonstrates an elegant way to do it.
I have noticed that extremely few find my code nor design elegant nor expressive :) We'll see during the AFIO review in two weeks. My main goal between now and then is to use these monadic futures to make the AFIO API much more conventional than at present, so I'm hoping to hammer down a whole load of unexpected rough edges for the conventional Boost community member. I'm hoping that will be enough to get it passed review and into Boost. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 7/1/2015 7:49 PM, Niall Douglas wrote:
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
I'm jumping in in the middle of this thread, but are you purposefully naming the value--true, false, unknown--to make the programmer more aware of the ternary logic? Otherwise, one could do the following, which boost::tribool supports (I'm probably stating the obvious; please forgive me): tribool t; if (t) { /* true */ } else if (!t) { /* false */ } else { /* unknown */ } This reminds me of the original three-way IF statement in FORTRAN, except it was an arithmetic test for less-than, equal-to, and greater-than zero. Oh, and it was sort of a computed goto, so there's that. This is what it looked (looks?) like: IF (N) LTLABEL, EQLABEL, GTLABEL I suppose the literal translation to ternary logic and C++ would be something like this: if (t) true_statement; else false_statement; else unknown_statement; Yes, two else's! One would have to be careful with the curly braces, as always, but I don't think this would be ambiguous if added to the grammar. Or how about introducing a new keyword, such as "otherwise?" That should be enough to tip off a programming that they're not in Kansas anymore: if (t) true_statement; else false_statement; otherwise unknown_statement; And how about a quaternary operator while we're at it? t ? true_expression : false_expression : unknown_expression Paul

2015-07-02 6:23 GMT+02:00 Paul Long <plong@packetizer.com>:
On 7/1/2015 7:49 PM, Niall Douglas wrote:
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
I'm jumping in in the middle of this thread, but are you purposefully naming the value--true, false, unknown--to make the programmer more aware of the ternary logic? Otherwise, one could do the following, which boost::tribool supports (I'm probably stating the obvious; please forgive me):
tribool t; if (t) { /* true */ } else if (!t) { /* false */ } else { /* unknown */ }
This reminds me of the original three-way IF statement in FORTRAN, except it was an arithmetic test for less-than, equal-to, and greater-than zero. Oh, and it was sort of a computed goto, so there's that. This is what it looked (looks?) like:
IF (N) LTLABEL, EQLABEL, GTLABEL
I suppose the literal translation to ternary logic and C++ would be something like this:
if (t) true_statement; else false_statement; else unknown_statement;
Yes, two else's! One would have to be careful with the curly braces, as always, but I don't think this would be ambiguous if added to the grammar. Or how about introducing a new keyword, such as "otherwise?" That should be enough to tip off a programming that they're not in Kansas anymore:
if (t) true_statement; else false_statement; otherwise unknown_statement;
And how about a quaternary operator while we're at it?
t ? true_expression : false_expression : unknown_expression
Is it not what the switch statement is for? Regards, &rzej

2015-07-02 2:49 GMT+02:00 Niall Douglas <s_sourceforge@nedprod.com>:
On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Under an alternative model, the common construct would be something like:
void foo(...) { return switch_(...) { ... } }
... and you manage your true/false/unknown cases explicitly. Works great for asynchronous and distributed, and for interfacing-with-hardware.
That's exactly what Rust does. In fact, you'll find yourself either writing match x { patterns ... } or using monadic programming. A lot, because there is little other choice.
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
Hi Niall, Because I cannot see the direct connection between the three-state monad you are exploring and a ternary logic (like in Boost.Tribool), I might be missing something obvious, but why do you insist on calling the states "true" and "false"? I know why Boost.Tribool does it: because it provides operators || and && that intuitively implement "true", "false" and "I do not know" concepts. In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error". Am I missing something? regards, &rzej

On 2 Jul 2015 at 10:40, Andrzej Krzemienski wrote:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Nobody here seemed to like it. I am looking for something the average programmer will notice immediately and not accidentally assume it's boolen logic going on here.
Because I cannot see the direct connection between the three-state monad you are exploring and a ternary logic (like in Boost.Tribool), I might be missing something obvious, but why do you insist on calling the states "true" and "false"? I know why Boost.Tribool does it: because it provides operators || and && that intuitively implement "true", "false" and "I do not know" concepts.
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley. There are also accessor member functions on monad, so empty()/has_value()/has_error()/has_exception(). And the continuation and bind operators can also select on state. You can also simply do monad<T>.get() wrapped with a try...catch block, just as with future. I've given four "styles" of doing logic from a monadic state. Each has its pros and cons for a given situation, generally the higher order functions like bind are lovely to use but impact build times, which is why those are disabled by default and only enabled via a compile time macro. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On July 2, 2015 11:16:31 AM Niall Douglas wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley.
I didn't see this question addressed.
There are also accessor member functions on monad, so empty()/has_value()/has_error()/has_exception().
So that should be enough, then? You could write the logic in clear terms: if (monad.has_value()) { .. } else if (monad.has_error()) { ... } ... Why even bring up "tribool" at all in this context? It seems like an invitation for people to make buggy / unclear code. -Steve

On 2 Jul 2015 at 8:31, Steve M. Robbins wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley.
I didn't see this question addressed.
A monad has an expected and an unexpected outcome. That naturally maps to true and false. My mongel monads can also be empty. That implies a ternary state. No one can disagree that the expected state ought to map to true. A valid disagreement is whether empty or unexpected should be false or indeterminate. I chose the errored/excepted state to be the indeterminate, because I felt that null and value_type states are exactly what they are, but an error_code and especially an exception_ptr is quite literally unknown until you interrogate/rethrow it.
There are also accessor member functions on monad, so empty()/has_value()/has_error()/has_exception().
So that should be enough, then? You could write the logic in clear terms:
if (monad.has_value()) { .. } else if (monad.has_error()) { ... } ...
Or: if(monad==tribool::true) {} else if(monad==tribool::false) {} else if(monad==tribool::unknown) {} or even, as Charley mentioned: switch(tribool(monad)) { case tribool::true: case tribool::false: ... }
Why even bring up "tribool" at all in this context? It seems like an invitation for people to make buggy / unclear code.
Maybe. It's why I've been asking here. As I mentioned, I can see a use case for this. If you watch Charley's talk from C++ Now, I found it compelling for its niche use case. I'll know the practical experience in a few weeks from now as I integrate these into AFIO and get some practice with constructing ternary AND/OR sequences from them. If I find them problematic, I'll remove tribool support completely. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On Thu, Jul 2, 2015 at 1:28 PM, Niall Douglas <s_sourceforge@nedprod.com> wrote:
On 2 Jul 2015 at 8:31, Steve M. Robbins wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley.
I didn't see this question addressed.
A monad has an expected and an unexpected outcome. That naturally maps to true and false.
My mongel monads can also be empty. That implies a ternary state. No one can disagree that the expected state ought to map to true. A valid disagreement is whether empty or unexpected should be false or indeterminate.
I chose the errored/excepted state to be the indeterminate, because I felt that null and value_type states are exactly what they are, but an error_code and especially an exception_ptr is quite literally unknown until you interrogate/rethrow it.
Just my 2 cents, but from that description, I'd map empty to unknown and failure to false. Empty means I haven't done it yet, I'll let you know when I know. Currently it is unknown. Success means yes I've done it. TRUE! Failure means no I couldn't. FALSE. But that's for something like expected<T,E>. For optional<T>, empty currently maps to false, at least in boost::optional and std::experimental::optional. In a functional language with "Maybe T", empty really does map better to unknown - I do not yet know the value of T. We force it into boolean because that's what we are comfortable with. Hmmm, optional<bool> would have less problems if empty mapped to unknown instead of false. Tony

<snip>,
But that's for something like expected<T,E>. For optional<T>, empty
currently maps to false, at least in boost::optional and std::experimental::optional. In a functional language with "Maybe T", empty really does map better to unknown - I do not yet know the value of T. We force it into boolean because that's what we are comfortable with.
Hmmm, optional<bool> would have less problems if empty mapped to unknown instead of false.
Tony
Bingo. bool forces us into false dilemmas. It's even worse when the "forced-conversion" could legitimately be either "true" or "false" for different reasons, and for different scenarios. It's even worse when this is done silently. The result is un-handled (and hidden) corner-cases, bad logic, bad reasoning, and ultimately bad APIs. You need "one-more-dimension" to talk about the dimension-below, so by this reasoning, only ternary (and above) can be self-reflective. (Consistent and logical.) Simply stated: The most atomic level of real-world reasoning is a true/false bit with an attached "property" of "unknown". No smaller atom exists. (Coercing this atom to "true/false" from "true/false/unknown" is asserted to be "error-injection" into the system, which has no legitimate place in logic.) Boole's book, "The Laws of Thought" is based on assumptions that are constantly violated in real-world systems. When you place those very-clearly-stated assumptions underlying bivalent logic on the wall, the impossibility of reasoning in boolean becomes obvious. </rant> ;-)) --charley

On 2 Jul 2015 at 13:38, Gottlob Frege wrote:
A monad has an expected and an unexpected outcome. That naturally maps to true and false.
My mongel monads can also be empty. That implies a ternary state. No one can disagree that the expected state ought to map to true. A valid disagreement is whether empty or unexpected should be false or indeterminate.
I chose the errored/excepted state to be the indeterminate, because I felt that null and value_type states are exactly what they are, but an error_code and especially an exception_ptr is quite literally unknown until you interrogate/rethrow it.
Just my 2 cents, but from that description, I'd map empty to unknown and failure to false. Empty means I haven't done it yet, I'll let you know when I know. Currently it is unknown. Success means yes I've done it. TRUE! Failure means no I couldn't. FALSE.
Alright, that's like four people now with the same opinion. I'll change it. BTW option<T> will now have the tribool states of true and unknown only. For the boolean conversion, that maps to true and false, so if(!option) handle_empty; works. Thanks for the feedback everyone. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On July 2, 2015 06:28:58 PM Niall Douglas wrote:
On 2 Jul 2015 at 8:31, Steve M. Robbins wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley.
I didn't see this question addressed.
A monad has an expected and an unexpected outcome. That naturally maps to true and false.
My mongel monads can also be empty. [...]
Sorry: the question is not "why does it have three states?" That is clear. The question is "why do you call two of those states 'true' and 'false'?" I don't personally find the mapping "natural".
Why even bring up "tribool" at all in this context? It seems like an invitation for people to make buggy / unclear code.
Maybe. It's why I've been asking here. As I mentioned, I can see a use case for this. If you watch Charley's talk from C++ Now, I found it compelling for its niche use case.
I confess that I haven't been paying close attention to this thread, so I've missed it. What is the use case that you can't use the more-explicit has_value()/has_error()/etc?
I'll know the practical experience in a few weeks from now as I integrate these into AFIO and get some practice with constructing ternary AND/OR sequences from them. If I find them problematic, I'll remove tribool support completely.
Not that I dare suggest what you do with your time, but: why not write it first with the more clear has_value()/has_error()/etc and introduce the tribool only if you find that problematic? -Steve

2015-07-02 20:09 GMT+02:00 Steve M. Robbins <steve@sumost.ca>:
On July 2, 2015 06:28:58 PM Niall Douglas wrote:
On 2 Jul 2015 at 8:31, Steve M. Robbins wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something?
Hopefully I explained it in my reply to Charley.
I didn't see this question addressed.
A monad has an expected and an unexpected outcome. That naturally maps to true and false.
My mongel monads can also be empty. [...]
Sorry: the question is not "why does it have three states?" That is clear. The question is "why do you call two of those states 'true' and 'false'?" I don't personally find the mapping "natural".
Exactly. A (non-mongel) monad has two states. A Boolean type also has two states, but so does anything else that has two states, e.g. enum {Up, Down}; or enum {Male, Female}; The reason people choose to use Boolean values is because at some point they need Boolean arithmetic. Unless I am missing something, you need not exercise the Boolean algebra on the state of the result of the asynchronous operation. It seams that the conceptual mapping of the state of the monad onto a Boolean type brings no value to the programmers that will be launching asynchronous tasks. A mongel monad has three states. But there are many things out there that have three states, and it does not mean they have to be mapped on a tribool type. For instance, a file can be empty or can contain some data or be non-existent. But why would I ever think of mapping it onto a tribool?
Why even bring up "tribool" at all in this context? It seems like an invitation for people to make buggy / unclear code.
Maybe. It's why I've been asking here. As I mentioned, I can see a use case for this. If you watch Charley's talk from C++ Now, I found it compelling for its niche use case.
I confess that I haven't been paying close attention to this thread, so I've missed it. What is the use case that you can't use the more-explicit has_value()/has_error()/etc?
I'll know the practical experience in a few weeks from now as I integrate these into AFIO and get some practice with constructing ternary AND/OR sequences from them. If I find them problematic, I'll remove tribool support completely.
Maybe that will explain it. Regards, &rzej

On 3/07/2015 08:54, Andrzej Krzemienski wrote:
A mongel monad has three states. But there are many things out there that have three states, and it does not mean they have to be mapped on a tribool type. For instance, a file can be empty or can contain some data or be non-existent. But why would I ever think of mapping it onto a tribool?
This instantly reminded me of this: http://thedailywtf.com/articles/What_Is_Truth_0x3f_
The reason people choose to use Boolean values is because at some point they need Boolean arithmetic. Unless I am missing something, you need not exercise the Boolean algebra on the state of the result of the asynchronous operation. It seams that the conceptual mapping of the state of the monad onto a Boolean type brings no value to the programmers that will be launching asynchronous tasks.
I can see some logic in the mapping {has_value() => true, has_error/exception() => false, !ready() => indeterminate}. However from the sounds of it this is not the mapping that is in the current code (I haven't looked too closely). I can also see some reasonable argument for {ready() => true, !ready() => false}, with no third possibility. (In terms of boolean arithmetic, both of the above are still potentially useful if eg. a bit of code wants to test if three futures are all ready [without waiting for them] it could use a && b && c... but the brevity could be confusing if you don't know *which* of those mappings were used.) For the avoidance of confusion, I'm inclined to agree with Andrzej, that at least initially it's probably better not to attempt to provide any sort of bool/tribool implicit mapping, and force the user to be explicit about what question they're asking. Such things can be added later if it turns out that there is a clear consensus on what it should actually mean.

On 2 Jul 2015 at 13:09, Steve M. Robbins wrote:
I confess that I haven't been paying close attention to this thread, so I've missed it. What is the use case that you can't use the more-explicit has_value()/has_error()/etc?
If you want to program as if working with futures, you have a Boost.Thread compatible API. If you want to program monadically, you have your bind and map operator overloads. If you want to program with ternary logic, you can write using the & and | operator overloads. If you want to lazy execute, as this is a natural thing to want to do with futures, there is a lazy functional execution API in there too. These aren't simply aliased functions for the same thing. They are all different ways of working with a fixed variant monadic state, so the styles aren't commensurate. The programming style you choose depends on your use case, and your personal preference, at the time. There are also consequences on build time. Using a functional programming style is much harder on the compiler than the future API or the ternary logic. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

2015-07-02 14:28 GMT-03:00 Niall Douglas <s_sourceforge@nedprod.com>:
I chose the errored/excepted state to be the indeterminate, because I felt that null and value_type states are exactly what they are, but an error_code and especially an exception_ptr is quite literally unknown until you interrogate/rethrow it.
Let's look like there is not any support for async code at all. You always know whether it's T or error_code/exception. There was a mention to "impurism" in the thread earlier, but I got lost. If it's "pure" and "sync-only", then you know false will map to error. The behaviour shouldn't change when you add code to also support async code. Somewhere in the thread, there were mentions to "this is not async, future is the one that will be async". But I got lost on this one too. Too much motivation spread around. -- Vinícius dos Santos Oliveira https://about.me/vinipsmaker

On 2 Jul 2015 at 15:36, Vinícius dos Santos Oliveira wrote:
Let's look like there is not any support for async code at all. You always know whether it's T or error_code/exception. There was a mention to "impurism" in the thread earlier, but I got lost.
If it's "pure" and "sync-only", then you know false will map to error. The behaviour shouldn't change when you add code to also support async code.
Somewhere in the thread, there were mentions to "this is not async, future is the one that will be async". But I got lost on this one too. Too much motivation spread around.
The inheritance diagram is very easy: future<T> => monad<T> future_result<T> => result<T> future_option<T> => option<T> It's protected inheritance, so only some not all APIs from the base classes on the right hand side are made available in the child class on the left hand side. For example, you can't do future<T>.set_value(). You can however construct a future directly with a value/error/exception to make an already ready future. This is an alternative to make_ready_future() etc. and in fact make_ready_future() simply calls the future constructor with the value. Each of the future types has a corresponding promise type, so: future<T> <=> promise<T> future_result<T> <=> promise_result<T> future_option<T> <=> promise_option<T> Does this make sense? Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

2015-07-02 23:37 GMT-03:00 Niall Douglas <s_sourceforge@nedprod.com>:
Does this make sense?
Yes, thanks for summarizing. I'm starting to like this abstraction. I'll let you know if I get any doubts about its design. Now you just need to address Vicente's concern about the monad name. Maybe rename result to light_result and monad to result. -- Vinícius dos Santos Oliveira https://about.me/vinipsmaker

Le 02/07/15 19:28, Niall Douglas a écrit :
On 2 Jul 2015 at 8:31, Steve M. Robbins wrote:
In your monad application, I have never seen operators || or && mentioned, so there seems to be no reason to adapt names "true" and "false" for the three states. You might as well call them "A", "B" or "C", or to more closely reflect your application: "value", "failure", "internal_error".
Am I missing something? Hopefully I explained it in my reply to Charley. I didn't see this question addressed. A monad has an expected and an unexpected outcome. That naturally maps to true and false.
A monad is something different and more general than what you describe. Anyway the logic you describe will be closer to a specific kind of monads, the monad error.
My mongel monads can also be empty. That implies a ternary state. No one can disagree that the expected state ought to map to true. A valid disagreement is whether empty or unexpected should be false or indeterminate.
In Haskel there is what they name Monad Transformers. Monad Transformers are in some way concrete classes that are a model of Monad that 'wraps' another Monad. You have two levels of Monads. This give a 3-logic state. You can take a look at this introduction to Monad transformer [1]. How to implement monad transformers efficiently in C++ is another question, but having in mind that your 3-logic state can be seen as a Monad Transformer could help to make the good abstractions.
I chose the errored/excepted state to be the indeterminate, because I felt that null and value_type states are exactly what they are, but an error_code and especially an exception_ptr is quite literally unknown until you interrogate/rethrow it.
HTH, Vicente [1] https://github.com/kqr/gists/blob/master/articles/gentle-introduction-monad-...

On 02-07-2015 02:49, Niall Douglas wrote:
On 1 Jul 2015 at 15:53, charleyb123 . wrote:
Out of interest, what do you think of my free function ternary logic programming:
tribool t; if(true_(t)) { /* true */ } else if(false_(t)) { /* false */ } else if(unknown(t)) { /* other state */ }
Is the last "if" really needed vs. just an "else"? -Thorsten

On 07/02/2015 02:49 AM, Niall Douglas wrote:
Out of interest, what do you think of my free function ternary logic programming:
So far, the discussion has focussed on how the ternary operators should behave in if-statements. In that case, I tend to agree with Lee's enum and switch suggestion, which also extends well into multi-variate logic. If we want to use the ternary logic in if-statements, then we should address composite conditions. For instance, what should the following evaluate to? if (Empty && Value) {} if (Empty && Error) {} if (Empty && Empty) {} The question really boils done to what influence Empty has. Do we want it to influence the results, or should it act like a "don't care" value? PS: Niall, I know that you are currently using Kleene logic, but I think that it is worthwhile to consider the use-cases before selecting the truth tables.

I believe boost::tribool addresses exactly this point. - Rob. On 3 July 2015 at 10:45, Bjorn Reese <breese@mail1.stofanet.dk> wrote:
On 07/02/2015 02:49 AM, Niall Douglas wrote:
Out of interest, what do you think of my free function ternary logic
programming:
So far, the discussion has focussed on how the ternary operators should behave in if-statements. In that case, I tend to agree with Lee's enum and switch suggestion, which also extends well into multi-variate logic.
If we want to use the ternary logic in if-statements, then we should address composite conditions. For instance, what should the following evaluate to?
if (Empty && Value) {}
if (Empty && Error) {}
if (Empty && Empty) {}
The question really boils done to what influence Empty has. Do we want it to influence the results, or should it act like a "don't care" value?
PS: Niall, I know that you are currently using Kleene logic, but I think that it is worthwhile to consider the use-cases before selecting the truth tables.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- ACCU - Professionalism in programming - http://www.accu.org

On 07/03/2015 12:09 PM, Robert Jones wrote:
I believe boost::tribool addresses exactly this point.
It supplies one answer to the question, but that is selecting the truth tables before regarding the use-cases. For an altenative approach see: http://permalink.gmane.org/gmane.comp.lang.c%2B%2B.isocpp.proposals/5250 and the motivation: http://permalink.gmane.org/gmane.comp.lang.c%2B%2B.isocpp.proposals/5270 So I would like to reiterate, let us start with the use-cases and then select the truth tables afterwards.

On 3 Jul 2015 at 11:45, Bjorn Reese wrote:
Out of interest, what do you think of my free function ternary logic programming:
So far, the discussion has focussed on how the ternary operators should behave in if-statements. In that case, I tend to agree with Lee's enum and switch suggestion, which also extends well into multi-variate logic.
My tribool allows switch and enum if you want it. It's just it requires a bit more typing, and I dislike typing :)
If we want to use the ternary logic in if-statements, then we should address composite conditions. For instance, what should the following evaluate to?
if (Empty && Value) {}
if (Empty && Error) {}
if (Empty && Empty) {}
The question really boils done to what influence Empty has. Do we want it to influence the results, or should it act like a "don't care" value?
That's exactly the nub of the problem. Which ought to be dominant over the other, empty or errored? i.e. is it: Empty < Errored < Valued i.e. Empty && Errored = Empty or: Errored < Empty < Valued i.e. Empty && Errored = Errored. Me personally, I chose the Kleene logic because I felt Empty is like NaN in floating point, so it is always dominant. And I suppose we can retain that, even with Errored => False, Empty => Unknown, with the appropriate truth tables. The tricky part is which is the best design? I'm not sure if that's answerable from a purely top down approach. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

Sorry for top posting, I blame my tablet. I see Empty as "I do not know *yet*" (like future). ie Empty == "Value or Error eventually" So I imagine "Empty && Error" as "(Value or Error) && Error". And for && it doesn't matter what the Empty is, the end result of anything || false == false ie Error. So Error && anything is Error. (false && anything is false) And Value || anything is Value. (true || anything is true) That leaves Error || Empty and Value && Empty. Error || Empty == Empty because (thinking of Empty as "unknown right now, might change in future"), you need to leave the chance that Empty becomes Value and the result of Error || Value is Value. Or looking at it as Empty == (Value or Error) ie 'or' not '||' because Empty is unknown: Error || Empty == Error || (Value or Error) == (Error || Value) or (Error || Error) == (Value) or (Error) == (Value or Error) == Empty Similarly, Value && Empty == Empty Not sure if it makes complete sense to look at Empty as "Value or Error eventually", but I think you get a reasonable truth table out of it, and it might apply well to your futures. Tony Sent from my portable Analytical Engine ------------------------------ *From:* "Niall Douglas" <s_sourceforge@nedprod.com> *To:* "boost@lists.boost.org" <boost@lists.boost.org> *Sent:* 4 July, 2015 11:26 AM *Subject:* Re: [boost] Ternary logic programming On 3 Jul 2015 at 11:45, Bjorn Reese wrote:
Out of interest, what do you think of my free function ternary logic programming:
So far, the discussion has focussed on how the ternary operators should behave in if-statements. In that case, I tend to agree with Lee's enum and switch suggestion, which also extends well into multi-variate logic.
My tribool allows switch and enum if you want it. It's just it requires a bit more typing, and I dislike typing :)
If we want to use the ternary logic in if-statements, then we should address composite conditions. For instance, what should the following evaluate to?
if (Empty && Value) {}
if (Empty && Error) {}
if (Empty && Empty) {}
The question really boils done to what influence Empty has. Do we want it to influence the results, or should it act like a "don't care" value?
That's exactly the nub of the problem. Which ought to be dominant over the other, empty or errored? i.e. is it: Empty < Errored < Valued i.e. Empty && Errored = Empty or: Errored < Empty < Valued i.e. Empty && Errored = Errored. Me personally, I chose the Kleene logic because I felt Empty is like NaN in floating point, so it is always dominant. And I suppose we can retain that, even with Errored => False, Empty => Unknown, with the appropriate truth tables. The tricky part is which is the best design? I'm not sure if that's answerable from a purely top down approach. Niall -- ned Productions Limited Consultinghttp://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

I think your tablet got confused? On 07/04/2015 10:54 PM, Gottlob Frege wrote:
Sorry for top posting, I blame my tablet.
I see Empty as "I do not know *yet*" (like future). ie Empty == "Value or Error eventually" So I imagine "Empty && Error" as "(Value or Error) && Error". And for && it doesn't matter what the Empty is, the end result of anything || false == false ie Error.
anything || false == anything anything && false == false -- Michael Caisse ciere consulting ciere.com

On 07/05/2015 07:54 AM, Gottlob Frege wrote:
I see Empty as "I do not know *yet*" (like future). ie Empty == "Value or Error eventually" [...] Not sure if it makes complete sense to look at Empty as "Value or Error eventually", but I think you get a reasonable truth table out of it, and it might apply well to your futures.
With this interpretation the logic becomes that of Kleene's strong logic of indeterminacy, which is what Boost.Tribool uses. I am going to call this the "I may care" approach here. However, there are alternative interpretations. We could regard Empty as "I don't care". You may have a set of these variables, and you have to take action at some point even if all the variables have not become Value or Error so far, but you still want an indication if none have been set. In that case, you get: Empty && Error => Error Empty && Value => Value Empty && Empty => Empty Empty || Error => Error Empty || Value => Value Empty || Empty => Empty Another alternative is to regard Empty as "I do care", which makes it behave like a NaN-like construct. This makes Empty "sticky"; if only one of the variables is Empty then the entire condition should be Empty: Empty && Error => Empty Empty && Value => Empty Empty && Empty => Empty Empty || Error => Empty Empty || Value => Empty Empty || Empty => Empty I agree that "I may care" looks fine for futures, but since Niall is aiming for general framework of monads, I am less convinced that this approach is best approach for all. It may very well be that different monads require different three-valued logic.

On July 5, 2015 5:44:58 AM EDT, Bjorn Reese <breese@mail1.stofanet.dk> wrote:
We could regard Empty as "I don't care". You may have a set of these variables, and you have to take action at some point even if all the variables have not become Value or Error so far, but you still want an indication if none have been set. In that case, you get:
Empty && Error => Error Empty && Value => Value Empty && Empty => Empty
Empty || Error => Error Empty || Value => Value Empty || Empty => Empty
That might be legitimate, and even useful, but doesn't it seem wrong that && and || yield the same results?
Another alternative is to regard Empty as "I do care", which makes it behave like a NaN-like construct. This makes Empty "sticky"; if only one of the variables is Empty then the entire condition should be Empty:
Empty && Error => Empty Empty && Value => Empty Empty && Empty => Empty
Empty || Error => Empty Empty || Value => Empty Empty || Empty => Empty
NaN is always a source of confusion at first, so that pattern may be a source of issues, too. It doesn't help that && and || behave alike, though for different reasons than before.
I agree that "I may care" looks fine for futures, but since Niall is aiming for general framework of monads, I am less convinced that this approach is best approach for all. It may very well be that different monads require different three-valued logic.
If the empty state is called the same thing in each case, different behavior will be confusing. Otherwise, I agree that forcing everything into the same pattern may be more trouble than it's worth. ___ Rob (Sent from my portable computation engine)

I've been lurking on this thread because It's an interesting topic. But I'm not sure it really belongs here. Multivalued logic is a very deep topic which has been studied in depth for the last ~150 years by some of the best mathematicians who ever existed. It's not clear to me how much we can realistically add to this. boost tribool implements the most widely accepted interpretation of 3 valued logic in a manner which users would expect. And its usage and purpose is well explained in it's documentation. So the question arises - what else is here for us to mine? Just taking a cursory look at literature in this area, it would seem that there is likely something interesting for us here. So if one wanted to implement one or more if the multivalued logics already studied along with a good document describing use cases and limitations that I think would be interesting. But spending huge amounts of time trying to agree on some particular usage seems to me sort of overkill - but maybe not - after all it's fun and interesting to talk about. But I'm skeptical that this would lead to something like a useful boost library. Robert Ramey

On 5 Jul 2015 at 7:15, Robert Ramey wrote:
So the question arises - what else is here for us to mine? Just taking a cursory look at literature in this area, it would seem that there is likely something interesting for us here. So if one wanted to implement one or more if the multivalued logics already studied along with a good document describing use cases and limitations that I think would be interesting. But spending huge amounts of time trying to agree on some particular usage seems to me sort of overkill - but maybe not - after all it's fun and interesting to talk about.
But I'm skeptical that this would lead to something like a useful boost library.
If one were to accept Charley's proposal that ternary logic enter the C++ standard, a good first step towards standardisation is a Boost library which makes ternary programming intuitive and powerful. I think Bjorn is right that the truth tables need to be set per use case. I also think you probably need some framework for sanely managing when different ternary truth tables collide. All that in an easy to use and intuitive syntax. I think there is a great potential Boost library in there, one potentially standardisable. But it's also far beyond my ken. For me at least, I've left my tribool in as a pure switch...case state test method, and I'm leaving the logic disabled for now. After all, just ten days to the AFIO review now. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 07/05/2015 12:10 PM, Rob Stewart wrote:
That might be legitimate, and even useful, but doesn't it seem wrong that && and || yield the same results?
The three cases under discussion may be easier to understand if we look at their Venn diagrams, so I have drawn them here: http://breese.github.io/2015/07/05/layers-of-three-valued-logic.html (the text could be more elaborate, but I wanted to make the diagrams available as quickly as possible.)
If the empty state is called the same thing in each case, different behavior will be confusing. Otherwise, I agree that forcing everything into the same pattern may be more trouble than it's worth.
Indeed. The empty state may have to be called different names for the three alternatives.

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Bjorn Reese Sent: 05 July 2015 17:06 To: boost@lists.boost.org Subject: Re: [boost] Ternary logic programming
On 07/05/2015 12:10 PM, Rob Stewart wrote:
That might be legitimate, and even useful, but doesn't it seem wrong that && and || yield the same results?
The three cases under discussion may be easier to understand if we look at their Venn diagrams, so I have drawn them here:
http://breese.github.io/2015/07/05/layers-of-three-valued-logic.html
(the text could be more elaborate, but I wanted to make the diagrams available as quickly as possible.)
If the empty state is called the same thing in each case, different behavior will be confusing. Otherwise, I agree that forcing everything into the same pattern may be more trouble than it's worth.
Indeed. The empty state may have to be called different names for the three alternatives.
Nice! Can this be added to the tribool docs - it might avoid confusion and discussion on future Boost lists. Might some MetaMagic allow the user to provide a compile-time template parameter to choose if wants tribool truth-table to be either 'don't, may, or do care'? But provide a default to keep the behaviour as now? Paul --- Paul A. Bristow Prizet Farmhouse Kendal UK LA8 8AB +44 (0) 1539 561830

On 07/06/2015 11:21 AM, Paul A. Bristow wrote:
Can this be added to the tribool docs - it might avoid confusion and discussion on future Boost lists.
I suppose so.
Might some MetaMagic allow the user to provide a compile-time template parameter to choose if wants tribool truth-table to be either 'don't, may, or do care'? But provide a default to keep the behaviour as now?
The truth tables can be encoded in traits. I wrote a prototype with this a while back: https://github.com/breese/tribool/blob/master/include/tribool.hpp

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Bjorn Reese Sent: 07 July 2015 21:47 To: boost@lists.boost.org Subject: Re: [boost] Ternary logic programming
On 07/06/2015 11:21 AM, Paul A. Bristow wrote:
Can this be added to the tribool docs - it might avoid confusion and discussion on future Boost lists.
I suppose so.
Might some MetaMagic allow the user to provide a compile-time template parameter to choose if wants tribool truth-table to be either 'don't, may, or do care'? But provide a default to keep the behaviour as now?
The truth tables can be encoded in traits. I wrote a prototype with this a while back:
Neat. Looks good to me - just needs a bit of Boostification and some docs - and tests that it really is a drop-in replacement for the current code? Paul

On 6/28/15 6:15 AM, Niall Douglas wrote:
https://github.com/ned14/boost.spinlock/blob/master/include/boost/spin lock/tribool.hpp
How is this better/different than the current boost::tribool? I've used this on several occasions with good success. It's well documented as well. Robert Ramey

On 28 Jun 2015 at 9:30, Robert Ramey wrote:
On 6/28/15 6:15 AM, Niall Douglas wrote:
https://github.com/ned14/boost.spinlock/blob/master/include/boost/spin lock/tribool.hpp
How is this better/different than the current boost::tribool?
1. Mine is 100% constexpr. 2. I think mine implements std::min/max and other ternary logic primitives Boost.Tribool does not. I didn't look into it in detail. 3. Mine is much more conservatively designed. Specifically: a. No automatic conversion operators, thus preventing assert(something==!!something). It also forces the programmer to explicitly state what they mean when working with tribools, see below. b. operator&& and operator|| return bool, not tribool. If you want tribool outcomes, use the bitwise operators. I'm also matching 100% the C++ standard text on this where logical operators are supposed to return only bool.
I've used this on several occasions with good success. It's well documented as well.
I was being incredibly paranoid with my tribool. Almost certainly excessively so. But they're new to me, so I was being extra cautious to make sure I didn't shoot myself in the foot later. Besides, writing this by hand: tribool t; if(true_(t)) ... else if(false_(t)) ... else if(unknown_(t)) ... ... isn't too bad, and has the advantage of being very clear even to programmers not familiar with tribool (i.e. me). Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 6/28/15 9:41 AM, Niall Douglas wrote:
On 28 Jun 2015 at 9:30, Robert Ramey wrote:
On 6/28/15 6:15 AM, Niall Douglas wrote:
https://github.com/ned14/boost.spinlock/blob/master/include/boost/spin lock/tribool.hpp
How is this better/different than the current boost::tribool?
1. Mine is 100% constexpr.
Looks to me that boost::tribool is also.
2. I think mine implements std::min/max and other ternary logic primitives Boost.Tribool does not. I didn't look into it in detail.
Hmmm - I don't see min/max in here. I do see in here a number of things I would not expect to see: operator~, operator| and operator& the enum shows 5 states - three of which have the same value. Very odd to me.
3. Mine is much more conservatively designed. Specifically:
a. No automatic conversion operators, thus preventing assert(something==!!something). It also forces the programmer to explicitly state what they mean when working with tribools, see below.
b. operator&& and operator|| return bool, not tribool. If you want tribool outcomes, use the bitwise operators.
These are design choices which one might or might not agree with. They don't resonate for me.
I'm also matching 100% the C++ standard text on this where logical operators are supposed to return only bool.
I don't see this. If you're referring 5.14,... I think those paragraphs referring to the builtin- not overloaded versions. On the other hand, some other part might refer to overloaded operations. I didn't see it and it would surprise me to find it.
I've used this on several occasions with good success. It's well documented as well.
I was being incredibly paranoid with my tribool. Almost certainly excessively so. But they're new to me, so I was being extra cautious to make sure I didn't shoot myself in the foot later. Besides, writing this by hand:
tribool t; if(true_(t)) ... else if(false_(t)) ... else if(unknown_(t)) ...
Arrrrgggghhh.
... isn't too bad, and has the advantage of being very clear even to programmers not familiar with tribool (i.e. me).
Niall
Robert Ramey

On 28 Jun 2015 at 10:40, Robert Ramey wrote:
How is this better/different than the current boost::tribool?
1. Mine is 100% constexpr.
Looks to me that boost::tribool is also.
Yes, you're right. The source code was updated without making the change obvious in the docs.
2. I think mine implements std::min/max and other ternary logic primitives Boost.Tribool does not. I didn't look into it in detail.
Hmmm - I don't see min/max in here.
I do see in here a number of things I would not expect to see:
operator~, operator| and operator&
Kleene logic provides all those tribitwise operations.
the enum shows 5 states - three of which have the same value. Very odd to me.
The indeterminate state in ternary logic has overloaded meaning. Sometimes by indeterminate you mean unknown, other times you mean a known state other than true or false. I too am not a fan of aliasing naming. I had been thinking of some method to enforce naming consistency, such that unknown state is a different type to other state and to indeterminate state. That way you're forced to use consistent naming. But that's a todo. Unfortunately enums aren't permitted to use an underlying type which is not an integral type, otherwise the tribool's states could themselves be enum types.
I'm also matching 100% the C++ standard text on this where logical operators are supposed to return only bool.
I don't see this. If you're referring 5.14,... I think those paragraphs referring to the builtin- not overloaded versions. On the other hand, some other part might refer to overloaded operations. I didn't see it and it would surprise me to find it.
I was worried that a container of tribool might not behave as expected. As I mentioned, I was being paranoid.
I've used this on several occasions with good success. It's well documented as well.
I was being incredibly paranoid with my tribool. Almost certainly excessively so. But they're new to me, so I was being extra cautious to make sure I didn't shoot myself in the foot later. Besides, writing this by hand:
tribool t; if(true_(t)) ... else if(false_(t)) ... else if(unknown_(t)) ...
Arrrrgggghhh.
I'm more than happy to reform my tribool into something better. In particular if you can suggest a better method equally as clear to non-Boost programmers I am very interested. Most programmers have no conceptualisation of a logic beyond boolean, and that's my number one criticism of Boost.Tribool's "appear as if I am a bool" programming style. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 6/28/2015 5:38 AM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course. First of, as I have already said before, Eggs.Variant is an experiment. As such it is highly unstable, and I reserve the right to change things in any way I see fit, with no regards for backwards compatibility, maintenance, or support. I have made these kinds of changes in the past, and I have more planned for the near future. I
On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote: think it would be unwise to make it a part of Boost, even as an experimental library, until the design has fully hatched ("Eggs", get it?). Agustin, this is exactly the kind of constraints of an experimental
Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : library. When the design is fully hatched then it moves to non experimental.
I fail to see the point in including an experimental library that changes its interface every month into Boost, which is released every 3 months or so. By the time you get your hands in a release, the interface of the library has already changed. And the library as such would only exist for a single release. Who would benefit from Boost packaging old snapshots of experimental libraries? Perhaps you'd wish to bring back the Sandbox (http://www.boost.org/community/sandbox.html), which has been superseded by standalone repositories since the move to Git.
For instance, the visible empty state is not going anywhere. As a fundamental building block, I cannot afford to pay the cost of double buffering, heap allocation, restrictions to nothrow-move-constructible alternative types, etc. There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C).
Eggs.Variants doesn't have an error state, only an empty state as `union`s do. Switching the active member of a `union` is a two step operation: - First the active member (if any) is destroyed, leaving the `union` in an empty state (no active member). - Then, a new member is activated via in-place construction. If construction fails with an exception, the `union` remains empty.
The abstractions I built on top of it might, since they are the ones who attach meaning to the variant, but it is up to them to make those choices. Likewise, I'm not considering implementing support for void and reference types as alternatives. Could you tell us more about the rationale? it is because some wrappers as void_t and reference_wrapper<T> can be used instead?
It is because `union`s don't take those as members. I did briefly experimented with implicit conversion to wrappers, as those you mention, but it interacted in funny ways with interfaces where a "scalar" value appears (construction, assignment, relational ops). I concluded that forcing the higher level abstraction to do the wrapping and be aware of it would be less surprising.
BTW, how eggs::variant<T,T> behaves?
Just as a `union U { T a; T b; };` would. It has two distinct members, and you can only access the active one. Eggs.Variants additionally provides access to the underlying storage, both raw and typed, via its `target` member function. Based on recent discussion on the reflectors, I will be modifying `target<T>()` to be able to access it when either of the two members are active, since it's logically equivalent to a cast on the underlying storage address and I understand it to be well-defined under strict aliasing rules (see http://melpon.org/wandbox/permlink/Zw59kxEmvp7M82M5). There are several other places in which I have yet to consider whether SFINAE or hard error is appropriate. As far as I know, I only depart from what a `union` would do for initialization. Due to the discriminator, I cannot provide both default-initialization and value-initialization as `union`s do, so I only provide the cheap one. Regards, -- Agustín K-ballo Bergé.- http://talesofcpp.fusionfenix.com

On 6/28/2015 5:38 AM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course. First of, as I have already said before, Eggs.Variant is an experiment. As such it is highly unstable, and I reserve the right to change things in any way I see fit, with no regards for backwards compatibility, maintenance, or support. I have made these kinds of changes in the past, and I have more planned for the near future. I
On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote: think it would be unwise to make it a part of Boost, even as an experimental library, until the design has fully hatched ("Eggs", get it?). Agustin, this is exactly the kind of constraints of an experimental
Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : library. When the design is fully hatched then it moves to non experimental.
I fail to see the point in including an experimental library that changes its interface every month into Boost, which is released every 3 months or so. By the time you get your hands in a release, the interface of the library has already changed. And the library as such would only exist for a single release.
Who would benefit from Boost packaging old snapshots of experimental libraries?
Perhaps you'd wish to bring back the Sandbox (http://www.boost.org/community/sandbox.html), which has been superseded by standalone repositories since the move to Git. Yes, Boost.Experimental could be something like the old sandbox, but instead of having a sandbox that is not fixed, it will be fixed 3 times a year. This gives some stability to users that want to play with.
For instance, the visible empty state is not going anywhere. As a fundamental building block, I cannot afford to pay the cost of double buffering, heap allocation, restrictions to nothrow-move-constructible alternative types, etc. There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C).
Eggs.Variants doesn't have an error state, only an empty state as `union`s do. Switching the active member of a `union` is a two step operation:
- First the active member (if any) is destroyed, leaving the `union` in an empty state (no active member).
- Then, a new member is activated via in-place construction. If construction fails with an exception, the `union` remains empty. This is what we are calling the error state :). So Eggs is confounding
Le 28/06/15 16:29, Agustín K-ballo Bergé a écrit : the empty and the error state (even if this is not stated explicitly)
The abstractions I built on top of it might, since they are the ones who attach meaning to the variant, but it is up to them to make those choices. Likewise, I'm not considering implementing support for void and reference types as alternatives. Could you tell us more about the rationale? it is because some wrappers as void_t and reference_wrapper<T> can be used instead?
It is because `union`s don't take those as members.
I did briefly experimented with implicit conversion to wrappers, as those you mention, but it interacted in funny ways with interfaces where a "scalar" value appears (construction, assignment, relational ops). I concluded that forcing the higher level abstraction to do the wrapping and be aware of it would be less surprising.
Thanks for clarifying.
BTW, how eggs::variant<T,T> behaves?
Just as a `union U { T a; T b; };` would. It has two distinct members, and you can only access the active one.
Eggs.Variants additionally provides access to the underlying storage, both raw and typed, via its `target` member function. Based on recent discussion on the reflectors, I will be modifying `target<T>()` to be able to access it when either of the two members are active, since it's logically equivalent to a cast on the underlying storage address and I understand it to be well-defined under strict aliasing rules (see http://melpon.org/wandbox/permlink/Zw59kxEmvp7M82M5).
While I see added value to variant <T,T> I would make any access with the type T ill-formed, as you do already for get, and provide access only throw the index. `union U { T a; T b; };` works because we have using tags `a` and `b`, but i doest provides access by type.
There are several other places in which I have yet to consider whether SFINAE or hard error is appropriate.
As far as I know, I only depart from what a `union` would do for initialization. Due to the discriminator, I cannot provide both default-initialization and value-initialization as `union`s do, so I only provide the cheap one.
Why you can not default initialize to index 0? Vicente

On 29 Jun 2015 at 8:58, Vicente J. Botet Escriba wrote:
Perhaps you'd wish to bring back the Sandbox (http://www.boost.org/community/sandbox.html), which has been superseded by standalone repositories since the move to Git.
Yes, Boost.Experimental could be something like the old sandbox, but instead of having a sandbox that is not fixed, it will be fixed 3 times a year. This gives some stability to users that want to play with.
Does this mean that there would be an additional Boost tarball of all the Boost.Experimental libraries three times per year? If you make it a rule that only Boost.Experimental libraries with a commit on their master branch within the last 12 months make it into that tarball, I wholeheartedly agree with this proposal. One of the big problems with Sandbox used to be abandoned libraries, I would urge that Experimental not repeat that mistake. After all, if an Experimental library is popular or has an active maintainer it would surely receive at least one new commit on master branch in a year. Vicente would you like to make a formal request for this on boost-steering? I will support you there. I would assume the main objectors would be the release managers, as it is additional work for them. They may request for new release managers to volunteer themselves just for Boost.Experimental. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On 06/29/2015 03:19 AM, Niall Douglas wrote:
On 29 Jun 2015 at 8:58, Vicente J. Botet Escriba wrote:
Perhaps you'd wish to bring back the Sandbox (http://www.boost.org/community/sandbox.html), which has been superseded by standalone repositories since the move to Git.
Yes, Boost.Experimental could be something like the old sandbox, but instead of having a sandbox that is not fixed, it will be fixed 3 times a year. This gives some stability to users that want to play with.
<snip>
Vicente would you like to make a formal request for this on boost-steering? I will support you there.
Please don't do that. That isn't the steering committee's function. The community (dev list) is the correct place to make such proposals. Thanks! michael -- Michael Caisse ciere consulting ciere.com

On 6/29/2015 3:58 AM, Vicente J. Botet Escriba wrote:
Le 28/06/15 16:29, Agustín K-ballo Bergé a écrit :
On 6/28/2015 5:38 AM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course. First of, as I have already said before, Eggs.Variant is an experiment. As such it is highly unstable, and I reserve the right to change things in any way I see fit, with no regards for backwards compatibility, maintenance, or support. I have made these kinds of changes in the past, and I have more planned for the near future. I
On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote: think it would be unwise to make it a part of Boost, even as an experimental library, until the design has fully hatched ("Eggs", get it?). Agustin, this is exactly the kind of constraints of an experimental
Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : library. When the design is fully hatched then it moves to non experimental.
I fail to see the point in including an experimental library that changes its interface every month into Boost, which is released every 3 months or so. By the time you get your hands in a release, the interface of the library has already changed. And the library as such would only exist for a single release.
Who would benefit from Boost packaging old snapshots of experimental libraries?
Perhaps you'd wish to bring back the Sandbox (http://www.boost.org/community/sandbox.html), which has been superseded by standalone repositories since the move to Git. Yes, Boost.Experimental could be something like the old sandbox, but instead of having a sandbox that is not fixed, it will be fixed 3 times a year. This gives some stability to users that want to play with.
I don't see the point in this. Stabilizing an experimental library interferes with experimentation. And once an experimental library is stable enough to leave the new distributed sandbox, the ideal place for it would be the blincubator. I think I have made this point several times already. My position should be clear by now, so I'll just shut up about it now.
BTW, how eggs::variant<T,T> behaves?
Just as a `union U { T a; T b; };` would. It has two distinct members, and you can only access the active one.
Eggs.Variants additionally provides access to the underlying storage, both raw and typed, via its `target` member function. Based on recent discussion on the reflectors, I will be modifying `target<T>()` to be able to access it when either of the two members are active, since it's logically equivalent to a cast on the underlying storage address and I understand it to be well-defined under strict aliasing rules (see http://melpon.org/wandbox/permlink/Zw59kxEmvp7M82M5).
While I see added value to variant <T,T> I would make any access with the type T ill-formed, as you do already for get, and provide access only throw the index. `union U { T a; T b; };` works because we have using tags `a` and `b`, but i doest provides access by type.
I'm not sure what your feedback is here. Access via `get<T>` is already ill-formed when `T` is not unique.
As far as I know, I only depart from what a `union` would do for initialization. Due to the discriminator, I cannot provide both default-initialization and value-initialization as `union`s do, so I only provide the cheap one.
Why you can not default initialize to index 0?
What I meant is that, unlike unions, I cannot give different meaning to these two: U u; // no active members U u{}; // first member active Regards, -- Agustín K-ballo Bergé.- http://talesofcpp.fusionfenix.com

On Sun, Jun 28, 2015 at 4:38 AM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> wrote:
The abstractions I built on top of it might, since they are the ones who attach meaning to the variant, but it is up to them to make those choices. Likewise, I'm not considering implementing support for void and reference types as alternatives.
Could you tell us more about the rationale? it is because some wrappers as void_t and reference_wrapper<T> can be used instead?
reference_wrapper<T> can be used by the client, if that is what the client wants. But reference_wrapper<T> doesn't work the same as T &, so it shouldn't be used by variant to handle references. Tony

On 06/28/2015 03:38 AM, Vicente J. Botet Escriba wrote:> Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : [snip]
I know that your variant is possibly empty and the C++ standard proposal is never-empty, and this makes them different from the user point of view. Bjarne S. and Anthony W. are pushing towards a possible empty variant, we don't know yet what the std::experimental::variant will be and less yet what std::variant will be in C++17.
boost::variant combined with boost::blanc gives this kind of possibly empty variant, but IMHO this is a quite different type.
template <class ...Ts> using optional_variant = boost::variant<boost::blanc, Ts...>; // +/-
I suggest to name them as variant<Ts...> : never-empty optionals<Ts...> : possibly empty
I believe that the usage of these classes is quite different.
There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C).
The above example is unclear to me. Is this what you mean: #include <assert.h> int main() { variant<B> vb; assert(is_empty(vb)); assert(!is_error(vb)); vb = B{}; assert(!is_empty(vb)); assert(!is_error(vb)); vb = A{} assert(is_empty(vb));//? assert(is_error(vb));//? return 0; } where the is_empty and is_error return true if the variant is empty or in error, respectively. [snip] -Larry

On 07/06/2015 09:10 AM, Larry Evans wrote:
On 06/28/2015 03:38 AM, Vicente J. Botet Escriba wrote:> Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit : [snip]
I know that your variant is possibly empty and the C++ standard proposal is never-empty, and this makes them different from the user point of view. Bjarne S. and Anthony W. are pushing towards a possible empty variant, we don't know yet what the std::experimental::variant will be and less yet what std::variant will be in C++17.
boost::variant combined with boost::blanc gives this kind of possibly empty variant, but IMHO this is a quite different type.
template <class ...Ts> using optional_variant = boost::variant<boost::blanc, Ts...>; // +/-
I suggest to name them as variant<Ts...> : never-empty optionals<Ts...> : possibly empty
I believe that the usage of these classes is quite different.
There is something that I don't like of the possibly empty variant. It confounds the empty state and a hidden error state. When we assign a type A to variant containing a type B the resulting variant can be empty (contains a different type C).
The above example is unclear to me. Is this what you mean:
#include <assert.h> int main() { variant<B> vb; assert(is_empty(vb)); assert(!is_error(vb)); vb = B{}; assert(!is_empty(vb)); assert(!is_error(vb)); vb = A{} assert(is_empty(vb));//? assert(is_error(vb));//? return 0; }
where the is_empty and is_error return true if the variant is empty or in error, respectively. [snip]
Or maybe, in keeping with the Ternary Logic Programming branch of this thread: #include <assert.h> enum var_stat { var_error , var_empty , var_full } struct A{}; struct B{}; template<typename... Types> struct variant{...}; int main() { variant<V> vb; assert(status(vb) == var_empty); vb = B{}; assert(status(vb) == var_full); vb = A{}; assert(status(vb) == var_error); return 0; }

On 06/28/2015 03:38 AM, Vicente J. Botet Escriba wrote:
Le 27/06/15 21:32, Agustín K-ballo Bergé a écrit :
On 6/27/2015 12:38 PM, Vicente J. Botet Escriba wrote:
I would accept Eggs.Variant without even a review (or with a minimal review) as an experimental library as part of Boost.Variant after some minimal adaptation to fit in Boost of course. [snip] I know that your variant is possibly empty and the C++ standard proposal is never-empty, and this makes them different from the user point of view. Bjarne S. and Anthony W. are pushing towards a possible empty variant, we don't know yet what the std::experimental::variant will be and less yet what std::variant will be in C++17.
boost::variant combined with boost::blanc gives this kind of possibly empty variant, but IMHO this is a quite different type.
template <class ...Ts> using optional_variant = boost::variant<boost::blanc, Ts...>; // +/-
I suggest to name them as variant<Ts...> : never-empty
What about variant<>, or is that disallowed? If it is disallowed, then tuple<> should, I guess, also be disallowed. FWIW, there was some discussion of the meaning of variant<> and tuple<> in the haskellforall page: http://www.haskellforall.com/2012/01/haskell-for-mainstream-programmers.html IOW, maybe: variant<> is somewhat like the Zero and tuple<> is somewhat like the One in that haskellforall page. Just thought it interesting, but I've no idea if Zero or One would be useful in c++. Anyone have any ideas about how they would be useful?
optionals<Ts...> : possibly empty
I believe that the usage of these classes is quite different.
[snip]

On 07/14/2015 11:16 AM, Nevin Liber wrote:
On 14 July 2015 at 08:01, Larry Evans <cppljevans@suddenlink.net> wrote:
What about variant<>, or is that disallowed? If it is disallowed, then tuple<> should, I guess, also be disallowed.
Why?
Because of the duality between category products and coproducts: https://en.wikipedia.org/wiki/Dual_%28category_theory%29 in particular, the following: https://en.wikiversity.org/wiki/Introduction_to_Category_Theory/Products_and... says: This is called duality principle. For every construction in category theory, there's an opposite construction with arrows reversed. Also, the following: https://en.wikipedia.org/wiki/Initial_and_terminal_objects specifically mention empty products and empty coproducts: a terminal object can be thought of as an empty product... Dually, an initial object is a colimit of the empty diagram ∅ → C and can be thought of as an empty coproduct or categorical sum. I know it that, at first glance, an empty product or empty coproduct doesn't make much sense, but, then again, adding 0 to a numerical sum doesn't make any difference; hence, why is there any need for 0? Apparently the category people think there's sufficient reason to define an empty coproduct and empty product; hence, I'd guess there's some good reason. Category experts (e.g. Louis Dionne), can you supply some better explanation? -regards Larry

Larry Evans wrote:
What about variant<>, or is that disallowed? If it is disallowed, then tuple<> should, I guess, also be disallowed.
variant<T1,...,Tn> has N(T1)+N(T2)+...+N(Tn) valid values, whereas tuple<T1,...,Tn> has N(T1)*N(T2)*...*N(Tn) valid values, where N(T) is the number of valid values T has. It follows that variant<> has zero valid values, whereas tuple<> has one.

On 07/14/2015 01:03 PM, Peter Dimov wrote:
Larry Evans wrote:
What about variant<>, or is that disallowed? If it is disallowed, then tuple<> should, I guess, also be disallowed.
variant<T1,...,Tn> has N(T1)+N(T2)+...+N(Tn) valid values, whereas tuple<T1,...,Tn> has N(T1)*N(T2)*...*N(Tn) valid values, where N(T) is the number of valid values T has. It follows that variant<> has zero valid values, whereas tuple<> has one.
Thanks Peter. To be explicit: tuple<> t0; would be allowed, but: variant<> v0; wouldn't compile because there are "zero valid values" in a variant<>?

Larry Evans wrote:
Thanks Peter.
To be explicit:
tuple<> t0;
would be allowed, but:
variant<> v0;
wouldn't compile because there are "zero valid values" in a variant<>?
Yes; you can use the type variant<> but you can't declare a variable of it. N4542 says variant<> A variant without alternatives cannot be constructed; it is otherwise an allowed type. It is easier to allow it than to forbid it. which is consistent with variant being a sum type without an extra empty state (same as boost::variant). For a variant that has an extra empty state, one would expect variant<> to be instantiable and always empty.

Larry Evans <cppljevans <at> suddenlink.net> writes:
On 07/14/2015 11:16 AM, Nevin Liber wrote:
On 14 July 2015 at 08:01, Larry Evans <cppljevans <at> suddenlink.net> wrote:
What about variant<>, or is that disallowed? If it is disallowed, then tuple<> should, I guess, also be disallowed.
Why?
[...]
I know it that, at first glance, an empty product or empty coproduct doesn't make much sense, but, then again, adding 0 to a numerical sum doesn't make any difference; hence, why is there any need for 0? Apparently the category people think there's sufficient reason to define an empty coproduct and empty product; hence, I'd guess there's some good reason.
Category experts (e.g. Louis Dionne), can you supply some better explanation?
Without false modesty, I can say that I am an absolute beginner in category theory. So don't expect any clear cut answer from me. Bartosz Milewski might be able to provide more insight, since he's been writing a book about category theory for programmers. That being said, I gathered my opinion on the mathematical aspect of the thing into a short blog post at [1]. I'd like to emphasize that I'm only looking at the problem from a mathematical perspective, disregarding implementation issues or actual usefulness, because I haven't been following the different variant discussions and proposals enough to have a strong or valuable opinion. TL;DR ----- It should be a compilation error to create an object of type variant<>. Also, seeing Peter Dimov's reply, it seems like the variant designers came to the same conclusion. Regards, Louis [1]: http://ldionne.com/2015/07/14/empty-variants-and-tuples/

On Tue, Jul 14, 2015 at 7:01 AM, Larry Evans <cppljevans@suddenlink.net> wrote:
IOW, maybe: variant<> is somewhat like the Zero and tuple<> is somewhat like the One in that haskellforall page.
Just thought it interesting, but I've no idea if Zero or One would be useful in c++. Anyone have any ideas about how they would be useful?
Heh, thanks for the fun observation Larry. I think the extension of this principle is: - given a binary function (say one of these is called 'foo') - if that function forms a monoid with some empty element 'bar' - then construct a syntax where - meaning( foo() ) = bar - meaning( foo( a, b ) ) = foo( meaning(a), meaning(b) ) - meaning( foo( a₁, a₂, …, aₙ ) ) = foo( meaning(a₁), foo( meaning( a₂ ), … meaning( aₙ ) ) … ) so if + and * have the normal arithmetic meanings and are prefix functions (not operators), we would have meaning( +( +(), 3 ) ) = 0 + 3 meaning( *( *(), 3 ) ) = 1 * 3 Of course we get the extension to binary type functions which are monoids. These include the sum type (variant) and the product type (tuple). I wonder which other common binary type functions are monoids. Another interesting thing would be a free function 'append' which appends strings together. That would imply that 'append()' is the empty string. -- David

-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Antony Polukhin Sent: 25 June 2015 22:19 To: boost@lists.boost.org List Subject: [boost] [variant] Maintainer
Hi,
I've been keeping an eye on Boost.Varinat for a few years. Since then multiple issues were closed; support for rvalues and variadic templates was added; other good things happened.
Could I be promoted to an official maintainer of Variant?
Yes please. Paul --- Paul A. Bristow Prizet Farmhouse Kendal UK LA8 8AB +44 (0) 1539 561830
participants (27)
-
Agustín K-ballo Bergé
-
Andrzej Krzemienski
-
Antony Polukhin
-
Bjorn Reese
-
charleyb123 .
-
David Sankel
-
Edward Diener
-
Gavin Lambert
-
Gottlob Frege
-
Joel de Guzman
-
Joseph Van Riper
-
Larry Evans
-
Lee Clagett
-
Louis Dionne
-
Michael Caisse
-
Nevin Liber
-
Niall Douglas
-
Paul A. Bristow
-
Paul Long
-
Peter Dimov
-
Rob Stewart
-
Robert Jones
-
Robert Ramey
-
Steve M. Robbins
-
Thorsten Ottosen
-
Vicente J. Botet Escriba
-
Vinícius dos Santos Oliveira