Swift 3.0 closure expression: what if the variadic parameters not at the last place in the parameters list? - swift

Update at 2016.09.19
There is a tricky, indirect way to use variadic parameters before some other parameters in closure expression parameters list, haha
let testClosure = { (scores: Int...) -> (_ name: String) -> String in
return { name in
return "Happy"
}
}
let k = testClosure(1, 2, 3)("John")
And I found some related issues in bugs.swift.org:
SR-2475
SR-494
Original Post
According to the document of Swift 3.0, for a closure expression, "variadic parameters can be used if you name the variadic parameter"(see Closure Expresssion Syntax part). But for Swift 2.x, the description is "Variadic parameters can be used if you name the variadic parameter and place it last in the parameter list", the border part has been removed in Swift 3.0 document, is it means variadic parameter can be a argument of closure expression even it is not at the last place? If so, why the codes below can't compile successfully?
let testClosure = { (scores: Int..., name: String) -> String in
return "Happy"
}
let k = testClosure(1, 2, 3, "John") // Missing argument for parameter #2 in call
If the argument label can be used in the call, I think the compiler can compile the code above successfully, but in Swift 3.0, closure expression's argument labels are regarded as Extraneous.
Besides, Swift 3.0 document indicates that the parameters in closure expression syntax can be in-out parameters, but Swift 3.0 said that closure expression syntax can use constant parameters, variable parameters, and inout parameters. Why Apple removed descriptions like constant parameters, variable paramters, is it because in Swift 3.0, the parameters can't be var?
Thank you very much for your help!

Still in Swift3 variadic arguments have to be the last parameter in the signature, because despite the fact that in your case the last parameter typed as String can be deduced, there's some cases where not, because of the infinite expansion of variadic argument:
let foo = { (i:Int..., j: Int) -> Int in
return j
}
foo(1,2)
...in Swift 3.0, the parameters can't be var?
var params where removed in Swift3 SE-0003 to avoid confusion with inout parameters, because both var and inout params can be assigned inside function, but just inout is reflected back.
func doSomethingWithVar(var i: Int) {
i = 2 // change visible inside function.
}
func doSomethingWithInout(inout i: Int) {
i = 2 // change reflected back to caller.
}
removing var from parameter list, remove the confusion above.

Variadic parameter have to be last and according to your situation, you can type this:
let testClosure = { (_ name: String, scores: Int...) -> String in
return "Happy"
}
let k = testClosure("John", 1, 2, 3)

You are able to create a func in Swift 3.0 where the variadic parameter is NOT the last argument. For example...
func addButtons(buttons: UIButton..., completion: (() -> ())? = nil)
I believe it's because the parameter following the variadic parameter is named, and so the func does not confuse the next named argument with more variadic arguments.
addButtons(buttons: button1, button2, button3) {
//do completion stuff
}

Related

Adding vars to a closure swift

hey I'm a little confused about block syntax. I currently have a function defined like so:
func presentRateAlert(ID: Int, didDismiss: (() -> Void)?)
Currently I do not have any parameters in the block, but I would like to include two. rating: Double? and message: String?. How would I include these?
In your function declaration, didDismiss is a closure. It's type is (() -> Void)?), which is an Optional closure that takes no parameters, and returns Void (no result.)
If you change it to (() -> (Double,String)?
Then your closure returns a Tuple which contains a Double and a String.
(In Swift a function can only return one result. Normally you make that result a Tuple when you want to return more than one thing.)
EDIT:
Based on your edits, it seems you want to add PARAMETERS to your closure, not a return value as you said originally.
An Optional closure that takes a Double and a String and does not return a value would be declared as ((Double, String) -> Void)?)
A function that takes such a closure might look like this:
func test(id: Int, closure: ((Double, String) -> Void)?) {
closure?(3.14, "pi")
}
And calling it might look like this:
test(id: 6, closure: {
(aDouble, aString) in
print("In closure, double = \(aDouble), string = \(aString)")
})

How flatMap API contract transforms Optional input to Non Optional result?

