Pre-RFC: Tuple-Style Syntax for Representing Certain Kinds of Enums


#1

Summary

Add a “Either” type to the language that allows representing certain kinds of enums. Similar to how a tuple of the type (A, B, C) would be equivalent to a struct defined below, an either of the type =<A, B, C>= would represent the enum as follows:

type Tuple = (A, B, C);

struct TupleLikeStruct {
    a: A,
    b: B,
    C: C,
}

type Either = =<A, B, C>=;

enum EitherLikeEnum {
    A(A),
    B(B),
    C(C),
}

Both are similar in function, however not usage. Just like not all structs can be easily represented by tuples, not all enums can be represented by eithers.

Motivation

Many functions may error return enums that represent the possibility of errors in multiple domains, but these enums only rewrap types already defined. A built-in type would cause such functions to be less verbose.

enum Error {
    Io(io::Error),
    Fmt(fmt::Error),
    Send(SendError),
}

fn do_stuff() -> Error {
    ...
}

fn do_stuff_improved() -> =<io::Error, fmt::Error, SendError>= {
    ...
}

While this code example lacks an Error implementation for Error, a built-in either type would also allow for implementations of Error and similar traits in the standard library.

In addition, while eithers could be used to replace the Result<T, E> type, it is intended to have a completely different usage. It may not be entirely uncommon for a function to have the return type -> Result<_, =<A, B, C>=>

Detailed design

Type

An either’s type could be expressed with the strawman syntax =<[types]>=, where [types] is zero or more types, with a comma in between each.

=<A, B, C>=

Initializing

An either can be initialized to any one of it’s valid values with the following syntax: =<[expr]>=, where [expr] is any expression whose type is one of the either’s types.

struct Foo(u32);
struct Bar(u64);

fn get_either() -> =<Foo, (Foo, Bar)>= {
    =<Foo>=
}

Attempting to initialize an empty either using the form =<>= is a compile time error.

Pattern Matching

An either would be matched with the form =<[pattern]: [type]>=, where [pattern] is an inner pattern to be further matched, such as a name, and where [type] is the type of the partial either you are trying to match.

match get_either() {
    =<foo: Foo>= => {}
    =<(foo, bar): (Foo, Bar)>= => {}
};

Empty Eithers and Eithers With One Value

An either with the type =<>= would be equivalent to an enum with the declared enum Void {}. In essence, it would have 0 possible states, and be impossible to obtain in safe rust.

An either with exactly one possible value can statically be guaranteed that it will only ever be that type. For example =<A>= would be functionally equivalent to A, and optimally compile to the same machine code.

Size

The size of an either is equivalent to the size of the enum that is equivalent to the either.

Drawbacks

  • Added complexity to the language. This RFC

Alternatives

  • Matching could also be done the same as enums. ie. Foo(foo) to match =<Foo, _>=

Unresolved questions

  • Should an either be called something else? Either is a callback to the Haskell type of the same name, and the removed Either enum in rust that Result replaced.
  • Should it be an error to have the same type twice in an either? ie. =<Foo, Foo>=.
  • Should two eithers that differ only in their order be equal on the type level? In other words, should =<A, B>= be the same as =<B, A>=?

Informal questions

  • Should I try to write my proposed syntaxes in something like Extended BNF, just provide examples, or should I just keep it as is?

Informal

I don’t think the strawman syntax I came up with was very great for readability. The other alternative I came up with was -<>-, however that has issues with function signatures. Over =<>= and -<>-, =<>= has the advantage of being only difficult to mentally parse in match blocks, instead of function signatures. Since function signatures already tend to be difficult to read at times, I chose =<>=.


#2

Take a look at how other languages do this. They sometimes use the | (pipe).


#3

Anonymous enums have been suggested and have been postponed because they are a good idea, but the design and implementation are too messy right now.

Comment on the rfc-issue if you have some new insights to add that weren’t in the RFC (I suggest you read the RFC, since it has discussed many pro’s and cons).


#4

That’s what I’m describing? Well I guess that does make sense. I didn’t really find anything when I googled a few phrases, but anonymous enums never came to mind. I’ll read that when I’ll get on my computer.