Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I use .reduce() in a one-liner Swift closure with a variadic, anonymous argument?

Tags:

swift

The first closure works. However, as soon as I take out the dummy assignment, the closure stops working (addUpClosureRedFlagged). Anyone knows why?

let addUpClosureWorking: (Int ...) -> Int = {
  let dummy = "Anything"  // Comment out and wait for the error...
  return $0.reduce(0, combine: +)
}

let addUpClosureRedFlagged: (Int ...) -> Int = {
  return $0.reduce(0, combine: +)   // Should work, but doesn't!
}


addUpClosureWorking(1, 2, 3, 4, 5) // 15
like image 289
Roelf Avatar asked Feb 26 '16 09:02

Roelf


People also ask

How do you pass closure to a function?

If we wanted to pass that closure into a function so it can be run inside that function, we would specify the parameter type as () -> Void . That means “accepts no parameters, and returns Void ” – Swift's way of saying “nothing”.

How many types of closures are there in Swift?

As shown in the above table, there are three types of closures in Swift, namely global functions, nested functions, and closure expressions. They differ in several aspects, including their use scopes, their names, and whether they capture values, which will be discussed more in a later section.

What is the difference between closure and function in Swift?

Difference between Function and ClosureFunction is declared using func keyword whereas Closure doesn't have func keyword. Function has always name but Closure doesn't have. Function doesn't have in keyword but closure has in the keyword.

How do you declare a closure in Swift?

Swift Closure Declarationparameters - any value passed to closure. returnType - specifies the type of value returned by the closure. in (optional) - used to separate parameters/returnType from closure body.


1 Answers

As of current Swift (2.1), the type inference of closures can behave a bit strange when containing only a single line, see e.g. this somewhat related issue; expecting explicit inclusion of the function type signature within the closure.

This (compiler) "bug"/closure limitation is present (at least) for variadiac as well as inout arguments:

// variadiac argument example
let fooErr1: (Int ...) -> Int = {
    return $0.reduce(0, combine: +)
} 
// or: let fooErr1: (Int ...) -> Int = { $0.reduce(0, combine: +) }
   /*  error: cannot convert value of type '(_) -> Int' 
              to specified type '(Int...) -> Int'       */

// inout argument example
let fooErr2: (inout _: Int) -> Int = {
    return $0
}
// or: let fooErr2: (inout _: Int) -> Int = { $0 } 
   /* error: cannot convert value of type '_ -> Int' 
             to specified type '(inout Int) -> Int'     */

This can be avoided by explicitly including the function:s type signature in the closure, or including any statement prior to the return statement (in so extending the closure to contain more than a single line).

/* Avoiding fooErr1:
   - explicitly state function type signature in closure    */
let foo: (Int ...) -> Int = {
    (bar: Int ...) -> Int in
    return bar.reduce(0, combine: +)
}

/* - include any kind of statment prior to return statement */
let foo2: (Int ...) -> Int = {
    (); return $0.reduce(0, combine: +)
}

/* Avoiding fooErr2:
   - explicitly state function type signature in closure    */
let foo3: (inout _: Int) -> Int = {
    (inout bar: Int) -> Int in 
    return bar
}

/* - include any kind of statment prior to return statement */
let foo4: (inout _: Int) -> Int = {
    (); return $0
}

Why? I've written "bug" above, but perhaps more of a current limitation in the type inference of closures by the compiler; as Pradeep K writes in his answer, this is most likely due to some intricacies of the Swift compiler, failing type inference in these cases.

like image 194
dfrib Avatar answered Oct 15 '22 05:10

dfrib