Beta Release 0.1.2

This release of Onyx brings a substantial new feature called tagged unions, and a collection of other small bugfixes.

Tagged Unions

Tagged unions are a big addition to the type system in Onyx, as they allow you to represent a new kind of type. They bring a number of safety and sanity features that Onyx had been lacking in its type system.

At their core, tagged unions store at most one type of data at a time, and know which type they are currently holding. In order to use the data in the tagged union, you must do a switch statement, which guarantees that you can only access the data if it is of the correct type.

Let's look at a code example.


MultipleTypes :: union {
    int: i32;
    float: f32;
    string: str;
}   

main :: () {
    value := MultipleTypes.{ string="Tagged unions are cool!" };

    switch value {
        case .int do println("It was an integer!");
        case .float do println("It was a float!");
        case .string do println("It was a string!");
    }
}

This code will print It was a string!, because value currently holds the string variant. This is specified with MultipleTypes.{ XXX = ... }; XXX must be one of the variants of MultipleTypes.

In order to access the data inside of the union, you can use a switch with a capture, like so.


main :: () {
    value := MultipleTypes.{ string="Tagged unions are cool!" };

    switch value {
        case .int => int_value do printf("It was an integer: {}\n", int_value);

        case .float => float_value do println("It was a float: {}\n", float_value);

        // You can also use it by pointer by placing a '&' before the variable.
        case .string => &str_value do println("It was a string: {}\n", *str_value);
    }
}

You can also directly access the fields, like you would a structure. However, instead of getting the data directly, you get an Optional of the data. You then have to use the methods of the Optional type to access the data.

MultipleTypes :: union {
    int: i32;
    float: f32;
    string: str;
}   

main :: () {
    v := MultipleTypes.{ float = 12.34 };
    
    // Using Optional.unwrap to get the data.
    // This will cause an exception if the union does not currently hold a 'float'.
    float_value := v.float->unwrap();
    println(float_value);
}

Tagged unions can also be polymorphic, just like structures. With this feature, this is how the Optional type is now defined.

Optional :: union (Value_Type: type_expr) {
    None: void;
    Some: Value_Type;
}

main :: () {
    v := Optional(i32).{ Some = 123 };

    switch v {
        case .None {
            println("No value :(");
        }

        case .Some => int_value {
            printf("Int value: {}\n", int_value);
        }
    }
}

Full Changelog

Additions:
* Tagged unions (union type)
* String literals can have unicode code points.
    - '\uXXXX' for small code points (less than U+FFFF)
    - '\UXXXXXX' for large code points
    - Does not support UTF-16 surrogate pairs
* iter.next_opt
* memory.ptr_add
* misc.any_member

Removals:

Changes:
* optional is now a tagged-union
* result is now a tagged-union
* iter.single can take a dispose function, which is called on close of the
    iterator, with the single value yielded.
* io.write_escaped_str supports escaping "\\" now.
* In Javascript runtime, made __read_from_input not defined right away, so
    it can be overridden depending on the needs of the program.

Bugfixes:
* json encoder was wrongly not encoding strings when using encode on an any.
© 2020-2024 Brendan Hansen