Reputation: 136
I am trying to build my understanding of Generics in Swift by creating a Min and Max extension for the Array class (similar to Min and Max Extension methods in C#). There may be a better way to do this, but as I said, it just to help me understand Generics.
I have created the following code:
extension Array {
func max<T, U : Comparable>(f: T -> U ) -> U? {
var maxSoFar : U? = nil
for i in self {
var itemValue = f(i as T)
if(maxSoFar == nil) {
maxSoFar = itemValue
}
if itemValue > maxSoFar {
maxSoFar = itemValue
}
}
return maxSoFar
}
func min<T, U : Comparable>(f: T -> U ) -> U? {
var minSoFar : U? = nil
for i in self {
var itemValue = f(i as T)
if(minSoFar == nil) {
minSoFar = itemValue
}
if itemValue < minSoFar {
minSoFar = itemValue
}
}
return minSoFar
}
}
To test, I have created a basic Person class:
class Person {
var name : String
var age : Float
init(name: String, age: Float) {
self.name = name
self.age = age
}
}
It seems to work fine for these cases when I am explicit in the closure:
var maximumAge = [Person(name: "Bob", age: 42), Person(name:"Mary", age:40)]
.max{ (p: Person) in p.age }! // Gives 42
var min = [100, 101].min{ (i: Int) in i }! // Gives 100
However, I cannot get it to infer the types for using the extreme shorthand case:
var maximumAge = [Person(name: "Bob", age: 42), Person(name:"Mary", age:40)]
.max{ $0.age }! // Error
var min = [100, 101].min{ $0 }! // Error
Or the medium length:
var maximumAge = [Person(name: "Bob", age: 42), Person(name:"Mary", age:40)]
.max{p in p.age }! // Error
var min = [100, 101].min{ i in i }! // Error
If anyone is an expert on this yet, can you let me know what I am doing wrong? I have to admit it took me a fair bit of reading and hacking to get this far!
Thanks in advance for any replies
Upvotes: 3
Views: 254
Reputation: 42325
When you define max
(and min
) like this:
func max<T, U : Comparable>(f: T -> U ) -> U?
You're actually saying that the closure f
might take a different type than the elements in the Array
. Since Array
is already a generic struct Array<T>
, you can re-use the element type T
that it's already defined. From the Swift language guide:
When you extend a generic type, you do not provide a type parameter list as part of the extension’s definition. Instead, the type parameter list from the original type definition is available within the body of the extension, and the original type parameter names are used to refer to the type parameters from the original definition.
So, since the element type T
is already available to you, simply take the T
out of the type parameter list, like so:
func max<U : Comparable>(f: T -> U ) -> U?
That guarantees that the closure f
will take the same type T
that the elements in the Array
have. Then the compiler can correctly infer which types you are using in your test cases and fixes the errors you're seeing.
Upvotes: 3