Topic: An alternative to P0095 for a language based variant types


Author: "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr>
Date: Sun, 13 Dec 2015 07:54:04 +0100
Raw View
Hi,

If we remove the field names in POD structs and unions

union S {
     struct left {int a;}  c1;
     struct right{string a;}  c2;
};

as in

union S {
     struct left {int;}  ;
     struct right{string;}  ;
};

The result is syntactically quite close to sum types in functional
languages like Haskhell

data S = Left int | Right string

However we need to add something else to mean that we want a variant (a
discriminated union)

P0095 proposes to extend unions so that the field name is used as
constructor and case selector.

enum union command {
   std::size_t set_score; // Set the score to the specified value
   std::monotype fire_missile; // Fire a missile
   unsigned fire_laser; // Fire a laser with the specified intensity
   double rotate; // Rotate the ship by the specified degrees.
};

//construction
command cmd = command::set_score( 10 );

// modifier
cmd = command::fire_missile( );

// access
switch( cmd ) {
   case set_score value:
     score = value;
   default:
}

I believe that the structs themselves are a better type constructors
than the field names. My proposal is to use struct as type constructor
for each variant case. This ensures that we would not have repeated types.

enum union command
{
   struct set_score {std::size_t };
   struct fire_missile { } ;
   struct fire_laser { unsigned } ;
   struct rotate { double } ;
};

or

union command
{
   case struct set_score {std::size_t };
   case struct fire_missile {} ;
   case struct fire_laser{unsigned} ;
   case struct rotate{double} ;
};

There will be an implicit conversion from set_score, fire_missile,
fire_laser and rotate to command

command cmd = command::set_score { 10 }; // Note the use of {}

cmd = command::fire_missile{};


I suggest to take the syntax proposed by BS in order to inspect the contents

inspect( cmd )
{
   when set_score{value}:  // the syntax is close to the p0144
structured bindings
     score = value;
   default:
}

Using struct as cases of a variant give us sum of product types directly
as the struct can contain several types. Next follow the usual Tree example.

template <class T>
union Tree {
     case struct Empty{};
     case struct Leaf{T;};
     case struct Node{unique_ptr<Tree>; unique_ptr<Tree>;};
};

Tree t = Node{ unique_ptr { Leaf { 1 } }, unique_ptr { Leaf { 2 } } };
// This will be valid after adoption of p0091r0


The previous statement will need the qualification of Tree::

Tree t = Tree::Node{ unique_ptr { Tree::Leaf { 1 } }, unique_ptr {
Tree::Leaf { 2 } } };


Using struct instead of a field name has the advantage (or the
liability) to be able to define the struct outside the variant.

We could define

template <class T>
struct Left { T;};

template <class T>
struct Right { T;};

template <class T, class U>
union Either {
     case Left<T>;
     case Right<U>;
}

Either<int, string> = Left { 10 }; // This will be valid after adoption
of p0091r0.

I said or the liability because when the structure is defined outside
the variant, the alternative, Left or Right in this case, can be also an
alternative to another variant and be subject to ambiguity.


As the compiler knows the alternatives, it can generate  whatever is
needed for reflection purposes, as variant_alternatives<S> : tuple of
the alternatives.

Comments welcome.

Vicente

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-proposals/.

.