Is there shorthand to specify a specific overload when passing a function as a closure-based argument? - swift

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.

Related

Why does a Swift enum with optional associated value require trailing parentheses when being declared?

I have a Swift Enum with a computed property. There are several cases that include a single, optional, associated value. If I want to call the computed property on a variable holding a case value that contains an associated value, then I have to declare the variable with trailing parentheses, otherwise I get a compiler error.
If I don't want to call the computed property, then I don't need to include the parentheses.
Why is that and how are the trailing parentheses changing the "type" of the property such that a computed property can then be called on it?
A simple example to highlight the issue:
import Foundation
enum Animal {
case dog
case other(String? = nil)
var isFriendly:Bool {
switch self {
case .dog: return true
default: return false
}
}
}
// This generates the error:
// Value of type '(String?) -> Animal' has no member 'isFriendly'
let exoticBird = Animal.other
print("Is exotic bird friendly: \(exoticBird.isFriendly)")
// This line, with the trailing parentheses, does not.
let exoticFish = Animal.other()
print("Is exotic fish friendly: \(exoticFish.isFriendly)")
It compiles with Xcode 11 beta and produces a case constructor. Here is the link to Swift evolution proposal.
Your exoticBird is not Animal, it is rather a case constructor. Alt + Click on your exoticBird variable and you will see type of it defined as follows.
let exoticBird: (String?) -> Animal
I think when you leave out associated type in enum, it creates a function based on associated type.
You could pass string to it and then call isFriendly on it like so,
isExoticBird(nil).isFriendly
So, it seems that you cannot leave associated type if you want to create the type explicitly unless you want to do some functional programming.
Now, Small functional transformation with the case constructor type.
Define a function like so,
func isOtherAnimalFriendly(_ f: #escaping (String?) -> Animal) -> (String?) -> Bool {
return { animalName in
f(animalName).isFriendly
}
}
And now, use the following code,
let exoticFish = Animal.other
let namedAnimal = isOtherAnimalFriendly(exoticFish)
Notice that the type of namedAnimal is now,
let namedAnimal: (String?) -> Bool
And you could use this new function with name,
print("Is exotic fish friendly: \(namedAnimal(nil))")
Also use higher order functions such as map on it,
["Cat", "Cow", "Horse"].map(Animal.other) // array of animals with associated values

Swift type inference in methods that can throw and cannot

