www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why does std.variant not have a tag?

reply evansl <cppljevans suddenlink.net> writes:
  http://dlang.org/phobos/std_variant.html

says:

 This module implements a discriminated union type (a.k.a. tagged union,
algebraic type).

Yet, the wiki page:

  http://en.wikipedia.org/wiki/Tagged_union

says:

  a tag field explicitly indicates which one is in use.

and I don't see any indication of a tag field in the std_variant.html
page.  Another wiki reference:

  http://en.wikipedia.org/wiki/Disjoint_union

is more explicit because it pairs the tag with the value:

  (x,i)

where x is the value and i is the tag.

One reason for an explicit tag is that the bounded types may contain
the same type twice.  This has lead to problems in boost::variant as
evidenced by the post:

  http://article.gmane.org/gmane.comp.parsers.spirit.general/17118

In addition, both variant and tuple have a common part, a metafunction
mapping from a tag to a type; hence, this same common part could be used
to implement both tuple and a tagged variant.

A variant which actually contained a tag field I think would be more
general in that it would allow duplicate types among the bounded types
just as a tuple's bounded types can contain duplicate types.

-regards,
Larry
Nov 04 2012
next sibling parent reply =?ISO-8859-1?Q?Alex_R=F8nne_Petersen?= <alex lycus.org> writes:
On 05-11-2012 00:31, evansl wrote:
    http://dlang.org/phobos/std_variant.html

 says:

   This module implements a discriminated union type (a.k.a. tagged union,
 algebraic type).

 Yet, the wiki page:

    http://en.wikipedia.org/wiki/Tagged_union

 says:

    a tag field explicitly indicates which one is in use.

 and I don't see any indication of a tag field in the std_variant.html
 page.  Another wiki reference:

    http://en.wikipedia.org/wiki/Disjoint_union

 is more explicit because it pairs the tag with the value:

    (x,i)

 where x is the value and i is the tag.

 One reason for an explicit tag is that the bounded types may contain
 the same type twice.  This has lead to problems in boost::variant as
 evidenced by the post:

    http://article.gmane.org/gmane.comp.parsers.spirit.general/17118

 In addition, both variant and tuple have a common part, a metafunction
 mapping from a tag to a type; hence, this same common part could be used
 to implement both tuple and a tagged variant.

 A variant which actually contained a tag field I think would be more
 general in that it would allow duplicate types among the bounded types
 just as a tuple's bounded types can contain duplicate types.

 -regards,
 Larry

Yes, this is a big problem with the current std.variant implementation (among other problems such as no recursive variants....). The best std.variant can offer right now is the 'type' property to identify what's stored in it. std.variant is, unfortunately, not very useful if you want the semantics of variants in ML-style languages. -- Alex Rønne Petersen alex lycus.org http://lycus.org
Nov 04 2012
next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Sunday, 4 November 2012 at 22:33:46 UTC, Alex Rønne Petersen
wrote:
 On 05-11-2012 00:31, evansl wrote:
   http://dlang.org/phobos/std_variant.html

 says:

  This module implements a discriminated union type (a.k.a. 
 tagged union,
 algebraic type).

 Yet, the wiki page:

   http://en.wikipedia.org/wiki/Tagged_union

 says:

   a tag field explicitly indicates which one is in use.

 and I don't see any indication of a tag field in the 
 std_variant.html
 page.  Another wiki reference:

   http://en.wikipedia.org/wiki/Disjoint_union

 is more explicit because it pairs the tag with the value:

   (x,i)

 where x is the value and i is the tag.

 One reason for an explicit tag is that the bounded types may 
 contain
 the same type twice.  This has lead to problems in 
 boost::variant as
 evidenced by the post:

   
 http://article.gmane.org/gmane.comp.parsers.spirit.general/17118

 In addition, both variant and tuple have a common part, a 
 metafunction
 mapping from a tag to a type; hence, this same common part 
 could be used
 to implement both tuple and a tagged variant.

 A variant which actually contained a tag field I think would 
 be more
 general in that it would allow duplicate types among the 
 bounded types
 just as a tuple's bounded types can contain duplicate types.

 -regards,
 Larry

Yes, this is a big problem with the current std.variant implementation (among other problems such as no recursive variants....). The best std.variant can offer right now is the 'type' property to identify what's stored in it. std.variant is, unfortunately, not very useful if you want the semantics of variants in ML-style languages.

