[Thoughts?][Phase2] default arguments and trailing closure syntax


(Jay) #1

When you have a function with a closure and then another optional default =
nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {

You can’t use trailing closure syntax for the update argument when leaving
the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that
you can add them to existing released functions without breaking the
calling code. This means you have to add a separate convenience function
without the extra argument, which is annoying and inelegant. Another
annoying thing is that you can easily miss this error if you happen to not
use trailing closure syntax in your tests or other usage, because adding
the extra default argument compiles fine for code that uses normal syntax.

Are there any issues/gotchas if the trailing closure syntax were to work
for the last *specified* argument rather than the last *defined* argument?


(Saagar Jha) #2

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

···

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant. Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last defined argument?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #3

Check out this thread–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last defined argument?

The main gotcha, and the reason the rule is the way it is, is that it means you'd lose trailing closure syntax if the caller wanted to specify an argument for the last parameter.

   - Doug

···

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Saagar Jha) #4

Saagar Jha

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
})

and didn’t receive a warning for it, either.

···

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com> wrote:
On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last definedargument?

The main gotcha, and the reason the rule is the way it is, is that it means you'd lose trailing closure syntax if the caller wanted to specify an argument for the last parameter.

   - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #5

We don’t warn here because ‘foo’ does have a trailing closure… it’s for the second parameter. I guess we could still warn about ‘a’ (maybe lump it into the same bug about the typealias case).

  - Doug

···

On Jan 4, 2017, at 9:32 PM, Saagar Jha <saagar@saagarjha.com> wrote:

Saagar Jha

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
})

and didn’t receive a warning for it, either.


(Saagar Jha) #6

So, then this should have a warning? I’m still not getting one.

func foo(a: () -> (), b: (() -> ())? = nil, c: Int) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
}, c: 0)

Saagar Jha

···

On Jan 4, 2017, at 9:34 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Jan 4, 2017, at 9:32 PM, Saagar Jha <saagar@saagarjha.com <mailto:saagar@saagarjha.com>> wrote:

Saagar Jha

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
})

and didn’t receive a warning for it, either.

We don’t warn here because ‘foo’ does have a trailing closure… it’s for the second parameter. I guess we could still warn about ‘a’ (maybe lump it into the same bug about the typealias case).

  - Doug


(Douglas Gregor) #7

So, then this should have a warning? I’m still not getting one.

func foo(a: () -> (), b: (() -> ())? = nil, c: Int) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
}, c: 0)

If you give “c” a default value (e.g., “= 0”), it will give a warning.

I’m happy for the warning to get more eager, so long as it also gets a Fix-It at the same time.

  - Doug

···

On Jan 4, 2017, at 9:44 PM, Saagar Jha <saagar@saagarjha.com> wrote:

Saagar Jha

On Jan 4, 2017, at 9:34 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 9:32 PM, Saagar Jha <saagar@saagarjha.com <mailto:saagar@saagarjha.com>> wrote:

Saagar Jha

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
})

and didn’t receive a warning for it, either.

We don’t warn here because ‘foo’ does have a trailing closure… it’s for the second parameter. I guess we could still warn about ‘a’ (maybe lump it into the same bug about the typealias case).

  - Doug


(Xiaodi Wu) #8

The only difference between your proposed spelling and not using trailing
closures at all is the placement of the closing parenthesis, which is not
at all a clear enough win sufficient to justify having two spellings for
the same thing.

I'm of the same opinion that was expressed by Erica Sadun and Jordan Rose
last summer, that where a function takes more than one closure
argument--defaulted to nil or no--it's arguable that we ought to prohibit
trailing closures altogether for the sake of clarity.

···

On Wed, Jan 4, 2017 at 21:48 Saagar Jha via swift-evolution < swift-evolution@swift.org> wrote:

