[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Matthew Johnson matthew at anandabits.com
Tue May 24 14:20:41 CDT 2016

> On May 24, 2016, at 2:07 PM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
> 'Any' is definitely a better name, but I think this is still syntax only a compiler can love. If we're going to repaint this bikeshed, I think we should also consider as an alternative some form of infix syntax for composing constraints. Rust uses `P1 + P2`, and various C++ proposals have suggested `P1 && P2`. Some pros(+) and cons(-) I can see with this approach:

Using infix operator syntax was discussed.  IIRC, the conclusion of the discussion is that it would be an alternative shorthand for the more verbose Any<> syntax (which would be necessary when you have constraints, but in that case you probably want to use a typealias anyway).  The shorthand was deemed likely impractical for Swift 3 and the change from `protocol` to `Any` a good idea to get the breaking change out of the way.  How do you feel about that approach?

> + Infix notation works not only in type position, but constraint position as well, providing a nicer way to express multiple type variable constraints:
> 	var x: P && Q
> 	func foo<T: P && Q, U>(x: T)
> + Infix notation feels subjectively lighter, being less nesty and angle-bracket-blinding. Compare the above with:
> 	var x: Any<P, Q>
> 	func foo<T: Any<P,Q>, U>(x: T)
> Particularly in the second declaration, I find the nested angle brackets and comma delimiters to be hard to visually parse.
> ± Like 'Any', an infix operator doesn't pass judgment on what kinds of constraint is being applied, so it naturally extends to expressing class-with-protocol constraints.
> - Infix notation doesn't provide an obvious place for generalized existentials to hang secondary constraints. The angle brackets for Any provide an enclosed syntactic space we can easily stuff a 'where' clause:
> 	func sum(_ collection: Any<Sequence where Element == Int>) -> Int { ... }
> (though this notation still feels heavy and awkward to me).
> - The bracketing of `Any` might let us address the curious case of protocol vs existential metatypes in a better way. Right now, the static metatype for a protocol type (the type of `P.self`) is spelled `P.Protocol`, and the dynamic metatype for any type conforming to the protocol (the erased type of `T.self` where T: P) is spelled `P.Type`. Unintuitively, when a protocol type is substituted into `T.Type` in a generic context, you get the static type `P.Protocol` rather than `P.Type`, for soundness reasons:
> 	func staticType<T>(of _: T) -> T.Type { return T.self }
> This substitution behavior could be made clearer if we moved a dynamic metatype's `.Type` into the Any brackets, so that `Any<P.Type>` would be the dynamic metatype of any type conforming to P, and `Any<P>.Type` would be the static metatype of the type `Any<P>`. Infix notation doesn't provide an opportunity to make this clarification.
> - A few people have also noted the similarity between Any<...> and normal generic types. This is potentially confusing today, but with enough magic language features from the future, Any *could* conceivably be made a library feature in the fullness of time:
> 	// Let's say 'protocol' constraints indicate second-order constraint variables:
> 	enum Any<Constraints: protocol> {
> 		// And we have GADTs:
> 		case value<T: Constraints>(T)
> 	}
> 	// And we have user-defined value subtyping:
> 	extension <Constraints: protocol, T: Constraints> T: Any<Constraints> { … }

This is cool.  If it is deemed a likely direction it is a good case for using the uppercase `Any`.  The preference I stated for lowercase is based on trying to apply our casing rules constantly and library types use uppercase.

> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

More information about the swift-evolution mailing list