As you may know, Swift can infer types from usage. For example, you can have overloaded methods that differ only in return type and freely use them as long as compiler is able to infer type. For example, with help of additional explicitly typed variable that will hold return value of such method.
I've found some funny moments. Imagine this class:
class MyClass {
enum MyError: Error {
case notImplemented
case someException
}
func fun1() throws -> Any {
throw MyError.notImplemented
}
func fun1() -> Int {
return 1
}
func fun2() throws -> Any {
throw MyError.notImplemented
}
func fun2() throws -> Int {
if false {
throw MyError.someException
} else {
return 2
}
}
}
Of course, it will work like:
let myClass = MyClass()
// let resul1 = myClass.fun1() // error: ambiguous use of 'fun1()'
let result1: Int = myClass.fun1() // OK
But next you can write something like:
// print(myClass.fun1()) // error: call can throw but is not marked with 'try'
// BUT
print(try? myClass.fun1()) // warning: no calls to throwing functions occur within 'try' expression
so it looks like mutual exclusive statements. Compiler tries to choose right function; with first call it tries to coerce cast from Int to Any, but what it's trying to do with second one?
Moreover, code like
if let result2 = try? myClass.fun2() { // No warnings
print(result2)
}
will have no warning, so one may assume that compiler is able to choose right overload here (maybe based on fact, that one of the overloads actually returns nothing and only throws).
Am I right with my last assumption? Are warnings for fun1() logical? Do we have some tricks to fool compiler or to help it with type inference?
Obviously you should never, ever write code like this. It's has way too many ways it can bite you, and as you see, it is. But let's see why.
First, try is just a decoration in Swift. It's not for the compiler. It's for you. The compiler works out all the types, and then determines whether a try was necessary. It doesn't use try to figure out the types. You can see this in practice here:
class X {
func x() throws -> X {
return self
}
}
let y = try X().x().x()
You only need try one time, even though there are multiple throwing calls in the chain. Imagine how this would work if you'd created overloads on x() based on throws vs non-throws. The answer is "it doesn't matter" because the compiler doesn't care about the try.
Next there's the issue of type inference vs type coercion. This is type inference:
let resul1 = myClass.fun1() // error: ambiguous use of 'fun1()'
Swift will never infer an ambiguous type. This could be Any or it could beInt`, so it gives up.
This is not type inference (the type is known):
let result1: Int = myClass.fun1() // OK
This also has a known, unambiguous type (note no ?):
let x : Any = try myClass.fun1()
But this requires type coercion (much like your print example)
let x : Any = try? myClass.fun1() // Expression implicitly coerced from `Int?` to `Any`
// No calls to throwing function occur within 'try' expression
Why does this call the Int version? try? return an Optional (which is an Any). So Swift has the option here of an expression that returns Int? and coercing that to Any or Any? and coercing that to Any. Swift pretty much always prefers real types to Any (and it properly hates Any?). This is one of the many reasons to avoid Any in your code. It interacts with Optional in bizarre ways. It's arguable that this should be an error instead, but Any is such a squirrelly type that it's very hard to nail down all its corner cases.
So how does this apply to print? The parameter of print is Any, so this is like the let x: Any =... example rather than like the let x =... example.
A few automatic coercions to keep in mind when thinking about these things:
Every T can be trivially coerced to T?
Every T can be explicitly coerced to Any
Every T? can also be explicitly coerce to Any
Any can be trivially coerced to Any? (also Any??, Any???, and Any????, etc)
Any? (Any??, Any???, etc) can be explicitly coerced to Any
Every non-throwing function can be trivially coerced to a throwing version
So overloading purely on "throws" is dangerous
So mixing throws/non-throws conversions with Any/Any? conversions, and throwing try? into the mix (which promotes everything into an optional), you've created a perfect storm of confusion.
Obviously you should never, ever write code like this.
The Swift compiler always tries to call the most specific overloaded function is there are several overloaded implementations.
The behaviour shown in your question is expected, since any type in Swift can be represented as Any, so even if you type annotate the result value as Any, like let result2: Any = try? myClass.fun1(), the compiler will actually call the implementation of fun1 returning an Int and then cast the return value to Any, since that is the more specific overloaded implementation of fun1.
You can get the compiler to call the version returning Any by casting the return value to Any rather than type annotating it.
let result2 = try? myClass.fun1() as Any //nil, since the function throws an error
This behaviour can be even better observed if you add another overloaded version of fun1 to your class, such as
func fun1() throws -> String {
return ""
}
With fun1 having 3 overloaded versions, the outputs will be the following:
let result1: Int = myClass.fun1() // 1
print(try? myClass.fun1()) //error: ambiguous use of 'fun1()'
let result2: Any = try? myClass.fun1() //error: ambiguous use of 'fun1()'
let stringResult2: String? = try? myClass.fun1() // ""
As you can see, in this example, the compiler simply cannot decide which overloaded version of fun1 to use even if you add the Any type annotation, since the versions returning Int and String are both more specialized versions than the version returning Any, so the version returning Any won't be called, but since both specialized versions would be correct, the compiler cannot decide which one to call.

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 method call is not by add(9,2) but add(9,b:2) [duplicate]

In answering this question it came about that argument labels were required for a call to init. This is normal in Swift.
class Foo {
init(one: Int, two: String) { }
}
let foo = Foo(42, "Hello world") // Missing argument labels 'one:two:' in call
However, stranger forces are at play:
extension Foo {
func run(one: String, two: [Int]) { }
}
foo.run(one: "Goodbye", two: []) // Extraneous argument label 'one:' in call
To use an argument label here it would have to be declared explicitly.
I haven't seen something very thorough explaining all of this in the documentation. For which varieties of class/instance/global functions are argument labels required? Are Obj-C methods always exported and imported with argument labels?
All init methods require parameter names:
var view = NSView(frame: NSRect(x: 10, y: 10, width: 50, height: 50))
class Foo {
init(one: Int, two: String) { }
}
let foo = Foo(one: 42, two: "Hello world")
All methods called on an object use parameter names for everything but the first parameter:
extension Foo {
func run(one: String, two: [Int]) { }
}
foo.run("Goodbye", two: [])
All including class functions in Swift and objective-c follow the same pattern. You also can explicitly add external names.
extension Foo{
class func baz(one: Int, two: String){}
class func other(exOne one: Int, exTwo two: String){}
}
Foo.baz(10, two:"str")
Foo.other(exOne: 20, exTwo:"str")
Swift functions that are not a class function don't require parameter names, but you still can explicitly add them:
func bar(one: Int, two: String){}
bar(1, "hello")
As Bryan said, it's to make Swift method calls make sense when called on objective-c methods that have parameter names in the method signature. Init methods include the first parameter because Swift changes the init methods from objective-c from initWith:... to Class() so the first parameter name is no longer included in the method name.
As of Swift 3.0 this has changed again: all methods, functions, and initializers require argument labels for all parameters, unless you have explicitly opted out using the external name _. This means methods such as addChildViewController(_:) are now written like this:
func addChildViewController(_ childController: UIViewController)
This was proposed and approved as part of the Swift Evolution process, and was implemented in SR-961.
Swift 3.0
In Swift 3.0, slated to be released in late 2016, the default behavior is simple:
All parameters to all methods have external labels by default.
You can find these rules most concisely in the Swift API Design Guidelines. This newest behavior was proposed in SE-0056, "establish consistent label behavior across all parameters including first labels," and implemented in SR-961. The default behavior may be changed as described below, in "Overriding the Default Behavior."
Swift 2.2
In Swift 2.2, the language's defaults for the presence of external argument labels have changed and are now simpler. The default behavior can be summarized as follows:
First parameters to methods and functions should not have external argument labels.
Other parameters to methods and functions should have external argument labels.
All parameters to initializers should have external argument labels.
The default behavior may be changed as described below, in "Overriding the Default Behavior."
An Example
These rules are best demonstrated with an example:
func printAnimal(animal: String, legCount: Int) {
let legNoun = legCount == 1 ? "leg" : "legs"
print("\(animal) has \(legCount) \(legNoun)")
}
struct Player {
let name: String
let lives: Int
init(name: String, lives: Int) {
self.name = name
self.lives = lives
}
func printCurrentScore(currentScore: Int, highScore: Int) {
print("\(name)'s score is \(currentScore). Their high score is \(highScore)")
}
}
// SWIFT 3.0
// In Swift 3.0, all argument labels must be included
printAnimal(animal: "Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(currentScore: 50, highScore: 110)
// SWIFT 2.2
// In Swift 2.2, argument labels must be included or omitted in exactly the following way
// given the definition of the various objects.
printAnimal("Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(50, highScore: 110)
// In Swift 2.2, none of the following will work
printAnimal(animal: "Dog", legCount: 4) // Extraneous argument label 'animal:' in call
let q = Player("Riley", lives: 3) // Missing argument label 'name:' in call
p.printCurrentScore(50, 110) // Missing argument label 'highScore:' in call
Overriding the Default Behavior
For any parameter to any method or function, you may deviate from the language's default, though the style guide rightly warns you not to do so unless there's a good reason.
To add an external parameter label where there would normally not be one – only applicable in Swift 2.2, since Swift 3.0 defaults to assigning external labels to every parameter – or to change an external parameter label – applicable to both versions – write the desired external parameter label before the local parameter label:
func printAnimal(theAnimal animal: String, legCount: Int) {
let legNoun = legCount == 1 ? "leg" : "legs"
print("\(animal) has \(legCount) \(legNoun)")
}
printAnimal(theAnimal: "Dog", legCount: 4)
To remove an external parameter label where there normally would be one, use the special external parameter label _:
func printAnimal(animal: String, _ legCount: Int) {
let legNoun = legCount == 1 ? "leg" : "legs"
print("\(animal) has \(legCount) \(legNoun)")
}
// SWIFT 3.0
printAnimal(theAnimal: "Dog", 4)
// SWIFT 2.2
printAnimal("Dog", 4)
These "default overrides" will work for any method or function, including initializers.
Here's what I've been able to gather through reading the (fairly sparse) documentation, and through plain experimentation:
Init methods always need their labels. Init methods like labels, as they make it clear what init method, exactly, you want to call. Otherwise, this:
FooBar(foos: 5)
And this:
FooBar(bars: 5)
Would look exactly the same:
FooBar(5)
Nowhere else is this the case - init methods are the only place in Swift where they all have the same name, but potentially different arguments. Which is why...
Functions, methods, etc (anything that isn't an init method) have the first label omitted - this is for style and to cut down on boring repetitiveness. Instead of
aDictionary.removeValueForKey(key: "four")
We have this:
aDictionary.removeValueForKey("four")
And still have fairly un-ambiguous and easy-to-read arguments to functions with two parameters. So instead of
anArray.insert("zebras", 9)
We have a much more understandable-on-reading form:
anArray.insert("zebras", atIndex: 9)
Which looks much better. When I was at WWDC, this was touted as a feature of Swift: Java-style modern, short arguments, without sacrificing readability. This also eases the transition from Objective-C, as Bryan Chen's answer shows.
You can make a parameter label required for calling a method using # before the label.
E.g.:
func addLocation(latitude : Double, longitude : Double) { /*...*/ }
addLocation(125.0, -34.1) // Not clear
Can be improved like so:
func addLocation(#latitude : Double, #longitude : Double) { /*...*/ }
addLocation(latitude: 125.0, longitude: -34.1) // Better
(From WWDC 2014 - 416 - Building Modern Frameworks, 15 mins in)
It is only make ObjC methods looks nice in Swift.
Documentation
Instance Methods
Local and External Parameter Names for Methods
Specifically, Swift gives the first parameter name in a method a local
parameter name by default, and gives the second and subsequent
parameter names both local and external parameter names by default.
This convention matches the typical naming and calling convention you
will be familiar with from writing Objective-C methods, and makes for
expressive method calls without the need to qualify your parameter
names.
...
The default behavior described above mean that method definitions in Swift are written with the same grammatical style as Objective-C, and are called in a natural, expressive way.
Customizing Initialization
Local and External Parameter Names
However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic external name for every parameter in an initializer if you don’t provide an external name yourself.
For example for this ObjC class
#interface Counter : NSObject
#property int count;
- (void)incrementBy:(int)amount numberOfTimes:(int)numberOfTimes;
#end
and it written in Swift
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
to call ObjC version
[counter incrementBy:10 numberOfTimes:2];
and Swift version
counter.incrementBy(10, numberOfTimes:2)
you can see they are almost the same

T, Optional<T> vs. Void, Optional<Void>

// this declaration / definition of variable is OK, as expected
var i = Optional<Int>.None
var j:Int?
// for the next line of code compiler produce a nice warning
// Variable 'v1' inferred to have type 'Optional<Void>' (aka 'Optional<()>'), which may be unexpected
var v1 = Optional<Void>.None
// but the next sentence doesn't produce any warning
var v2:Void?
// nonoptional version produce the warning 'the same way'
// Variable 'v3' inferred to have type '()', which may be unexpected
var v3 = Void()
// but the compiler feels fine with the next
var v4: Void = Void()
What is the difference? Why is Swift compiler always happy if the type is anything else than 'Void' ?
The key word in the warning is "inferred." Swift doesn't like inferring Void because it's usually not what you meant. But if you explicitly ask for it (: Void) then that's fine, and how you would quiet the warning if it's what you mean.
It's important to recognize which types are inferred and which are explicit. To infer is to "deduce or conclude (information) from evidence and reasoning rather than from explicit statements." It is not a synonym for "guess" or "choose." If the type is ambiguous, then the compiler will generate an error. The type must always be well-defined. The question is whether it is explicitly defined, or defined via inference based on explicit information.
This statement has a type inference:
let x = Foo()
The type of Foo() is explicitly known, but the type of x is inferred based on the type of the entire expression (Foo). It's well defined and completely unambiguous, but it's inferred.
This statement has no type inference:
let x: Foo = Foo()
But also, there are no type inferences here:
var x: Foo? = nil
x = Foo()
The type of x (Foo?) in the second line is explicit because it was explicitly defined in the line above.
That's why some of your examples generate warnings (when there is a Void inference) and others do not (when there is only explicit use of Void). Why do we care about inferred Void? Because it can happen by accident very easily, and is almost never useful. For example:
func foo() {}
let x = foo()
This is legal Swift, but it generates an "inferred to have type '()'" warning. This is a very easy error to make. You'd like a warning at least if you try to assign the result of something that doesn't return a result.
So how is it possible that we assign the result of something that doesn't return a result? It's because every function returns a result. We just are allowed to omit that information if the return is Void. It's important to remember that Void does not mean "no type" or "nothing." It is just a typealias for (), which is a tuple of zero elements. It is just as valid a type as Int.
The full form of the above code is:
func foo() -> () { return () }
let x = foo()
This returns the same warning, because it's the same thing. We're allowed to drop the -> () and the return (), but they exist, and so we could assign () to x if we wanted to. But it's incredibly unlikely that we'd want to. We almost certainly made a mistake and the compiler warns us about that. If for some reason we want this behavior, that's fine. It's legal Swift. We just have to be explicit about the type rather than rely on type inference, and the warning will go away:
let x: Void = foo()
Swift is being very consistent in generating warnings in your examples, and you really do want those warnings. It's not arbitrary at all.
EDIT: You added different example:
var v = Optional<Void>()
This generates the error: ambiguous use of 'init()'. That's because the compiler isn't certain whether you mean Optional.init() which would be .None, or Optional.init(_ some: ()), which would be .Some(()). Ambiguous types are forbidden, so you get a hard error.
In Swift any value will implicitly convert with it's equivalent 1-tuple. For example, 1 and (1) are different types. The first is an Int and the second is a tuple containing an Int. But Swift will silently convert between these for you (this is why you sometimes see parentheses pop up in surprising places in error messages). So foo() and foo(()) are the same thing. In almost every possible case, that doesn't matter. But in this one case, where the type really is (), that matters and makes things ambiguous.
var i = Optional<Int>()
This unambiguously refers to Optional.init() and returns nil.
The compiler is warning you about the "dummy" type Void, which is actually an alias for an empty tuple (), and which doesn't have too many usages.
If you don't clearly specify that you want your variable to be of type Void, and let the compiler infer the type, it will warn you about this, as it might be you didn't wanted to do this in the first place.
For example:
func doSomething() -> Void {
}
let a = doSomething()
will give you a warning as anyway there's only one possible value doSomething() can return - an empty tuple.
On the other hand,
let a: Void = doSomething()
will not generate a warning as you explicitly tell the compiler that you want a Void variable.