Idea with pre-RFC: Typesets

I want to get feedback about the feature so i'll post it here

Gist:

Prior art:

2 Likes
struct St<A,B,typeset TS1, typeset TS2>(){
  ...
  typeset enumTs = [TS1...,TS2...];
  match enumTs::len {
    0 => {//some usage},
    1 => {another usage},
    _ => {...},
  }
  ...
}

This syntax looks like an entirely different programming language.

The pre-RFC describes what this feature would do, but not the reason why.

Generally, in every language I've used so far, wanting to special-case a closed set of types turned out, sooner rather than later, to be a code smell, for which the proper, principled alternative was the use of an interface. What's the motivation here for not just using a trait implemented for T_1T_n?

4 Likes
typeset TS = [u8: byte, String: str];
TS::enum //produces anonymous enum{byte(u8),str(String)}
TS::struct //produces anonymous struct{byte: u8, str: String}

While I see the value in converting a typeset to an enum, why would you want to convert it to a struct?

Should we consider typesets without horizontal specialization and variadic generics?

That would be better. Currently the RFC proposes at least 4 distinct features, so it's better to move some parts into a Future possibilities section.

I fully agree!

2 Likes

Seems like this can at least mostly be accomplished with a macro that generates a type with an impl of a trait Typeset that has "enum", "struct", etc. associated types, represents the type set with a frunk::HList or similar, and membership with impl of TypeInTypeset<T, U>.

Adding this a language feature seems extremely unlikely, while adding simpler language features that fix any shortcomings that the macro approach has seems more plausible.

Also note that there is a difference between a "set" (order irrelevant, no duplicates), a "multiset" (order irrelevant, duplicates possible) and a "sequence"/"tuple" (order relevant, duplicates possible), and that "enum", if anonymous, only makes sense for sets, while "struct" makes sense for sequences, so the RFC seems problematic since it doesn't talk at all about this.

3 Likes

Can you share your experience? I agree that in a lot of cases it's not what one actually wants, but why do you think that closed set of types is bad?

Because I don't see how it has any benefits over creating a (possibly sealed / private) trait and only implementing it for a set of types. As far as I know, every situation of "I need these N ≥ 1 concrete types to behave similarly for my use case" can be solved by either putting them in an enum or by implementing the same trait for them. This is basically the same argument why I consider traditional function overloading bad as well (a sentiment that the design of Rust largely shares).


To ponder a philosophical point, in my view, it's not even me / us the one(s) having to prove that the proposal is "bad". I think when proposing a new feature, the burden of proof that its benefits sufficiently outweigh the downside of the added complexity should be on the person making the proposal. IOW we shouldn't add features just because "why not"; we should add them for very good reasons only.

8 Likes

Sometimes using a marker trait can be useful to provide limited function overloading.

I can't remember the exact situation off the top of my head, but there have definitely been cases where proper sealed traits (aka the compiler knowing it's exhaustively implemented) would have saved me a lot of trouble.

A few reasons, but primarily, because it prevents extension which would otherwise be naturally allowed. With a trait, you can always add a new impl, and other people can add new implementations.

I'd argue if the intent is to have sealed traits, that that should be explicit. I can get behind a feature which says "disallow external implementations of this trait for semver reasons".

For all other uses, though, it seems much less useful than actually having a trait which encompass the types than trying to list the types. Listing all the types in one place is limiting to implementations, and repeats information that would otherwise only need to live in one place in the codebase (I'd rather only have to specify my type implements a trait in one place, rather than making the implementation and listing it in a typeset, even when implementing within the same crate).

I'd say it's similar to motivation for why rust has traits and not interfaces. Talking about what something can do than what it is leads to better overall patterns.

2 Likes

One place this would be useful is type-level-metaprogramming al la typenum. Here you can use sealed traits to dramatically reduce verbosity and makes things far easier to understand.

2 Likes