How to use a variadic closure in swift?
Compiler limitation/bug with argument type inference for single-expression closures
I believe the source of this is a current limitation (/bug) in the compiler w.r.t. inferring the argument types in single-line closures using variadic parameters, see e.g. the following Q&A
- Why can't I use .reduce() in a one-liner Swift closure with a variadic, anonymous argument?
A similar issue was also present for inout
arguments in Swift 2.1 (but no longer in 2.2), as is explained in the following thread
- Inline if statement mutating inout parameter in a void return closure, weird error (Error: type 'Int1' does not conform to protocol 'BooleanType')
Looking at thread 1. as well as attempting to find the described bug flagged in Swift JIRA, however, it seems as if the OP of thread 1. never filed a bug for this, after all. Possibly I just haven't found an existing bug report, but if none exists, one should possibly be filed.
Current workarounds
Possible workarounds, until the compiler's closure argument type inference catches up, are
Extend the closure beyond single-line body
// ...
[1, 2].satisfy { (args) in
() // dummy
print (args) // [1, 2, 3]
}Or, explicitly include type of
args
, e.g.[1, 2].satisfy { (args: Int...) in
print (args) // [1, 2, 3]
}Note that
Generator.Element
resolves toInt
in this example above.
Current status for Swift 3.0-dev
As mentioned briefly above, curiously enough, this bug
inout: is apparently no longer present in Swift 2.2 or Swift 3.0-dev for
inout
arguments, w.r.t. the issues described in Q&A 2. as linked to above- it was possibly fixed as bug [SR-7] was resolved (-> Swift 2.2)
however, seems to be regression 2.2->3.0-dev, w.r.t. type inference for
inout
arguments, as reported in bug report [SR-892]. E.g. the following snippet works in Swift 2.2, but not in 3.0-dev (minimally modified snipper from bug report [SR-7])func f(inout a: Int) {}
let g = { x in f(&x) } // OK 2.2, crashes 3.0-dev
variadic: is still present in Swift 2.2 as well as Swift 3.0-dev for variadic arguments (this thread and Q&A 1. above).
a more condensed example of the bug:
let a: (Int...) -> () = { (args) in print(args) } // bug: crashes
let b: (Int...) -> () = { (args: Int...) in print(args) } // explicitly state argument type, OK
let c: (Int...) -> () = { (args) in (); print(args) } // extend to more than single line closure, OK
(For Swift 3.0-dev, tested using the IBM Swift Sandbox running Swift 3.0-dev.
Swift arguments not allowed when function is assigned to a variable?
- Yes.
- See SE-0111 - Remove type system significance of function argument labels.
Example:
func foo(bar: Int, baz: String) {
print(bar, baz)
}
foo(bar: 123, baz: "abc") // Valid, prints: 123 abc
let x = foo //Inferred type: (Int, String) -> Void
x(123, "abc") // Valid, prints: 123 abc
let y: (bar: Int, baz: String) -> Void = foo // Invalid
// ERROR at line 10, col 9: function types cannot have argument label 'bar'; use '_' instead
// let y: (bar: Int, baz: String) -> Void
// ^
// _
// ERROR at line 10, col 19: function types cannot have argument label 'baz'; use '_' instead
// let y: (bar: Int, baz: String) -> Void
// ^
// _
y(bar: 123, baz: "abc") // Invalid
// ERROR at line 19, col 2: extraneous argument labels 'bar:baz:' in call
// y(bar: 123, baz: "abc") // 123 abc
// ^~~~~~ ~~~~~
why swift function with variadic parameters can not receive an array as argument
Overload the function definition...
func test(ids : Int...) {
print("\(ids.count) rx as variadic")
}
func test(idArr : [Int]) {
print("\(idArr.count) rx as array")
}
//call function test like this now succeeds
test([1,3])
//... as does this
test(1,3)
// Output:
// "2 rx as array"
// "2 rx as variadic"
Of course, to avoid duplicate code, the variadic version should just call the array version:
func test(ids : Int...) {
print("\(ids.count) rx as variadic")
test(ids, directCall: false)
}
func test(idArr : [Int], directCall: Bool = true) {
// Optional directCall allows us to know who called...
if directCall {
print("\(idArr.count) rx as array")
}
print("Do something useful...")
}
//call function test like this now succeeds
test([1,3])
//... as does this
test(1,3)
// Output:
// 2 rx as array
// Do something useful...
// 2 rx as variadic
// Do something useful...
Swift Variadic Closures Syntax?
Try to specify parameter type and return type in closure to helps compiler to understand what value it should take and return. Also, you have a mistake in for
loop. The interval should be like this 0 ..< values.count
:
func isAllAbove(lower: Double) -> (Double...) -> Bool {
return { (values: Double...) -> Bool in
var conditions: [Bool] = []
for i in 0 ..< values.count {
conditions.append(lower < values[i])
}
return !conditions.contains(false)
}
}
let allAbove = isAllAbove(lower: 2)
print(allAbove(1, 2, 3)) // false
Also, you can write it almost in 1 line of code:
let lower = 2
let isAllAbove = ![1, 2, 3].contains { $0 < lower }
print(isAllAbove1) // false
Assign print function to a variable
I believe you've found a known bug (or limitation) of the Swift compiler.
The built-in print
function has three parameters. Parameter #1 is unlabeled and is type String ...
(variadic). Parameter #2 has label separator
, is type String
, and has a default value. Parameter #3 has label terminator
, is type String
, and has a default value. Since parameters #2 and #3 have default values, you can call it like print("hello")
.
Closures (like myPrint
) cannot use labeled parameters. So myPrint
takes three unlabeled parameters. Parameter #1 is type String ...
(variadic). Parameter #2 is type String
. Parameter #3 is also type String
. Closure parameters cannot have default values, so myPrint
requires all three parameters. You cannot call it like myPrint("hello")
, because you haven't passed anything for parameters #2 and #3 and they do not (and cannot) have default values.
In Swift, a variadic parameter (like parameter #1 of print
and myPrint
) consumes all arguments until in reaches a label. If you try to call myPrint("hello", " ", "\n")
, all three arguments will be assigned to parameter #1, and nothing will be assigned to parameters #2 and #3.
Since you cannot have labeled parameters in a closure, there is no way to call your myPrint
closure, because there is no way to pass values for parameters #2 and #3.
This is a known limitation. See SR-2475 and SR-494.
How do you document the parameters of a function's closure parameter in Swift 3?
As far as I know, you can only document the closure parameters if you label them:
/// Calls bar with "Hello, world"
/// - parameter bar: A closure to call
/// - parameter theString: A string to use
func foo(bar: (theString: String) -> Void) {
bar(theString: "Hello, world")
}
This is less than ideal: it forces you to use an argument label when you call the closure, and if there are naming conflicts, there seems no way to distinguish between the two.
Edit: As @Arnaud pointed out, you can use _
to prevent having to use the parameter label when calling the closure:
/// Calls bar with "Hello, world"
/// - parameter bar: A closure to call
/// - parameter theString: A string to use
func foo(bar: (_ theString: String) -> Void) {
bar("Hello, world")
}
In fact, this is the only valid approach in Swift 3 because parameter labels are no longer part of the type system (see SE-0111).
Related Topics
How to Post Parameter with (+ Plus Sign) in Alamofire
Uicolor Extension in Swift Error
iOS Charts - Single Values Not Showing Swift
How to Use The Spritekit Method Body(At: Cgpoint)
How to Programmatically Scroll iOS Wkwebview, Swift 4
Swift: Gradient Splits on Rotation
How to Center Nspopover When Using Swiftui
Pinterest-iOS 2.3 and Swift: Calling Createpinwithimageurl Results in Exc_Bad_Access
Macos Change Menubar Icon Using Swift 4
Swift Spritekit Get Visible Frame Size
How to Check for Self with Contains() in Swift
Arkit: How to Tell If User's Face Is Parallel to Camera
Type Alias Declaration with Templates in Swift
Why Are Image Views Sometimes Not Appearing in Collection View Cells