I've been working on an update to std.variant whose formal submission has been held up by a PhD thesis and some health issues, although I'm currently (slowly) doing a code review/cleanup of it in the hope of finally submitting it. ( Old code: https://jshare.johnshopkins.edu/rjacque2/public_html/ ) Anyways, my implementation has an internal 'tag' as does the current implementation, IIRC. However, as the tag is a meaningless random number, instead .type is provide access to the meaningful typeinfo object of that class. And .type provides most (all?) of the functionality of an int id: auto var = Variant(5); if(var.type == typeid(int)) { // Do something... } else if(var.type == typeid(string)) { // Do something else... } But I am missing something as I found that the linked post wasn't clear what the exact issue was, only that there was an issue. If someone would like to clarify the problem (or any other with Variant) it would be appreciated.
Nov 04 2012
parent reply evansl <cppljevans suddenlink.net> writes:
On 11/05/12 00:33, Robert Jacques wrote:
 On Sunday, 4 November 2012 at 22:33:46 UTC, Alex Rønne Petersen
 wrote:
 On 05-11-2012 00:31, evansl wrote:
   http://dlang.org/phobos/std_variant.html

 says:

  This module implements a discriminated union type (a.k.a. tagged union,
 algebraic type).

 Yet, the wiki page:

   http://en.wikipedia.org/wiki/Tagged_union

 says:

   a tag field explicitly indicates which one is in use.

 and I don't see any indication of a tag field in the std_variant.html
 page.  Another wiki reference:

   http://en.wikipedia.org/wiki/Disjoint_union

 is more explicit because it pairs the tag with the value:

   (x,i)

 where x is the value and i is the tag.

 One reason for an explicit tag is that the bounded types may contain
 the same type twice.  This has lead to problems in boost::variant as
 evidenced by the post:

   http://article.gmane.org/gmane.comp.parsers.spirit.general/17118

 In addition, both variant and tuple have a common part, a metafunction
 mapping from a tag to a type; hence, this same common part could be used
 to implement both tuple and a tagged variant.

 A variant which actually contained a tag field I think would be more
 general in that it would allow duplicate types among the bounded types
 just as a tuple's bounded types can contain duplicate types.

 -regards,
 Larry

Yes, this is a big problem with the current std.variant implementation (among other problems such as no recursive variants....). The best std.variant can offer right now is the 'type' property to identify what's stored in it. std.variant is, unfortunately, not very useful if you want the semantics of variants in ML-style languages.

I've been working on an update to std.variant whose formal submission has been held up by a PhD thesis and some health issues, although I'm currently (slowly) doing a code review/cleanup of it in the hope of finally submitting it. ( Old code: https://jshare.johnshopkins.edu/rjacque2/public_html/ ) Anyways, my implementation has an internal 'tag' as does the current implementation, IIRC. However, as the tag is a meaningless random number, instead .type is provide access to the meaningful typeinfo object of that class. And .type provides most (all?) of the functionality of an int id: auto var = Variant(5); if(var.type == typeid(int)) { // Do something... } else if(var.type == typeid(string)) { // Do something else... }

Robert, I see no bounded types in the above Variant. Looking back at: http://dlang.org/phobos/std_variant.html I see now that std.Variant doesn't require the set of types to be bounded. It's only Algebraic: http://dlang.org/phobos/std_variant.html#Algebraic that has a bounded set of types. I should have read closer. I mistakenly thought std.variant served the same purpose as Boost.variant, but now I see that Boost.variant and std.Algebraic serve similar purposes in that they require a set of bounded types, but that Boost.any and std.variant are similar in that the types are unbounded (IOW, any type can be stored in std.variant or Boost.any). Sorry for not carefully reading. So my question then is why doesn't ALgebraic have a tag. This would allow: Algebraic!(int,int)
 
 But I am missing something as I found that the linked post wasn't
 clear what the exact issue was, only that there was an issue. If
 someone would like to clarify the problem (or any other with
 Variant) it would be appreciated.
 

If std.Algebraic is like Boost.Variant, then duplicate bounded types are not allowed and leads to the problem mentioned in the post on the spirit mailing list which I linked to in my OP. OOPS, now I see why reading that post was not clear enough. Maybe this earlier post in same spirit thread would be clearer. http://article.gmane.org/gmane.comp.parsers.spirit.general/17113 In particular, note the phrase: neither can you use variant<string,string,int> because variant can't take duplicate types. This can lead to problems in the spirit parser because the attributes of parsing: a | b where: phrase a has an attribute of type A phrase b has an attribute of type B is: variant<A,B> as noted near the bottom of: http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/abstracts/attributes/compound_attributes.html and if A and B are the same, then there's a problem because Boost.variant can't handle duplicates. Hope that's clearer. -regards, Larry BTW, recently there was a review of another Boost library that has some similarity to Boost.any. It's called type_erasure: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/doc/html/index.html Since std.Variant is similar to Boost.any (as noted above), and since Boost.any is, in some ways, like Boost.type_erasure, and since you're working on a revised std.Variant, you might be interested in looking at type_erasure.
Nov 05 2012
parent evansl <cppljevans suddenlink.net> writes:
On 11/05/12 13:53, Robert Jacques wrote:
 On Monday, 5 November 2012 at 14:13:41 UTC, evansl wrote:
 On 11/05/12 00:33, Robert Jacques wrote:
 On Sunday, 4 November 2012 at 22:33:46 UTC, Alex Rønne Petersen
 wrote:
 On 05-11-2012 00:31, evansl wrote:



[snip]
 If std.Algebraic is like Boost.Variant, then duplicate
 bounded types are not allowed and leads to the problem mentioned
 in the post on the spirit mailing list which I linked to in my
 OP.  OOPS,  now I see why reading that post was not clear enough.
 Maybe this earlier post in same spirit thread would be clearer.

 http://article.gmane.org/gmane.comp.parsers.spirit.general/17113

 In particular, note the phrase:

   neither can you use variant<string,string,int>
   because variant can't take duplicate types.

 This can lead to problems in the spirit parser because
 the attributes of parsing:

     a | b

 where:
   phrase a has an attribute of type A
   phrase b has an attribute of type B
 is:

     variant<A,B>

 as noted near the bottom of:


 http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/abstracts/attributes/compound_attributes.html


 and if A and B are the same, then there's a problem because
 Boost.variant can't handle duplicates.

 Hope that's clearer.

 -regards,
 Larry

Thank you for the clarification. Implementing an id seems reasonable feature request for algebraic. I've added a bugzilla request for it: http://d.puremagic.com/issues/show_bug.cgi?id=8962 Please have a look in case I missed anything.

The bugzilla request looks good to me, especially the way that the tuple and algebraic are made as similar as possible. As I mentioned before, the thing that's similar is the metafunction mapping from tags to types. Maybe that could be a future enhancement. BTW, the idea of this metafunction map I first found in NuPrl, where these types of functions are called type-valued functions, expressed as: B:(A -> U_1) where U_1 represents all the types, primitive and user defined, in c++, and A is something like an enum or unsigned or some other type serving as the set of possible tag values. The above B expression is found here: http://www.nuprl.org/book/Introduction_Type_Theory.html#SECTION00321000000000000000 where it's used to define: dependent functions (corresponding to std.tuple) dependent products (corresponding to std.Algebraic) Thought you might find that interesting, as I did.
 
 BTW, recently there was a review of
 another Boost library that has some similarity to
 Boost.any.  It's called type_erasure:


 http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/doc/html/index.html


 Since std.Variant is similar to Boost.any (as noted above),
 and since Boost.any is, in some ways, like Boost.type_erasure,
 and since you're working on a revised std.Variant,
 you might be interested in looking at type_erasure.

Thanks, I'll take a look at it.

IIRC, the main difference between Boost.any and Boost.type_erasure is type_erasure can be used to put some constraints on the contained type such as requiring the type to support certain functions. I think type_erasure calls the constraints concepts. You can search for "type_erasure" here: http://thread.gmane.org/gmane.comp.lib.boost.devel to access some interesting discussions. HTH. -regards, Larry -regards,
Nov 05 2012
prev sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Monday, 5 November 2012 at 14:13:41 UTC, evansl wrote:
 On 11/05/12 00:33, Robert Jacques wrote:
 On Sunday, 4 November 2012 at 22:33:46 UTC, Alex Rønne 
 Petersen
 wrote:
 On 05-11-2012 00:31, evansl wrote:



[snip]
 If std.Algebraic is like Boost.Variant, then duplicate
 bounded types are not allowed and leads to the problem mentioned
 in the post on the spirit mailing list which I linked to in my
 OP.  OOPS,  now I see why reading that post was not clear 
 enough.
 Maybe this earlier post in same spirit thread would be clearer.

 http://article.gmane.org/gmane.comp.parsers.spirit.general/17113

 In particular, note the phrase:

   neither can you use variant<string,string,int>
   because variant can't take duplicate types.

 This can lead to problems in the spirit parser because
 the attributes of parsing:

     a | b

 where:
   phrase a has an attribute of type A
   phrase b has an attribute of type B
 is:

     variant<A,B>

 as noted near the bottom of:


 http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/abstracts/attributes/compound_attributes.html

 and if A and B are the same, then there's a problem because
 Boost.variant can't handle duplicates.

 Hope that's clearer.

 -regards,
 Larry

Thank you for the clarification. Implementing an id seems reasonable feature request for algebraic. I've added a bugzilla request for it: http://d.puremagic.com/issues/show_bug.cgi?id=8962 Please have a look in case I missed anything.
 BTW, recently there was a review of
 another Boost library that has some similarity to
 Boost.any.  It's called type_erasure:


 http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/doc/html/index.html

 Since std.Variant is similar to Boost.any (as noted above),
 and since Boost.any is, in some ways, like Boost.type_erasure,
 and since you're working on a revised std.Variant,
 you might be interested in looking at type_erasure.

Thanks, I'll take a look at it.
Nov 05 2012
prev sibling next sibling parent Johannes Pfau <nospam example.com> writes:
Am Mon, 05 Nov 2012 07:33:47 +0100
schrieb "Robert Jacques" <sandford jhu.edu>:

 On Sunday, 4 November 2012 at 22:33:46 UTC, Alex R=C3=B8nne Petersen
 wrote:
 On 05-11-2012 00:31, evansl wrote:
   http://dlang.org/phobos/std_variant.html

 says:

  This module implements a discriminated union type (a.k.a.=20
 tagged union,
 algebraic type).

 Yet, the wiki page:

   http://en.wikipedia.org/wiki/Tagged_union

 says:

   a tag field explicitly indicates which one is in use.

 and I don't see any indication of a tag field in the=20
 std_variant.html
 page.  Another wiki reference:

   http://en.wikipedia.org/wiki/Disjoint_union

 is more explicit because it pairs the tag with the value:

   (x,i)

 where x is the value and i is the tag.

 One reason for an explicit tag is that the bounded types may=20
 contain
 the same type twice.  This has lead to problems in=20
 boost::variant as
 evidenced by the post:

  =20
 http://article.gmane.org/gmane.comp.parsers.spirit.general/17118

 In addition, both variant and tuple have a common part, a=20
 metafunction
 mapping from a tag to a type; hence, this same common part=20
 could be used
 to implement both tuple and a tagged variant.

 A variant which actually contained a tag field I think would=20
 be more
 general in that it would allow duplicate types among the=20
 bounded types
 just as a tuple's bounded types can contain duplicate types.

 -regards,
 Larry

Yes, this is a big problem with the current std.variant=20 implementation (among other problems such as no recursive=20 variants....). The best std.variant can offer right now is the=20 'type' property to identify what's stored in it. std.variant is, unfortunately, not very useful if you want the=20 semantics of variants in ML-style languages.

I've been working on an update to std.variant whose formal submission has been held up by a PhD thesis and some health issues, although I'm currently (slowly) doing a code review/cleanup of it in the hope of finally submitting it. ( Old code: https://jshare.johnshopkins.edu/rjacque2/public_html/ ) Anyways, my implementation has an internal 'tag' as does the current implementation, IIRC. However, as the tag is a meaningless random number, instead .type is provide access to the meaningful typeinfo object of that class. And .type provides most (all?) of the functionality of an int id: =20 auto var =3D Variant(5); if(var.type =3D=3D typeid(int)) { // Do something... } else if(var.type =3D=3D typeid(string)) { // Do something else... } =20 But I am missing something as I found that the linked post wasn't clear what the exact issue was, only that there was an issue. If someone would like to clarify the problem (or any other with Variant) it would be appreciated. =20

A little off-topic, but IIRC the typeid stuff is only used to allow arbitrary types in Variant. This is a valid use case, but could we use a simple enum/integer tag for Algebraic? There was a discussion about Variant's performance some time ago and if you don't need to store arbitrary types you're currently better of writing a custom discriminated union than using Algebraic.
Nov 05 2012
prev sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 4 November 2012 at 22:31:56 UTC, evansl wrote:
 Yet, the wiki page:
   http://en.wikipedia.org/wiki/Tagged_union
 says:
   a tag field explicitly indicates which one is in use.

 and I don't see any indication of a tag field in the 
 std_variant.html

I remember a while back asking about the variant. I wanted to be able to specify an area in memory and say 'this is xxx', but couldn't; This was to help avoid having to manually make and manage something like 60 structs and far more complex code to manage that. This leads me to develop my own version (for built-in types); My version isn't anywhere complete (or clean enough) for publication, but enough for my own project (as read-only use). Hmmm curiously enough, i might be able to convert/rebuild by using a polymorphic struct that I'm tinkering with, but that seems like a lot of extra work.
Nov 05 2012