How to constrain function parameter's protocol's associated types - swift

For fun, I am attempting to extend the Dictionary class to replicate Python's Counter class. I am trying to implement init, taking a CollectionType as the sole argument. However, Swift does not allow this because of CollectionType's associated types. So, I am trying to write code like this:
import Foundation
// Must constrain extension with a protocol, not a class or struct
protocol SingletonIntProtocol { }
extension Int: SingletonIntProtocol { }
extension Dictionary where Value: SingletonIntProtocol { // i.e. Value == Int
init(from sequence: SequenceType where sequence.Generator.Element == Key) {
// Initialize
}
}
However, Swift does not allow this syntax in the parameter list. Is there a way to write init so that it can take any type conforming to CollectionType whose values are of type Key (the name of the type used in the generic Dictionary<Key: Hashable, Value>)? Preferably I would not be forced to write init(from sequence: [Key]), so that I could take any CollectionType (such as a CharacterView, say).

You just have a syntax problem. Your basic idea seems fine. The correct syntax is:
init<Seq: SequenceType where Seq.Generator.Element == Key>(from sequence: Seq) {
The rest of this answer just explains why the syntax is this way. You don't really need to read the rest if the first part satisfies you.
The subtle difference is that you were trying to treat SequenceType where sequence.Generator.Element == Key as a type. It's not a type; it's a type constraint. What the correct syntax means is:
There is a type Seq such that Seq.Generator.Element == Key, and sequence must be of that type.
While that may seem to be the same thing, the difference is that Seq is one specific type at any given time. It isn't "any type that follows this rule." It's actually one specific type. Every time you call init with some type (say [Key]), Swift will create an entirely new init method in which Seq is replaced with [Key]. (In reality, Swift can sometimes optimize that extra method away, but in principle it exists.) That's the key point in understanding generic syntax.
Or you can just memorize where the angle-brackets go, let the compiler remind you when you mess it up, and call it day. Most people do fine without learning the type theory that underpins it.

Related

Differences generic protocol type parameter vs direct protocol type

This is my playground code:
protocol A {
init(someInt: Int)
}
func direct(a: A) {
// Doesn't work
let _ = A.init(someInt: 1)
}
func indirect<T: A>(a: T) {
// Works
let _ = T.init(someInt: 1)
}
struct B: A {
init(someInt: Int) {
}
}
let a: A = B(someInt: 0)
// Works
direct(a: a)
// Doesn't work
indirect(a: a)
It gives a compile time error when calling method indirect with argument a. So I understand <T: A> means some type that conforms to A. The type of my variable a is A and protocols do not conform to themselfs so ok, I understand the compile time error.
The same applies for the compile time error inside method direct. I understand it, a concrete conforming type needs to inserted.
A compile time also arrises when trying to access a static property in direct.
I am wondering. Are there more differences in the 2 methods that are defined? I understand that I can call initializers and static properties from indirect and I can insert type A directly in direct and respectively, I can not do what the other can do. But is there something I missed?
The key confusion is that Swift has two concepts that are spelled the same, and so are often ambiguous. One of the is struct T: A {}, which means "T conforms to the protocol A," and the other is var a: A, which means "the type of variable a is the existential of A."
Conforming to a protocol does not change a type. T is still T. It just happens to conform to some rules.
An "existential" is a compiler-generated box the wraps up a protocol. It's necessary because types that conform to a protocol could be different sizes and different memory layouts. The existential is a box that gives anything that conforms to protocol a consistent layout in memory. Existentials and protocols are related, but not the same thing.
Because an existential is a run-time box that might hold any type, there is some indirection involved, and that can introduce a performance impact and prevents certain optimizations.
Another common confusion is understanding what a type parameter means. In a function definition:
func f<T>(param: T) { ... }
This defines a family of functions f<T>() which are created at compile time based on what you pass as the type parameter. For example, when you call this function this way:
f(param: 1)
a new function is created at compile time called f<Int>(). That is a completely different function than f<String>(), or f<[Double]>(). Each one is its own function, and in principle is a complete copy of all the code in f(). (In practice, the optimizer is pretty smart and may eliminate some of that copying. And there are some other subtleties related to things that cross module boundaries. But this is a pretty decent way to think about what is going on.)
Since specialized versions of generic functions are created for each type that is passed, they can in theory be more optimized, since each version of the function will handle exactly one type. The trade-off is that they can add code-bloat. Do not assume "generics are faster than protocols." There are reasons that generics may be faster than protocols, but you have to actually look at the code generation and profile to know in any particular case.
So, walking through your examples:
func direct(a: A) {
// Doesn't work
let _ = A.init(someInt: 1)
}
A protocol (A) is just a set of rules that types must conform to. You can't construct "some unknown thing that conforms to those rules." How many bytes of memory would be allocated? What implementations would it provide to the rules?
func indirect<T: A>(a: T) {
// Works
let _ = T.init(someInt: 1)
}
In order to call this function, you must pass a type parameter, T, and that type must conform to A. When you call it with a specific type, the compiler will create a new copy of indirect that is specifically designed to work with the T you pass. Since we know that T has a proper init, we know the compiler will be able to write this code when it comes time to do so. But indirect is just a pattern for writing functions. It's not a function itself; not until you give it a T to work with.
let a: A = B(someInt: 0)
// Works
direct(a: a)
a is an existential wrapper around B. direct() expects an existential wrapper, so you can pass it.
// Doesn't work
indirect(a: a)
a is an existential wrapper around B. Existential wrappers do not conform to protocols. They require things that conform to protocols in order to create them (that's why they're called "existentials;" the fact that you created one proves that such a value actually exists). But they don't, themselves, conform to protocols. If they did, then you could do things like what you've tried to do in direct() and say "make a new instance of an existential wrapper without knowing exactly what's inside it." And there's no way to do that. Existential wrappers don't have their own method implementations.
There are cases where an existential could conform to its own protocol. As long as there are no init or static requirements, there actually isn't a problem in principle. But Swift can't currently handle that. Because it can't work for init/static, Swift currently forbids it in all cases.

What's the difference between using a generic where condition and specifying argument type? [duplicate]

This question already has answers here:
What is the in-practice difference between generic and protocol-typed function parameters?
(2 answers)
Closed 3 years ago.
What advantages are there to using generics with a where clause over specifying a protocol for an argument, as in the following function signatures?
func encode<T>(_ value: T) throws -> Data where T : Encodable {...}
func encode(value: Encodable) throws -> Data {...}
The first is a generic method that requires a concrete type that conforms to Encodable. That means for each call to encode with a different type, a completely new copy of the function may be created, optimized just for that concrete type. In some cases the compiler may remove some of these copies, but in principle encode<Int>() is a completely different function than encode<String>(). It's a (generic) system for creating functions at compile time.
In contrast, the second is a non-generic function that accepts a parameter of the "Encodable existential" type. An existential is a compiler-generated box that wraps some other type. In principle this means that the value will be copied into the box at run time before being passed, possibly requiring a heap allocation if it's too large for the box (again, it may not be because the compiler is very smart and can sometimes see that it's unnecessary).
This ambiguity between the name of the protocol and the name of the existential will hopefully be fixed in the future (and there's discussion about doing so). In the future, the latter function will hopefully be spelled (note "any"):
func encode(value: any Encodable) throws -> Data {...}
The former might be faster. It might also take more space for all the copies of the function. (But see above about the compiler. Do not assume you know which of these will be faster in an actual, optimized build.)
The former provides a real, concrete type. That means it can be used for things that require a real, concrete type, such as calling a static method, or init. This means it can be used when the protocol has an associated type.
The latter is boxed into an existential, meaning it can be stored into heterogeneous collections. The former can only be put into collections of its particular concrete type.
So they're pretty different things, and each has its purpose.
You can use multiple type constraints.
func encode<T>(encodable: T) -> Data where T: Encodable, T: Decodable {
...
}

In Swift, from a technical standpoint, why does the compiler care if a protocol can only be used as a generic constraint?

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

How to know which type to instantiate from something like JSON?

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.

Swift dynamic type checking for structs?

I'm confused about dynamic type checking in Swift.
Specifically, I have a weirdo case where I want to, essentially, write (or find) a function:
func isInstanceOf(obj: Any, type: Any.Type) -> Bool
In Objective-C, this is isKindOfClass, but that won't work because Any.Type includes Swift structs, which are not classes (much less NSObject subclasses).
I can't use Swift is here, because that requires a hardcoded type.
I can't use obj.dynamicType == type because that would ignore subclasses.
The Swift book seems to suggest that this information is lost, and not available for structs at all:
Classes have additional capabilities that structures do not:
...
Type casting enables you to check and interpret the type of a class instance at runtime.
(On the Type Casting chapter, it says "Type casting in Swift is implemented with the is and as operators", so it seems to be a broader definition of "type casting" than in other languages.)
However, it can't be true that is/as don't work with structures, since you can put Strings and Ints into an [Any], and pull them out later, and use is String or is Int to figure out what they were. The Type Casting chapter of the Swift Book does exactly this!
Is there something that's as powerful as isKindOfClass but for any Swift instances? This information still must exist at runtime, right?
Actually you can use is operator.
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
Since struct can't be subclassed, is is guaranteed to be consistent when applied to instance of struct because it will check on it static type, and for classes it will query the dynamic type in runtime.
func `is`<T>(instance: Any, of kind: T.Type) -> Bool{
return instance is T;
}
This work for both, struct and class.
As already stated, is/as should work fine with structs. Other corner cases can usually be done with generic functions, something like:
let thing: Any = "hi" as Any
func item<T: Any>(_ item: Any, isType: T.Type) -> Bool {
return (item as? T) != nil
}
print(item(thing, isType: String.self)) // prints "true"
No idea if this fits your specific use case.
More generally, keep in mind that Swift (currently) requires knowing the type of everything at compile time. If you can't define the specific type that will be passed into a function or set as a variable, neither will the compiler.