Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding two enums that share some same identifiers

Tags:

raku

Is it possible to define two enums in raku that share the same identifiers?

For example, if I have the following code:

#!/usr/bin/raku
use v6;

enum Color <Red Blue>;
enum TrafficLight <Red Green>;

sub MAIN(
    Color:D :c(:$color)!, #= the color
    TrafficLight:D :t(:$traffic-light)!, #= the traffic-light
) {
    say "Selected $color, Selected $traffic-light"
}

you can see that here, the identifer Red is part of the enum color and the enum TrafficLight.

But when I execute this script, I get the Redeclaration-exception:

$ ./test.p6
Potential difficulties:
    Redeclaration of symbol 'Red'
    at /home/martin/mnt/release-notes/./scripts/test.p6:5
    ------> enum TrafficLight <Red Green>⏏;

Usage:
  ./scripts/test.p6 -c|--color=<Color> (Blue Red) -t|--traffic-light=<TrafficLight> (Green Red)

    -c|--color=<Color> (Blue Red)                    the color
    -t|--traffic-light=<TrafficLight> (Green Red)    the traffic-light

Interestingly, when I execute this script with the parameters -c=Blue and -t=Red, the output is that that I would expect:

$ ./test.p6 -c=Blue -t=Red
Potential difficulties:
    Redeclaration of symbol 'Red'
    at /home/martin/mnt/release-notes/./scripts/test.p6:5
    ------> enum TrafficLight <Red Green>⏏;
Selected Blue, Selected Red

But when I exeucte this script with the parameters -c=Red and -t=Green, it doesn't work at all and error-code 2 is returned (showing the help-message).

My questions are now:

  • Is it possible to define two same Enum-Identifiers that have the same name (but different types). Maybe it is possible to define an enum similar to class-enums in C++ with a namespace surrounding them?
  • Is it possible to catch this exception somehow?

Thanks

like image 231
byteunit Avatar asked Sep 30 '20 16:09

byteunit


People also ask

Can 2 enums have the same value?

1. Two enum names can have same value. For example, in the following C program both 'Failed' and 'Freezed' have same value 0.

Can two enums have the same name?

No two enum members can have the same name. Each enum member has an associated constant value.

Can we have enum containing enum with same constant?

The values assigned to the enum names must be integral constant, i.e., it should not be of other types such string, float, etc. All the enum names must be unique in their scope, i.e., if we define two enum having same scope, then these two enums should have different enum names otherwise compiler will throw an error.

Do enum values have to be unique?

Show activity on this post. I also found the same idea on this question: Two enums have some elements in common, why does this produce an error? Enum names are in global scope, they need to be unique.


2 Answers

The problem is that enum create symbols in their scope. Your code

enum Color <Red Blue>;
enum TrafficLight <Red Green>;

is basically doing

my \Color = Map.new(Red => 0, Blue => 1) does Enumeration;
my \Red  := Color<Red>;
my \Blue := Color<Blue>;

my \Traffic-Light = Map.new(Red => 0, Green => 1) does Enumeration;
my \Red   := Traffic-Light<Red>;
my \Green := Traffic-Light<Green>;

Thus you can see what generates the warning -- you can't create the symbol twice anymore than you can declare $x twice in the same scope. Nonetheless, the two enum classes still exist, and can create values from the string "Red". One solution I've used in this case is to create a package and call the enum inside the package: Enum

package Color        {  enum Enum <Red Blue>   }
package TrafficLight {  enum Enum <Red Green>  }

sub MAIN(
    Color::Enum:D        :c(:$color        )!, #= the color
    TrafficLight::Enum:D :t(:$traffic-light)!, #= the traffic-light
) {
    say "Selected $color, Selected $traffic-light"
}

If you want to match against the values, then you just say Color::Red or TrafficLight::Green, or if you store things in a module so that you can use, you could still use just Red or Green, just not in the same scope. So you could do:

sub MAIN(
    Color::Enum:D        :c(:$color        )!, #= the color
    TrafficLight::Enum:D :t(:$traffic-light)!, #= the traffic-light
) {
    say "Selected $color, Selected $traffic-light"

    { # new scope 
        use MyEnums::Color;
        given $color { 
            when Red   { ... }
            when Green { ... }
        }
    }

    { # separate new scope 
        use MyEnums::TrafficLight;
        ... 
    }
}
like image 126
user0721090601 Avatar answered Oct 22 '22 13:10

user0721090601


If you only want the enum exported and not the values in the enum, you can use a constant with a do block.

constant Color = do {
  my enum Color <Red Blue>;
  Color
}

constant Traffic-Light = do {
  my enum Traffic-Light <Red Green>;
  Traffic-Light
}

By doing this you only have to access the values in the enum by its fully qualified name, or by hash access.

say Color::Red.raku;
say Traffic-Light::Red.raku;

say Color::{'Red'}.raku;
say Traffic-Light::{'Red'}.raku;

say Red; # ERROR: Undeclared name: Red
multi foo ( Color $c ){
  say "the color $c"
}
multi foo ( Traffic-Light::Red ){
  say "stop"
}
multi foo ( Traffic-Light::Green ){
  say "go"
}
multi foo ( Str $s ){
  samewith Traffic-Light::{$s} // Color::{$s}
}

foo Color::Red;  # the color Red
foo Color::Blue; # the color Blue

foo Traffic-Light::Red;   # stop
foo Traffic-Light::Green; # go

foo 'Green'; # go
foo 'Red';   # stop
foo 'Blue';  # the color Blue
like image 30
Brad Gilbert Avatar answered Oct 22 '22 14:10

Brad Gilbert