将多个调用链接到同一方法时出现编译器问题

Compiler issues when chaining multiple calls to the same method

我正在尝试通过为所有惰性序列和集合重载 prefix(_ maxLength) 来修复 this bug,但我 运行 遇到了奇怪的编译器问题。

我正在使用 Xcode 9.0 beta 6 (9M214v),但它也可以在 4.0 的所有最新快照中重现。

给定以下迭代器,...

public struct LazyPrefixIterator <Base: IteratorProtocol>: IteratorProtocol {
  public typealias Element = Base.Element

  private var baseIterator: Base
  private let maxLength: Int
  private var taken = 0

  internal init (_ baseIterator: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from an iterator")

    self.baseIterator = baseIterator
    self.maxLength = maxLength
  }

  public mutating func next () -> Element? {
    if self.taken >= self.maxLength {
      return nil
    }

    self.taken += 1

    return self.baseIterator.next()
  }
}

...以下顺序,...

public struct LazyPrefixSequence <Base: Sequence>: LazySequenceProtocol {
  public typealias Iterator = LazyPrefixIterator<Base.Iterator>

  private let baseSequence: Base
  private let maxLength: Int

  internal init (_ baseSequence: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from a sequence")

    self.baseSequence = baseSequence
    self.maxLength = maxLength
  }

  public func makeIterator() -> Iterator {
    return LazyPrefixIterator(self.baseSequence.makeIterator(), self.maxLength)
  }
}

...下面合集...

public struct LazyPrefixCollection <Base: Collection>: LazyCollectionProtocol {
  public typealias Iterator = LazyPrefixIterator<Base.Iterator>
  public typealias Index = Base.Index
  public typealias Element = Base.Element

  private let baseCollection: Base
  private let maxLength: Int

  internal init (_ baseCollection: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from a collection")

    self.baseCollection = baseCollection
    self.maxLength = maxLength
  }

  public func makeIterator() -> Iterator {
    return LazyPrefixIterator(self.baseCollection.makeIterator(), self.maxLength)
  }

  public var startIndex: Index {
    return self.baseCollection.startIndex
  }

  public var endIndex: Index {
    var maxLength = 0
    var index = self.baseCollection.startIndex
    let baseCollectionEndIndex = self.baseCollection.endIndex

    while maxLength < self.maxLength && index != baseCollectionEndIndex {
      index = self.baseCollection.index(after: index)
      maxLength += 1
    }

    return index
  }

  public func index (after i: Index) -> Index {
    precondition(i != self.endIndex, "Can't advance past endIndex")

    return self.baseCollection.index(after: i)
  }

  public subscript (position: Index) -> Element {
    precondition(position >= self.startIndex && position < self.endIndex, "Index out of range")

    return self.baseCollection[position]
  }
}

...以及以下重载(以消除歧义问题),...

public extension LazySequence {
  func prefix (_ maxLength: Int) -> LazyPrefixSequence<Elements> {
    return LazyPrefixSequence(self.elements, maxLength)
  }
}

public extension LazySequenceProtocol {
  func prefix (_ maxLength: Int) -> LazyPrefixSequence<Self> {
    return LazyPrefixSequence(self, maxLength)
  }
}

public extension LazyCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<Base> {
    return LazyPrefixCollection(self.elements, maxLength)
  }
}

public extension LazyCollectionProtocol {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<Self> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyDropWhileBidirectionalCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyDropWhileBidirectionalCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyPrefixWhileBidirectionalCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyPrefixWhileBidirectionalCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyRandomAccessCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyRandomAccessCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

...以下按预期工作(这些印刷品中的每一个 true)...

print(Array(AnySequence(sequence(first: 0, next: {[=14=] + 1})).lazy.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {[=14=] + 1}).lazy.drop(while: {_ in false}).prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {[=14=] + 1}).lazy.filter{_ in true}.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {[=14=] + 1}).lazy.map{[=14=]}.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {[=14=] + 1}).lazy.prefix(while: {_ in true}).prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {[=14=] + 1}).lazy.prefix(2)) == [0, 1])
print(Array(AnyCollection([0, 1, 2]).lazy.prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy as LazyBidirectionalCollection).prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.drop(while: {_ in false}) as LazyDropWhileCollection).prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.filter{_ in true}.prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.filter{_ in true} as LazyFilterCollection).prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.map{[=14=]} as LazyMapBidirectionalCollection).prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.map{[=14=]} as LazyMapCollection).prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.map{[=14=]}.prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.prefix(while: {_ in true}).prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.prefix(while: {_ in true}) as LazyPrefixWhileCollection).prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.prefix(2)) == [0, 1])

...,但是,当在一个集合上多次链接该方法时,会出现奇怪的编译器行为。以下适用于 return 类型的 LazyPrefixCollection<LazyRandomAccessCollection<[Int]>>:

_ = [0, 1, 2].lazy.prefix(3)

以下也适用,return 类型 LazyPrefixCollection<LazyPrefixCollection<LazyRandomAccessCollection<[Int]>>>:

_ = [0, 1, 2].lazy.prefix(3).prefix(3)

但是一旦我们添加了另一个方法,它就会出现问题。它告诉我 Expression type '()' is ambiguous without more context:

_ = [0, 1, 2].lazy.prefix(3).prefix(3).prefix(3)

如果我们添加另一个它在类型检查时会出现段错误:

_ = [0, 1, 2].lazy.prefix(3).prefix(3).prefix(3).prefix(3)

当然,为每个 'step' 创建中间变量有效:

