Swift mutating Function as first class value - swift

I can have a function to swap the first two elements of an array:
func swapFirstTwo(array: inout [Int]) {
if array.count >= 2 {
array.swapAt(0, 1)
}
}
typealias Swapper = (inout [Int]) -> ()
// And I can have a variable = the function
let swapThem: Swapper = swapFirstTwo
// And it works like this:
var array = [1,2,3]
print(array)
swapThem(&array)
print(array)
// But I'm allergic to Global functions!
// It would be more swifty to have:
extension Array where Element == Int {
mutating func swapFirstTwo2() {
if count >= 2 {
swapAt(0, 1)
}
}
}
typealias Swapper2 = (inout [Int]) -> () -> ()
// But when I do this:
let swapThemAgain: Swapper2 = Array.swapFirstTwo2
// I get the error:
// Partial application of 'mutating' method is not allowed; calling the function has undefined behavior and will be an error in future Swift versions
var array2 = [1,2,3]
print(array2)
array2.swapFirstTwo2()
print(array2)
// This in fact works but I've tried similar things and sometimes they appear to be unstable.
// How can I achieve doing: array2.swapFirstTwo2() without getting the error?
This in fact works but I've tried similar things and sometimes they appear to be unstable.
Also the compiler warning needs to be heeded.
How can I achieve doing: array2.swapFirstTwo2() without getting the warning/error?

The reason why you get a warning (and soon to be an error in Swift 5 mode) on:
extension Array where Element == Int {
mutating func swapFirstTwo2() {
if count >= 2 {
swapAt(0, 1)
}
}
}
typealias Swapper2 = (inout [Int]) -> () -> ()
let swapThemAgain: Swapper2 = Array.swapFirstTwo2
is due to the fact that inout arguments are only valid for the duration of the call they're passed to, and therefore cannot be partially applied.
So if you were to call the returned (inout [Int]) -> () -> () with &array2, you would get back a () -> () which now has an invalid reference to array2. Attempting to call that function will yield undefined behaviour, as you're trying to mutate an inout argument outside of the window where it's valid.
This problem will be fixed if/when unapplied instance methods get flat signatures, as Array.swapFirstTwo2 will instead evaluate to a (inout [Int]) -> ().
But in the mean time, you can workaround the issue by using a closure instead:
typealias Swapper2 = (inout [Int]) -> ()
let swapThemAgain: Swapper2 = { $0.swapFirstTwo2() }
var array2 = [1,2,3]
print(array2) // [1, 2, 3]
array2.swapFirstTwo2()
print(array2) // [2, 1, 3]

Related

Swift type of shuffle()

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)
}

Using "!" on contains

Given this code:
let a = [1, 2, 3, 4, 5]
let b = [5, 6, 7, 8, 9]
let result = a.filter(b.contains)
print(result) // [5]
But what if I want let result = a.filter { !b.contains($0) }? How can I do it the same way as the example above?
let result = a.filter { !b.contains($0) } // works
let result = a.filter(!b.contains) // fails
let result = a.filter { !b.contains($0) }
This is syntactic sugar for:
let result = a.filter({ !b.contains($0) })
filter takes a function value. {...} is a literal function value (a "closure"), just like 1 is a literal Int value. So that's fine. Inside the closure, you apply the ! prefix operator to the result of b.contains($0). That's also fine.
let result = a.filter(!b.contains) // fails
Again, filter takes a function value. b.contains is a function value, so that part is fine, which is why a.filter(b.contains) works. But the ! prefix operator can't be applied to a function value, so that fails.
Anurag demonstrates how you would write an implementation of ! that does take a function and return a function, which works, but would be confusing IMO because this is not a standard use of that operator, and these kinds of operator overloads tend to slow down the compiler (sometimes dramatically, because it has to chase more type possibilities). While there are various ways you might try to "make this work," the best Swift here would generally be the closure syntax because it's clear and standard.
That said, a reasonable approach if you were doing this kind of thing a lot, would be to create a not function that takes a function and returns a function, like Anurag's !, but without overloading the operator:
func not<T>(_ predicate: #escaping (T) -> Bool) -> (T) -> Bool {
return { !predicate($0) }
}
Then you could call a.filter(not(b.contains)). But again, I'd probably only do that if building up complex functions were an important part of your program.
You can always create a custom operator.
prefix func !<T>(_ predicate: #escaping (T) -> Bool) -> (T) -> Bool {
return { element in
return !predicate(element)
}
}
and then a.filter(!b.contains) will work.
Or if that feels like operator overloading abuse, then you could just extend Sequence and use it as a.filter(b.not(b.contains)) but it's kind of ugly:
extension Sequence {
func not(_ predicate: #escaping (Self.Iterator.Element) -> Bool) -> (Self.Iterator.Element) -> Bool {
return { element in
return !predicate(element)
}
}
}

Updating an inout param async does not update reference [duplicate]

