I’m making a language with a lot of inspiration from rust and was experimenting with alternative enum syntax. It relies on literals to be types in order to convey information on the different options.
I don’t really get on well with Typescript but having the ability to use literals as types is something I really liked as a lot of the times I use static string literals as errors. and having all the variants upcast through types makes it easier to do pattern matching.
Plain-text transcription of the image:
// using rust like enum syntax
Option<T> (
| "Some" T
| "None"
)
fn match_demo() {
let some_option = Option "Some" "text";
let none_option = Option "None";
match some_option {
"Some" "hello" => print("oh hi there"),
"Some" text => print("Option is {text}"),
"None" => print("Option is {text}"),
}
}
// Or maybe more experimental syntax
Option<T> (
| T
| ()
)
fn match_demo2() {
let opt = Option "something";
match opt {
"text" => "matching directly",
var => "bind to variable",
() => "nothing",
}
}
The second syntax isn’t actually enums at all, it’s closer to a union type definition. You can’t even define
enum Colour = Red | Green | Blue
using your second syntax.Use a keyword, it’ll make your life a lot easier for parsing. If you’re reluctant on using “enum” as a keyword, you should check out OCaml/ML’s “type” keyword (they are the exact same contept: ADTs, but rust weirdly associates them with enums)
Don’t use magic strings! Not only is this hard to check for errors while writing code, you also can’t tell easily if it is a normal string, or an enum.
In my old project, my syntax looked like this:
// Flavour and vec of ingredients type Food[T] { | Pizza str, vec[T] | Cake str, vec[T] | Soup vec[T] }
not only is this easy to parse, i’d say it sticks true to its ML roots. You should check odin’s enum syntax if you’re keen on making a unique syntax.
I hate both of them. The first one is very clunky with all the
"
. The second one is not self-docummenting at all, and it makes some enums impossible.For example, you can’t represent:
enum A { B(u32) C(u32) D }
It would be
A { | u32 | u32 | () }
Also, the pipe is very awkward to type, specially depending on keyboard layout. Since it’s a rare character. If you need to separate between enums and struts and really don’t want to use the
enum
andstruct
keywords, you can use different delimiters, like:A [ u32, u32 ] B { u32, u32 }
Yeah I think you’ve made it worse than Rust in both cases. They clearly shouldn’t be strings. And the second option is just unnecessarily confusing.
Wait, why literals? Could you not have just Some, without quotes? Or does this syntax imply that enums use these constants as variant tags?
Re: the pipe symbol - i wanted to also use this syntax but have decided against because pipes just looked stranger than commas.
The Idea is that the enum acts as a union, capable of holding any of the member types, It’s not that different from using identifiers and when transpiling to rust I will probably only support variants beginning with string literals (or maybe generate them).
The main reason is that I could use type inference to define the variants in a returned anonymous enum.
I like the pipe symbol because it is useful for distinguishing between enums and structs without keywords. And I just personally think it looks better. And allow for pretty anonymous enums like
(|String |Int)
for something that can accept both a string and an integer.