Convergent Evolution: Rejections and Acceptances


SE-0096: “Converting dynamicType from a property to an operator” is accepted with revision. “This proposal establishes dynamicType as a named operator rather than a property.” Team feedback:

The feedback on the community on the proposal was very light, but there were significant concerns about its name. After discussion, the core team agrees that x.dynamicType should use the syntax of a global function as stated by the proposal, but would prefer for that function to be spelled type(of: x) to follow the naming guidelines. The rationale is:

  • The function is side-effect free, so it should have a noun that describes what is returned.
  • “type” is ambiguously a noun, verb, and perhaps other things, so the “of” preposition is necessary for clarity.

The core team recognizes that this means that we should probably resyntax the existing sizeof/strideof functions, but that should be a follow-on discussion.

Returned for Revision

SE-0050 “Decoupling Floating Point Strides from Generic Implementations” returned for revision. “Swift strides create progressions along “notionally continuous one-dimensional values” using a series of offset values. This proposal supplements Swift’s generic stride implementation with separate algorithms for floating point strides that avoid error accumulation.” Team feedback:

There was very little feedback from the community – the feedback was positive about solving the floating point error accumulation problem indicated by the proposal, but wasn’t strongly positive about the solution.  The core team also agrees that the problem needs to be solved, however they don’t think this API change is the right approach.

The core team believes that the existing strideable API cannot efficiently and correctly handle all the real-world use cases one would want.  However, a multiplication-based implementation similar to the one proposed in SE-0050 (but potentially extended) seems sufficiently general to solve the existing use cases as well as solve the floating point error accumulation issue.  Once the design of this iterates on swift-evolution, it would be great to see a revised version of this proposal.

SE-0095: Replace protocol<P1, P2> syntax with Any<P1, P2> is returned for revision. “The current protocol<> construct, which defines an existential type consisting of zero or more protocols, should be renamed Any<>.” Team feedback:

There was an incredible amount of feedback from the community, continuing even today.  The principle problem identified by community about the proposal is that Any<T1, T2> implies very strongly an “any of T1 OR T2” relationship (aka disjunction) and not “any type conforming to T1 AND T2” relationship (aka conjunction).  There was also some related discussion as to how the Any<> syntax aligns with future generalized existential syntax, much discussion as to whether it should be spelled Any<> or any<>, and some discussion about “angle bracket blindness”.

The core team extensively discussed the feedback and considered many different possible paths forward.  The conclusion of this is that it recommends that SE-0095 be revised into a fairly different proposal, one that introduces a new infix “&” type operator for representing protocol and other type compositions.  Instead of:

func f(a : protocol<A, B>) {}
func g<T : A>(x : T) where T : B {}    or   func g<T : protocol<A, B>>(x : T) {}

You would instead write:

func f(a : A & B) {}
func g<T : A & B>(x : T) {}

The degenerate case of protocol<> needs an answer, but only for the definition of the Any typealias in the standard library, so it can be left out of the proposal.

The core team feels that this is an elegant solution for Swift 3 that conveys exactly the right intent.  When generalized existentials are introduced, an elaborated syntax can be introduced to generalize this, e.g. Any<A & B where … > or (T : A & B where …) or whatever else makes sense in context of its design.

The principle concern with this is that having an “&” operator for generic constraints leads the question of whether the language should introduce an “|” operator to represent disjunctions in type constraints (something that the type system cannot and should not support).  This is a topic that the C++ committee grappled with in its discussions of C++ concepts.  That said, the core team feels that “&” directly expresses the relationship that we want, that “|” can be addressed in the “commonly rejected proposals” list, and that other proposals for an infix operator (like +) skirt this issue but are strictly worse at communicating intent in code.


SE-0098: “Lowercase didSet and willSet for more consistent keyword casing” is rejected. “This proposal adopts consistent conjoined keyword lowercasing.” Team feedback:

The feedback on the proposal from both the core team and the community was that these should remain camel cased, particularly given that they may become user-definable aspect names in a future release.

SE-0097: Normalizing naming for “negative” attributes is rejected.

The core team agrees with the principle guiding the proposal (that negative attributes should start with “non” instead of “no”) and highly values standardized naming for attributes.  The community was lukewarm about nonescaping but pretty significantly opposed to nonreturning.

The core team discussed this at length and agreed that the problem identified by the proposal needs to be solved, but prefers to explore directions that would define away these attributes completely:

1) For noreturn, the core team prefers to explore a solution where a function can be declared as returning an non-constructable bottom type (e.g. an enum with zero cases).  This would lead to something like:

func abort() -> NoReturn { … }

This will require some new support in the compiler, but should flow better through the type system than @noreturn in function composition and other applications.  Joe Groff offered to write a proposal for this.

2) For noescape, the core team feels that the right solution is for closure arguments to default to noescape, which means that the attribute we should really need is @escaping.To provide some more details, this approach has the following advantages:

  • Most functional algorithms written in pure Swift will benefit because they are naturally noescape. The core team feels that this will reduce the boilerplate involved with writing these algorithms.
  • The compiler has enough logic in it to provide a great QoI experience when a developer doesn’t think about escaping, and tries to escape a closure – it can provide a fixit that suggests adding @escaping.
  • Recent changes (to disallow escaping closures to close over an inout parameter) are pushing the language to prefer noescape closures. noescape closures have also always been the preferred default, since they eliminate a class of retain cycle issues.
  • @autoclosure(escaping)” can be simplified and standardized to “@autoclosure @escaping

The two primary concerns with taking this direction were that it is would adversely impact resilience, and that imported Objective-C APIs would be too annoying to work with, because the compiler would have to be conservative and assume they are escaping:

On resilience, the concern with this approach is that an API may not thinking about whether a closure parameter should be escaping or not, and this behavior makes it possible that someone could write “V1” of an API and not accidentally promise noescape semantics, but then need it in “V2” of the same API.

John McCall pointed out that resilience in the type system is different than resilience in practice: An API changing to capture a closure and use it long after it was originally passed is likely to break the clients regardless of whether the type system captures this as an issue.

He argues (and the argument is strong IMO) that it is better for resilient APIs to default to @noescape, since that forces the author of V2 to think about whether they are breaking their clients. If they are doing something that is “logically” noescape in their V2, then they can unsafe bitcast away the escaping aspect of the closure. This is better than changing the client’s view of the API in any case.

On imported Objective-C API, the core team did a quick study of the Cocoa APIs and found that most closure/block parameters are escaping in practice. As such, the core team feels that it isn’t overly burdensome to ask that imported Objective-C APIs annotate their semantically noescape block parameters with the clang __attribute__((noescape)) attribute.

I’m happy to write up this proposal, but won’t have cycles to do so for several weeks. If someone else wants to take it up, that would be great.

Comments are closed.