简体   繁体   中英

How to constrain a generic sequence parameter to a tuple in Swift 3?

In Swift 2, I was able to write a function which operated on any sequence of, for example, (String, Int) . It looked something like this:

func test<T: SequenceType where T.Generator.Element == (String, Int)>(input: T) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

Using a tuple as the constrained type was particularly useful so that it could accept dictionaries, such as [String:Int] , as their sequence type consisted of tuples.

In Swift 3, I believe a similar function would be:

func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

But attempting to pass in a [String:Int] , for example: test(input: ["a": 1, "b": 2]) , causes the error:

Generic parameter 'T' could not be inferred

As far as I can tell, dictionaries in Swift 3 still use a (Key, Value) tuple as their iterator type, so I think this should work. In fact, if I don't use a single type as the constrained iterator type, such as where T.Iterator.Element == String , I can pass in something such as a [String] and it works correctly.

Is there something I'm missing, or might this be a regression in Swift 3?

An interesting example.

Let's check the definition of Dictionary about conforming to Sequence :

public func makeIterator() -> DictionaryIterator<Key, Value>

And then DictionaryIterator :

public mutating func next() -> (key: Key, value: Value)?

So, for Dictionary , T.Iterator.Element seems to be (key: Key, value: Value) , not (Key, Value) .

If you rewrite your function as:

func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
    for (k, v) in input {
        print(k, "=", String(v))
    }
}

this works:

test(input: ["a": 1, "b": 2])

But this does not work:

test(input: [("a", 1),("b",2)]) //->Generic parameter 'T' could not be inferred

I'm not sure this is an intended feature or some sort of regression, or simply a bug.

This is known bug ( SR-992 ) which in some cases prevents the compiler from matching tuples of the same type, but different labels.

One possible workaround that would allow you to both pass sequences of (String, Int) or (key: String, value: Int) tuples and a [String : Int] dictionary would be to overload test(input:) with a function that expects a sequence with a (key: String, value: Int) element as an input. You could then use a lazily evaluated map in order to 'erase' the tuple labels and then pass that sequence onto your original implementation of test .

// Overload to deal with [String : Int] inputs – workaround for a bug.
func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
    // 'Erase' the tuple labels with a lazily evaluated map(_:)
    // and pass the sequence onto the original implementation of test(input:)
    test(input: AnySequence(input.lazy.map{($0.key, $0.value)}))
}

func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
    for (k, v) in input {
        print(k, "=", v)
    }
}

let d = ["foo" : 5]
let a = [("key", 3)]

test(input: d) // prints: foo = 5
test(input: a) // prints: key = 3

Perhaps not the most ideal solution – but as far as I can see there's no other easy workaround to this problem. Would certainly be interested if anyone has any better ideas.

It isn't for swift 3. However, it might help.

Swift 4

Parameterised extensions are not allowed yet. So, we need to use function.

extension Result {
    func zip<A, B, C>(with other: Result<C, Failure>) -> Result<(A, B, C), Failure> where Success == (A, B) {
        return flatMap { (a, b) in
            Result<(A, B, C), Failure>.init(catching: {
                let c = try other.get()
                return (a, b, c)
            })
        }
    }
}

Where Result is

public enum Result<Success, Failure: Error> {
    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM