this post was submitted on 14 Jan 2025
24 points (90.0% liked)

Programming

17810 readers
505 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities !webdev@programming.dev



founded 2 years ago
MODERATORS
 

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",
  }
}
top 6 comments
sorted by: hot top controversial new old
[–] calcopiritus@lemmy.world 14 points 1 day ago

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 and struct keywords, you can use different delimiters, like:

A [
 u32,
 u32
]

B {
 u32,
 u32
}
[–] fxomt@lemm.ee 5 points 1 day ago

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.

[–] FizzyOrange@programming.dev 8 points 1 day ago

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.

[–] Kacarott@aussie.zone 2 points 1 day ago

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.

[–] verstra@programming.dev 8 points 1 day ago (1 children)

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.

[–] Val@lemm.ee 3 points 1 day ago

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.