Suppose I want to add up all the values of an entry of an array. Not only integers, but also double values or some type I created myself which implements the + operator. So my question is: Is it possible to create such a function with a constraint that is based on the fact if the type implements the operator? Such as or something like that (obviously THIS isn't working).
Thanks in advance
There are several ways you can approach the problem.
IntegerArithmeticType
While I know of no explicit way to say "Hey Swift, I want to only allow type T where T has this operator", all types we commonly think of as capable of being added, subtracted, multiplied, and divided automatically conform to IntegerArithmeticType, which can be used as a generic constraint for any generic function or type.
For example:
func addAll<T: IntegerArithmeticType>(array: [T]) -> T {
var count = array[0]
for (index, value) in array.enumerate() {
if index != 0 {
count += value
}
}
return count
}
Notice in this quick mock-up version I initialized count with the first value of the array and then avoiding double-counting by checking the index against 0 inside the for loop. I can't initialize count with 0 because 0 is an Int while I want count to be of type T.
You mentioned having your own types work with this too. One option is to have your custom type conform to IntegerArithmeticType, although it requires a lot more than just the + operator. For details on IntegerArithmeticType, see SwiftDoc.
Custom Protocol
If conforming to IntegerArithmeticType imposes some sort of limitation on you, you can create a custom protocol with the + function and whatever other requirements you would like. After all, operators are really just functions. Note that you can add conformance to already existing types with extensions. For example:
protocol Addable {
func +(left: Self, right: Self) -> Self
// Other requirements...
}
extension Int: Addable {}
extension Double: Addable {}
Related
I have a protocol defined that has within it a function that returns an associated type that the user can define in their protocol implementation. The only requirement is that the associated type conform to Comparable.
I have another class that uses elements that conform to this protocol. The issue I'm running into is that the compiler is complaining that I can't compare elements of the associated type, even though I've specifically marked them as Comparable in the protocol definition. Anyone see what I'm doing wrong here?
protocol MyElement {
associatedtype T : Comparable
func getValue() -> T
}
class MyNode {
init(elements:[any MyElement]) {
// Sort the elements
let sortedElements = elements.sorted(by: { ( a:any MyElement, b:any MyElement ) -> Bool in
let aT = a.getValue()
let bT = b.getValue()
return aT < bT
})
}
}
The compiler complains at the line aT < bT:
binary operator '<' cannot be applied to two 'Comparable' operands
The < operator applies to two values of a single type that conforms to Comparable — not to two things designated merely as Comparable. A protocol does not conform to itself.
The use of any with MyElement doesn't affect this basic fact about the Comparable-constrained generic placeholder T. Since MyElement is generic over T, we would have to know that the two different MyElement-conforming objects you propose to sort resolve their T to the same type as one another — and in the code you wrote, we don't know that, so the compiler balks.
I'm trying to compare two objects whose type is known at run time but not at compile time. So far I have the following function which seems to work as written:
/// Compares two objects of the given type.
/// Returns -1 if a is "less than" b, 1 if a is "greater than" b, 0 if they are equal, and nil if no comparison could be made.
func compare<T: Comparable>(type: T.Type, a: Any?, b: Any?) -> Int? {
guard let at = a as? T, let bt = b as? T else { return nil }
return at < bt ? -1 : at > bt ? 1 : 0
}
The problem is, the type is not necessarily known to comply with the Comparable protocol at compile time; I really need to be able to pass in any type (Any.Type), not just Comparable ones. I'd then like the function to return nil if the passed-in type does not conform to Comparable. How can I do this?
Edit (30/08/2018): Some more context. I'm using this function to sort various arrays of strings, integers, and other Comparable types. However, because these arrays are retrieved via reflection the element types are not known at compile time. I know that they will always be Comparable but the compiler doesn't. To resolve this, I'm passing in the type as a separate parameter as shown. However, because I'd like to keep the logic as general as possible I'm performing this sort function inside a conditional statement which chooses the necessary type from an array. The type of this array must be [Any.Type] to hold the required types, even though all its contents conform to Comparable (String.Type, Date.Type, etc.).
I'm confused about the difference between the syntax used for associated types for protocols, on the one hand, and generic types on the other.
In Swift, for example, one can define a generic type using something like
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
while one defines a protocol with associated types using something like
protocol Container {
associatedtype T
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Why isn't the latter just:
protocol Container<T> {
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Is there some deep (or perhaps just obvious and lost on me) reason that the language hasn't adopted the latter syntax?
RobNapier's answer is (as usual) quite good, but just for an alternate perspective that might prove further enlightening...
On Associated Types
A protocol is an abstract set of requirements — a checklist that a concrete type must fulfill in order to say it conforms to the protocol. Traditionally one thinks of that checklist of being behaviors: methods or properties implemented by the concrete type. Associated types are a way of naming the things that are involved in such a checklist, and thereby expanding the definition while keeping it open-ended as to how a conforming type implements conformance.
When you see:
protocol SimpleSetType {
associatedtype Element
func insert(_ element: Element)
func contains(_ element: Element) -> Bool
// ...
}
What that means is that, for a type to claim conformance to SimpleSetType, not only must that type contain insert(_:) and contains(_:) functions, those two functions must take the same type of parameter as each other. But it doesn't matter what the type of that parameter is.
You can implement this protocol with a generic or non-generic type:
class BagOfBytes: SimpleSetType {
func insert(_ byte: UInt8) { /*...*/ }
func contains(_ byte: UInt8) -> Bool { /*...*/ }
}
struct SetOfEquatables<T: Equatable>: SimpleSetType {
func insert(_ item: T) { /*...*/ }
func contains(_ item: T) -> Bool { /*...*/ }
}
Notice that nowhere does BagOfBytes or SetOfEquatables define the connection between SimpleSetType.Element and the type used as the parameter for their two methods — the compiler automagically works out that those types are associated with the right methods, so they meet the protocol's requirement for an associated type.
On Generic Type Parameters
Where associated types expand your vocabulary for creating abstract checklists, generic type parameters restrict the implementation of a concrete type. When you have a generic class like this:
class ViewController<V: View> {
var view: V
}
It doesn't say that there are lots of different ways to make a ViewController (as long as you have a view), it says a ViewController is a real, concrete thing, and it has a view. And furthermore, we don't know exactly what kind of view any given ViewController instance has, but we do know that it must be a View (either a subclass of the View class, or a type implementing the View protocol... we don't say).
Or to put it another way, writing a generic type or function is sort of a shortcut for writing actual code. Take this example:
func allEqual<T: Equatable>(a: T, b: T, c: T) {
return a == b && b == c
}
This has the same effect as if you went through all the Equatable types and wrote:
func allEqual(a: Int, b: Int, c: Int) { return a == b && b == c }
func allEqual(a: String, b: String, c: String) { return a == b && b == c }
func allEqual(a: Samophlange, b: Samophlange, c: Samophlange) { return a == b && b == c }
As you can see, we're creating code here, implementing new behavior — much unlike with protocol associated types where we're only describing the requirements for something else to fulfill.
TLDR
Associated types and generic type parameters are very different kinds of tools: associated types are a language of description, and generics are a language of implementation. They have very different purposes, even though their uses sometimes look similar (especially when it comes to subtle-at-first-glance differences like that between an abstract blueprint for collections of any element type, and an actual collection type that can still have any generic element). Because they're very different beasts, they have different syntax.
Further reading
The Swift team has a nice writeup on generics, protocols, and related features here.
This has been covered a few times on the devlist. The basic answer is that associated types are more flexible than type parameters. While you have a specific case here of one type parameter, it is quite possible to have several. For instance, Collections have an Element type, but also an Index type and a Generator type. If you specialized them entirely with type parameterization, you'd have to talk about things like Array<String, Int, Generator<String>> or the like. (This would allow me to create arrays that were subscripted by something other than Int, which could be considered a feature, but also adds a lot of complexity.)
It's possible to skip all that (Java does), but then you have fewer ways that you can constrain your types. Java in fact is pretty limited in how it can constrain types. You can't have an arbitrary indexing type on your collections in Java. Scala extends the Java type system with associated types just like Swift. Associated types have been incredibly powerful in Scala. They are also a regular source of confusion and hair-tearing.
Whether this extra power is worth it is a completely different question, and only time will tell. But associated types definitely are more powerful than simple type parameterization.
To add to the already great answers, there's another big difference between generics and associated types: the direction of the type generic fulfilment.
In case of generic types, it's the client that dictates which type should be used for the generic, while in case of protocols with associated types that's totally in the control of the type itself. Which means that types that conform to associated types are in liberty to choose the associated type that suits them best, instead of being forced to work with some types they don't know about.
As others have said, the Collection protocol is a good example of why associated types are more fit in some cases. The protocol looks like this (note that I omitted some of the other associated types):
protocol Collection {
associatedtype Element
associatedtype Index
...
}
If the protocol would've been defined as Collection<Element, Index>, then this would've put a great burden on the type conforming to Collection, as it would've have to support any kind of indexing, many of them which don't even make sense (e.g. indexing by a UIApplication value).
So, choosing the associated types road for protocol generics it's also a matter of empowering the type that conforms to that protocol, since it's that type the one that dictates what happens with the generics. And yes, that might sound less flexible, but if you think about it all types that conform to Collection are generic types, however they only allow generics for the types that make sense (i.e. Element), while "hardcoding" the other associated types (e.g. Index) to types that make sense and are usable in their context.
What is the difference between an any type and generic type in swift?
Any Type Example:
let swiftInt: Int = 1
let swiftString: String = "miao"
var array: [Any] = []
array.append(swiftInt)
array.append(swiftString)
Generic Type Example:
func duplicate<T>(item: T, numberOfTimes n: Int) -> [T] {
var buffer : [T] = []
for _ in 0 ..< n {
buffer.append(item)
}
return buffer
}
Is this a matter of preference because both appear to solve the same problem by being able to substitute the desired type.
I'm not going to explain generics in details and i'll just point out the essential differences.
In the first example, you'll be able to append any type in that array, without being able to restrict beforehand your array to a specific type and to leverage compile time checks to guarantee that the array will not contain extraneous types. Not much to see in that example.
The second example contains instead a generic function that provides all of the above functionalities, consistency checks on the content of the array will come for free and if you want you'll also be able to specify additional characteristics of that generic type T, like requesting that it implements a specific protocol (e.g. limit duplicate() to object that implement Comparable or Equatable).
But that is just a simple example of a generic function, you can also have parametrized classes (what you'll use the most) and there are a lot of additional functionalities.
Never use Any as a poor-man generics, real generics are way more flexible, add useful checks and make more explicit your intentions, with minimal additional effort required to implement them.
Any means "I don't want any type checking and I won't be able to call type-specific methods without casting"
For example, try to call:
var array: [Any] = [1, 2]
var sum = array[0] + array[1] // you cannot do this! you have to cast to Int first
A generic type is a placeholder for a type. When used, a concrete type is used instead of it (e.g. an Int or a String).
In short, never use Any. There are very very few specific situations when Any is what you want to use.
Coming from a C++ background (templates), I'm struggling to understand why the following piece of Swift code (generics) does not compile:
func backwards<T>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}
The way I understand it is that T is a generic type on which I do not put any constraint (<T>) and declare array to be of type Array<T>. Yet this produces the following error:
Ambiguous reference to member 'sort()'
I understand that constraints can be put on the generic type using protocols. However, in this case, I don't want any constraint on T. Rather, I want to constrain the type of the first parameter.
I've been reading Apple's documentation on Generic Types for a few hours now but I'm still not much wiser. I have the impression that this is not possible and constraints are put solely on the declared types, but that's as far as I got.
So the question is: If possible, how do I put constraints on the types of the function arguments? If not, how do I achieve the same result?
sort(>) is legal only when Element is Comparable. Since the element type of [T] is T, T must conform to Comparable in order for the array [T] to be sortable via >:
func backwards<T: Comparable>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}