Reputation: 7205
The following (working) code creates a UIAlertAction that calls the function foo() after the button ok is pressed.
let action = UIAlertAction(title: "OK", style: .Default, handler: {action in self.foo()})
My question is why does action have to be passed to the closure?
For example, something like this makes sense to me as we do not reference action at all in the closure. I would expect to write something like
{ () in self.foo() }
or
{ self in self.foo() }
In C++ I would expect to write something like
[this](){this->foo();};
Whats going on here?
Upvotes: 2
Views: 274
Reputation: 13316
@ncerzo's answer is correct - this just clarifies a couple of things. Like everything else in Swift, closures have a type, and the compiler won't compile until it can confirm that the type of closure that you are passing matches the type that is required. The compiler does its best to infer the type of a closure from the context, but sometimes it can't and it requires that the code be specific.
You are talking about a closure that simply ignores the argument that is passed. A very simple example would be a closure that looks like this:
let ignoreMe: Int -> Int = { _ in 42 }
No matter what argument you pass to ignoreMe
, it gets ignored and the value returned will be 42
. So you seem to be asking why the underscore is necessary at all. You'd like to write this instead:
let ignoreMe: Int -> Int = { 42 }
The problem is that the compiler cannot match the type of the right-hand side with the type of the left-hand side. The closure { 42 }
is inferred to have the type Void -> Int
, while on the left-hand side ignoreMe
is declared to have the type Int -> Int
. The types don't match.
The error is the compiler's way of telling you, "I was expecting a closure of type Int -> Int
, but you appear to have given me a closure of type Void -> Int
. Please clarify your intentions."
The underscore in { _ in 42 }
is how you provide that clarification. You are telling the compiler, "Yes, this closure is going to take an argument, but it will be ignored. You can safely infer its type to be Int -> Int
."
Requiring this clarification may seem like unnecessary baggage, but the baggage is light. { 42 }
vs. { _ in 42 }
is not much of a burden on the coder. And the benefits are enormous. For every intentional mismatch between the left-hand side type and the right-hand side type, there must be lots of un-intentional mismatches (aka mistakes, or bugs). And those un-intentional mismatches will have to be fixed before the code will compile.
Upvotes: 1
Reputation: 1371
If the compiler can determine the type of your closure, you can use implicit return and shorthand argument names, thus declaring the closure like:
let sorted = [ 4, 3, 1, 40, 19 ].sort { $0 > $1 }
However, if the compiler can't determine it, you need to declare the parameters so it can be sure.
Take this example, with your own code:
class Bar {
func foo() {
}
func foo( s: String ) {
}
func sample() {
// this works
let a = UIAlertAction(title: "a", style: .Default, handler: { self.foo($0.title) } )
// this doesn't work
let a = UIAlertAction(title: "a", style: .Default, handler: { self.foo() } )
}
}
The handler closure has a definition of ((UIAlertAction!)->(Void))!, so it expects a closure with a single argument (inferred of type UIAlertAction) and it returns nothing.
The first example works because by using $0 you're telling the compiler that the closure has in fact one argument, and since the last call is to function foo which returns nothing, your closure has an argument and returns nothing, so the compiler can use it that way.
However, the second one doesn't work because even if foo returns nothing, you're not using any parameters and thus the compiler can not tell that your closure has an argument. For the compiler your closure is: (Void)->(Void) which is not the right type.
ABakerSmith answer is probably what you need in this case, if your foo function or closure definition will not allow the detection of the closure type by the compiler. You still need to declare the parameters, but ignoring them would be the best course of action:
let a = UIAlertAction(title: "a", style: .Default, handler: { _ in self.foo() } )
Upvotes: 3
Reputation: 22939
You can ignore the arguments passed to the handler closure using an underscore, making your code:
{ _ in self.foo() }
You can use an underscore to ignore arguments in other situations too, for
loops for example:
for _ in 0..<5 {
...
}
Upvotes: 2