Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anonymous enum types (A|B) take 2 #514

Closed
wants to merge 4 commits into from

Conversation

scialex
Copy link

@scialex scialex commented Dec 10, 2014

I really liked this idea and have tried to address some of the concerns with it.

rendered

Previous discussion

Thanks to @reem for making the original version of this.

@ftxqxd
Copy link
Contributor

ftxqxd commented Dec 11, 2014

I like the idea, but I still feel that this RFC doesn’t go into enough detail in a few areas. Specifically:

  • How is A | B represented? Would it be represented the same way as enum AorB { A(A), B(B) }? If so, how is A | B | C represented? This is implied in the RFC, but I can’t find anywhere that explicitly states it.
  • Are (A | B) | C, A | (B | C), and A | B | C distinct types? The RFC says that ‘we always flatten as much as possible’, so I assume they are all the same, and are represented the same as a three-variant enum?
  • The RFC is unclear about how a as Type works in pattern-matching. Not only does the RFC contain an impossible impl for an unsized type (impl Talker for Enter | Leave), it also uses the a as Type syntax to specify what type a union type is in pattern-matching in some places, but seems to use a as Trait in some places to specify what trait the type of a union type implements (not that it is an unsized trait object type, which would be consistent with the other behaviour). This lack of a distinction between trait objects and types implementing a trait is present in quite a few places throughout the RFC.

And I also have a few problems with some details of the feature the RFC proposes:

  • The syntax a as Type when pattern-matching seem strange to me. a: Type would be more natural, but would conflict with type ascription in assignment (let x: Type = foo;). This could be removed, and fn parameters required to be fully-typed patterns.
  • The need for parentheses in a as (A | B) seems unnecessary. We already have a similar ambiguity with <: a as A < b > c parses as a as (A<B>) c, so not needing parentheses for | either seems consistent (although would be a breaking change).

While I like the idea, and while the parts of the feature that are explained at all in the RFC are explained well, with many examples, I still think that this RFC is lacking detail in some areas.

@reem
Copy link

reem commented Dec 11, 2014

For what it's worth, I'm working on another major revision to this feature which includes a more detailed explanation of existing features and will also cover relatively under explained details such as in-memory representation, compilation strategy (especially cross-crate), subtyping relationships, the exact details of conversion and coercion between joins, interaction with type inference, syntax and parsing, and other such topics.

@scialex
Copy link
Author

scialex commented Dec 11, 2014

@P1start To answer you're questions as best I can.

How is A | B represented? Would it be represented the same way as enum AorB { A(A), B(B) }? If so, how is A | B | C represented? This is implied in the RFC, but I can’t find anywhere that explicitly states it.

I am not sure at the moment what the exact best way to represent these would be but I expect that at some level, yes the layout of a union is similar to the layout of an equivalent enum.

Are (A | B) | C, A | (B | C), and A | B | C distinct types? The RFC says that ‘we always flatten as much as possible’, so I assume they are all the same, and are represented the same as a three-variant enum?

I do think that it might be possible or necessary at times for two unions that are semantically the same type (i.e. A | (B | C) and (A | B) | C) to be laid out differently with the compiler adding conversions between them when necessary.

Unsized type syntax

Fixed that was just an oversight on my part.

the a as Type syntax is just my suggestion, it is completely possible that some other one would be better.

Furthermore I address the necessity of parens in the unresolved questions section, saying I am not sure if they should be required or not. I added them in this example to be more clear.

@reem: That's cool. I would love to see it. Also I added some stuff about a possible compilation strategy, although I admit I am not familiar enough with Rust internals to know if it is workable.

@canndrew
Copy link
Contributor

I'd love to have some kind of union type in Rust, they'd be excellent for returning errors. But I disagree with the design here. Unions should just be anonymous enums in the same sense that tuples are anonymous structs, but that's not what this RFC proposes. For example you say:

The notation is order independent, A | B is the same type as B | A

presumably based on the logic that A|B can be either an A or a B and B|A can be either an A or a B so they must be the same type. But (A, B) is both an A and B and (B, A) is both an A and a B and we don't make them the same type. Similarly, we don't access the elements of x: (int, f64) by writing x.int and x.f64 which would be the dual of your suggestion to eliminate x: int | f64 by writing match x { _ as int => ..., _ as 64 => ...}. An example where this will go wrong, what if we write:

fn foo<T>(arg: int | T) {
  match arg {
    a as int => ...,
    b as T => ...,
  }
}

This'd be fine for foo<f64> but not foo<int>. You get similar problems defining things like coercion from A to A|B.

Basically, I get the feeling that if we don't make a A|B a type coproduct that behaves just like an ordinary enum then things will end up getting very messy. In this vein, my suggestion would be to name union variants after natural numbers like we do with tuple members. So you could write:

fn foo<T>(arg: int | T) {
  match arg {
    0(x) => println!("{} is an int"),
    1(x) => println!("we got a T"),
  }
}

And to instantiate them write:

fn inL() -> int | f64 {
  0(123)
}

fn inR() -> int | f64 {
  1(123.0)
}

And then just drop the other fancy stuff like methods on unions or leave it for a seperate RFC.

@nrc
Copy link
Member

nrc commented Dec 11, 2014

See also #409

@brson brson assigned brson and zwarich and unassigned brson Dec 11, 2014
@zwarich
Copy link

zwarich commented Dec 11, 2014

We discussed this briefly in the weekly triage meeting. The original RFC (#402) was closed as postponed, because there is no bandwidth to consider this feature before 1.0. Since there is even less bandwidth available for new 1.0 features at this point, the decision was made to close this RFC PR as postponed, just like the original. We encourage those who are interested in this feature to continue discussing it, just not as an RFC pull request.

@zwarich zwarich closed this Dec 11, 2014
@reem
Copy link

reem commented Dec 12, 2014

@zwarich My impression was that the RFC was also closed due to lack of clarity on several implementation issues. I filled out a new version of the original RFC last night, and was thinking to post it today even with the understanding it would not be accepted for 1.0.

My impression was that the RFC process still works for post-1.0 features, they may just be marked as postponed instead of "will do" or can be accepted as "will do later". Are RFCs not the place to discuss post-1.0 features?

@zwarich
Copy link

zwarich commented Dec 12, 2014

@reem The original RFC was postponed, which means that we would like until after 1.0 to consider it again. As per the README, a PR on the RFCs repo is a request for an RFC to be moved to one of three states: acceptance, postponement until after 1.0, or outright closure.

When an RFC is postponed, there may still be feedback for how to improve the RFC in the future, but that doesn't mean it would be reconsidered before 1.0 if all of that feedback were addressed. The proper place for the discussion of a postponed RFC is a plain issue, not a PR.

Disclaimer: this is just my interpretation of the process.

@reem
Copy link

reem commented Dec 12, 2014

@zwarich Ok, that seems reasonable. My intention in submitting my new version of the RFC would simply be to get further feedback and see if the feature is in a state to be implemented based on the new RFC after 1.0 - I have no delusions about this feature landing pre-1.0.

@huonw huonw mentioned this pull request Jul 16, 2015
@Centril Centril added A-syntax Syntax related proposals & ideas A-typesystem Type system related proposals & ideas A-expressions Term language related proposals & ideas A-sum-types Sum types related proposals. A-structural-typing Proposals relating to structural typing. T-lang Relevant to the language team, which will review and decide on the RFC. labels Nov 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-expressions Term language related proposals & ideas A-structural-typing Proposals relating to structural typing. A-sum-types Sum types related proposals. A-syntax Syntax related proposals & ideas A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants