I'm trying to create a method that takes an array of structs that conform to a protocol in Swift.
For the simplest example of this, I define an empty protocol and a method that takes an array of objects conforming to that protocol and just prints them
protocol SomeProtocol {}
func methodTakingProtocol(objects: [SomeProtocol]) {
// do something with the array of objects
print(objects)
}
When I try to feed this method an array of structs that conform to SomeProtocol, however, I get an error
struct SomeStruct: SomeProtocol {}
let arrayOfStructs = [ SomeStruct(), SomeStruct() ]
methodTakingProtocol(arrayOfStructs)
// ^ "Cannot convert value of type '[SomeStruct]' to expected argument type '[SomeProtocol]'"
Poking around a little, I've found that I can get around this problem by explicitly calling out SomeStruct's adoption of SomeProtocol
let arrayOfStructs: [SomeProtocol] = [ SomeStruct(), SomeStruct() ]
// This will work
methodTakingProtocol(arrayOfStructs)
Can someone tell me what's going on here? Is this a bug that I should file a radar for, or is there some reasoning as to why the compiler doesn't recognize this array of structs as conforming to the protocol they have been marked as adopting?
This is actually working as intended. In order to pass the array to the method you have to either cast it or explicitly declare it as the protocol:
protocol SomeProtocol {}
struct SomeStruct: SomeProtocol {}
// explicitly typed
let arrayOfStructs:[SomeProtocol] = [ SomeStruct(), SomeStruct() ]
func foo(bar:[SomeProtocol]) { }
foo(arrayOfStructs) // Works!
Here is an excellent article on this topic: Generic Protocols & Their Shortcomings
But that begs the question; Why can't we use generic protocols outside
of generic constraints?
The short answer is: Swift wants to be type-safe. Couple that with the
fact that it's an ahead-of-time compiled language, and you have a
language that NEEDS to be able to infer a concrete type at anytime
during compilation. I can't stress that enough. At compile time, every
one of your types that aren't function/class constraints need to be
concrete. Associated types within a protocol are abstract. Which means
they aren't concrete. They're fake. And no one likes a fake.
edit: It's still a great article but upon re-reading I realized that it doesn't exactly apply here since we're discussing "concrete protocols" and not "generic protocols".
Related
Is there an equivalent in Swift to pass as an argument an empty interface{} like in Go?
I am not very familiar with Go, but I think the Swift equivalent would be an argument of type Any or AnyObject. You can't do much with such an argument other than try to cast it to a more specific type.
Go's interfaces and Swift's protocols are quite different:
Golang's interfaces are "structural" (akin to C++'s "Concepts"), meaning that their identity is defined by their structure. If a go struct has the structure required by an interface, it implicitly implements that interface. That is, the struct doesn't say anywhere "I am strust S, and I implement interface I."
Swift's protocols are "nominal", meaning that their identity is defined their name. If a Swift struct fulfills all of the requirements of a protocol, it doesn't conform to that protocol, unless an explicit struct S: P { ... } declaration is made (the body of that declaration can even be empty, but it's still necessary for the conformance to exist).
The closest analogue to interface{} in Swift is Any. It's built-in protocol to which all type conform. It gets special treatment, it's defined by the compiler, and has hard-coded logic to make all other types conform to it. You won't see protocol Any {} or struct S: Any {} declared anywhere explicitly. AnyObject is similar, and also gets special treatment. But no other Swift protocols do. Their conformance need to be explicit.
If I am not mistaking Swift's equivalent would be:
protocol EmptyProtocol {
}
In swift interfaces are called protocols don't ask me :D
Are you trying to do type erasure or something? Just curious...
I have the following setup:
protocol Resource : Codable {}
class A<T> {}
extension A where T : Codable {
func doThingWithCodable() {}
}
let a = A<Resource>()
a.doThingWithCodable()
//Error: Protocol type 'Resource' cannot conform to Codable because
//only concrete types can conform to protocols.
I understand the error message, and I've read numerous Q&A's on generics, protocols and conditional conformance. I have a number of classes that conform to the Resource protocol, so it'd be really convenient if there was a way to let A<Resource> know that it will always be working with concrete types that conform to Codable, so I could still have one instance of A<Resource>, and that instance could have access to its conditional Codable methods. I did think about just making an instance like A<Codable>, but I need some of the properties that are inside the Resource protocol too.
Is there a swifty way to resolve this besides creating a new instance of A for each concrete type that conforms to Resource?
The question as posed, "How can I get conditional protocol conformance with a protocol's parent?" is meaningless, because a protocol always conforms with its parent; there is no "conditional" about it.
As for your actual code, the problem is the phrase A<Resource>. Saying A<Resource> is not a correct resolution of A's T. You need to resolve T as a class, struct, or enum — not as a protocol.
For example, if you have a class B that conforms to Resource, you can declare
let a = A<B>()
and all is well.
if there was a way to let A<Resource> know that it will always be working with concrete types that conform to Codable
Well, as I said, your code compiles fine as soon as A is working with a concrete type that does conform to Codable. So if that's what A will always be working with, there's nothing more to do. You could of course tell A that its T will always conform to Resource (which by definition will always conform to Codable):
class A<T:Resource> {}
Just think about this:
let a = A<Resource>()
This statement is trying to create an instance of class A and assigning it to a. The compiler doesn't know what this resource exactly is and how much memory it should allocate and what optimization it should run; because here Resource can be anything.
To resolve that, you need to provide little more information i.e. which you are already aware of.
struct CoffeeResouce: Resource {
let isWarm: Bool
}
Now when you write this:
let a: A<CoffeeResource> = A()
The compiler is well about the type and it's memory requirements. This is one way of resolving.
Or permanently tell the class that T conform to the protocol by declaring the class.
class A<T: Resource> {}
In Swift, from a technical standpoint, why does the compiler care if a protocol can only be used as a generic constraint?
Say I have:
protocol Fooable {
associated type Bar: Equatable
func foo(bar: Bar) {
bar==bar
}
}
Why can't I later declare a func that takes a Fooable object as an argument?
It seems to me that the compiler should only care that a given Fooable can be sent a message "foo" with an argument "bar" that is an Equatable and therefore responds to the message, "==".
I understand Swift is statically typed, but why should Swift even really care about type in this context, since the only thing that matters is whether or not a given message can be validly sent to an object?
I am trying to understand the why behind this, since I suspect there must be a good reason.
In your example above, if you wrote a function that takes a Fooable parameter, e.g.
func doSomething(with fooable:Fooable) {
fooable.foo(bar: ???) // what type is allowed to be passed here? It's not _any_ Equatable, it's the associated type Bar, which here would be...???
}
What type could be passed into fooable.foo(bar:)? It can't be any Equatable, it must be the specific associated type Bar.
Ultimately, it boils down the the problem that protocols that reference "Self" or have an associated type end up having different interfaces based on which concrete implementation has conformed (i.e. the specific type for Self, or the specific associated type). So these protocols can be considered as missing information about types and signatures needed to address them directly, but still serve as templates for conforming types and so can be used as generic constraints.
For example, the compiler would accept the function written like this:
func doSomething<T: Fooable>(with fooable:T, bar: T.Bar) {
fooable.foo(bar: bar)
}
In this scenario we aren't trying to address the Fooable protocol as a protocol. Instead, we are accepting any concrete type, T, that is itself constrained to conform to Fooable. But the compiler will know the exact concrete type of T each time you call the function, so it therefore will know the exact associated type Bar, and will know exactly what type may be passed as a parameter to fooable.foo(bar:)
More Details
It may help to think of "generic protocols" — i.e. protocols that have an associated type, including possibly the type Self — as something a little different than normal protocols. Normal protocols define messaging requirements, as you say, and can be used to abstract away a specific implementation and address any conforming type as the protocol itself.
Generic protocols are better understood as part of the generics system in Swift rather than as normal protocols. You can't cast to a generic protocol like Equatable (no if let equatable = something as? Equatable) because as part of the generic system, Equatable must be specialized and understood at compile time. More on this below.
What you do get from generic protocols that is the same as normal protocols is the concept of a contract that conforming types must adhere to. By saying associatedtype Bar: Equatable you are getting a contract that the type Bar will provide a way for you to call `func ==(left:Bar, right: Bar) -> Bool'. It requires conforming types to provide a certain interface.
The difference between generic protocols and normal protocols is that you can cast to and message normal protocols as the protocol type (not a concrete type), but you must always address conformers to generic protocols by their concrete type (just like all generics). This means normal protocols are a runtime feature (for dynamic casting) as well as a compile time feature (for type checking). But generic protocols are only a compile time feature (no dynamic casting).
Why can't we say var a:Equatable? Well, let's dig in a little. Equatable means that one instance of a specific type can be compared for equality with another instance of the same type. I.e. func ==(left:A, right:A) -> Bool. If Equatable were a normal protocol, you would say something more like: func ==(left:Equatable, right:Equatable) -> Bool. But if you think about that, it doesn't make sense. String is Equatable with other Strings, Int is Equatable with other Ints, but that doesn't in any way mean Strings are Equatable with Ints. If the Equatable protocol just required the implementation of func ==(left:Equatable, right:Equatable) -> Bool for your type, how could you possibly write that function to compare your type to every other possible Equatable type now and in the future?
Since that's not possible, Equatable requires only that you implement == for two instances of Self type. So if Foo: Equatable, then you must only define == for two instances of Foo.
Now let's look at the problem with var a:Equatable. This seems to make sense at first, but in fact, it doesn't:
var a: Equatable = "A String"
var b: Equatable = 100
let equal = a == b
Since both a and b are Equatable, we could be able to compare them for equality, right? But in fact, a's equality implementation is limited to comparing a String to a String and b's equality implementation is limited to comparing an Int to an Int. So it's best to think of generic protocols more like other generics to realize that Equatable<String> is not the same protocol as Equatable<Int> even though they are both supposedly just "Equatable".
As for why you can have a dictionary of type [AnyHashable: Any], but not [Hashable: Any], this is becoming more clear. The Hashable protocol inherits from Equatable, so it is a "generic protocol". That means for any Hashable type, there must be a func ==(left: Self, right:Self) -> Bool. Dictionaries use both the hashValue and equality comparisons to store and retrieve keys. But how can a dictionary compare a String key and an Int key for equality, even if they both conform to Hashable / Equatable? It can't. Therefore, you need to wrap your keys in a special "type eraser" called AnyHashable. How type erasers work is too detailed for the scope of this question, but suffice it to say that a type eraser like AnyHashable gets instantiated with some type T: Hashable, and then forward requests for a hashValue to its wrapped type, and implements ==(left:AnyHashable, right: AnyHashable) -> Bool in a way that also uses the wrapped type's equality implementation. I think this gist should give a great illustration of how you can implement an "AnyEquatable" type eraser.
https://gist.github.com/JadenGeller/f0d05a4699ddd477a2c1
Moving onward, because AnyHashable is a single concrete type (not a generic type like the Hashable protocol is), you can use it to define a dictionary. Because every single instance of AnyHashable can wrap a different Hashable type (String, Int, whatever), and can also produce a hashValue and be checked for equality with any other AnyHashable instance, it's exactly what a dictionary needs for its keys.
So, in a sense, type erasers like AnyHashable are a sort of implementation trick that turns a generic protocol into something like a normal protocol. By erasing / throwing away the generic associated type information, but keeping the required methods, you can effectively abstract the specific conformance of Hashable into general type "AnyHashable" that can wrap anything Hashable, but be used in non-generic circumstances.
This may all come together if you review that gist for creating an implementation of "AnyEquatable": https://gist.github.com/JadenGeller/f0d05a4699ddd477a2c1 and then go back an see how you can now turn this impossible / non-compiling code from earlier:
var a: Equatable = "A String"
var b: Equatable = 100
let equal = a == b
Into this conceptually similar, but actually valid code:
var a: AnyEquatable = AnyEquatable("A String")
var b: AnyEquatable = AnyEquatable(100)
let equal = a == b
My app loads a JSON file at launch. The JSON file includes instructions to create various instances of different types. As a basic example, I have a protocol called BasicType with some extensions to make some built-in Swift types conform to it:
protocol BasicType {
}
extension Int: BasicType { }
extension Float: BasicType { }
extension String: BasicType { }
I also have a dictionary which maps these type 'names' (as found in the JSON) to the types themselves:
let basicTypes = [
"integer": Int.self,
"float": Float.self,
"string": String.self
] as [String : BasicType.Type]
When I'm loading BasicTypes from the JSON, I look up the name specified by the JSON using the dictionary above to know which type to instantiate (in reality, the protocol also defines initialisers, so this is possible). I have some other protocols in addition to BasicType which work in exactly the same way, and each one gets its own dictionary map.
For example, my JSON might include an array of BasicTypes I want to instantiate:
{
"basic_types": [{
"type": "integer",
...
}, {
"type": "integer",
...
}, {
"type": "string",
...
}, {
"type": "float",
...
}]
}
The ellipses denote other attributes which are passed to the initialiser, like what value the integer should be. In actuality, these are custom structs and classes which take various properties. In my Swift code, I open up the basic_types array, look at the type key for each JSON object, look up the appropriate types and initialise them. So my loaded array would include two Ints, a String and a Float.
This is similar to how UIKit instantiates views from a storyboard, for example. The view's class name is stored in the storyboard, and in this case presumably it uses something like NSClassFromString to perform the mapping. I don't want to rely on the Objective-C runtime though.
The problem with this approach is that it's difficult to look up the name of a type if I already have the type or instance, as I have to inefficiently iterate the dictionary to search.
Instead, I thought a better solution might be to include each type's name (or 'type identifier') statically as a variable in the type itself, and then generate the type maps from that. So, I created a new protocol:
protocol TypeIdentifiable {
static var typeIdentifier: String { get }
}
And made BasicType (and all the other similar protocols) inherit from it:
protocol BasicType: TypeIdentifiable {
}
Which means I need to provide the names directly in the types themselves:
extension Int: BasicType {
static let typeIdentifier = "integer"
}
extension Float: BasicType {
static let typeIdentifier = "float"
}
extension String: BasicType {
static let typeIdentifier = "string"
}
I made a function (which compiles) which I intended to generically take a protocol conforming to TypeIdentifiable and including an array of types, and produce a dictionary containing the mapping:
func typeMap<T : TypeIdentifiable>(_ types: [T.Type]) -> [String : T.Type] {
return Dictionary(uniqueKeysWithValues: types.map { type in
(key: type.typeIdentifier, value: type)
})
}
I can then replace the dictionary literal with:
let basicTypes = typeMap([
Int.self,
Float.self,
String.self
] as [BasicType.Type])
This way, if I have a type I can easily get its name by accessing its static property, and if I have the name I can get the type through the generated dictionary.
Unfortunately, this doesn't work:
error: cannot convert value of type '[BasicType.Type]' to expected argument type '[_.Type]'
I think using a protocol as a generic type is forbidden in Swift, which probably makes it impossible to do what I'm trying to do.
My question is: Is there a way of doing what I'm trying to do? Otherwise, on a more general level, is there a better way of knowing what types to instantiate from something like JSON?
This is SR-3038 and is roughly "as designed." Protocols do not conform to themselves, so they cannot fulfill generic requirements. BasicType cannot be T because BasicType is a protocol. Why? Well, imagine I've written this code (which I expect is really similar to what you want to write):
protocol P {
init()
}
func makeOne<T:P>(what: T.Type) -> T {
return T.init()
}
Awesome. That's legal. Now, if protocols conformed to themselves, I could write this code:
makeOne(what: P.self)
What init should that call? Swift fixes this impasse by only allowing concrete types to conform to protocols. Since BasicType does not conform to BasicType, it also does not conform to TypeIdentifiable, and cannot be T.
So what do we do?
First and most important-top-of-the-list: Do you actually need this to be generic? How many times do you actually plan to use it for radically different JSON formats? Could you just write the parsing code directly (ideally with the new Codeable)? The tears that have been spilled trying to write complex JSON parsers to parse simple JSON formats could flood WWDC. My rule of thumb is to avoid making things generic unless I already know at least 3 different ways it will be used. (If that happens later, I refactor to generic when I have the examples.) You just don't know enough about the problem until have have several concrete examples and your generic solution will probably be wrong anyway.
OK, let's assume this really is a very general JSON format you have to parse where you have no idea what's going to be in it (maybe like the NIB format you mention). The first and most obvious answer: Use your existing solution. Your concern is that it's hard to look them up backwards (type->identifier), but that's trivial to write a method to make that easy. Unless you have many thousands, possibly millions, of types, the difference in time is beyond trivial; linear search can even be faster than dictionaries for small collections.
Next in line is to just write typeMap for each protocol (i.e. explicitly for BasicType rather than making it generic. There are several cases in stdlib where they do this. Sometimes you just have to do that.
And of course you could create a TypeDefinition struct that holds the info (like a type eraser). In practice this really just moves the problem around. Eventually you'll need to duplicate some code if there are a bunch of protocols.
But almost always when I head down this kind of road, I discover it was way over-engineered and just writing the silly code directly is the easy and scalable solution.
And of course look carefully at the new JSONDecoder in Swift 4. You want to be moving in that direction if you can anyway.
I am using an NSOperation that conforms to SomeProtocol that has a results property
let op : NSOperation, SomeProtocol = ...
op.completionBlock = {
print(op.results)
}
I get the following error:
Value of type 'NSOperation' has no member 'results'
I know that I can subclass NSOperation to get the intended behaviour, but can I achieve what I want using protocols?
That code shouldn't even get that far... unlike Objective-C, Swift does not allow specifying a variable as a combination of both a concrete type AND a protocol. You can only declare a variable to be of a specific type, a specific protocol, or a composition of protocols, e.g.
let op : protocol<SomeProtocol, AnotherProtocol> = ...
But there is currently no way to declare a variable as being of the specific type NSOperation AND conforming to the protocol SomeProtocol