This is the contract of flatMap in Swift 3.0.2
public struct Array<Element> : RandomAccessCollection, MutableCollection {
public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
}
If I take an Array of [String?] flatMap returns [String]
let albums = ["Fearless", nil, "Speak Now", nil, "Red"]
let result = albums.flatMap { $0 }
type(of: result)
// Array<String>.Type
Here ElementOfResult becomes String, why not String? ? How is the generic type system able to strip out the Optional part from the expression?
As you're using the identity transform { $0 }, the compiler will infer that ElementOfResult? (the result of the transform) is equivalent to Element (the argument of the transform). In this case, Element is String?, therefore ElementOfResult? == String?. There's no need for optional promotion here, so ElementOfResult can be inferred to be String.
Therefore flatMap(_:) in this case returns a [String].
Internally, this conversion from the closure's return of ElementOfResult? to ElementOfResult is simply done by conditionally unwrapping the optional, and if successful, the unwrapped value is appended to the result. You can see the exact implementation here.
As an addendum, note that as Martin points out, closure bodies only participate in type inference when they're single-statement closures (see this related bug report). The reasoning for this was given by Jordan Rose in this mailing list discussion:
Swift's type inference is currently statement-oriented, so there's no easy way to do [multiple-statement closure] inference. This is at least partly a compilation-time concern: Swift's type system allows many more possible conversions than, say, Haskell or OCaml, so solving the types for an entire multi-statement function is not a trivial problem, possibly not a tractable problem.
This means that for closures with multiple statements that are passed to methods such as map(_:) or flatMap(_:) (where the result type is a generic placeholder), you'll have to explicitly annotate the return type of the closure, or the method return itself.
For example, this doesn't compile:
// error: Unable to infer complex closure return type; add explicit type to disambiguate.
let result = albums.flatMap {
print($0 as Any)
return $0
}
But these do:
// explicitly annotate [ElementOfResult] to be [String] – thus ElementOfResult == String.
let result: [String] = albums.flatMap {
print($0 as Any)
return $0
}
// explicitly annotate ElementOfResult? to be String? – thus ElementOfResult == String.
let result = albums.flatMap { element -> String? in
print(element as Any)
return element
}

Swift: function compiles, no warning, but can this function even be used?

In Swift it seems to be possible to create a variadic function with a vararg parameter which isn't the last parameter of the function.
I created the following function:
func test1(_ array: String..., _ tag: String){
print("[\(tag)] array: \(array)")
}
Because it's possible to define a vararg parameter without making it the last parameter I suspected that the Swift compiler would just take the last argument and make it the argument for the second parameter.
test1("Hello", "World", "Greeting")
But this doesn't work, which is not too strange.
This doesn't work either:
let arrayOfStrings: [String] = ["Hello", "World"]
test1(arrayOfStrings, "Greeting")
Obviously giving the second parameter a label works.
func test2(_ array: String..., tag: String){
print("[\(tag)] array: \(array)")
}
But I decided to make another try. This function compiles doesn't generate a warning, but can I even use it?
func test3(_ array: String..., _ tag: Int){
print("[\(tag)] array: \(array)")
}
test3("Hello", "World", 1)
Is it possible to call the first method or the third method, without giving the second parameter a label?
Side note: Is this a language feature? Shouldn't the compiler have warn me? It currently tells me that I'm missing the second parameter when calling the function, but to me it seems that it's not even possible to provide this parameter.

Why don't shorthand argument names work in this Swift closure?

