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

时间:2017-09-11 14:42:48

标签: swift lazy-evaluation swift4

我正在尝试通过为所有延迟序列和集合重载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: {$0 + 1})).lazy.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {$0 + 1}).lazy.drop(while: {_ in false}).prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {$0 + 1}).lazy.filter{_ in true}.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {$0 + 1}).lazy.map{$0}.prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {$0 + 1}).lazy.prefix(while: {_ in true}).prefix(2)) == [0, 1])
print(Array(sequence(first: 0, next: {$0 + 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{$0} as LazyMapBidirectionalCollection).prefix(2)) == [0, 1])
print(Array(([0, 1, 2].lazy.map{$0} as LazyMapCollection).prefix(2)) == [0, 1])
print(Array([0, 1, 2].lazy.map{$0}.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])

...但是,当在集合上多次链接该方法时,会发生奇怪的编译器行为。以下工作的返回类型为LazyPrefixCollection<LazyRandomAccessCollection<[Int]>>

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

以下内容也适用,返回类型为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)

当然,为每个“步骤”创建中间变量有效:

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{$0}.map{$0}.map{$0}.map{$0}.map{$0}.map{$0}

这让我相信我在代码中做错了,特别是在LazyPrefixCollection

可能导致此行为的原因是什么?

1 个答案:

答案 0 :(得分:1)

prefix(_ maxLength)LazyPrefixSequence上为LazyPrefixCollection添加重载会导致所有编译器问题消失。然后代码变为以下内容:

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: {$0 + 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)>>

反馈意见。特别是当涉及到正确的typealias es和where条款时。那东西对我来说仍然像武断的黑魔法一样;如果我不对where Base.SubSequence: Sequence LazyPrefixSequence进行SubSequence限制,那么它会在其他方法上向我询问一大堆无用的重载。为什么Sequence不符合old_list对我没有意义。