Why a Swift existential overload is preferred rather and the generic one? - swift

In the following code snippet, I defined two structs conforming to a common protocol. Then, I defined three overloads for a function: a concrete one, a generic one and an existential one:
protocol Foo { }
struct Bar: Foo { }
struct Baz: Foo { }
func process(_ data: Bar) { print("Bar") } // Concrete
func process<D: Foo>(_ data: D) { print("Generic") } // Generic
func process(_ data: any Foo) { print("Existential") } // Existential
process(Bar()) // prints "Bar"
process(Baz()) // prints "Existential"
Why does for the call to process(Baz()) the compiler chooses the existential overload while there is a more specific one, i.e. the generic one?
Also, I would have expected the compiler to enforce the programmer to write something like this for it to acknowledge that the instance is being wrapped in the existential type:
process(Baz() as Foo) // Explicit conversion to the existential type
Is this behavior expected, and is the only solution is to use the #disfavoredOverload attribute on the existential overload?

Related

Implement generic protocol method with but use generic for whole class

I'm am trying to implement a protocol method that has a generic argument, but then use the generic type for my entire class instead of just on the method, something like this
protocol FirstProtocol {
}
protocol SecondProtocol {
func foo<T: FirstProtocol>(argument: T)
}
class MyType<T: FirstProtocol>: SecondProtocol {
var value: T? = nil
func foo<T>(argument: T) {
value = argument // ERROR: Cannot assign value of type 'T' to type 'T?'
}
}
So the swift compiler accepts that foo<T>(argument:T) matches the method of SecondProtocol, if I comment out the error line it compiles fine, but it will not let me assign argument to value even though value and argument should be the same type, the compiler complains as if they are different types.
The type of argument and value are indeed different types. The T generic parameter in foo is just an identifier, and I can change it to anything else:
class MyType<T: FirstProtocol>: SecondProtocol {
var value: T? = nil
func foo<AnythingElse>(argument: AnythingElse) {
// MyType still conforms to SecondProtocol
}
}
The T in foo is a brand new generic parameter, different from the T in MyType. They just so happens to have the same name.
Note that when you declare a generic method, it's the caller that decides what the generic type is, not the generic method. What foo is trying to say here is "I want the T in foo to be the same type as the T in MyType", but it can't say that about its own generic parameters!
One way to fix it is to make SecondProtocol have an associated type:
protocol SecondProtocol {
// name this properly!
associatedtype SomeType: FirstProtocol
func foo(argument: SomeType)
}
class MyType<T: FirstProtocol>: SecondProtocol {
typealias SomeType = T // here is where it says "I want 'SomeType' to be the same type as 'T'!"
var value: T? = nil
func foo(argument: T) {
value = argument
}
}
it will not let me assign argument to value even though value and argument should be the same type, the compiler complains as if they are different types.
think about this case:
class A: FirstProtocol {
}
class B: FirstProtocol {
}
class A and B is the acceptable generic type for func foo(argument: T){}, but can you assign an instance of class A to class B?
class MyType<T: FirstProtocol>: SecondProtocol
remove ": FirstProtocol"should work, or use a base class to replace FirstProtocol

Swift generic type defined by method context

Is there a name for a pattern where the type is inferred by the context of the result type?
Eg in this example what language could i use to document the foo method and explain that a type needs to be defined for the method to work?
protocol FooType {
init()
}
func foo<T: FooType>() -> T {
return T()
}
struct Bar: FooType {
init() {
print("bar")
}
}
let bar: Bar = foo()
// works returns instance of Bar
let fooType = foo()
// fails because foo doesn't know what type to use
You don't need to document this!
Everyone that writes code in Swift knows that to call a generic function, all its type parameters must be inferred and cannot be spoon-fed like this:
foo<Bar>()
People will see foo and say, "Oh I need the compiler to infer the type for this generic parameter." They will understand what this means.

Function that takes a protocol and a conforming class (!) instance as parameters

I am trying to figure out how to define a function which takes the following
two parameters:
A protocol.
An instance of a class (a reference type) conforming to that protocol.
For example, given
protocol P { }
class C : P { } // Class, conforming to P
class D { } // Class, not conforming to P
struct E: P { } // Struct, conforming to P
this should compile:
register(proto: P.self, obj: C()) // (1)
but these should not compile:
register(proto: P.self, obj: D()) // (2) D does not conform to P
register(proto: P.self, obj: E()) // (3) E is not a class
It is easy if we drop the condition that the second parameter is a class instance:
func register<T>(proto: T.Type, obj: T) {
// ...
}
but this would accept the struct (value type) in (3) as well.
This looked promising and compiles
func register<T: AnyObject>(proto: T.Type, obj: T) {
// ...
}
but then none of (1), (2), (3) compile anymore, e.g.
register(proto: P.self, obj: C()) // (1)
// error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
I assume that the reason for the compiler error is the same as in
Protocol doesn't conform to itself?.
Another failed attempt is
func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
// error: non-protocol type 'T' cannot be used within 'protocol<...>'
A viable alternative would be a function which takes as parameters
A class protocol.
An instance of a type conforming to that protocol.
Here the problem is how to restrict the first parameter such that only
class protocols are accepted.
Background: I recently stumbled over the
SwiftNotificationCenter
project which implements a protocol-oriented, type safe notification mechanism.
It has a
register
method which looks like this:
public class NotificationCenter {
public static func register<T>(protocolType: T.Type, observer: T) {
guard let object = observer as? AnyObject else {
fatalError("expecting reference type but found value type: \(observer)")
}
// ...
}
// ...
}
The observers are then stored as weak references, and that's why they
must be reference types, i.e. instances of a class.
However, that is checked only at runtime, and I wonder how to make it a compile-time check.
Am I missing something simple/obvious?
You can't do what you are trying to do directly. It has nothing to do with reference types, it's because any constraints make T existential so it is impossible to satisfy them at the call site when you're referencing the protocol's metatype P.self: P.Protocol and an adopter C. There is a special case when T is unconstrained that allows it to work in the first place.
By far the more common case is to constrain T: P and require P: class because just about the only thing you can do with an arbitrary protocol's metatype is convert the name to a string. It happens to be useful in this narrow case but that's it; the signature might as well be register<T>(proto: Any.Type, obj: T) for all the good it will do.
In theory Swift could support constraining to metatypes, ala register<T: AnyObject, U: AnyProtocol where T.Type: U>(proto: U, obj: T) but I doubt it would be useful in many scenarios.

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

Can we test if objects conforming to the same protocol are identical in swift without casting?

I was trying to test if two objects generated from a factory were identical, but the compiler does not seem to allow identity checking of objects that merely conform to the same protocol. Casting both objects to AnyObject seems fine however. Is there anyway to avoid what seems like unnecessary casting?
Here is a simple example the demonstrates what I am seeing (in swift 1.2)
protocol FooBar {
}
class Foo: FooBar {
}
class Bar {
let foo1: FooBar?
let foo2: FooBar?
init() {
foo1 = Foo()
foo2 = Foo()
if foo1! as? AnyObject === foo2! as? AnyObject { // this is fine
}
if foo1! === foo2! { // Birnary operator '===' cannot be applied to two FooBar operands
}
}
}
The identity operator === can only be applied to references, i.e. instances of classes.
If all types conforming to the FooBar protocol are classes then
you can declare it as a “class-constrained protocol”
protocol FooBar : AnyObject { }
(AnyObject is the protocol to which all classes implicitly conform.)
Then
if foo1! === foo2! { ... }
compiles and works as expected, because the compiler "knows" that
both operands are references to a class instance.