- Proposal: TBD
- Authors: Matthew Johnson, Erica Sadun, Rob Napier
- Status: TBD
- Review manager: TBD
This proposal loosens closure requirements to provide developer flexibility. It removes the _ in
requirement
for ignored parameters and allow closures to use any, all, or none of the implicit $n variables as desired.
*The Swift-evolution thread about this topic can be found here: Removing "_ in" from empty closures
Swift closures that do not explicitly declare an internal parameter list must reference all
arguments using implicit $N
shorthand names.
This requirement nullifies the effectivenes of Swift's $N
syntactic sugar for closures that do not use all their arguments. Eliminating this requirement means:
{}
becomes a valid 'noop' closure in any context requiring aVoid
-returning closure. Implementations discard unnecessary code cruft, and streamline the minimum implementation from{ _ in }
to{}
.{ expression }
is a valid closure in any context requiring return value. For example, when declaring a default closure argument the expression can simply offer a literal such as{ 42 }
.
Furthermore, the current approach is inconsistent in how it treats $0
versus other parameters. $0
is treated as the entire tuple, while $1
is treated as the second element of the tuple. For example, given the closure:
typealias Closure = (Int, String) -> Any
let f: Closure = { $0 }
f(1, "Test") // (.0 1, .1 "Test")
let g: Closure = { $1 }
g(1, "Test") // "Test"
This proposal changes $0
to be of type Int
(the first parameter) in this case.
This proposal only applies to closures that would be eligible for $N
shorthand names. Closures that name some parameters must still provide placeholders for all of them. For example:
Given a closure of type (Int, String) -> Int
, the following closures would be valid:
{ 1 }
{ $0 }
{ x, y in x }
{ x, _ in x }
{ _, _ in 1 }
The following closure would not be valid:
{ x in x }
Parameterized closures types should accept closures that do not explicitly mention any or all of the implicit arguments, as Swift currently does. These should all be valid:
let _: () -> Void = {}
let _: (Int) -> Void = {}
let _: (Int, Int) -> Int = { 5 }
let _: (Int, Int) -> Int = { $0 }
let _: (Int, Int) -> Int = { $1 }
The empty closure literal {}
should autopromote to satisfy
any void-returning closure type (T...) -> Void
.
// Current
doThing(withCompletion: { _ in })
let x: (T) -> Void = { _ in }
// Proposed
doThing(withCompletion: {})
let x: (T) -> Void = {}
- Encourage optional closures. Rather than simplify the
{}
case, encourage APIs to use optional closures in cases where this would be meaningful. This approach does not scale to non-Void cases, and does not align well with current Cocoa APIs (most completion handlers are not optional in Cocoa).