I'm trying to insert functions with inout parameter to append data received from async callback to an outside array. However, it does not work. And I tried everything I know to find out why - with no luck.
As advised by #AirspeedVelocity, I rewrote the code as follows to remove unnecessary dependencies. I also use an Int as the inout parameter to keep it simple.The output is always:
c before: 0
c after: 1
I'm not able to figure out what goes wrong here.
func getUsers() {
let u = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
var a = UserData()
a.userIds = u
a.dataProcessor()
}
struct UserData {
var userIds = [String]()
var counter = 0
mutating func dataProcessor() -> () {
println("counter: \(counter)")
for uId in userIds {
getOneUserApiData(uriBase + "user/" + uId + ".json", &counter)
}
}
}
func getOneUserApiData(path: String, inout c: Int) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c)")
c++
println("c after: \(c)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}
Thanks.
Sad to say, modifying inout parameter in async-callback is meaningless.
From the official document:
Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.
...
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.
Semantically, in-out parameter is not "call-by-reference", but "call-by-copy-restore".
In your case, counter is write-backed only when getOneUserApiData() returns, not in dataTaskWithRequest() callback.
Here is what happened in your code
at getOneUserApiData() call, the value of counter 0 copied to c1
the closure captures c1
call dataTaskWithRequest()
getOneUserApiData returns, and the value of - unmodified - c1 is write-backed to counter
repeat 1-4 procedure for c2, c3, c4 ...
... fetching from the Internet ...
callback is called and c1 is incremented.
callback is called and c2 is incremented.
callback is called and c3 is incremented.
callback is called and c4 is incremented.
...
As a result counter is unmodified :(
Detailed explaination
Normally, in-out parameter is passed by reference, but it's just a result of compiler optimization. When closure captures inout parameter, "pass-by-reference" is not safe, because the compiler cannot guarantee the lifetime of the original value. For example, consider the following code:
func foo() -> () -> Void {
var i = 0
return bar(&i)
}
func bar(inout x:Int) -> () -> Void {
return {
x++
return
}
}
let closure = foo()
closure()
In this code, var i is freed when foo() returns. If x is a reference to i, x++ causes access violation. To prevent such race condition, Swift adopts "call-by-copy-restore" strategy here.
Essentially it looks like you’re trying to capture the “inout-ness” of an input variable in a closure, and you can’t do that – consider the following simpler case:
// f takes an inout variable and returns a closure
func f(inout i: Int) -> ()->Int {
// this is the closure, which captures the inout var
return {
// in the closure, increment that var
return ++i
}
}
var x = 0
let c = f(&x)
c() // these increment i
c()
x // but it's not the same i
At some point, the variable passed in ceases to be x and becomes a copy. This is probably happening at the point of capture.
edit: #rintaro’s answer nails it – inout is not in fact semantically pass by reference
If you think about it this makes sense. What if you did this:
// declare the variable for the closure
var c: ()->Int = { 99 }
if 2+2==4 {
// declare x inside this block
var x = 0
c = f(&x)
}
// now call c() - but x is out of scope, would this crash?
c()
When closures capture variables, they need to be created in memory in such a way that they can stay alive even after the scope they were declared ends. But in the case of f above, it can’t do this – it’s too late to declare x in this way, x already exists. So I’m guessing it gets copied as part of the closure creation. That’s why incrementing the closure-captured version doesn’t actually increment x.
I had a similar goal and ran into the same issue where results inside the closure were not being assigned to my global inout variables. #rintaro did a great job of explaining why this is the case in a previous answer.
I am going to include here a generalized example of how I worked around this. In my case I had several global arrays that I wanted to assign to within a closure, and then do something each time (without duplicating a bunch of code).
// global arrays that we want to assign to asynchronously
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
// kick everything off
loadAsyncContent()
func loadAsyncContent() {
// function to handle the query result strings
// note that outputArray is an inout parameter that will be a reference to one of our global arrays
func resultsCallbackHandler(results: [String], inout outputArray: [String]) {
// assign the results to the specified array
outputArray = results
// trigger some action every time a query returns it's strings
reloadMyView()
}
// kick off each query by telling it which database table to query and
// we're also giving each call a function to run along with a reference to which array the results should be assigned to
queryTable("Table1") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array1)}
queryTable("Table2") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array2)}
queryTable("Table3") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array3)}
}
func queryTable(tableName: String, callback: (foundStrings: [String]) -> Void) {
let query = Query(tableName: tableName)
query.findStringsInBackground({ (results: [String]) -> Void in
callback(results: results)
})
}
// this will get called each time one of the global arrays have been updated with new results
func reloadMyView() {
// do something with array1, array2, array3
}
#rintaro perfectly explained why it doesn't work, but if you really want to do that, using UnsafeMutablePointer will do the trick:
func getOneUserApiData(path: String, c: UnsafeMutablePointer<Int>) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c.memory)")
c.memory++
println("c after: \(c.memory)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}

swift code error page 21

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)

Inout parameter in async callback does not work as expected

