[swift-evolution] [Proposal] Disallow redundant `Any<...>` constructs

Matthew Johnson matthew at anandabits.com
Fri May 20 10:36:46 CDT 2016


> On May 20, 2016, at 10:18 AM, Tony Allevato via swift-evolution <swift-evolution at swift.org> wrote:
> 
> What's the behavior if you have something like this:
> 
> struct Foo<T, U> {
>     func foo(bar: Any<T, U>) { ... }
> }
> let f = Foo<A, A>
> 
> …?

We already discussed this topic.  You cannot use names dependent on generic arguments in `Any`.  This is the same as with `protocol` today.  This is because many times it would be invalid (if T was String and U was Int in your example).  

The closest you could get is if the compiler used the constraints on T and U to synthesize a similarly constrained Any.  However, I don’t see why you would do that and believe it would be more confusing.  If you want an existential with the same constraints as your generic arguments we should do that using the typealias mechanism discussed in the generalized existential proposal.

typealias Foo = Any<…constraints…>
typealias Bar = Any<…constraints…>

struct Foo<T: Foo, U, Bar> {
    func foo(bar: Any<Foo, Bar>) { ... }
}

Thorsten’s idea to lowercase `Any` to `any` is a very good one.  This makes it clear that `Any` does not behave like user-defined generic types (where it *is* possible to use generic arguments as long as they are suitably constrained).

> 
> More generally, my fear is that being *too* restrictive about banning redundant types could get you into a situation through generic metaprogramming where you might *want* that argument to coalesce to just "A", but if the redundancy check is implemented a certain way, you've just prevented a perhaps legitimate usage with no easy workaround.
> 
> I'm inclined to just say: minimize redundant types automatically and if a user wants to write something redundant, that's fine—so a variable declared with type `Any<A, A>` would actually be of type `A`. You might be able to avoid this if you only check the *literal* type or type parameter name going into the Any<>, but I'm not sure how much difficulty that is on the type checking side, and then you'd only catch some of the cases and you'd still have to be able to minimize.

Yes, I have made the same argument.  We should not be arbitrarily banning valid, well-defined syntactic constructs for stylistic reasons.  They should just be simplified by the compiler such that all syntactic forms are equivalent.  

On the other hand, as you note, if there are implementation reasons for introducing restrictions that is an entirely separate conversation.  If we cannot simplify conceptually equivalent syntactic forms such that they have identical behavior that would be a valid reason to consider syntactic restrictions.

