High-Level Parameterized Enums

So there's a neat feature in Haskell and Rust (and other languages) which is that you can make an enumerated type which takes parameters. Here's Rust's version:

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

In C++ there's not built-in support for this. You have to implement it as a "tagged union"...where you have a struct that has a plain enum integer in it, and then a union of all the possibilities of what could be in that spot.

So this made me wonder what we could do to support it.

Fans of History May Recall I Made an Enumerated Type Once

I wasn't much involved with the language until it was open-sourced. But I did tinker with it in 2009

>> fruit: make-enum-type [apple orange banana mango]

>> favorite_fruit: make-enum fruit 'apple

>> set-enum favorite_fruit 'shoe
** User Error: illegal enum value ( shoe ) when 
     possibilities are [ apple orange banana mango ]

Clearly we want better than that, which might look more like:

fruit!: enum [apple [subtype [word!]] orange banana mango]

favorite-fruit: fruit/apple 'honeycrisp
; or
favorite-fruit: make fruit! ['honeycrisp]

It raises some questions about how SWITCH might be able to be hooked to have some augmented feature, where it enforces you covering all the cases for the ENUM.

I don't have any great ideas offhand...but I just was hearing a talk where this was brought up and it reminded me that maybe it would be a good example to try and think through, as a feature users could add after-the-fact.

Tthe main thing that makes this interesting to me is if it isn't built in, and yet comes off feeling like a built-in language feature.

2 Likes

Looking at Oldes's enum reminded me that I really do want to have something to offer in the enum space.

What Oldes has done is that enum makes a "utility class" for testing and mapping enum values. You still use WORD! and INTEGER!, but the OBJECT! that enum creates can translate between them.

But in a different conception where enums are something more akin to a DATATYPE!, how can you reconcile the desire to be "light" with the desire to be accurate?

fruit: make fruit! 'orange

if fruit = 'orange [  ; oh it's a word...
     assert [word? fruit]  ; oh it's not!
     ...
]

You'd have to write something more like:

assert [fruit! = type of fruit!.orange]

if fruit = fruit!.orange [
     print ["Your fruit is" fruit/name]  ; or something like this?
     ...
]

That way you'd at least get some protection against testing against members that aren't in FRUIT! (because e.g. fruit!.eggplant would error if it didn't exist in the enum).

You wouldn't get that same protection if you did an extraction, and comparison to the extraction:

if fruit/name = 'eggplant [  ; no guard against meaningless comparison
     print ["Your fruit is" fruit/name]
     ...
]

Are We Stuck With fruit = fruit!.orange ?

I really don't think it would be a good idea to overload = to say that a WORD! and a FRUIT! could be equal.

It could be that a "similarity" operator that was able to fail on "incomparables" could be useful:

if fruit like 'orange [
     comment [assert [word? fruit]]  ; we didn't check for *equality*
     ...
]

if fruit like 'eggplant [ ... ]   ; could panic, not comparable

But now, how would you use this in a switch, which goes based on equality by default?

It gets drawn out if you have to come up with enum values:

 switch fruit [
     fruit!.orange [...]
     fruit!.apple [...]
     fruit!.banana [...]
     ...
 ]

This Drawn-Out-Ness Is Typical, But...

There's nothing particularly unusual about having to "scope" your enums. And maybe you could bring them into scope as a search space:

use fruit!

switch fruit [
    orange [...]
    apple [...]
    banana [...]
    ...
]

But once you move away from WORD! you are losing some of the lightness that is Rebol's aesthetic. You've got a complex type now, with its own molding, at best something like:

>> mold fruit
== &[fruit! orange]

Other Angles: Type Constraints

With the ability to typecheck certain variables, we could constrain things to patterns that FRUIT! approves.

fruit: typed [fruit?] 'orange

This could keep you from assigning non-fruits to the variable. Though again, it wouldn't help you with meaningless comparisons.

if fruit = 'eggplant [...]

:thinking:

Anyway, just writing down some thoughts...there's some competing wishes here.

1 Like