I'm trying to insert functions with inout parameter to append data received from async callback to an outside array. However, it does not work. And I tried everything I know to find out why - with no luck.
As advised by #AirspeedVelocity, I rewrote the code as follows to remove unnecessary dependencies. I also use an Int as the inout parameter to keep it simple.The output is always:
c before: 0
c after: 1
I'm not able to figure out what goes wrong here.
func getUsers() {
let u = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
var a = UserData()
a.userIds = u
a.dataProcessor()
}
struct UserData {
var userIds = [String]()
var counter = 0
mutating func dataProcessor() -> () {
println("counter: \(counter)")
for uId in userIds {
getOneUserApiData(uriBase + "user/" + uId + ".json", &counter)
}
}
}
func getOneUserApiData(path: String, inout c: Int) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c)")
c++
println("c after: \(c)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}
Thanks.
Sad to say, modifying inout parameter in async-callback is meaningless.
From the official document:
Parameters can provide default values to simplify function calls and can be passed as in-out parameters, which modify a passed variable once the function has completed its execution.
...
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.
Semantically, in-out parameter is not "call-by-reference", but "call-by-copy-restore".
In your case, counter is write-backed only when getOneUserApiData() returns, not in dataTaskWithRequest() callback.
Here is what happened in your code
at getOneUserApiData() call, the value of counter 0 copied to c1
the closure captures c1
call dataTaskWithRequest()
getOneUserApiData returns, and the value of - unmodified - c1 is write-backed to counter
repeat 1-4 procedure for c2, c3, c4 ...
... fetching from the Internet ...
callback is called and c1 is incremented.
callback is called and c2 is incremented.
callback is called and c3 is incremented.
callback is called and c4 is incremented.
...
As a result counter is unmodified :(
Detailed explaination
Normally, in-out parameter is passed by reference, but it's just a result of compiler optimization. When closure captures inout parameter, "pass-by-reference" is not safe, because the compiler cannot guarantee the lifetime of the original value. For example, consider the following code:
func foo() -> () -> Void {
var i = 0
return bar(&i)
}
func bar(inout x:Int) -> () -> Void {
return {
x++
return
}
}
let closure = foo()
closure()
In this code, var i is freed when foo() returns. If x is a reference to i, x++ causes access violation. To prevent such race condition, Swift adopts "call-by-copy-restore" strategy here.
Essentially it looks like you’re trying to capture the “inout-ness” of an input variable in a closure, and you can’t do that – consider the following simpler case:
// f takes an inout variable and returns a closure
func f(inout i: Int) -> ()->Int {
// this is the closure, which captures the inout var
return {
// in the closure, increment that var
return ++i
}
}
var x = 0
let c = f(&x)
c() // these increment i
c()
x // but it's not the same i
At some point, the variable passed in ceases to be x and becomes a copy. This is probably happening at the point of capture.
edit: #rintaro’s answer nails it – inout is not in fact semantically pass by reference
If you think about it this makes sense. What if you did this:
// declare the variable for the closure
var c: ()->Int = { 99 }
if 2+2==4 {
// declare x inside this block
var x = 0
c = f(&x)
}
// now call c() - but x is out of scope, would this crash?
c()
When closures capture variables, they need to be created in memory in such a way that they can stay alive even after the scope they were declared ends. But in the case of f above, it can’t do this – it’s too late to declare x in this way, x already exists. So I’m guessing it gets copied as part of the closure creation. That’s why incrementing the closure-captured version doesn’t actually increment x.
I had a similar goal and ran into the same issue where results inside the closure were not being assigned to my global inout variables. #rintaro did a great job of explaining why this is the case in a previous answer.
I am going to include here a generalized example of how I worked around this. In my case I had several global arrays that I wanted to assign to within a closure, and then do something each time (without duplicating a bunch of code).
// global arrays that we want to assign to asynchronously
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
// kick everything off
loadAsyncContent()
func loadAsyncContent() {
// function to handle the query result strings
// note that outputArray is an inout parameter that will be a reference to one of our global arrays
func resultsCallbackHandler(results: [String], inout outputArray: [String]) {
// assign the results to the specified array
outputArray = results
// trigger some action every time a query returns it's strings
reloadMyView()
}
// kick off each query by telling it which database table to query and
// we're also giving each call a function to run along with a reference to which array the results should be assigned to
queryTable("Table1") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array1)}
queryTable("Table2") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array2)}
queryTable("Table3") {(results: [String]) -> Void in resultsCallbackHandler(results, outputArray: &self.array3)}
}
func queryTable(tableName: String, callback: (foundStrings: [String]) -> Void) {
let query = Query(tableName: tableName)
query.findStringsInBackground({ (results: [String]) -> Void in
callback(results: results)
})
}
// this will get called each time one of the global arrays have been updated with new results
func reloadMyView() {
// do something with array1, array2, array3
}
#rintaro perfectly explained why it doesn't work, but if you really want to do that, using UnsafeMutablePointer will do the trick:
func getOneUserApiData(path: String, c: UnsafeMutablePointer<Int>) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
println("c before: \(c.memory)")
c.memory++
println("c after: \(c.memory)")
println("thread on: \(NSThread.currentThread())")
}
task.resume()
}