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.
Related
I'm wondering if Swift has a way to let you pass in a specific overload of a function as an argument to a second function that takes a closure with a matching signature, based on type, but without explicitly creating a closure yourself.
Note: This is not a problem I'm trying to solve. It's just a curiosity about the language based on something I ran into when creating an extension to a struct that suddenly broke the compile. It was easily addressed with a closure, like below. It just had me wondering if there was another way to solve it.
Here's code showing what I mean. Let's start with this struct Foo...
struct Foo {
let value: Int
}
Once defined, we can use it as part of a mapping operation via a closure, like so...
let values = [1, 2, 3, 4]
let foos = values.map{ Foo(value: $0) }
However, since the initializer itself already matches the signature of the closure argument type-wise, you can skip the manually-created closure entirely and pass in the function directly, like so...
let values = [1, 2, 3, 4]
let foos = values.map(Foo.init)
What's interesting to note is Swift's compiler finds that match based only on the type of the argument to init, not it's label, which was required earlier in the closure version.
What I discovered is by defining the following extension somewhere in code-scope view of the values.map(Foo.init) call site...
extension Foo {
init(valueToDouble value: Int) { self.value = value * 2 }
}
...that call site suddenly gets flagged as ambiguous and it breaks the compile. This is because even though the labels are different, the argument type--Int in this example--is the same and it doesn't know which one to use.
Now again, this is easily solved with a simple closure, like so...
// Original init
let foos = values.map{ Foo(value: $0) }
// or init in the extension
let foos = values.map{ Foo(valueToDouble: $0) }
I'm just wondering if the Swift compiler has some 'magic sauce' that lets me do something like this...
let foos = values.map(Foo.init(valueToDouble:))
...which obviously doesn't work. :)
So is there anything like that, or is the closure-based version the (only) way to go?
Update
Well, nothing "obvious" about it being wrong because what was wrong is I had a typo (now fixed) and the above syntax does in fact work! Thanks, Itai! :)
In Swift, the "base name" of a method is the method name without any arguments:
Foo.init(x:y:z:) → Foo.init
Foo.bar(_:) → Foo.bar
Foo.baz(baz:) → Foo.baz
Foo.quux() → Foo.quux
When referring to a method by name (rather than calling it), Swift will allow you to refer to it by its base name, so long as the usage is not ambiguous:
struct Foo {
func f(intValue: Int) {}
}
let f = Foo().f
However, when there are multiple methods with the same base name, you can run into situations with ambiguity:
struct Foo {
func f(intValue: Int) {}
func f(stringValue: String) {}
}
let f = Foo().f // error: ambiguous use of 'f'
When this happens, you can either:
Use explicit typing to help disambiguate the methods, if possible:
let f1: (Int) -> Void = Foo().f
let f2: (String) -> Void = Foo().f
Or,
Refer to the method by its fully-qualified name (name with parameter names included):
let f1 = Foo().f(intValue:)
let f2 = Foo().f(stringValue:)
In your case, because both methods have the same type signature, you can't use approach (1) to disambiguate between the calls, and will have to resort to (2).
The issue you ran into is that the parameter name you were using was slightly off:
// ❌ Foo.init(doubleValue:)
// ✅ Foo.init(valueToDouble:)
let foos = values.map(Foo.init(valueToDouble:)) // ✅
This does work, and will work as a shorthand instead of having to call the method directly inside of a closure argument.
This question already has answers here:
What is _: in Swift telling me?
(3 answers)
Closed 1 year ago.
Beginner question, I realise that when the Xcode declares a function for #IBAction, it declares it as below:
#IBAction func hardnessSelected(_ sender: UIButton),
which I read is,
Create a function called hardness selected which accepts a parameter
called sender which accepts the type UI button.
From my understanding so far _ is used when you want to declare a variable that you are not going to mutate e.g. in a for loop to tell swift the value of this variable doesn't matter to optimize performance.
However, in the above case there is a name for the variable "sender" as well as _ which I don't understand why.
Can someone please explain?
This place is declared for the label which means that once you call the function it won't appear to you for example:
func sum (_ number1: Int, _ number2: Int) {
print(number1 + number2)
}
once you call the function you won't need to mention number1 or number2 but you will only write the number directly :
sum(1, 2)
To be clear it's the same as using the function below:
func summation(myFirstNumber number1: Int, mySecondNumber number2: Int) {
print (number1 + number2)
}
but here instead of using _ I've used a label so when I called the function, I will use these labels :
summation(myFirstNumber: 1, mySecondNumber: 2)
So it's clear now that _ is instead of writing a label.
For more information check: Function Argument Labels and Parameter Names from here
Swift allows having an argumentLabel different than actual argument itself for better readability.
In case your function signature is like this -
func value(for key: String)
In this case, these values are argumentLabel == for & argument == key. At the call site, you have to mention the argumentLabel like following.
value(for: "myCustomKey")
In case your function signature is like this -
func valueForKey(_ key: String)
In this case you are explicitly asking compiler to allow you to omit argumentLabel while calling this function. Please note that argument == key will not be visible at call site for this like following.
valueForKey("myCustomKey")
How can I access the str label inside the function body? If it's impossible then why the compiler doesn't throw an error ?
class Test {
func methodA(str _: String) {
}
func methodB() {
methodA(str: "")
}
}
Using _ for the parameter name means you have no need to use the parameter within the method. The str argument label has no use inside the implementation of the method itself. It's only useful in the calling syntax.
If you actually have a need to use a parameter in a method, don't give it the anonymous name of _. Give it a useful name so it can be referenced in the implementation of the method. Or simply remove the _ so both the parameter name and label are the same.
Either do:
func methodA(str: String) {
// do something with str
}
or something like:
func methodA(str val: String) {
// do something with val
}
In both cases the caller is the same as you currently have:
methodA(str: "")
The only time you would want an anonymous parameter name is if your implementation of the method doesn't actually make use of the parameter. Maybe the implementation isn't finished or over time it's changed and a parameter isn't needed any more and you don't want to update lots of existing code calling the method.
There is a compiler setting to get warnings about unused parameters. If you have an unused parameter, changing the name to _ eliminates the warning.
See Function Argument Labels and Parameter Names in the Swift book (though it doesn't cover this case of anonymous parameter names).
This question is just having fun with Swift. Please don't take it too seriously...
In Swift, given this function:
func greet(name: String) {
print("Hello \(name)!")
}
the proper way of calling it is like this:
greet(name: "Bob") // "Hello Bob!"
If you omit the argument label name:
greet("Bob")
you get the error: Missing argument label 'name:' in call
But treating the function as a closure allows you to call it without the argument label:
let g = greet; g("Bob") // "Hello Bob!"
This made me wonder, what is the most concise way of calling a function without using its argument label?
Wrap it in a tuple?
I thought wrapping the function in a tuple and then extracting it would work:
(greet).0("Bob")
but this resulted in the error: Value of type '(String) -> ()' has no member '0'.
This works:
(greet,greet).0("Bob") // "Hello Bob!"
So apparently, Swift treats simple tuples of 1 value as just the value itself.
Giving the tuple argument a label works:
(g:greet).g("Bob") // "Hello Bob!"
(g:greet).0("Bob") // "Hello Bob!"
Put it in an array?
So far, the most concise way I have found is to put the function into an array:
[greet][0]("Bob") // "Hello Bob!"
This uses only 5 characters more than greet("Bob") which would seem to be the shortest possible call if it worked.
Is there any shorter way to call the greet(name:) function without using the argument label than this?
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
}