> On 9 Mar 2017, at 08:07, Daniel Duan via swift-evolution 
> <[email protected]> wrote:
> 
> Thanks for the thoughtful feed Xiaodi! Replies are inline. I'm going to 
> incorporate some of the responses into the proposal.
> 
> On Mar 8, 2017, at 9:56 PM, Xiaodi Wu <[email protected] 
> <mailto:[email protected]>> wrote:
> 
>> The rendered version differs from the text appended to your message. I'll 
>> assume the more fully fleshed out version is what you intend to submit. 
>> Three comments/questions:
>> 
>> Enum Case "Overloading"
>> 
>> An enum may contain cases with the same full name but with associated values 
>> of different types. For example:
>> 
>> enum Expr {
>>     case literal(Bool)
>>     case literal(Int)
>> }
>> The above cases have overloaded constructors, which follow the same rules as 
>> functions at call site for disambiguation:
>> 
>> // It's clear which case is being constructed in the following.
>> let aBool: Expr = .literal(false)
>> let anInt: Expr = .literal(42)
>> User must specify an as expression in sub-patterns in pattern matching, in 
>> order to match with such cases:
>> 
>> case .literal(let value) // this is ambiguous
>> case .literal(let value as Bool) // matches `case literal(Bool)`
>> 
>> Comment/question 1: Here, why aren't you proposing to allow `case 
>> .literal(let value: Bool)`? For one, it would seem to be more consistent.
> 
> The example in proposal doesn't include any labels. Are you suggesting two 
> colons for sub-patterns with labels? Like `case .literal(value: let value: 
> Bool)`?  This looks jarring. But I'm definitely open to other suggestions.

No, I think he was proposing replicating variable declaration syntax, where the 
colon is used preceding the type (instead of using as):

let value: Bool = ...

>> Second, since we still have some use cases where there's Obj-C bridging 
>> magic with `as`, using `as` in this way may run into ambiguity issues if 
>> (for example) you have two cases, one with associated value of type `String` 
>> and the other of type `NSString`.
> 
> Either this should be rejected at declaration, or we need a way to accept a 
> "pre-magic" resolution at pattern matching, when this scenarios is at hand. 
> I'm on the phone so I can't verify. Wouldn't function overloading face a 
> similar problem?
> 
>> Also, since enum cases are to be like functions, I assume that the more 
>> verbose `as` version would work for free: `case .literal(let value) as 
>> (Bool) -> Expr`?
>> 
> 
> This is not being proposed. When a user sees/authors a case, their 
> expectation for the declared case constructor should resemble that of a 
> function. Pattern matching was considered separately since it's not relatable 
> syntactically.
>>  
>> <https://github.com/dduan/swift-evolution/blob/SE0155-rev2/proposals/0155-normalize-enum-case-representation.md#alternative-payload-less-case-declaration>Alternative
>>  Payload-less Case Declaration
>> 
>> In Swift 3, the following syntax is valid:
>> 
>> enum Tree {
>>     case leaf() // the type of this constructor is confusing!
>> }
>> Tree.leaf has a very unexpected type to most Swift users: (()) -> Tree
>> 
>> We propose this syntax declare the "bare" case instead. So it's going to be 
>> the equivalent of
>> 
>> enum Tree {
>>     case leaf // `()` is optional and does the same thing.
>> }
>> 
>> 
>> Comment/question 2: First, if associated values are not to be modeled as 
>> tuples, for backwards compatibility the rare uses of `case leaf()` should be 
>> migrated to `case leaf(())`.
>> 
> Yes, and when user uses a arbitrary name when they should have used a label, 
> or when labels are misspelled, the compiler should suggest the correct 
> labels. I wasn't sure how much of migrator related thing should go into a 
> proposal. Perhaps there should be more.
>> Second, to be clear, you are _not_ proposing additional sugar so that a case 
>> without an associated value be equivalent to a case that has an associated 
>> value of type `Void`, correct? You are saying that, with your proposal, both 
>> `case leaf()` and `case leaf` would be regarded as being of type `() -> 
>> Tree` instead of the current `(()) -> Tree`?
>> 
> Correct. I'm _not_ proposing implicit `Void`.
>> [The latter (i.e. `() -> Tree`) seems entirely fine. The former (i.e. 
>> additional sugar for `(()) -> Tree`) seems mostly fine, except that it would 
>> introduce an inconsistency with raw values that IMO is awkward. That is, if 
>> I have `enum Foo { case bar }`, it would make case `bar` have implied 
>> associated type `Void`; but, if I have `enum Foo: Int { case bar }`, would 
>> case `bar` have raw value `0` of type `Int` as well as associated value `()` 
>> of type `Void`?]
>> 
>> 
>>  
>> <https://github.com/dduan/swift-evolution/blob/SE0155-rev2/proposals/0155-normalize-enum-case-representation.md#pattern-consistency>Pattern
>>  Consistency
>> 
>> (The following enum will be used throughout code snippets in this section).
>> 
>> indirect enum Expr {
>>     case variable(name: String)
>>     case lambda(parameters: [String], body: Expr)
>> }
>> Compared to patterns in Swift 3, matching against enum cases will follow 
>> stricter rules. This is a consequence of no longer relying on tuple patterns.
>> 
>> When an associated value has a label, the sub-pattern must include the label 
>> exactly as declared. There are two variants that should look familiar to 
>> Swift 3 users. Variant 1 allows user to bind the associated value to 
>> arbitrary name in the pattern by requiring the label:
>> 
>> case .variable(name: let x) // okay
>> case .variable(x: let x) // compile error; there's no label `x`
>> case .lambda(parameters: let params, body: let body) // Okay
>> case .lambda(params: let params, body: let body) // error: 1st label 
>> mismatches
>> User may choose not to use binding names that differ from labels. In this 
>> variant, the corresponding value will bind to the label, resulting in this 
>> shorter form:
>> 
>> case .variable(let name) // okay, because the name is the same as the label
>> case .lambda(let parameters, let body) // this is okay too, same reason.
>> case .variable(let x) // compiler error. label must appear one way or 
>> another.
>> case .lambda(let params, let body) // compiler error, same reason as above.
>> Comment/question 3: Being a source-breaking change, that requires extreme 
>> justification, and I just don't think there is one for this rule. The 
>> perceived problem being addressed (that one might try to bind `parameters` 
>> to `body` and `body` to `parameters`) is unchanged whether enum cases are 
>> modeled as tuples or functions, so aligning enum cases to functions is not 
>> in and of itself justification to revisit the issue of whether to try to 
>> prohibit this. 
>> 
> To reiterate, here patterns are changed not for any kind of "alignment" with 
> function syntax. It changed because we dropped the tuple pattern (which 
> remains available for matching with tuple values, btw), therefore we need to 
> consider what a first-class syntax for enum case would look like.
> 
> The justification for this breaking change is this: with tuples, labels in 
> pattern is not well enforced. User can skip them, bind value to totally 
> arbitrary names, etc. I personally think emulating such rule prevents us from 
> making pattern matching easier to read for experienced devs and easier to 
> learn for new comers. 
> 
> It's reasonable to expect existing patterns in the wild either already use 
> the labels found in declaration or they are matching against label-less 
> cases. In other words, existing code with good style won't be affected much. 
> For the code that actually would break, I think the migrator and the compiler 
> can provide sufficient help in form of migration/fixits/warnings.
> 
> Ultimately I think requiring appearance of labels one way or another in 
> patterns will improve both the readability of the pattern matching site as 
> well as forcing the author of case declaration consider the use site more.
>> In fact, I think the proposed solution suffers from two great weaknesses. 
>> First, it seems ad-hoc. Consider this: if enum cases are to be modeled as 
>> functions, then I should be able to write something intermediate between the 
>> options above; namely: `case .variable(name:)(let x)`. Since `.variable` 
>> unambiguously refers to `.variable(name:)`, I should also be allowed to 
>> write `.variable(let x)` just as I am now.
>> 
> Again, patterns are not to be modeled after functions. Only the declaration 
> and usage of case constructors are.
>> Second, it seems unduly restrictive. If, in the containing scope, I have a 
>> variable named `body` that I don't want to shadow, this rule would force me 
>> to either write the more verbose form or deal with shadowing `body`. If a 
>> person opts for the shorter form, they are choosing not to use the label.
>> 
> In fact this (to avoid label conflict in nesting) is the only reason the 
> longer form allows rebinding to other names at all! You say "unduly 
> restrictive", I say "necessarily flexible" :)
> 
>> 
>> Only one of these variants may appear in a single pattern. Swift compiler 
>> will raise a compile error for mixed usage.
>> 
>> case .lambda(parameters: let params, let body) // error, can not mix the two.
>> Some patterns will no longer match enum cases. For example, all associated 
>> values can bind as a tuple in Swift 3, this will no longer work after this 
>> proposal:
>> 
>> // deprecated: matching all associated values as a tuple
>> if case let .lambda(f) = anLambdaExpr {
>>     evaluateLambda(parameters: f.parameters, body: f.body)
>> }
>> 
>>  
>> <https://github.com/dduan/swift-evolution/blob/SE0155-rev2/proposals/0155-normalize-enum-case-representation.md#source-compatibility>_______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to