Check out this thread
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s
very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to
this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
// update
}, completion: {
// completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default =
nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {

You can’t use trailing closure syntax for the update argument when
leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is
that you can add them to existing released functions without breaking the
calling code. This means you have to add a separate convenience function
without the extra argument, which is annoying and inelegant. Another
annoying thing is that you can easily miss this error if you happen to not
use trailing closure syntax in your tests or other usage, because adding
the extra default argument compiles fine for code that uses normal syntax.

Are there any issues/gotchas if the trailing closure syntax were to work
for the last *specified* argument rather than the last *defined* argument?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Derrick Ho) #9

So could we add a @trailing or @nontrailing to the block argument to toggle
on and off the feature?

···

On Wed, Jan 4, 2017 at 8:35 PM Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution < > swift-evolution@swift.org> wrote:

Check out this thread
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s
very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to
this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
// update
}, completion: {
// completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default =
nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {

You can’t use trailing closure syntax for the update argument when
leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is
that you can add them to existing released functions without breaking the
calling code. This means you have to add a separate convenience function
without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure?
That would still allow existing callers to work, without having to change
the language.

Another annoying thing is that you can easily miss this error if you
happen to not use trailing closure syntax in your tests or other usage,
because adding the extra default argument compiles fine for code that uses
normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't
the last parameter. The warning is actually disabled in the specific case
above because you've written it using a typealias... maybe we should warn
on such cases (it's worth a bug report). Regardless, in the majority of
instances, you'll get a warning, so it won't be silent on disabling
trailing closure syntax.

Are there any issues/gotchas if the trailing closure syntax were to work
for the last *specified* argument rather than the last *defined* argument?

The main gotcha, and the reason the rule is the way it is, is that it
means you'd lose trailing closure syntax if the caller wanted to specify an
argument for the last parameter.

   - Doug


_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jay) #10

Some good arguments regarding warnings and multiple closures. But I can't
help that feel when designing an API, if you have to add a convenience
method that takes just one closure simply to call the real method, this is
exactly the sort of boilerplate code that default arguments are supposed to
replace.

···

On Thu, 5 Jan 2017 at 05:48 Douglas Gregor <dgregor@apple.com> wrote:

On Jan 4, 2017, at 9:44 PM, Saagar Jha <saagar@saagarjha.com> wrote:

So, then this should have a warning? I’m still not getting one.

func foo(a: () -> (), b: (() -> ())? = nil, c: Int) {
a()
b?()
}

foo(a: {
print(“Bar”)
}, c: 0)

If you give “c” a default value (e.g., “= 0”), it will give a warning.

I’m happy for the warning to get more eager, so long as it also gets a
Fix-It at the same time.

- Doug

Saagar Jha

On Jan 4, 2017, at 9:34 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Jan 4, 2017, at 9:32 PM, Saagar Jha <saagar@saagarjha.com> wrote:

Saagar Jha

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution < > swift-evolution@swift.org> wrote:

Check out this thread
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s
very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to
this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
// update
}, completion: {
// completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default =
nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {

You can’t use trailing closure syntax for the update argument when
leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is
that you can add them to existing released functions without breaking the
calling code. This means you have to add a separate convenience function
without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure?
That would still allow existing callers to work, without having to change
the language.

Another annoying thing is that you can easily miss this error if you
happen to not use trailing closure syntax in your tests or other usage,
because adding the extra default argument compiles fine for code that uses
normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't
the last parameter. The warning is actually disabled in the specific case
above because you've written it using a typealias... maybe we should warn
on such cases (it's worth a bug report). Regardless, in the majority of
instances, you'll get a warning, so it won't be silent on disabling
trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
a()
b?()
}

foo(a: {
print(“Bar”)
})

and didn’t receive a warning for it, either.

We don’t warn here because ‘foo’ does have a trailing closure… it’s for
the second parameter. I guess we could still warn about ‘a’ (maybe lump it
into the same bug about the typealias case).

- Doug


(Haravikk) #11

I'm not sure I'd call having another convenience function "annoying and inelegant" personally, as that's kind of the whole point of them, but yes I do agree in this case it could be handled more easily.

Personally I like the idea of having a new @trailing attribute to allow any closure to be specified as the trailing closure, regardless of placement, as this would also be useful for methods where you may accept multiple closures, but where it may make the most logical sense to have say a main body closure as the first parameter, yet it also makes most sense to be the trailing closure, which becomes awkward.

I'm not in favour at all of allowing multiple trailing closures as someone mentioned, I think one is enough (and even then, I only really like them personally for language-construct like methods such as .forEach and similar).

However, I think the real issue in this specific example is that the function/method is being modified after its initial definition, and I wonder if this falls into the ability to reorder arguments? Since most Swift methods and functions have argument labels it doesn't necessarily matter if they are given in a different order as they are still well-defined; thing is, I think Swift actually had this ability and no-one was using it so I don't know if reintroducing it would be the right fix, unless perhaps it is done as an attribute of the function/method?

For example:

@reorderable
static func animate(identifier: String, duration: String, update: @escaping AnimationUpdate, completion: AnimationCompletion? = nil) { … }

With this attribute the order of the parameters is no-longer fixed, but as update is still the last closure it is considered to be the trailing closure? A @trailing attribute would still be useful in this case though as it's possible the new parameter(s) might include a new closure, and you wouldn't want it to suddenly become the new trailing closure.

Also, can't test right now, but can you not add your new completion parameter before the update closure? This should still satisfy existing calls to the method while allowing others to optionally set the completion parameter?

Finally, one other interesting thought, but could we simply allow trailing closures to be any closure the developer hasn't defined? For example:

  function myFunc(body:() -> Void, loop: () -> Bool) { … }
  myFunc(loop: myLoopClosure) { /* this is the body */ }
  myFunc(body: myBodyClosure) { /* this is the loop closure */ }

Perhaps that's a bit too confusing and should be activated with an attribute, but it should be possible to allow this as in both calls above the closure that is trailing is unambiguous.

Just my thoughts on all possible solutions I can think of :wink:

···

On 5 Jan 2017, at 02:25, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant. Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last defined argument?


(Douglas Gregor) #12

So could we add a @trailing or @nontrailing to the block argument to toggle on and off the feature?

But... why? That's a nontrivial amount of complexity for a fairly limited use case.

  - Doug

···

Sent from my iPhone

On Jan 4, 2017, at 8:40 PM, Derrick Ho <wh1pch81n@gmail.com> wrote:

On Wed, Jan 4, 2017 at 8:35 PM Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:

Check out this thread–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last defined argument?

The main gotcha, and the reason the rule is the way it is, is that it means you'd lose trailing closure syntax if the caller wanted to specify an argument for the last parameter.

   - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(thislooksfun) #13

I think the way it is now works just fine. The only use-case I could see for this would be for having multiple closures, but in that case I think defining them before the function call is clearer anyway.

-thislooksfun (tlf)

···

On Jan 4, 2017, at 10:40 PM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:

So could we add a @trailing or @nontrailing to the block argument to toggle on and off the feature?

On Wed, Jan 4, 2017 at 8:35 PM Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Are there any issues/gotchas if the trailing closure syntax were to work for the last specified argument rather than the last defined argument?

The main gotcha, and the reason the rule is the way it is, is that it means you'd lose trailing closure syntax if the caller wanted to specify an argument for the last parameter.

   - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Karl) #14

Do you mean you want a default closure?

func forEach<U,T>(transform: (U)->T = { return $0 }, do: (T)->Void)

doSomething {
    if let g = $0 as? Giraffe { g.eatTheTallLeaves() }
}

doSomething(transform: { $0 as! Giraffe }) {
    $0.eatTheTallLeaves()
}

···

On 6 Jan 2017, at 15:28, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Some good arguments regarding warnings and multiple closures. But I can't help that feel when designing an API, if you have to add a convenience method that takes just one closure simply to call the real method, this is exactly the sort of boilerplate code that default arguments are supposed to replace.

On Thu, 5 Jan 2017 at 05:48 Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 9:44 PM, Saagar Jha <saagar@saagarjha.com <mailto:saagar@saagarjha.com>> wrote:

So, then this should have a warning? I’m still not getting one.

func foo(a: () -> (), b: (() -> ())? = nil, c: Int) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
}, c: 0)

