Swift inferred closure parameter puzzle - ios

As a homage to Ruby I've been playing with an extension to Int that allows me to write useful code like this:
3.times { println("I keep holding on") }
This works well and here's the extension:
extension Int {
func times(fn: () -> ()) {
for i in 1...self {
fn()
}
}
}
Now, I'd like to pass the iteration number in to the closure, so I added a 2nd times() function to extension:
extension Int {
func times(fn: (iteration: Int) -> ()) {
for i in 1...self {
fn(iteration: i)
}
}
}
Which can be invoked like this:
5.times { (i: Int) -> () in println("Year \(i)") }
Now, according to the Swift docs,
It is always possible to infer the parameter types and return type
when passing a closure to a function as an inline closure expression.
As a result, you never need to write an inline closure in its fullest
form when the closure is used a function argument.
That sounds great, because then I can omit the parameter and return types, i.e. (i: Int) -> (), and just use the following syntax instead:
5.times { i in println("Year \(i)") }
But this leads to the following error: Error: Ambiguous use of 'times'
Is this way of invoking my times() function really ambigous to the compiler?

It is ambiguous. Both .times() methods can be used with the given closure expression if the parameter type of the closure is not known.
If you just write { i in println("Year \(i)") }, it is just a closure that takes one parameter of any type. Well, EVERY function type in Swift can be viewed as taking one parameter:
What you think of as zero-parameter functions actually take one parameter of type () (a.k.a. Void), of value (), that's why the type is written as () -> something
What you think of as multiple-parameter functions actually take one parameter of tuple type, a tuple of all the "multiple arguments", that's why the type is written as (foo, bar) -> something.
So, basically, your closure expression, without specifying the type of i, can be inferred to ANY function type in Swift that returns Void. The functions taken by both .times() methods match this -- for the first .times() method, it is inferred as a function of type () -> (), i.e. i has type (); for the second .times() method, it is inferred as a function of type Int -> (), i.e. i has type Int.

it looks like the ambiguity derives from the 2 extension methods having the same name. If you comment your first version of the times function, it works fine - if you comment the 2nd instead, surprisingly, you do not get an error from the compiler.
There is no ambiguity in my opinion, because the 2 functions have different signature - () -> () is different than (iteration: Int) -> (). I think it's a bug in the compiler, specifically type inference is failing.
Making the call explicit instead it works fine
5.times { (i: Int) -> () in println("Year \(i)") }
If the first version with the parameterless closure is commented, the line above compiles correctly, if instead the 2nd overload is commented, compilation fails as expected.
To prove that something is wrong in the compiler, this seems to work, whereas I'd expect a compilation error instead:
extension Int {
func times(fn: () -> ()) {
for i in 1...self {
fn()
}
}
}
5.times { i in println("Year \(i)") }
This is the output from the playground console:
Year ()
Year ()
Year ()
Year ()
Year ()

Related

Using closures as parameters without trailing closures Swift

When using trailing closure syntax it seems quite easy to pass a function as a parameter of another function.
However I want to do this WITHOUT using the trailing closure syntax
func doSomethingTwo(closure: (String) -> Void) {
closure("AAA")
}
doSomethingTwo(closure: print("TEST") )
gives
Cannot convert value of type '()' to expected argument type '(String) -> Void'
I know that
doSomethingTwo{ (test: String) in
print ("\(test)")
}
works, but want this without the trailing closure syntax.
This is not a homework problem, I am looking at tutorials and have done research that has not given me an answer to this question.
You need to define your own print method to pass it as a parameter:
func doSomethingTwo(closure: (String) -> ()) {
// when you call the closure you need to pass the string to be printed
closure("TEST")
}
Define the method to print the string that will be passed to the closure:
let closure: (String) -> () = { print($0) }
Then you can call it as you wish:
doSomethingTwo(closure: closure)
You're not calling the function correctly. You should call it like this:
doSomethingTwo(closure: { (text) in
print(text)
})
This is how you call a function
doSomethingTwo()
If we follow the pattern, put () first, then comes with its parameter, you'll get something like this.
doSomethingTwo(closure: { (str) in
print(str)
})
By the way, Xcode will helps you to complete. But if you don't want its help, when you have this, don't tap, type the rest by your own.
if you're getting this error you're probably missing an = sign
for e.g
yourClosure = { variable: VariableType in
print(variable)
}

Cannot convert value of type '(Int) -> Void' to expected argument type '(Any) -> Void'