> 
> 
> On Fri, May 20, 2016 at 7:53 AM Adrian Zubarev via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> What I meant here is, that the example should produce an error because it is basically `Any<Any<Any<…>>>` which should be banned. It depends on the context. Using typealias in general can be allowed but it depends how you will use and nest it.
> 
> This example is valid:
> 
> protocol A {} protocol B {} protocol C {}
> 
> typealias AB = Any<A, B>
> typealias ABC = Any<AB, C>
> 
> If we ban `Any<Any<...>>` one should not abuse `typealias` to achieve that, even if its useless. I’m not sure how hard that would be implement that restriction.
> 
> The example you was pointing at wouldn’t work anyway because the rule #3 and rule #4 should raise an error. Thats how I see it.
> 
> If I’m not correct please provide an example that makes more sense. :)
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 20. Mai 2016 bei 16:44:35, Matthew Johnson (matthew at anandabits.com <mailto:matthew at anandabits.com>) schrieb:
> 
>> 
>> 
>> Sent from my iPad
> 
>> 
>> On May 20, 2016, at 4:39 AM, Adrian Zubarev via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>>> This is a follow up proposal to SE-0095 <https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md> which should be considered for Swift 3 if SE-0095 will be accepted.
>>> 
>>> Here is the formatted draft: https://github.com/DevAndArtist/swift-evolution/blob/master/proposals/nnnn-ban-redundancy-in-any-existential.md <https://github.com/DevAndArtist/swift-evolution/blob/master/proposals/nnnn-ban-redundancy-in-any-existential.md>
>>> 
>>> Please provide your feedback in this thread, and don’t make a race who is making a better proposal on the exact same topic.
>>> 
>>> If you spot any types or other mistakes I’d be happy to see you pointing me to them. ;)
>>> 
>>> -- 
>>> Adrian Zubarev
>>> Sent with Airmail
>>> Disallow redundant Any<...> constructs
>>> 
>>> Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
>>> Author: Adrian Zubarev <https://github.com/DevAndArtist>
>>> Status: Awaiting review <x-msg://300/#m_-6273636555714890771_rationale>
>>> Review manager: TBD
>>> Introduction
>>> 
>>> This is a follow up proposal to SE–0095 <https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md>, if it will be accepted for Swift 3. The current concept of Any<...> introduced in SE–0095 will allow creation of redundant types like Any<A> == A. I propose to disallow such redundancy in Swift 3 to prevent breaking changes in a future version of Swift.
>>> 
>>> Swift-evolution thread: [Proposal] Disallow redundant Any<...> constructs <>
>>> Motivation
>>> 
>>> If SE–0095 will be accepted there will be future proposals to enhance its capabilities. Two of these will be Any-type requirement (where type could be class, struct or enum) and Class requirement. Without any restrictions these will introduce more redundancy.
>>> 
>>> As said before it is possible to create redundant types like Any<A> == A or endless shadowed redundant nesting:
>>> 
>>> typealias A_1 = Any<A>
>>> typealias A_2 = Any<A_1>
>>> typealias A_3 = Any<A_2>
>>> /* and so on */
>>> 
>>> This proposal should ban redundancy right from the beginning. If there might be any desire to relax a few things, it won’t introduce any breaking changes for Any<...> existential.
>>> 
> 
>> Why do you think having multiple typealiases for the same type is a problem?  The whole point of typealias is to alias the name of the type.  
> 
>> 
>> 
>>> Proposed solution
>>> 
>>> If empty Any<> won’t be disallowed in SE–0095, we should disallow nesting empty Any<> inside of Any<...>.
>>> 
>>> Disallow nesting Any (type refers to current typealias Any = protocol<>) inside of Any<...>.
>>> 
>>> Disallow Any<...> containing a single Type like Any<Type>.
>>> 
>>> The first three rules will ban constructs like Any<Any<>, Type> or Any<Any, Type> and force the developer to use Type instead.
>>> 
>>> Disallow nesting a single Any<...> inside another Any<...>.
>>> e.g. Any<Any<FirstType, SecondType>>
>>> Disallow same type usage like Any<A, A> or Any<A, B, A> and force the developer to use A or Any<A, B> if A and B are distinct.
>>> 
>>> Disallow forming redundant types when the provided constraints are not independent.
>>> 
>>> // Right now `type` can only be `protocol` but in the future Any<...>   
>>> // could also allow `class`, `struct` and `enum`.
>>> // In this example `B` and `C` are distinct.
>>> type A: B, C {}   
>>>       
>>> // all following types are equivalent to `A`
>>> Any<A, Any<B, C>>
>>> Any<Any<A, B>, C>
>>> Any<Any<A, C>, B>
>>> Any<A, B, C>
>>> Any<A, B>
>>> Any<A, C>
>>> 
>>> If all contraints form a known Type provide a Fix-it error depending on the current context. If there is more than one Type, provide all alternatives to the developer.
>>> 
>>> Using Any<...> in a generic context might not produce a Fix-it error:
>>> 
>>> protocol A {}
>>> protocol B {}
>>> protocol C: A, B {}
>>>           
>>> // there is no need for `Fix-it` in such a context
>>> func foo<T: Any<A, B>>(value: T) {}
>>> 
>>> Impact on existing code
>>> 
>>> These changes will break existing code. Projects abusing Any<...> to create redundant types should be reconsidered of usings the equivalent Type the compiler would infer. One would be forced to use A instead of Any<A> for example. A Fix-it error message can help the developer to migrate his project.
>>> 
>>> Alternatives considered
>>> 
>>> Leave redundancy as-is for Swift 3 and live with it.
>>> Deprecate redundancy in a future version of Swift, which will introduce breaking changes.
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160520/b76fb4f1/attachment.html>


More information about the swift-evolution mailing list