cabin-lang / cabin

A dead simple, highly performant, extremely safe programming language.
GNU General Public License v3.0
7 stars 1 forks source link

Implement tagged unions #7

Open vi013t opened 4 months ago

vi013t commented 4 months ago

One of the core features of Cabin should be tagged unions. There are many different styles to implementing tagged unions, but I'm leaning towards non-nested type unions. For example, to declare a union that can have a value of type T or type E, you'd simply have something like T | E, with no named fields, that you can pattern match on.

A final keyword has not been decided on this yet, but one consideration is oneof. The syntax might look like this:

let Result = oneof<T> {
    T,
    Error,

    is_error = function<T>(this: Result<T>): Boolean {
        it is match this {
            value: T => false,
            error: Error => true
        };
    }
};

The function/field syntax here is a little strange, since the declaration is fundamentally different from a union variant. One possible solution is separating this kind of thing into something like a Rust impl block, but then we should probably do the same for groups for consistency, and I'm not sure if I love that idea. Regardless, it's a possibility. Another idea is we can use named union fields, which may be more consistent:

let Result = oneof<T> {
    value: T,
    error: Error,

    is_error = function<T>(this: Result<T>): Boolean {
        it is match this {
            value: T => false,
            error: Error => true
        };
    }
};

However, this often ends with deeply nested confusion and complex destructuring. This very compiler suffers from this, in which you may find Statement::Expression(Expression::Literal(Literal(LiteralValue::Group(group), 0))) on more than one occasion.

Alternatively, we can keep it simple with a pipe:

let Result<T> = T | Error;

However, this has some limitations. Firstly, it puts the generic parameter on the name of the variable, which is inconsistent with how groups and functions work. Secondly, there's no obvious way to add functions or "static" fields onto this.

vi013t commented 3 months ago

Tagged unions are now taking over the either keyword. I have yet to decide what enums will be called. Also, I wonder if it'd be handy to support a mix like Rust, where some variants can have values and some won't. This can be handy, but I'm not sure if I'd want to complicate either enums or tagged unions, or make an entirely new syntax.