How to Use a Variadic Closure in Swift

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

  1. 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


  1. 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 to Int 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 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

Is it possible to use Variadic Parameters within Closures?

You need to include the type in the closure:

this { (foo: Int...) in }

The type-inference engine isn't quite powerful enough to figure out this case. I suspect it's related to SR-6030.

Marking a variadic closure parameter as escaping

You don't need to use @escaping here at all. Only a closure that is directly an argument (with nothing wrapping it) can be non-escaping.

A closure that is part of a variadic argument is (under the hood) wrapped in an Array, so it is already implicitly @escaping.

For example, this compiles and runs just fine:

class MyObject {

var closures: [() -> ()] = []

func add(_ closures: () -> () ...) {
self.closures += closures
}

func run() {
for closure in closures { closure() }
}

}

let object = MyObject()
object.add({ print("first") }, { print("second") })
object.run()

Variadic @autoclosure's in Swift 1.2?

I opened rdar://19782845, "Swift 1.2: Variadic auto-closures no longer supported," and was given the following response from Apple Developer Relations:

This issue behaves as intended based on the following:

@autoclosure is only allowed on parameters of function type, and varargs is an array. This is not expected to work.

Swift Programming -- Function And Closure

Those ... mean that the function can take a variable number of arguments

From the The Swift Programming Language book:

Variadic Parameters

A variadic parameter accepts zero or more values of a specified type.
You use a variadic parameter to specify that the parameter can be
passed a varying number of input values when the function is called.
Write variadic parameters by inserting three period characters (...)
after the parameter’s type name.

The values passed to a variadic parameter are made available within
the function’s body as an array of the appropriate type. For example,
a variadic parameter with a name of numbers and a type of Double... is
made available within the function’s body as a constant array called
numbers of type [Double].



Related Topics



Leave a reply



Submit