Search code examples
swiftgenericshamcrest

Swift not finding the correct type


I am trying to use SwiftHamcrest

I have a function

func equalToArray<T, S>(_ vector:Array<S>) -> Matcher<T> {
  let v: Matcher<T> = Hamcrest.hasCount(16)
  return v
}

This gives an error

Error:(16, 31) 'hasCount' produces 'Matcher<T>', not the expected contextual result type 'Matcher<T>'

SwiftHamcrest has two hasCount functions

public func hasCount<T: Collection>(_ matcher: Matcher<T.IndexDistance>) -> Matcher<T> 
public func hasCount<T: Collection>(_ expectedCount: T.IndexDistance) -> Matcher<T> 

Why is my code complaining isn't it returning the same type that is needed.

As a note and possibly a different question I had to add the Hamcrest. before the hasCount method call as otherwise it tried to match to the first function

What am I missing with types?


Solution

  • Your method equalToArray<T, S> does not know that T is a collection, so the result from the generic hasCount(...) methods above will not be assignable to v in your method (since these results returns Matcher<T> instances constrained to T:s that are Collection:s). I.e., v is of a type Matcher<T> for a non-constrained T, meaning, in the eyes of the compiler, there is e.g. no T.IndexDistance for the T of v:s type.

    If you add a Collection type constraint to the T of your method, the assignment from hasCount(...) result to v should compile:

    func equalToArray<T: Collection, S>(_ vector: Array<S>) -> Matcher<T> {
        let v: Matcher<T> = Hamcrest.hasCount(16)
        return v
    }
    

    In a perfect world, the compiler could've given us a more telling error message, say along the lines of

    Error:(16, 31) 'hasCount' produces 'Matcher<T>' where 'T: Collection', not the expected contextual result type 'Matcher<T>'


    Now, I don't know what you're intending to test here, but as @Hamish points out, you might actually want to return a Matcher<[S]> and drop the T placeholder. E.g. using the count property of the supplied vector parameter as argument to hasCount(...)?

    func equalToArray<S>(_ vector: Array<S>) -> Matcher<[S]> {
        return hasCount(vector.count)
    }
    

    Not having used Hamcrest myself, I might be mistaken, but based on a quick skim over the SwiftHamcrest docs, I believe equalToArray(_:) defined as above would construct a matcher for "vector equality" (w.r.t. semantics of the function name) based only on the count of two vectors, in which case the following assert would be a success

    let arr1 = ["foo", "bar"]
    let arr2 = ["bar", "baz"]
    
    assertThat(arr1, equalToArray(arr2)) // success! ...
    

    But this is just a byline, as you haven't shown us the context where you intend to apply your equalToArray(_:) method/matcher; maybe you're only showing us a minimal example, whereas the actual body of you custom matcher is more true to the method's name.