I have to admit I’m quite fond of the trailing closure syntax. This is partly
because of my earlier experiences with Ruby. But partly I just really found it
more readable to see a separation between a regular function call, with just
normal parameters passed in, and higher order functions, like the `map` example
you’ve made. Having two sets of parentheses/braces like foo({ … }) really makes
them blend in and I don’t notice the curly braces as much. Having a stylistic
separation between:
foo(argument)
foo { code }
helps me catch the difference between the two.
But it is a stylistic choice. Like I mentioned in a different reply, if we’re
going to have trailing closures at all, I don’t see a point in enforcing this
other than with a linter.
* * *
The only way I see this working is if the trailing closure syntax has *other*
semantics as well. For example, there’s been a discussion about allowing
specially marked closures to return/break/continue in the scope of the caller
function, not the closure’s scope (like Ruby’s blocks do). Now, that would make
a bit more sense. You’d have a special attribute (can’t think of a name, but
let’s stick with your @trailing), that would imply:
- required trailing closure syntax
- @noescape, I think
- and return/break/continue affects outside functions
And you could use that to make your own pseudo-language-constructs. This would
enforce trailing closure syntax, and nothing else could use trailing closure
syntax.
Now, I wouldn’t be super happy about not being able to write `map { foo }`
myself 😁 — but _that_ proposal would make sense to me. It wouldn’t just be
enforcement of a stylistic choice, but the syntax would actually convey some
important semantics.
What do you think?
— Radek
> On 24 Mar 2016, at 14:57, Haravikk via swift-evolution
> <[email protected]> wrote:
>
> When I started using Swift I was initially very enthusiastic about trailing
> closures, but I’ve actually kind of gone off them somewhat and I’d like to
> discuss why.
>
> Firstly, here are two ways to write a common example using the .map() method:
>
> let foo = myArray.map { $0 + 1 }
> let foo = myArray.map({ $0 + 1 })
>
> It’s tough to say that the first form is any neater than the second, other
> than the second having more brackets. However, the first form is somewhat
> ambiguous, as .map in this case looks like a property rather than a method,
> it also visually looks like a statement, followed by a closure rather than
> the two things logically being related. Of course it’s quick to learn that
> these are related, but for consistency I’m starting to now prefer the use of
> parenthesis in almost all cases.
>
> The other advantage of trailing closures is the omission of the label, but
> trailing closures aren’t strictly necessary for this, as we can already omit
> external labels for parameters if we want to, and the example above shows
> that a trailing closure isn’t necessary for this. The only real difference is
> that the trailing closure form makes a label optional, because you can either
> provide the closure with label in parenthesis (if the label is required) or
> omit it by trailing, like so:
>
> something.someMethod(foo: 1, predicate: { $0 < $1})
> something.someMethod(foo: 1) { $0 < $1}
>
> However this kind of arbitrarily makes the following impossible:
>
> something.someMethod(foo: 1, { $0 < $1 })
>
> With this in mind it seems to me that we might be better served by the
> ability to make external labels optional, as this would allow us to be just
> as succinct, while being completely clear about what is being passed into
> this method.
>
>
> The only real remaining advantage that I see to trailing closures is the
> ability to define pseudo language constructs, for example:
>
> func repeatUntilEmpty<C:CollectionType>(collection:C, @noescape _
> body:() throws -> Void) rethrows { while !collection.isEmpty { body() } }
> repeatUntilEmpty(myArray) {
> /* Do something over and over until myArray is empty */
> }
>
> Which I think is a pretty uncommon type of structure, but could be useful in
> some specialised situations. To support this though we could easily use a new
> @trailing attribute instead to indicate that the closure can be used in this
> way. My example isn’t very good as I can’t think of a case that really,
> really needs this, but I think they’re probably out there.
>
>
> To summarise, having come down off my initial enthusiasm for trailing
> closures I’m not sure that they really add that much syntactically,
> especially in the most common cases, while actually being a little ambiguous
> looking and adding inconsistency to the language. I think they should remain
> for the less common cases that can really benefit from them, but as a feature
> that is opted into, so that we can go for consistency by default.
>
> I’m interested to hear other people’s thoughts.
> _______________________________________________
> 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