Sorry for the generic title, it's hard to describe the problem without examples.
Suppose I define the following generic function that is constrained to Equatable
types:
func test<T: Equatable>(expect expected: T, run: () -> T) {
let value = run()
if value == expected {
print("OK")
} else {
print("Expected: \(expected), Actual: \(value)")
}
}
Here's an example of using said function:
test(expect: 100) { 10 * 10 } // prints "OK"
test(expect: 1000) { 10 * 10 } // prints "Expected: 1000, Actual: 100"
And, of course, I can store the value instead of using literals:
let e = 100
test(expect: e) { e } // prints "OK"
So far so good, everything works as expected (no pun intended).
Now let's try this with an array:
test(expect: [1, 2]) { [1, 2] } // prints "OK"
Once again, things work out.
But now we try this:
let a = [1, 2]
test(expect: a) { a } // error: cannot convert value of type '() -> [Int]' to expected argument type '() -> _'
So the question I have been building up to is: Why doesn't this work?
Playground correctly infers the type of a
to be [Int]
, so where does the expectation of () -> _
come from?
Trying a bunch of variations of the last example:
test(expect: a) { return a }
test(expect: a) { return a as [Int] }
test(expect: a as [Int]) { return a as [Int] }
test(expect: [1, 2]) { a }
test(expect: [1, 2] as [Int]) { a }
They all result in the same problem. For some reason, Swift seems to think the function expects () -> _
.
So maybe it's just because arrays aren't Equatable
, but this works:
let a = [1, 2]
[1, 2] == [1, 2]
a == a
I thought I understood generics pretty well, and I'm completely stumped by this. Is this a bug in Swift or a bug in my definition of test()
? Can the goal even be accomplished?
Thanks to @Sulthan's answer below, I was able to write another version of this function to handle the array case (and any SequenceType
for that matter):
public func test<T: SequenceType where T.Generator.Element: Equatable>(expect expected: T, run: () -> T) {
let result = run()
// Note: zip() will stop at the shorter array, so this implementation isn't correct, don't use it (it will incorrectly end up saying [1] == [1,2]). This code is just here to demonstrate the function's generic constraint.
let eq = zip(expected, result).filter(!=).isEmpty
if eq {
print("OK")
} else {
print("Expected: \(expected), Actual: \(result)")
}
}
let a: [Int] = [1, 2]
test(expect: [1,2]) { a } // prints "OK"
test(expect: [1,3]) { a } // prints "Expected: [1, 3], Actual: [1, 2]"
A generic function is a function that is declared with type parameters. When called, actual types are used instead of the type parameters.
The print() function is an example of a generic function. A generic function is simply a function that performs a common task by dispatching its input to a particular method-function that is selected on the basis of the class of the input to the generic function.
To call a generic method, you need to provide types that will be used during the method invocation. Those types can be passed as an instance of NType objects initialized with particular . NET types.
For example, classes like HashSet, ArrayList, HashMap, etc., use generics very well. There are some fundamental differences between the two approaches to generic types.
Arrays don't automatically conform to Equatable
, even if their values are Equatable
. However, when you use an array literal directly, the compiler tries to match the type and converts the array to an NSArray
which does conform to Equatable
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With