[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Dave Abrahams dabrahams at apple.com
Wed Jun 8 17:18:19 CDT 2016


on Wed Jun 08 2016, Austin Zheng <austinzheng-AT-gmail.com> wrote:

> We might be talking past each other. I think Matthew is talking about using
> an existential outside the context of generic functions. For example,
> something like this should be trap-proof (as long as 'x' is immutable,
> which it is in this example):

[Ugh, Austin, your mail program is stripping the tabs out of the
plaintext part so the indendation is lost.  Grabbing from browser...]

> func copySequenceIntoArray(x: Any<Sequence where .Iterator.Element == Int>) -> [Int] {
> 	var buffer : [Int] = []
>         // Stupid implementation to make a point
> 	var iterator : x.Iterator = x.makeIterator()
> 	while true {
> 		let nextItem : Int? = iterator.next()
> 		if let nextItem = nextItem {
> 			buffer.append(nextItem)
> 		} else {
> 			return buffer
> 		}
> 	}
> }

Presumably this would “work” as well?

  typealias IntSequence = Any<Sequence where .Iterator.Element == Int>
  func f(x: IntSequence, y: IntSequence) {
    var i = x.makeIterator()
    i = y.makeIterator()     // <== NO TRAP HERE, EVER.
  }

> Even this would never trap as well:
> 
> func copySequenceIntoArray<T>(x: Any<Sequence where .Iterator.Element == T>) -> [T] {
> 	var buffer : [T] = []
> 	for item in x {
> 		buffer.append(item)
> 	}
> 	return buffer
> }

Sure, this one is simple because the associated type is never even
exposed.

> Where we run into difficulty is something like this (forgive my abuse
> of the collections API; I don't remember all the new indexing APIs off
> the top of my head):
> 
> func doSomething<T : Collection where T.Element == Int>(x: T, y: T) {
> 	// Get indexes out of x and use them to index into y
> 	var idx = x.startIndex
> 	while (idx != x.endIndex || idx != y.endIndex) {
> 		print(x[idx])
> 		print(y[idx])
> 		idx = x.nextIndex(idx)
> 	}
> }
> let someSeq : Any<Collection where .Element == Int> = // ...
> let anotherSeq : Any<Collection where .Element == Int> = // ...
> // Trouble!
> // someSeq and anotherSeq are the same existential type
> // But the concrete index types within each of the existential variables may be different
> doSomething(someSeq, anotherSeq)
> 
> may be different
> doSomething(someSeq, anotherSeq)
>
> It's this situation (using an existential type to fulfill a generic
> type parameter constrained to the same requirements that comprise that
> existential) that requires either of the two options that Dave
> presented, due to our lack of compile-time type information about the
> fulfilling type's associated types.

Exactly.  But much simpler cases will also either have to trap at
runtime or be prohibited outright:

  func subscript_<C: Collection>(c: C, i: C.Index) -> C.Collection.Element {
    return c[i]
  }

  typealias IntCollection = Any<Collection where Element == Int>
  let c1: IntCollection = ...
  let c2: IntCollection = c1[3..<10]
  let c3: IntCollection = ...
  let c4: IntCollection = c1.reversed()

  // Note: the underlying index types are the same, and are supposed to
  // interoperate.  What do you do (work/trap/nocompile)?
  _ = subscript_(c1, c2.startIndex)

  // The underlying index types happen to be the same, and are not
  // supposed to interoperate.  What do you do (silently “work”/trap/nocompile)?
  _ = subscript_(c1, c3.startIndex)

  // The underlying index types are different.  What do you do (trap/nocompile)?
  _ = subscript_(c1, c4.startIndex)

-- 
Dave


More information about the swift-evolution mailing list