Here's a Swift function that takes in two ints and a three-arg function, and calls the passed-in function.
func p(x:Int, _ y:Int, _ f: (Int, Int, Int) -> ()) {
f(x, y, 0)
}
I can call this just fine using both trailing closure syntax and shorthand argument names, no problem:
> p(1, 2) {print($0 + $1 + $2)}
3
That worked as expected. But in the Foundation library, there is a string method called enumerateSubstringsInRange defined as follows:
func enumerateSubstringsInRange(
_ range: Range<Index>,
options opts: NSStringEnumerationOptions,
_ body: (substring: String?,
substringRange: Range<Index>,
enclosingRange: Range<Index>,
inout Bool) -> ())
Okay, that's easy enough: the function takes three arguments, the last of which is four-argument function. Just like my first example! Or so I thought....
I can use this function with the trailing closure syntax, but I cannot use shorthand argument names! I have no idea why. This is what I tried:
let s = "a b c"
"a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {(w,_,_,_) in print(w!)}
a
b
c
All good; I just wanted to print out the matched words, one at a time. That worked when I specified by closure as ``{(w,,,_) in print(w!)}`. HOWEVER, when I try to write the closure with shorthand argument syntax, disaster:
> "a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {print($0!)}
repl.swift:9:86: error: cannot force unwrap value of non-optional type '(substring: String?, substringRange: Range<Index>, enclosingRange: Range<Index>, inout Bool)' (aka '(substring: Optional<String>, substringRange: Range<String.CharacterView.Index>, enclosingRange: Range<String.CharacterView.Index>, inout Bool)')
So what did I do wrong?! The error message seems to say that closure argument $0 is the whole tuple of args. And indeed, when I tried that, that sure seems to be the case!
>"a b c".enumerateSubstringsInRange(s.characters.indices, options: .ByWords) {print($0.0!)}
a
b
c
So I'm terribly confused. Why in the first case (my function p, are the arguments understood to be $0, $1, etc., but in the second case, all the arguments are rolled up into a tuple? Or are they? FWIW, I found the signature of enumerateSubstringsInRange here.
It depends on the number of parameters.
For example,
func test( closure: (Int,Int,Int) -> Void ){
// do something
}
To make test works as you expect, you must specify $2 ( 3rd argument ). The compiler will infer to the values inside tuple, otherwise it will infer to the tuple itself.
If you don't specify $number that match the number of parameters. For example, only specify $1, will make compile error.
// work as expected ( infer to int )
test{
print($2)
}
test{
print($1+$2)
}
test{
print($0+$1+$2)
}
// not work ( infer to tuple )
test{
print($0)
}
// not work ( cannot infer and compile error )
test{
print($1)
}
There is a question relate to this question. Why is the shorthand argument name $0 returning a tuple of all parameters?

Accessing number of parameters in a closure in Swift

Suppose I have a function:
func test(closure: (closureArgs: Double ...) -> Double){
//some logic
}
Then later, I call it with:
test({$0 + $1 + $2 + $3})
Is it possible to get the number of closureArgs provided within test? The goal would be to do overloading. For example, test could include some code like:
func test(closure: (closureArgs: Double ...) -> Double){
//somehow get access to number of arguments in closureArgs within the closure that was passed.
}
To clarify - I mean I need to access closureArgs's length INSIDE test but OUTSIDE closure
Is it possible to get the number of closureArgs provided within test?
The Short Answer
No.
Slightly Longer Answer
No, it is not. Here's why:
The function is taking a closure as it's argument that by definition takes a variadic number of arguments. There's no possible way for someone calling the function to designate how many arguments the closure should take ahead of time. For example, if I were to call your function, it might look like this:
test() { (closureArgs: Double...) -> Double in
var n: Double = 0
for i in closureArgs {
n += i
}
return n
}
As you'll notice, I don't define the number of arguments anywhere because the number of arguments is only specified when the closure is called. Then the number can be determined inside, or possibly returned.
Basically, the closure is called within test, so only you the caller know how many arguments it takes. Anyone consuming this function has no control over it.
The only way I can think of is to have the closure return a tuple that contains the number of arguments and a function that gives the answer, like this:
func test(c: (Double...) -> (Int, (Double...) -> Double)) {
let (argCount, function): (Int, (Double...) -> Double) = { c($0) }()
switch argCount {
// do something with function here
}
}
func sum(a: Double...) -> (Int, ((Double...) -> Double)) {
return (a.count, { reduce(a, 0, +) })
}
Something like that might work, but I'm not in a place where I can test it.
Edit: I'm trying to test this now, and it's close, but it doesn't work yet...
Edit: Okay, this works, but maybe not in the way that you want...
func test(c: (Int, Double)) {
let (argCount, result) = c
switch argCount {
case 2:
println("2 args were passed")
println("The result was \(result)")
default:
println("nothing")
}
}
func sum(a: Double...) -> (Int, Double) {
return (a.count, reduce(a, 0, +))
}
Now test(sum(2, 4)) will print that the number of arguments was 2 and the result was 6.
The thing is, when you are passing a closure that already has arguments supplied (and it needs arguments to know how to calculate the count) then you're closure function already evaluates to a result. So have the closure return the number of arguments that it used to calculate the result along with the result. Then in test you can work with both the number of arguments and the result.
I don't know...it's hard to know exactly what you are after because I'm not sure what kind of logic you need to implement once you know the number of arguments...
Edit: Sorry I keep going on and on, but it seems like you know how many arguments are going to be included when you call test, right? I mean, in your example you supplied 4 arguments:
test({$0 + $1 + $2 + $3})
(Although, you really need to rewrite that as:
test({ return $0[0] + $0[1] + $0[2] + $0[3] })
...cuz remember, variadic parameters are passed as an array...)
So if you know how many arguments are going to be in the closure when you pass it to test, just include the number of arguments as a parameter in test, like this:
func test(count: Int, closure: (Double...) -> Double) {
switch count {
case 1:
// do what you want if there is only one argument
case 2:
// do what you want if there are two arguments...etc.
}
}
Now when you call test, you just pass the number of arguments as an Int:
test(4, { return $0[0] + $0[1] + $0[2] + $0[3] })
In other words, you always know how many arguments you are passing to the closure when you pass it. If you want test to know, just include that as a parameter.
The answer goes back to how Swift passes arguments to varargs functions, which is as an array. Generally speaking, the function definition:
func takesVarArgs(args: Double...)
Is syntactic sugar, providing for simpler invocation, for the declaration:
func takesVarArgs(args:[Double])
So, in your case, your closure will be invoked as:
func test(closure: (closureArgs:[Double]) -> Double)) -> Double
Then, within your closure you could get the number of arguments with:
closureArgs.count