I'm surprised this proposal wasn't returned for revision, given how active the debate on the first label was, and how many "+1"s were conditional on the first label not being omitted.
I've run out of time I can spend compiling this tonight, but out of first 107/194 posts (where a vote was cast):
- For (as proposed): 5
- For (if first label added): 18
- Against (because first label omitted): 3
- Against (other): 3
- Neutral: 2 (with 1 calling out first label omission as odd)
That feels like consensus was building around being for iff the first label could be spelled out.
I understand the core team’s decision although I’m in favor of a more consistent and unified approach to MTC. At least for Swift 5, though, there is nothing that can be done.
I think something we could do is optimize formatting tools to help us avoid the “Binding” situation. I don’t know if it would be possible for formatting tools to autocomplete calls as MTC calls only if the first label (of the function definition) is unlabeled.
I don't think you're differentiating "for (but I think it would be better if you could add the first label)" from "for (but only if you can add the first label)".
I was looking forward to this proposal but now I am very disappointed in it. In its current form I find it to be unusable as it makes the call site so much harder to understand and I have to strain to find what closure is the one with the missing label.
The need for multiple trailing closures clearly exists but I'd rather not have this feature at all if this is what we end up for the future.
This decision feels like repeating the history of SE-0270, which started a discussion between the core team and the rest of the community only 3 month ago.
Please remain civil, thank you. (Reply to a flagged post.)
Functions that will adapt well to the proposal's API naming guideline amendments certainly exist, ones that will work well the "asymmetric form" that omit the first closure label. I wonder if developers that disagree so strongly with this proposal will at least concede this point? Existing single trailing closure syntax is asymmetric in sort of the same way and I never saw a cry for its abolishment.
But it sounds like such developers at the least think the ones like Binding
that call out for symmetry will turn out to be numerous. Is that the point, or are the objections philosophical in nature?
As standard library and frameworks are revisited in reaction to this acceptance, and overloads added where appropriate, I would hope that the core team will learn the extent of the API functions that do or do not align well to this proposal.
I'm not automatically sold on the notion that API that work poorly with this proposal, and demand symmetry, won't be significant in number or importance. However, I take comfort that adding the option of providing the first closure label where it works better would, it seems, be an additive change to this proposal.
I expect developers who dislike this proposal will continue to write code with the canonical syntax Binding(get: { ... }, set: { ... })
and suffer no ill effects. I do hope though that autocomplete doesn't become their enemy and always expand with syntax they dislike. Surely there's a way to avoid that.
From the proposal:
In this case, the trailing closure syntax is harder to read : the role of the trailing closure is unclear
By preventing a label for the first trailing closure, now the role of the first trailing closure is unclear.
The whole point of this was to make it clear which closure was doing what, this has merely rearranged that problem rather than solved it. Eg in Binding it’s not as clear what the first closure does (get) as it is for the second closure (set)
It doesn’t have to be consistent with single trailing closure syntax because it’s a different situation where because there are multiple of something disambiguation is required.
It’s been mentioned how the core team wished it was up to the API author to force the label in single trailing closure syntax. But in this new syntax this opportunity has been deliberately missed...
Also from the proprosal:
and something about the asymmetry is unsettling.
how has this been solved in the accepted proposal? As others have pointed out with the Binding example it’s still unsettlingly asymmetrical.
Forgive me if I’m missing something or got something wrong as this seems completely illogical, so I feel I must be misunderstanding.
This is the odd part about the announcement and rationale. All the voluminous and strong feedback has been taken In consideration and addressed, but how it is an exercise to the reader even when there are things in the approved solution that seem to go against the intent made explicit in the announcement itself.
It feels like this was approved in its current state due to internal time constraints on the run up to WWDC. I can imagine that there are a bunch of internal Apple teams waiting on this feature so they can tweak function names/argument orders to improve call site ergonomics.
That being said I can't help but be disappointed and feel that the review process is too opaque when it comes to final decisions. It really does feel like the community was asked their opinion and some key information was, not so much ignored, but discarded, in the final decision.
Considering the quite intense backlash being received for this decision I think this is a good opportunity to learn how to avoid this in future. Not everyone is going to be happy with certain decisions that are made. But you would hope that when the majority express a particular opinion that its listened to. Unfortunately I don't think that's been the case here.
When all is said and done it's worth remembering that the decisions made on these forums affect thousands of developers around the world and not just the few voices in this forum. While there are a majority of voices opposing this feature in its current state here (given the temptation of a solution which a lot of people perceive to be better), there are many people who aren't even aware that this feature is coming. For a lot of them this feature will just be "oh so that's how it works, cool!" and move on. But at the same time there will also be a lot of others out there saying "well that's not great, why have they done that? I'm going to stick to the old way". Which way the community at large will go is yet to be seen.
This is what makes me worry the most. I've always praised Swift for its focus on clarity at the point of use, in which argument labels play an important role. Yet it seems like now we're going to design our APIs around a feature that breaks a lot of rules (and that we're "not entirely satisfied with")?
This change to the guidelines makes trailing closures pretty much mandatory, as the library author is asked to design their API assuming the user will specify the arguments as trailing closures. It will become increasingly hard for library authors to design functions that support both regular closure arguments as well as trailing closures.
And for what benefit?
// Multiple closure arguments -> no trailing closure
UIView.animate(withDuration: 0.3, animations: {
self.view.alpha = 0
}, completion: { _ in
self.view.removeFromSuperview()
})
// Multiple trailing closure arguments
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0
} completion: { _ in
self.view.removeFromSuperview()
}
This:
- removes two commas, which does remove clutter, but is also less clear and confusing for beginners.
- moves the closing parentheses, which is very confusing for beginners (this confusion is a "feature" of trailing closures in general).
- removes an argument label, and thus clarity.
How does this meet a higher bar than all of the other syntactic sugar suggested on these forums?
I understand the reasoning of the core team that trailing closures are a given and that it's hard to redesign them at this point, but as an educator, I'd rather see them deprecated entirely, than built upon even further.
As a developer, I care less about this, but developing is not my main job, so I'm trying to advocate for future generations of students, all of whom will have to go through a phase where trailing closures are just confusing. The more we build upon this feature instead of fixing it, the longer that phase will take. For some, it may never end.
I'm disappointed at the acceptance of this proposal as is. I agree the original problem exists and deserves a better solution than what exists today. What ends up happening now when I need to invoke a function with multiple closures is that I use the full labeled and enclosed syntax to avoid both the ambiguity problem of which closure does what, and the asymmetry issue described in the original problem.
The solution accepted here, in my view, fixes neither of those problems. There can still be ambiguity about the role of each closure, and the syntax is still asymmetrical. Therefore I don't see why anyone would pick this syntax over the full labeled one.
Another issue I have is with the conclusions provided in the rationale:
However, Swift is now an established language, and there are limits to what changes we can justify, even in a new source-compatibility version.
This was not an issue when String was redesigned twice, at a considerable change cost for a significant user base. And the reason is obvious: Both redesigns were arguably better than the previous implementations. Of course issues persisted, but the ergonomics of the API were considerably better on each new proposal. I feel this was a missed opportunity to repeat this, albeit on a smaller scope.
In the end I know the full syntax is still there and I'll keep using it. My concern is that lots of other developers and teams will do the same, and this will be yet another Swift quirk that may only be used by hobbyist projects because the semantics loss on a large and unspecialized codebase are not worth the benefits of adopting it.
Thanks to the Core team for the work you do maintaining Swift, and for reading my feedback.
The core team must take seriously that the response to this decision is not merely because of dissatisfaction with the specific result. It's dismissive to phrase it in those terms.
Here's a simple thought experiment:
- Mask out entirely the subject matter of proposal and the content of the replies.
- Present the following scenario to a member of the community: proposal SE-XXXX garnered nearly 400 replies on first review and was locked down; it was then revised and then re-reviewed, garnering nearly 200 replies on second review, then accepted without modification.
- What do you imagine the community member would predict as to the reaction to the review?
I have said this before, but I will say it again: the core team needs to be much more clear up-front in delineating what feedback is being solicited and under what constraints. During the first review, you stated on behalf of the core team:
What is implied by "further review" is what's at issue here. As evidenced by the 194 replies which that further review has received, the community took it to mean that we were actively engaging in the feature's redesign. The second review process would have gone differently--and would not have garnered 194 replies--if the core team had stated something to the following effect from the get-go:
- We have already determined that this is an important problem to be solved, and intend to implement some multiple trailing closure syntax.
- We intend this feature to be integrated into
master
by early May for inclusion in the next version of Swift.- Based on feedback, we accept that an unbraced syntax has superior ergonomics to the braced version.
- After reviewing existing APIs, we have determined that the first argument label should be mandatorily omitted for reasons spelled out in the revised proposal.
- Our remaining questions for the community are: (1) Are there any unanticipated harms of the syntax as we propose which have not already been touched on in the proposal text or first review? (2) That's it; in your review, please keep in mind that the conclusions stated above are already decided.
It would be interesting to know how many people in this forum would prefer this feature redesigned rather than stitched up because of some self-imposed restrictions.
With the new syntax, your code will silently change meaning when the function definition is updated.
Consider this example from the proposal document:
func foo(a: () -> Int = { 42 }, b: Any? = nil, c: () -> Void) {}
foo {
42
} c: {
...
}
It is said to be equivalent to:
foo(b: { 42 }, c: { ... })
Now, if you decide to insert another optional argument d
between b
and c
like so…
func foo(a: () -> Int = { 42 }, b: Any? = nil, d: (() -> Int)? = nil, c: () -> Void) {}
…your code (using multiple trailing closure syntax) at the point of use will change meaning and will now be equivalent to:
foo(d: { 42 }, c: { ... })
You now have a bug (we meant to define b
, not d
– to be fair, at the first glance I was expecting it to define a
, the backwards order is another source of confusion) and the compiler will not catch it. The user may not be even aware that the function has changed from under them, their code will just silently change meaning.
I wish there is this much diverse rationale during the review.
This is something I wish is more clear in the rationale.
Also this.
Maybe we should put together a pitch to allow optional first label on both single & multiple trailing closures (but of course, someone has to implement it).
Honestly, this is the first time I’ve ever seen the Core Team’s feedback as directly hostile to the vast majority of feedback on a proposal and I’m quite shocked.
The comments from the vast majority asked for the ability to optionally allow the first parameter to be named. This is neither source breaking, nor harmful in any way to any existing paradigm. It’s asking for an option to make this better before acceptance. I must admit I didn’t comment in the original thread despite following it, but I felt the views already presented adequately presented this perspective.
I’ve followed Swift Evolution since the beginning and this has really dealt a blow to my respect for this process. I suspect WWDC played a part in this decision, and if so, this is quite a indictment of the current state of Swift.