Z# (Zee-sharp)

A new .NET language

Enums

Enums are groups of values that belong together. An enum is basically a value type that has its possible values predefined.

These values all have to be available at compile time, they cannot be calculated at runtime.

As with all type names, the name of the enum type has to start with a capital letter. The enum declaration looks very much like the declaration of a structure, but the use of the equals sign (and the absence of data types) make it an enum.

Here is an intuitive declaration of an enum type.

MyEnum
    opt1, opt2, opt3

TBD: Comma Operator

Because the values were not specifically listed, they are assigned by the compiler and start at 0 (zero) in order of declaration. So opt1 would be 0, opt2 would be 1 and opt3 would be 2. You can assign these values specifically.

MyEnum
    opt1 = 0
    opt2 = 10
    opt3 = 20

The literal values can be specified using any valid format. See Literals

Enums can be exported from a module. In that case it is part of the API interface and all values must be assigned explicitly. You’re less likely to break a public API when adding new enum options if its values are explicitly listed.

#export MyEnum
    opt0 = 0
    opt1 = 1
    opt2        // error!

The type of the enum in the previous example is defaulted to U8. But you can be explicit about it and / or choose a different type.

Here is how that would look:

MyEnum: I8
    opt1 = -1      // can have negative values
    opt2 = 0
    opt3 = 1

Here is an example how to do flags:

MyBigEnum: U16
    flag = 0x8000
    mask = 0x00F0

Flags are just enums with their value carefully controlled.

How to detect [Flags] for .NET interop?

Do we want additional validation on operators applied to enums and allow only Flags to use the bitwise operators?

You can even specify a string:

StrEnum: Str
    Low = "Low_Option"
    Mid = "Mid_Option"
    High = "High_Option"

Each option has a string value. Also note that the option names may also begin with a capital letter.

Leave out the string literal values:

StrEnum: Str
    Low
    Mid
    High

And now StrEnum.Low is the value "Low", the same as the name of the enum option.

Lets do bits:

MyEnum: Bit<4>      // 4-bits wide
    opt1 = 0x1
    opt2 = 0x2
    opt3 = 0x4
    opt4 = 0x8
    optx = 0x10    // error: too large!

Using the Bit<T> base type suggests that you are working with flags.

Floating point types can also be used. The auto-numbering interval for floating points is 1.0.

MyEnum: F16
    opt1, op2, opt3

opt1 would be 0.0, opt2 would be 1.0 and opt3 would be 2.0.

Or if you set a value on one option:

MyEnum: F16
    opt1 = 3.14
    opt2
    opt3

opt1 would be 3.14, opt2 would be 4.14 and opt3 would be 5.14.

There is one type that cannot be used:

MyEnum: Bool        // error!

Usage

When using an enum, the type name has to be specified too:

MyEnum
    opt1, opt2, opt3

e := MyEnum.opt1

The data type of an enum option is the enum type itself. So e in the example above is of type MyEnum with a value of 0 (zero).

When the enum type can be inferred from context it does not need repeating (optional).

MyFunc: (p1: MyEnum)
    ...

// the parameter type dictates what enum to use
MyFunc(opt2)    // MyEnum.opt2
MyFunc(.opt2)   // MyEnum.opt2 (I like this best)

If opt2 is ambiguous the type needs to be specified to resolve it.


TBD: Allow Str enums to be used as indexers into a Map.

Names: Str
    Field1
    Field2


map = (Field1 = "Field1", Field2 = "Field2")

v := map[Names.Field1]   // 'Field1'
// alternate map syntax?
v := map.Field1          // 'Field1'
v := map.$Field1         // 'Field1'

Have validation functions on Enums to verify if values are in range. In .NET any (casted) integer is valid for an Enum.

For .NET interop on enum base types that are not supported by .NET, a (record) class is generated with the options as static fields (and a private constructor).

// C#
public  // if exported
sealed class MyEnum : IEquatable<MyEnum>, IComparable<MyEnum>
{
    private readonly string _value;
    private MyEnum(string value)
        => _value = value;

    public static MyEnum opt1 = new MyEnum("opt1");
    public static MyEnum opt2 = new MyEnum("opt2");

    public static implicit operator string(MyEnum myEnum)
        => myEnum._value;
    
    // IEquatable, IComparable implementation
}

Value Iteration

How to iterate through the values of an enumeration:

MyEnum
    opt1, opt2, opt3

// compiler attributes?
loop o in MyEnum#values
    ...

// first and last option value?
first := MyEnum#first
last := MyEnum#last

TBD

Combine enums with normal types and allow extra members per enum option. (SmartEnums/Java-enums).

Extend enum to be more like Rust enums? https://doc.rust-lang.org/rust-by-example/custom_types/enum.html They’re more like union types.


Are enumerations nothing more than compile-time lists or dictionaries?

// list-literal syntax with a compile-time token
myEnum := #(option1, option2, option3)

^^ Declaring a Type as if it was a var looks weird.


Enum options and flags:

// single
myEnum
    option1 | option2 | option3

// flags (multiple)
myEnum
    flag1 & flag2 & flag3