From a perspective of a framework writer it is important to provide clear and understandable documentation for the correct usage of their APIs. Swift allows us to write functions that are at some extent self documenting, and clearly define what the function will actually do.
since swift 3, closures in function parameters are by default nonescaping: you pass a closure as function parameter, and you know that this closure will be synchronously used within the function scope before this function returns.
declaring the closure to be @escaping as function parameter we say:
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns . When you declare a function that takes a closure as one of its parameters, you can write
@escapingbefore the parameter’s type to indicate that the closure is allowed to escape.
Quote from Swift documentation
func nonescaping(closure: () -> Void)
func escaping(closure: @escaping () -> Void)
But for optional closures, things are different.
func foo(closure: (() -> Void)? = nil)
Here we are saying that this closure parameter is not essential for the function to work, but what about its escaping nature?
In this case the closure is implicitly escaping because of the definition of Optionality in swift. But is that closure intended to escape the actual function scope? Is this closure executed before or after the function return?
Just by reading the function signature this is unknown, and we are forced by the compiler to assume that it is escaping the function.
From this point of view
func foo(closure: (() -> Void)? = nil) {
....
closure?()
...
}
Is the same as
func foo(closure: @escaping () -> Void = { }) {
....
closure()
...
}
except that closure is not escaping foo in the second case, and if it wasn't for the definition of Optional it wasn't escaping foo in the first case either.
In this parallel pitch @John_McCall gave us an important information:
It should be quite straightforward to extend escape analysis to optional parameter functions and allow them to be explicitly marked as
@escapingor@nonEscaping(or however we want to spell that). We can also investigate changing the default language rule for optional parameter functions so that it's consistent for different optionalities, but that's a separable question.
Ideally, I believe that Optional closures should behave exactly the same as normal closures when passed as parameters. Meaning non escaping by default, with possibility to declare them @escaping. This would allow an uniform experience while dealing with closures in functions as parameters, regardless the fact that they are optional or not.
While I think it's the right thing to do, I also understand that to change the default now would mean to change the contract in existing functions and this would be source breaking for both implementations and clients of the functions.
I think that either
func foo(completion: @escaping ((Bool) -> Void)? = nil) // with default non escaping
or
func foo(completion: @nonescaping ((Bool) -> Void)? = nil) // with default escaping
should be allowed by the swift compiler.
What do you think guys?