Reputation: 1173
The following code compiles within reasonable time on its ownEXAMPLE:
public struct LazyProduct6Sequence <
T1: LazySequenceProtocol, T2: LazySequenceProtocol, T3: LazySequenceProtocol,
T4: LazySequenceProtocol, T5: LazySequenceProtocol, T6: LazySequenceProtocol
>: LazySequenceProtocol {
public typealias Element = (T1.Element, T2.Element, T3.Element, T4.Element, T5.Element, T6.Element)
public typealias Iterator = AnyIterator<Element>
private let iterator: Iterator
internal init (
_ sequence1: T1, _ sequence2: T2, _ sequence3: T3,
_ sequence4: T4, _ sequence5: T5, _ sequence6: T6
) {
self.iterator = AnyIterator(
sequence1.flatMap { element1 in
sequence2.flatMap { element2 in
sequence3.flatMap { element3 in
sequence4.flatMap { element4 in
sequence5.flatMap { element5 in
sequence6.map { element6 in
(element1, element2, element3, element4, element5, element6)
}
}
}
}
}
}.makeIterator()
)
}
public func makeIterator () -> Iterator {
return self.iterator
}
}
public func product <
T1: LazySequenceProtocol, T2: LazySequenceProtocol, T3: LazySequenceProtocol,
T4: LazySequenceProtocol, T5: LazySequenceProtocol, T6: LazySequenceProtocol
> (
_ sequence1: T1, _ sequence2: T2, _ sequence3: T3,
_ sequence4: T4, _ sequence5: T5, _ sequence6: T6
) -> LazyProduct6Sequence<T1, T2, T3, T4, T5, T6> {
return LazyProduct6Sequence(sequence1, sequence2, sequence3, sequence4, sequence5, sequence6)
}
However, when compiling code that uses it, it takes about 40 seconds to compile (or refuses to compile at all)EXAMPLE:
_ = product([1, 2].lazy, [3, 4].lazy, [5, 6].lazy, [7, 8].lazy, [9, 10].lazy, [11, 12].lazy)
Why does it take such a huge amount of time to compile?
Upvotes: 0
Views: 144
Reputation: 1173
From what I can gether this has to do with Swift's type inference system. Adding explicit return types to each closure didn't help very much as the compile times stayed pretty much the same. But compilation is near instantaneous when I add an explicit type to the assignment that uses the functionEXAMPLE:
let sequence: LazyProduct6Sequence<
LazyRandomAccessCollection<[Int]>,
LazyRandomAccessCollection<[Int]>,
LazyRandomAccessCollection<[Int]>,
LazyRandomAccessCollection<[Int]>,
LazyRandomAccessCollection<[Int]>,
LazyRandomAccessCollection<[Int]>
> = product(
[1, 2].lazy,
[3, 4].lazy,
[5, 6].lazy,
[7, 8].lazy,
[9, 10].lazy,
[11, 12].lazy
)
let iterator = sequence.makeIterator()
while let element = iterator.next() {
print(element)
}
Obviously this isn't ideal and is fugly.
I have no idea why it happens, since there is only one function that matches the expression.
It might be worth mentioning that for lower arities, the compilation time goes down. If I overload product
to also take eager Sequence
s then compiling will freeze my machine.
EDIT: I've since then figured out that these type of Sequence
s should be lazy by default. Reworking my function using the same naming convention as Swift's zip
the above example becomes:
public struct Product6Iterator <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
Sequence5: Sequence, Sequence6: Sequence>: IteratorProtocol {
public typealias Element = (Sequence1.Element, Sequence2.Element, Sequence3.Element, Sequence4.Element,
Sequence5.Element, Sequence6.Element)
private let iterator: AnyIterator<Element>
internal init (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
_ sequence5: Sequence5, _ sequence6: Sequence6) {
self.iterator = AnyIterator(
sequence1.lazy.flatMap { element1 in
sequence2.lazy.flatMap { element2 in
sequence3.lazy.flatMap { element3 in
sequence4.lazy.flatMap { element4 in
sequence5.lazy.flatMap { element5 in
sequence6.lazy.map { element6 in
(element1, element2, element3, element4, element5, element6)
}
}
}
}
}
}.makeIterator()
)
}
public mutating func next () -> Element? {
return self.iterator.next()
}
}
public struct Product6Sequence <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
Sequence5: Sequence, Sequence6: Sequence>: Sequence {
public typealias Iterator = Product6Iterator<Sequence1, Sequence2, Sequence3, Sequence4, Sequence5, Sequence6>
public typealias Element = Iterator.Element
private let sequence1: Sequence1
private let sequence2: Sequence2
private let sequence3: Sequence3
private let sequence4: Sequence4
private let sequence5: Sequence5
private let sequence6: Sequence6
internal init (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
_ sequence5: Sequence5, _ sequence6: Sequence6) {
self.sequence1 = sequence1
self.sequence2 = sequence2
self.sequence3 = sequence3
self.sequence4 = sequence4
self.sequence5 = sequence5
self.sequence6 = sequence6
}
public func makeIterator () -> Iterator {
return Iterator(sequence1, sequence2, sequence3, sequence4, sequence5, sequence6)
}
}
public func product <Sequence1: Sequence, Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence,
Sequence5: Sequence, Sequence6: Sequence> (_ sequence1: Sequence1, _ sequence2: Sequence2, _ sequence3: Sequence3,
_ sequence4: Sequence4, _ sequence5: Sequence5, _ sequence6: Sequence6) -> Product6Sequence<Sequence1, Sequence2,
Sequence3, Sequence4, Sequence5, Sequence6> {
return sequence1.product(sequence2, sequence3, sequence4, sequence5, sequence6)
}
public extension Sequence {
func product <Sequence2: Sequence, Sequence3: Sequence, Sequence4: Sequence, Sequence5: Sequence,
Sequence6: Sequence> (_ sequence2: Sequence2, _ sequence3: Sequence3, _ sequence4: Sequence4,
_ sequence5: Sequence5, _ sequence6: Sequence6) -> Product6Sequence<Self, Sequence2, Sequence3, Sequence4,
Sequence5, Sequence6> {
return Product6Sequence(self, sequence2, sequence3, sequence4, sequence5, sequence6)
}
}
This, along with other similar Sequence
extensions, can be found in a library I've created called SequenceExtensions.
Upvotes: 1