In the book "Learning Swift" (Wagner) there's this example about the closures ( closures as parameters in particular):
func firstInNumbers(numbers: [Int], passingTest: (number: Int) -> Bool -> Int? {
for number in numbers {
if passingTest(number: number) {
return number
}
}
return nil
}
let numbers = [1,2,3,4,5]
func greaterThanThree(number: Int) -> Bool {
return number > 3
}
var firstNumber = firstInNumbers(numbers, greaterThanThree)
println(firstNumber)
<code>
So, why the (number: number) syntax in the if statement ?
The second number:
passingTest(number: number)
^^^^^^^
was added because the type of the closure passingTest is (number: Int) -> Bool. See the number: in the type? That specifies the argument label you need to put when invoking the closure. The reason why the parameter label was put there was probably because the author tries to be clearer, but IMO it is already quite clear what the parameter of that closure is doing. Plus, the label number doesn't really add much meaning... So the first number: was really just for "aesthetics". Maybe it makes it easier to understand for beginners?
However, in Swift 4, you become unable to add parameter labels to closure types. In Swift 4, the method would be like this:
func firstInNumbers(numbers: [Int], passingTest: (Int) -> Bool) -> Int? {
for number in numbers {
if passingTest(number) {
return number
}
}
return nil
}
It seems the thing confusing you is that the closure's parameter name, "number", is the same as the variable you are passing into the closure.
//The parameter name "number" here -----------------V
func firstInNumbers(numbers: [Int], passingTest: (number: Int) -> Bool -> Int? {
//And the variable "number" here -\
// V---------------------------/
for number in numbers {
if passingTest(number: number) {
return number
}
}
return nil
}
Perhaps it's easier to tell the difference if you use a different name, like this:
func firstInNumbers(numbers: [Int], passingTest: (number: Int) -> Bool -> Int? {
for n in numbers {
if passingTest(number: n) {
return n
}
}
return nil
}
In Swift 4 closure parameters can't be labeled. Additionally there were a couple small syntax errors in the original post. Correcting the syntax errors, and bringing the closure examples up to Swift 4, the full example would look like this:
func firstInNumbers(numbers: [Int], passingTest: (_ number: Int)->Bool) -> Int? {
for number in numbers {
if passingTest(number) {
return number
}
}
return nil
}
let numbers = [1,2,3,4,5]
func greaterThanThree(_ number: Int) -> Bool {
return number > 3
}
var firstNumber = firstInNumbers(numbers: numbers, passingTest: greaterThanThree)
print(firstNumber)
Related
I got a surprise today while looking at another SO question:
let s = "1,a"
let arr = s.split(separator: ",")
let result = arr.compactMap{Int($0)} // ok
let result2 = arr.compactMap(Int.init) // error
Why is line 3 legal but line 4 is not? I would have thought these two ways of saying "coerce the incoming parameter to Int if possible" would be completely equivalent.
I understand that line 4 is choking on the Subsequence, and I see how to get out of the difficulty:
let result2 = arr.map(String.init).compactMap(Int.init) // ok
What I don't understand is why they both don't choke in the same way.
Looks like the Int.init overload that accepts a Substring has the following signature:
public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol
So, Int($0) works because it uses the default radix, but there isn't an Int.init(_:) that accepts a Substring - there's only Int.init(_:radix:) that does - and so it fails.
But if there was one:
extension Int {
public init?<S>(_ text: S) where S : StringProtocol {
self.init(text, radix: 10)
}
}
then this would work:
let result1 = arr.compactMap(Int.init)
In fact the first version (Int($0)) calls this initializer, which has two parameters (one of them has a default value):
#inlinable public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol
If I define a custom initializer like so, then the second example works too.
extension Int {
init?<S>(_ string: S) where S: StringProtocol {
// convert somehow, e.g: self.init(string, radix: 10)
return nil
}
}
let result2 = arr.compactMap(Int.init)
It seems to me that if I write Int.init in the compactMap, it can call only the exact initializer (or function), and the second parameter of the first called initializer cannot be inferred.
Another example:
func test1<S>(param1: S) -> String where S: StringProtocol {
return ""
}
func test2<S>(param1: S, defaultParam: String = "") -> String where S: StringProtocol {
return ""
}
extension Sequence {
func customCompactMap<ElementOfResult>(_ transform: (Element) -> ElementOfResult?) -> [ElementOfResult] {
compactMap(transform)
}
}
arr.customCompactMap(test1)
arr.customCompactMap(test2) // error
I think the function references cannot hold any default values. Unfortunately I didn't find any official reference to this, but seems interesting.
Proof, last example:
func test3(param1: String, defaultParam: String = "") { }
let functionReference = test3
functionReference("", "")
functionReference("") // error
Here the functionReference's type is (String, String) -> (), even though the test3 function has a default value for the second parameter. As you can see functionReference cannot be called with only one value.
I tried looking for the Swift forum post where someone on the core team explained this, but sorry, I couldn't find it. You can go asking there and get clarification on this point:
Default arguments don't actually produce overloads.
Instead, using default arguments at call site is syntactic sugar for using all arguments. The compiler inserts the defaults for the ones you don't use.
A few results of that…
You cannot use functions with default arguments as closures with simplified signatures. You have to wrap them in new closures, as you demonstrated in your question.
func ƒ(_: Int = 0) { }
let intToVoid: (Int) -> Void = ƒ // compiles
// Cannot convert value of type '(Int) -> ()' to specified type '() -> Void'
let voidToVoid: () -> Void = ƒ
Methods with different default argument patterns, that look the same at call site, are not considered overrides.
class Base {
func ƒ(_: Any? = nil) -> String { "Base" }
}
final class Derived: Base {
// No `override` required.
func ƒ() -> String { "Derived" }
}
Base().ƒ() // "Base"
Derived().ƒ() // "Derived"
(Derived().ƒ as (Any?) -> String)("argument") // "Base"
Default arguments do not allow for satisfaction of protocol requirements.
protocol Protocol {
func ƒ() -> String
}
// Type 'Base' does not conform to protocol 'Protocol'
extension Base: Protocol { }
I believe I have some misunderstanding of how generics work. I have the protocol:
protocol CommandProtocol {
func execute<T>() -> T
func unExecute<T>() -> T
}
And a class that conforms to it:
class CalculatorCommand: CommandProtocol {
...
func execute<String>() -> String {
return calculator.performOperation(operator: `operator`, with: operand) as! String
}
func unExecute<Double>() -> Double {
return calculator.performOperation(operator: undo(operator: `operator`), with: operand) as! Double
}
...
}
The calculator.performOperation() method actually returns Double, but here I just try to play with generics so I replace return type from Double to String.
After that, I have a class which invokes these methods:
class Sender {
...
// MARK: - Public methods
func undo() -> Double {
if current > 0 {
current -= 1
let command = commands[current]
return command.unExecute()
}
return 0
}
func redo() -> Double? {
if current < commands.count {
let command = commands[current]
current += 1
let value: Double = command.execute()
print(type(of: value))
return command.execute()
}
return nil
}
...
}
In the undo() method everything works as expected (one thing that I did not understand fully is how Swift really knows whether the unExecute value will return Double or not, or compiler infers it based on the undo() return type?)
But in the redo() method, I am calling the execute() method which returns String, but the method expects Double, so I thought that my program would crash, but not, it works totally fine as if execute() method returns Double.
Please, could someone explain to me what exactly happens under the cover of this code? Thank you in advance.
You are correct that you misunderstand generics. First, let's look at this protocol:
protocol CommandProtocol {
func execute<T>() -> T
func unExecute<T>() -> T
}
This says "no matter what type the caller requests, this function will return that type." That's impossible to successfully implement (by "successfully" I mean "correctly returns a value in all cases without crashing"). According this protocol, I'm allowed to write the following code:
func run(command: CommandProtocol) -> MyCustomType {
let result: MyCustomType = command.execute()
return result
}
There's no way to write an execute that will actually do that, no matter what MyCustomType is.
Your confusion is compounded by a subtle syntax mistake:
func execute<String>() -> String {
This does not mean "T = String," which is what I think you expect it to mean. It creates a type variable called String (that has nothing to do with Swift's String type), and it promises to return it. when you later write as! String, that means "if this values isn't compatible with the type requested (not "a string" but whatever was requested by the caller), then crash.
The tool that behaves closer to what you want here is an associated type. You meant to write this:
protocol CommandProtocol {
associatedType T
func execute() -> T
func unExecute() -> T
}
But this almost certainly won't do what you want. For example, with that, it's impossible to have an array of commands.
Instead what you probably want is a struct:
struct Command {
let execute: () -> Void
let undo: () -> Void
}
You then make Commands by passing closures that do what you want:
let command = Command(execute: { self.value += 1 },
undo: { self.value -= 1 })
Alternately, since this is a calculator, you could do it this way:
struct Command {
let execute: (Double) -> Double
let undo: (Double) -> Double
}
let command = Command(execute: { $0 + 1 }, undo: { $0 - 1 })
Then your caller would look like:
value = command.execute(value)
value = command.undo(value)
You think this returns a Swift.Double, but no. This code is no different than using T instead of Double. Swift does not require the names of generic placeholders to match what you put in a protocol.
func unExecute<Double>() -> Double {
return calculator.performOperation(operator: undo(operator: `operator`), with: operand) as! Double
}
You're not actually looking for generic methods. You want this, instead.
protocol CommandProtocol {
associatedtype ExecuteValue
associatedtype UnExecuteValue
func execute() -> ExecuteValue
func unExecute() -> UnExecuteValue
}
I want to test a function, passing in a shuffle function so I can Unit test the function adequately. In other words:
func testme(testArr: [Int], shufflefunction : Shufflefunction = .shuffle) -> [Int] {
return tetsArr.shufflefunction
}
I know the syntax is incorrect; and that is the question.
What is the correct type of the shuffled function so I can make a reusable function for any particular shuffle implementation, as passed in the function above.
The second part of the problem is how to pass the standard shuffle() implementation as a default parameter for testing this function.
If you look at Array.shuffled, you will see that it is actually a function that takes an array, and returns a () -> [T]:
Therefore, the type of Array.shuffled could be written as
(([T]) -> (() -> [T]))
We can then use this as our parameter type:
func testShuffle<T>(array: [T], function: (([T]) -> (() -> [T])) = Array.shuffled) {
let shuffleFunction = function(array)
let shuffledArray = shuffleFunction()
// do stuff with shuffledArray...
}
// usage
extension Array {
func myCustomShuffled() -> [Element] {
// ...
}
}
let arr = [1,2,3,4,5,6,7]
testShuffle(array: arr, function: Array.myCustomShuffled)
Note that the same approach doesn't work with Array.shuffle (the mutating version), because Swift doesn't support partial applications on mutating functions.
You can also write the parameter type as ([T]) -> [T], then you would have to pass the default parameter like this:
func testShuffle<T>(array: [T], function: ([T]) -> [T] = { $0.shuffled() }) {
let shuffledArray = function(array)
}
When a function's return value is another function,there's no way to get the returned function's argument names.Is this a pitfall of swift language?
For example:
func makeTownGrand(budget:Int,condition: (Int)->Bool) -> ((Int,Int)->Int)?
{
guard condition(budget) else {
return nil;
}
func buildRoads(lightsToAdd: Int, toLights: Int) -> Int
{
return toLights+lightsToAdd
}
return buildRoads
}
func evaluateBudget(budget:Int) -> Bool
{
return budget > 10000
}
var stopLights = 0
if let townPlan = makeTownGrand(budget: 30000, condition: evaluateBudget)
{
stopLights = townPlan(3, 8)
}
Be mindful of townPlan,townPlan(lightsToAdd: 3, toLights: 8) would be much more sensible to townPlan(3, 8), right?
You're correct. From the Swift 3 release notes:
Argument labels have been removed from Swift function types... Unapplied references to functions or initializers no longer carry argument labels.
Thus, the type of townPlan, i.e. the type returned from calling makeTownGrand, is (Int,Int) -> Int — and carries no external argument label information.
For a full discussion of the rationale, see https://github.com/apple/swift-evolution/blob/545e7bea606f87a7ff4decf656954b0219e037d3/proposals/0111-remove-arg-label-type-significance.md
Hi all I am learning Swift, just going through the book provided by Apple on the app store. On page 21 there is some code and for the life of me I cannot get it to work. Just wondered if anybody could shed light. I am pretty sure it's an update thing but if someone could point me or help that would be great. Here is the code taken from the book (yes I have re-typed exactly)
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, condition: lessThanTen)
However when I put the code in it changes it and shows the condition arg in the function call as shown below. I should point out that I have placed a question mark after condition: as I am not sure what data type Int -> Bool is asking for.
The type Int -> Bool is a function type that takes a single argument of type Int, and returns a value of type Bool. In this sense, hasAnyMatches is a higher order function, in that it expects, in addition to an integer array, a function as an argument. Hence, you can send e.g. a function reference (to an (Int) -> Bool function) or a closure as the second argument to hasAnyMatches).
An example follows below, calling hasAnyMatches with 1. a function reference; 2. an anonymous closure; 3. a pre-defined closure:
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
/* 1. function reference: to 'lessThanTen' function */
hasAnyMatches(numbers, condition: lessThanTen)
/* 2. anonymous (trailing) closure: (Int) -> Bool: "integer less than 0?" */
hasAnyMatches(numbers) { myInteger in myInteger < 0 }
hasAnyMatches(numbers) { $0 < 0 } /* short form */
/* 3. pre-defined closure: (Int) -> Bool: "integer larger than 0?" */
let myIntToBoolClosure : (Int) -> Bool = {
myInteger in
return myInteger > 0
}
hasAnyMatches(numbers, condition: myIntToBoolClosure)