What's the purpose of "with" keyword in Swift? So far I have found that the keyword can be used if you need to override an existing global function, such as toDebugString.
// without "with" you get "Ambiguous use of 'toDebugString'" error
func toDebugString<T>(with x: T) -> String
{
return ""
}
toDebugString("t")
with is not a keyword - it's just an external parameter identifier. This works as well:
func toDebugString<T>(whatever x: T) -> String
Since the toDebugString<T>(x: T) function is already defined, by using an external parameter you are creating an overload: same function name, but different parameters. In this case the parameter is the same, but identified with an external name, and in swift that makes it a method with a different signature, hence an overload.
To prove that, paste this in a playground:
func toDebugString<T>(# x: T) -> String {
return "overload"
}
toDebugString(x: "t") // Prints "overload"
toDebugString("t") // Prints "t"
The first calls your overloaded implementation, whereas the second uses the existing function
Suggested reading: Function Parameter Names
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.
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 already has answers here:
Swift : missing argument label 'xxx' in call
(6 answers)
Closed 4 years ago.
var sum1 = 0
func calculatorMath(arg1: Int, arg2: Int) -> Int {
sum1 = arg1 + arg2
return sum1
}
calculatorMath(20,50)
//the problem is "Missing argument labels 'arg1:arg2:' in call". What do I need to do?
I agree with Martin R. If you are using Xcode, you should have gotten an auto fix error. The correct way to call the function would be:
calculatorMath(arg1: 20,arg2: 50)
When you have an error with argument labels, make sure to check that when you call the function, you are including them.
Good Luck!
Arnav
It has already been suggested that you can fix this by changing the call to:
calculatorMath(arg1: 20, arg2: 50)
you can also fix it by changing the declaration to:
func calculatorMath(_ arg1: Int, _ arg2: Int) -> Int {
Explanation: In Swift each parameter can have two names; an optional external name a caller must specify, and a local name that the body of the function uses (the callee). The optional external name is listed first. If it is omitted the local name must be used by the caller, if the external name is _ (an underscore) then the caller must not use any name.
For example you could declare your function as:
func calculatorMath(_ arg1: Int, arg2: Int) -> Int {
and then a call would require no label on the first arg and one on the second are:
calculatorMath(20, arg2: 50)
Note: The Swift book tends to vary in what it calls the two names/labels:
Function Argument Labels and Parameter Names
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function. By default, parameters use their parameter name as their argument label.
vs.
parameter → external-parameter-nameopt local-parameter-name type-annotation
external-parameter-name → identifier
local-parameter-name → identifier
Excerpts from: Apple Inc. “The Swift Programming Language (Swift 4.0.3).”
My goal is to use a typealias as a one-word reminder “attached” to a function declaration. Say,
typealias VoidToVoid = () -> ()
I can use the type alias when stating the expected type of a closure, thus
let f : VoidToVoid = { return }
assert(f is VoidToVoid)
However, this does not seem the most usual way of declaring functions. I was hoping for a way to tell the reader that a function declared like f′ below, should be of type VoidToVoid, and using that name.
(But without the reader having to infer, or to think, or to wait for the compiler to tell him or her about the type. Aggravating the situation, the compiler cannot know the type alias name I had wanted with f′ and will likely output messages stating the bare type, not the alias.)
func f′() : VoidToVoid { // tentative syntax
return
}
Can I get there at all?
Edit: There are two ends at which the type alias is to be used. At one end, this is now possible:
func giveMeAClosure(_ f: VoidToVoid) {
f()
}
At the other end, being VoidToVoid is implicit:
func canBeGivenAsClosure() {
// ...
}
That is, where canBeGivenAsClosure is declared, it is true but not obvious that there is a connection between the two ends through VoidToVoid. This is different from the let case, which can have "VoidToVoid" as its type annotation.
Closures are unnamed closure expressions, which is why we may use a function type typealias to specify the type of the closure, even for closures that have a non-empty argument list.
typealias VoidToVoid = () -> ()
typealias IntToVoid = (Int) -> ()
// unnamed closure expressions
let f: VoidToVoid = { print("foo") }
let g: IntToVoid = { print($0) }
Note here that f and g in these exaples are not functions: they are simply immutable properties that holds references to (the backing storage of) the unnamed closures specified at the time of their declaration.
A function, one the other hand, is a special case of a closure; a closure with a name. Moreover, any function with a non-empty argument list needs to supply in internal (and optionally) and external parameter names in its declaration. A function type typealias, however, may not contain argument labels for its parameter names:
typealias IntToVoid = (a: Int) -> ()
/* Error: function types cannot have argument
label 'a'; use '_' instead */
This means that a function type typealias can't possible be used a substitute for the combined parameter and return type declaration part of a function (not even in the () -> () case).
For details, see e.g.:
The Language Guide - Closures
Closures
...
Global and nested functions, as introduced in Functions, are actually
special cases of closures. Closures take one of three forms:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
On another note, you may naturally test the type of a given function to some existing function type typealias.
typealias VoidToVoid = () -> ()
typealias IntToVoid = (Int) -> ()
func f() -> () {}
func g(_ a: Int) -> () { _ = a }
print(f is VoidToVoid) // true
print(g is IntToVoid) // true
Suppose that we have several overloaded functions in one class:
func appendToABC(string s: String) -> String {
return "ABC \(s)"
}
func appendToABC(duplicatedString s: String) -> String {
return "ABC \(s)\(s)"
}
And we have some API that gets function as an argument:
func printString(function: (String) -> String) {
print(function("ASD"))
}
How can we pass one of appendToABC functions as an argument to a printString function?
I've thought about wrapping the function with a closure, but it doesn't look nice
printString { appendToABC(duplicatedString: $0) }
This is a known limitation in Swift. There is an open proposal to address it. Currently the only solution is a closure.
Note that this is true of many things in Swift. You also can't refer to properties directly as functions, even though they behave like functions. You must use a closure. And even some free functions cannot be directly passed (print is the most common example).