let a = [0, 1, 2].lazy.prefix(3)
let b = a.prefix(3)
let c = b.prefix(3)
let d = c.prefix(3)
// Etc.

还值得注意的是,当我们使用序列而不是集合时它会起作用:

_ = sequence(first: 0, next: {(e: Int) -> Int in e + 1}).lazy.prefix(3).prefix(3).prefix(3).prefix(3).prefix(3)

将多个 map 或标准库中的任何其他方法链接到集合上不会导致任何问题。编译器很高兴地排除了这个怪物:

_ = [0, 1, 2].lazy.map{[=21=]}.map{[=21=]}.map{[=21=]}.map{[=21=]}.map{[=21=]}.map{[=21=]}

这让我相信我的代码有问题,尤其是 LazyPrefixCollection

是什么导致了这种行为?

LazyPrefixSequenceLazyPrefixCollection 上为 prefix(_ maxLength) 添加重载可以解决所有编译器问题。然后代码变成如下:

public struct LazyPrefixIterator <Base: IteratorProtocol>: IteratorProtocol {
  public typealias Element = Base.Element

  private var baseIterator: Base
  private let maxLength: Int
  private var taken = 0

  internal init (_ baseIterator: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from an iterator")

    self.baseIterator = baseIterator
    self.maxLength = maxLength
  }

  public mutating func next () -> Element? {
    if self.taken >= self.maxLength {
      return nil
    }

    self.taken += 1

    return self.baseIterator.next()
  }
}

public struct LazyPrefixSequence <Base: Sequence>: LazySequenceProtocol {
  public typealias Iterator = LazyPrefixIterator<Base.Iterator>

  private let baseSequence: Base
  private let maxLength: Int

  internal init (_ baseSequence: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from a sequence")

    self.baseSequence = baseSequence
    self.maxLength = maxLength
  }

  public func makeIterator() -> Iterator {
    return LazyPrefixIterator(self.baseSequence.makeIterator(), self.maxLength)
  }
}

public extension LazyPrefixSequence where Base.SubSequence: Sequence {
  func prefix (_ maxLength: Int) -> LazyPrefixSequence {
    return LazyPrefixSequence(self.baseSequence, Swift.min(self.maxLength, maxLength))
  }
}

public struct LazyPrefixCollection <Base: Collection>: LazyCollectionProtocol {
  public typealias Iterator = LazyPrefixIterator<Base.Iterator>
  public typealias Index = Base.Index
  public typealias Element = Base.Element

  private let baseCollection: Base
  private let maxLength: Int

  internal init (_ baseCollection: Base, _ maxLength: Int) {
    precondition(maxLength >= 0, "Can't take a prefix of negative length from a collection")

    self.baseCollection = baseCollection
    self.maxLength = maxLength
  }

  public func makeIterator() -> Iterator {
    return LazyPrefixIterator(self.baseCollection.makeIterator(), self.maxLength)
  }

  public var startIndex: Index {
    return self.baseCollection.startIndex
  }

  public var endIndex: Index {
    var maxLength = 0
    var index = self.baseCollection.startIndex

    while maxLength < self.maxLength && index != self.baseCollection.endIndex {
      index = self.baseCollection.index(after: index)
      maxLength += 1
    }

    return index
  }

  public func index (after i: Index) -> Index {
    precondition(i != self.endIndex, "Can't advance past endIndex")

    return self.baseCollection.index(after: i)
  }

  public subscript (position: Index) -> Element {
    precondition(position >= self.startIndex && position < self.endIndex, "Index out of range")

    return self.baseCollection[position]
  }

  public func prefix (_ maxLength: Int) -> LazyPrefixCollection {
    return LazyPrefixCollection(self.baseCollection, Swift.min(self.maxLength, maxLength))
  }
}

public extension LazySequence {
  func prefix (_ maxLength: Int) -> LazyPrefixSequence<Elements> {
    return LazyPrefixSequence(self.elements, maxLength)
  }
}

public extension LazySequenceProtocol {
  func prefix (_ maxLength: Int) -> LazyPrefixSequence<Self> {
    return LazyPrefixSequence(self, maxLength)
  }
}

public extension LazyCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<Base> {
    return LazyPrefixCollection(self.elements, maxLength)
  }
}

public extension LazyCollectionProtocol {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<Self> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyDropWhileBidirectionalCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyDropWhileBidirectionalCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyPrefixWhileBidirectionalCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyPrefixWhileBidirectionalCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

public extension LazyRandomAccessCollection {
  func prefix (_ maxLength: Int) -> LazyPrefixCollection<LazyRandomAccessCollection<Base>> {
    return LazyPrefixCollection(self, maxLength)
  }
}

正在测试:

let xs = [0, 1, 2, 3, 4].lazy.prefix(3).prefix(10).prefix(100).prefix(10).prefix(5).prefix(1)
let ys = sequence(first: 0, next: {[=11=] + 1}).lazy.prefix(3).prefix(10).prefix(100).prefix(10).prefix(5).prefix(1)

print(Array(xs)) // [0]
print(type(of: xs)) // LazyPrefixCollection<LazyRandomAccessCollection<Array<Int>>>

print(Array(ys)) // [0]
print(type(of: ys)) // LazyPrefixSequence<UnfoldSequence<Int, (Optional<Int>, Bool)>>

感谢反馈。特别是涉及到正确的 typealiases 和 where 子句时。那些东西对我来说仍然感觉像是武断的巫毒黑魔法;如果我不对 LazyPrefixSequence 添加 where Base.SubSequence: Sequence 限制,那么它会要求我对其他方法进行一大堆无用的重载。为什么 SubSequence 不符合 Sequence 对我来说毫无意义。