Specifically for the language in - handler: { action in
self.startNewRound()
})
So, I can kinda understand what is happening when self.startNewRound() is called. I guess I need help understanding what exactly is going on with the “action in” portion of this code. I understand that “in” is used in for-loops. And in closures I read around and it seems like it has something to do with types, or return types probably. So actually maybe I can get help with why the word “action” is used in the “action in” portion of this code.
First a clarification, the in in the closure is not directly related to the in in for loops, except in the sense that they both describe a value which comes from something or somewhere …
In the case of the closure, the in simply identifies the parameters passed to the closure and action is the name of the parameter. Closures, just like methods/functions can have parameters passed to them. There can be no parameters, one parameter, or many parameters.
So, in Swift, if you have a list of parameters for a closure, you indicate the parameter names at the very beginning of the closure and then include the keyword in to identify that the preceding values are parameters.
In this particular instance, the action parameter is not used anywhere in the closure. But since the parameter is present, it needs to be declared.
So, if I understand your explanation correctly, the term/identifier “action” used in this case of “action in” does not necessarily have to be named “action” at all, and could thus be any other arbitrary identifier. This seems to be the gist of what I’m gathering.
I guess it kind of throws me that “action” is also used a few times so closely nearby in that section of code while trying to make sense of a new concept.
This reply is just to confirm that I’m getting it right with this one.
EDIT:
So I just renamed the paramater from “action in” to “somethingelse in” and the code still seems to work. I might be the only one that was confused by this, but maybe something to consider for future revisions.
Yes, it’s just a parameter name and so can be anything. The name action was used because the parameter passed to the closure is the action that invoked the closure. I do agree that having two separate action variable there could, in hind-sight, be confusing. Will consider re-naming for the next revision. Thank you for the feedback
And in Swift, when you aren’t going to use a variable, a convention is to use _ as the variable name. But it’s even more than a convention because I’m sure by now you’ve experienced an Xcode warning when you type something like:
let x = 10 //Warning: Initialization of immutable value 'x' was never used; ...
I always think, "Yeah! I’m not done typing the lines that use that variable yet! You can actually get rid of the warning by changing the name of the variable to:
let _ = 10
The underscore name tells the compiler that you don’t plan on using the variable, so there’s no need to give the warning.
The closure syntax in Swift is pretty ugly. But lets begin at the beginning, The method UIAlertAction(title:style:handler:) takes three arguments. The 3rd argument is a function, so you need to pass a function as the third argument when you call the method (or nil). You could define an actual function:
That compiles with no errors, and it works fine: when you click the ‘OK’ button in the alert window, iOS executes myfunc.
But, a computer programming scientist looking at that code will have a deep thought: nothing else is ever going to call myfunc(), so why define that function at the top level of your controller class? When you define a function at the top level of a controller class, any other function can call it. Also, for anyone reading the code, when they come to the line handler: myfunc, they will have to go hunting for the function myfunc() to see what it does.
To alleviate those anxieties, a lot of computer programming languages allow you to define what are called anonymous functions, i.e. a function without a name. Here’s an example:
let add_func = { (x: Int, y: Int) -> Int in //=>The thing on the right hand side of = is the anonymous function
return x+y
}
let result = add_func(1, 2)
print(result) //=>3
See how the definition of the anonymous function looks similar to the definition of a regular function? The syntax is just a little weird, but you can see the parameter variables and their types, the return type, and the function body. Here is the same function defined as a regular function:
func sum(x: Int, y: Int) -> Int {
return x+y
}
let result = sum(x: 1, y: 2)
print(result) //=>3
@IBAction func showScore(_ sender: UIButton) {
...
...
let action = UIAlertAction(
title: "OK",
style: .default,
handler: { x in //<=== HERE ****
self.startNewRound()
}
)
Now, no other code in your app can call the anonymous function–because it has no name there is no way to refer to it–and the anonymous function is defined right at the spot where the anonymous function is used.
The rest of the anonymous function story is just about syntax.
1 – If you pass an anonymous function as an argument to another function, you don’t have to write the types of the parameter variables or the return type, so this:
{ (x: Int, y: Int) -> Int in
return x+y
}
becomes:
{ (x, y) in
return x + y
}
In addition, you don’t need to write the parentheses:
{ x, y in
return x + y
}
The reason you don’t have to specify the types is that the other function is defined to take a certain type of function. A function’s type is made up of the parameter types and the return type:
print(type(of: add_func) ) //=> (Int, Int) -> Int
So Swift looks at the types specified in the accepting function’s definition and infers those types for the anonymous function passed as the argument. And because the type of a function does not indicate whether a function is an anonymous function or a named function, you can pass either an anonymous function or a named function to the accepting function.
2 – But…when you pass an anonymous function as an argument to another function, the syntax can be even briefer still: if the last argument for a function is an anonymous function, then you can do this:
// original code:
let action = UIAlertAction(
title: "OK",
style: .default,
handler: { _ in
self.startNewRound()
}
)
//briefer version:
let action = UIAlertAction(title: "OK", style: .default) {
_ in
self.startNewRound()
}
The changes are:
You get to eliminate the last argument’s label, i.e. handler:
You get to move the anonymous function immediately after the function call.
Finally, why are anonymous functions in Swift called closures? Briefly, a closure is a function that can see the variables that are defined outside the function, and those variables can be used inside the function. A function that can see the variables outside the function is said to “close” over those variables, hence the term “closure”.
Below is an example that you can play around with in an Xcode playground (File>New>Playground). Try changing the execute() function call to use the briefest form where the anonymous function comes immediately after the function call. Then try defining execute() so that it takes a different type of function as an argument, and call it with a different anonymous function.
func add_func(x: Int, y: Int) -> Int {
return x+y
}
let func_type = type(of: add_func)
print(func_type)
let closure_type = type(of: { (x: Int, y: Int) -> Int in
return x+y
})
print(closure_type) //Compare func_type and closure_type
+---------------+ <--- syntax for a function type
| |
func execute( x: Int, y: Int, afunc: (Int, Int) -> Int ) -> String {
let result = afunc(x, y) ^
return "The result was \(result)" | execute()'s return type
}
let string = execute(x: 1, y: 2, afunc: { (x, y) in
return x+y
})
print(string)
Also, try:
let z = 10
let another_string = execute(x: 1, y: 2, afunc: { x, y in
return x+y+z //Will Xcode complain that z is undefined?
})
print(another_string)