If you give “c” a default value (e.g., “= 0”), it will give a warning.

I’m happy for the warning to get more eager, so long as it also gets a Fix-It at the same time.

  - Doug

Saagar Jha

On Jan 4, 2017, at 9:34 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 9:32 PM, Saagar Jha <saagar@saagarjha.com <mailto:saagar@saagarjha.com>> wrote:

Saagar Jha

On Jan 4, 2017, at 8:35 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 4, 2017, at 7:48 PM, Saagar Jha via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Check out this thread <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020470.html>–it’s very similar to what you proposed, but it didn’t go anywhere. FWIW +1 to this as well as the ability to use multiple trailing closures like so:

animate(identifier: “”, duration: 0, update: {
  // update
}, completion: {
  // completion
}

Saagar Jha

On Jan 4, 2017, at 6:25 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When you have a function with a closure and then another optional default = nil closure at the end, like this:

open static func animate(identifier: String,
                         duration: Double,
                         update: @escaping AnimationUpdate,
                         completion: AnimationCompletion? = nil) {
You can’t use trailing closure syntax for the update argument when leaving the completion argument out/default.

This kind of breaks one of the benefits of default arguments, which is that you can add them to existing released functions without breaking the calling code. This means you have to add a separate convenience function without the extra argument, which is annoying and inelegant.

Why not simply add the "completion" parameter before the trailing closure? That would still allow existing callers to work, without having to change the language.

Another annoying thing is that you can easily miss this error if you happen to not use trailing closure syntax in your tests or other usage, because adding the extra default argument compiles fine for code that uses normal syntax.

The Swift compiler warns when a parameter written as a closure type isn't the last parameter. The warning is actually disabled in the specific case above because you've written it using a typealias... maybe we should warn on such cases (it's worth a bug report). Regardless, in the majority of instances, you'll get a warning, so it won't be silent on disabling trailing closure syntax.

Tried this out in the playground:

func foo(a: () -> (), b: (() -> ())? = nil) {
  a()
  b?()
}

foo(a: {
  print(“Bar”)
})

and didn’t receive a warning for it, either.

We don’t warn here because ‘foo’ does have a trailing closure… it’s for the second parameter. I guess we could still warn about ‘a’ (maybe lump it into the same bug about the typealias case).

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution