c3c icon indicating copy to clipboard operation
c3c copied to clipboard

Tagged unions?

Open lerno opened this issue 1 year ago • 10 comments

Tagged unions are useful BUT you'd ideally also want:

  1. The ability to store the tag freely in the struct.
  2. Specify the size of the tag
  3. Switch over the tag.
  4. Get the tag "value" and store it somewhere else.
  5. Know the layout of the resulting union
  6. Have a syntax that is just slightly different from C3's other constructs.
  7. Have a syntax to allow them to be embedded as members in structs and untagged unions.
tagged union Foo {
    int i;
    const char *c;
};

Foo foo;
foo.i = 3;
@istag(foo.i) // => true
@istag(foo.c) // => false
foo.c = "hello";
@istag(foo.i) // => false
@istag(foo.c) // => true

switch(@tag(foo)) 
{
    case Foo.i: 
        io::printf("Was %d\n", foo.i);
    case Foo.c: 
        io::printf("Was %s\n", foo.c);
}

Alternative syntax etc:

struct Shape
{
    int centerX;
    int centerY;
    byte kind; // Implicit enum
    union (kind) 
    {
        SQUARE: { int side; }
        RECTANGLE: { int length, height; }
        CIRCLE: { int radius; }
    }
}

And another...

struct Shape
{
    int centerX;
    int centerY;
    byte kind; // Implicit enum
    union (kind) 
    {
        struct square 
        {
            int side;
        }    
        struct rectangle 
        {
            int length;
            int height;
        }
        struct circle
        {
            int radius;
        }
    }
}


And yet another...

struct Shape
{
    int centerX;
    int centerY;
    tagged union (kind)
    {
        case SQUARE:
            int side; 
        case RECTANGLE:
            int length;
            int height;
        case CIRCLE:
            int radius;            
    }
    byte kind;
}

lerno avatar Jul 06 '23 21:07 lerno

tagged union Foo : int
{
   int a;
   int b;
   char* c;
}

Foo f = ...
switch (f)
{
   case Foo.a:
      ...
   case Foo.b:
      ...
}
if (try f.a) { ... }
if (try char* c = f.c) { ... }
int x = f.a; // Implicit panic on fail
int! z = f.a; // optional on fail
int w = f.a ?? 123;

lerno avatar Jul 18 '23 13:07 lerno

union Foo @tagged(int)
{
  int a;
  int b;
  char* c;
}
...
Foo f = ...
switch (f.tag)
{
  case Foo.a.tag:
    ...
}
if (f.tag == Foo.a.tag) { ... }

Another variant.

lerno avatar Jul 18 '23 14:07 lerno

what about:

union Foo : int @tagged
{
   int a;
   int b;
   char* c;
}

Foo f = ...
switch (f)
{
   case Foo.a:
      ...
   case Foo.b:
      ...
}
if (try f.a) { ... }
if (try char* c = f.c) { ... }
int x = f.a; // Implicit panic on fail
int! z = f.a; // optional on fail
int w = f.a ?? 123;

pierrec avatar Jul 24 '23 11:07 pierrec

@pierrec That is also a possibility. But probably if there is try etc with changes the semantics, then it needs to be a completely different type. So the question is if one should be able to change the type with an attribute.

lerno avatar Jul 25 '23 09:07 lerno

My favourite is

struct Shape
{
    byte kind; // Implicit enum
    union (kind) 
    {
        SQUARE: { int side; }
        RECTANGLE: { int length, height; }
        CIRCLE: { int radius; }
    }
}

I think it strikes the right balance between explicitness and brevity. Another possibility is

struct Shape
{
    union (byte kind)  // implicit enum definition 
    {
        SQUARE: { int side; }
        RECTANGLE: { int length, height; }
        CIRCLE: { int radius; }
    }
}

this ensures that the tag variable definition cannot be separated from the union definition.

switch s.kind {
case SQUARE:
...
}

DrGo avatar Sep 27 '23 17:09 DrGo

@DrGo The latter loses the ability to place the "kind" variable though, so that one cannot as efficiently pack the tagging enum.

// Size = 12
struct Shape
{
    char kind;
    union (kind) 
    {
        SQUARE: { int side; }
        RECTANGLE: { int length, height; }
        CIRCLE: { int radius; }
    }
}
// Also size = 12
struct Shape2
{
    char kind;
    char foo;
    short bar;
    union (kind) 
    {
        SQUARE: { int side; }
        RECTANGLE: { int length, height; }
        CIRCLE: { int radius; }
    }
}

lerno avatar Sep 28 '23 07:09 lerno