I have two functions. The first one is taking an Integer as parameter and returns nothing:
func myIntFunction(_ arg: Int) -> Void {
print(arg)
}
The second one is taking a function of type (Any) -> Void as parameter and returns nothing:
func myAnyFunction(arg: #escaping (Any) -> Void) {
arg(5)
}
So there is something I am not catching here obviously as Int is a subset of Any right?
So if I trying to use this code:
myAnyFunction(arg: myIntFunction)
I get a compilation error:
Cannot convert value of type '(Int) -> Void' to expected argument type '(Any) -> Void'
and a suggestion to change my code into:
myAnyFunction(arg: myIntFunction as! (Any) -> Void)
But then I get a runtime error as (Int) -> Void cannot be casted into (Any) -> Void
What am I not getting there? I thought with a method/function asking for an argument of type Any, giving an Integer as argument would work.
So there is something I am not catching here obviously as Int is a subset of Any right?
Right. However, since Int is a subset of Any, a closure that expects Any can be passed an Int, but not the other way around (this cast is not possible either).
Imagine that it were possible to cast a closure taking an Int to a closure taking Any. Now the users would be able to pass an object other than Int to your closure, rendering type checking irrelevant. That is why casting among closure types like that is not allowed.
You can create another closure that calls myIntFunction, like this:
myAnyFunction(arg: {a -> Void in myIntFunction(a as! Int)})

Passing closure to another closure

I am new to swift programing development. I want to know to how to pass a closure to another closure.
Is there any difference between closure in swift and blocks in objective.
How to pass a closure to a closure?
A closure can be seen as just any other (non-closure) type. This means you can construct a closure where the argument of the closure describes another closure type.
E.g.
let sendMeAClosure: ((Int) -> String) -> () = {
print($0(42)) /* ^^^^^^^^^^^^^^^- argument of sendMeAClosure
is a closure itself */
}
let myClosure: (Int) -> String = {
return "The answer is \($0)."
}
sendMeAClosure(myClosure) // The answer is 42
Note that functions in Swift are just a special type of closure, so you might as well supply a function reference (which has a signature matching the argument type) to sendMeAClosure above.
/* sendMeAClosure as above */
func myFunc(arg: Int) -> String {
return "The answer is \(arg)."
}
sendMeAClosure(myFunc) // The answer is 42
Is there any difference between closure in swift and blocks in objective?
For your 2nd question, refer to the following Q&A
Difference between block (Objective C) and closure (Swift) in ios

Swift Closure why does calling function return error?

just learning about closures and nesting functions. Given the nested function below:
func printerFunction() -> (Int) -> () {
var runningTotal = 0
func printInteger(number: Int) {
runningTotal += 10
println("The running total is: \(runningTotal)")
}
return printInteger
}
Why does calling the func itself have an error, but when I assign the func to a constant have no error? Where is printAndReturnIntegerFunc(2) passing the 2 Int as a parameter to have a return value?
printerFunction(2) // error
let printAndReturnIntegerFunc = printerFunction()
printAndReturnIntegerFunc(2) // no error. where is this 2 going??
First of all you are getting error here printerFunction(2) because printerFunction can not take any argument and If you want to give an argument then you can do it like:
func printerFunction(abc: Int) -> (Int) -> (){
}
And this will work fine:
printerFunction(2)
After that you are giving reference of that function to another variable like this:
let printAndReturnIntegerFunc = printerFunction()
which means the type of printAndReturnIntegerFunc is like this:
that means It accept one Int and it will return void so this will work:
printAndReturnIntegerFunc(2)
(1) The function signature of printerFunction is () -> (Int) -> () which means it takes no parameter and returns another function, thats why when you try to call printerFunction(2) with a parameter gives you an Error.
(2) And the signature of the returned function is (Int) -> () which means it takes one parameter of Int and returns Void. So printAndReturnIntegerFunc(2) works

String is not a subtype of ()

I was trying to create a block that I could enter parameters but had no return (a type block void). We know that the structure of a block that does not have parameters and not return anything is this:
var block: () -> () = {
//Hello World goes here
}
In the case of a block with parameters, but does not return anything, we can use this command:
var bloco7: (String) -> () = {
//Hello World goes here
}
But we have two problems: The first is that this code not working, the second is this code only works if we call a temporary variable like this:
var bloco7: (String) -> () = {
$0//I only need to call
//Hello World goes here and all the things OK
}
I wonder know why this is happening, it does not seem to make much sense ...
There's two parts here.
First, we declare the type of our closure:
var bloco7: (String) -> ()
bloco7 is a variable whose type is (String) -> () (a closure which takes a string and returns nil. We don't define the name of the parameter here. The parameter is named by the "closure literal syntax" so-to-speak.
So, if we want to have an inline closure, we can later to:
var bloco7: (String) -> () = {
yourArgument in // <-- here we named the parameter, it's called 'yourArgument'
println(yourArgument)
}
But it's important to note that we aren't defining the variable's name when we are declaring bloco7 and its type. Keep in mind that we can assign methods and functions to closure variables.
For example, given this function:
func sayHello(name: String) -> Void {
println("Hello, " + name + "!")
}
We can actually have bloco7 point to this function:
bloco7 = sayHello
And now, the following two lines actually do the exact same thing:
sayHello("World")
bloco7("World")
The important take-away here is that the argument names are irrelevant to the type of closure's type. The argument names are part of the value we assign to the variable, not part of its type.
In the first,you need to do it like tis
var bloco7: (String) -> () = {
input in
//Do something with input
}
bloco7 type is:(String)->()
{} type is :() -> ()
They do not match,so you get an error
But in the second way,you use $0,swift know that it has a input,it will match.Match $0 to the input String,so type is right

Resources