The problem is when one protocol depends on another through its associated type, compiler isn't able to infer generic types.
So, I was playing around with Swift's type erasure technique trying to become familiar with its idea. Basically it'd been pretty understandable until I got to Sequence protocol. It's known that it has an associated type - Iterator, which conforms IteratorProtocol. That said, I've been trying to achieve similar behavior in my own implementation. That's what I've done:
final class CustomAnySequence<Element>: Sequence {
class CustomAnyIterator<Element>: IteratorProtocol {
private let _next: () -> Element?
init<I: IteratorProtocol>(_ iterator: I) where I.Element == Element {
var iterator = iterator
_next = { iterator.next() }
}
func next() -> Element? {
return _next()
}
}
typealias Iterator = CustomAnyIterator<Element>
typealias Element = Iterator.Element
private let _makeIterator: () -> Iterator
init<S: Sequence>(_ sequence: S) where S.Iterator == Iterator {
_makeIterator = sequence.makeIterator
}
func makeIterator() -> Iterator {
return _makeIterator()
}
}
let sequence = CustomAnySequence([1, 2, 3])
So, the last line gives the following error: Generic parameter 'Element' could not be inferred.
Then if I try to fix it by explicitly specifying Element type:
let sequence = CustomAnySequence<Int>([1, 2, 3])
it's not making it better. The next Xcode complaint is: Generic parameter 'S' could not be inferred.
So is there my fault, or it's just too much overhead for Swift's type inference?
Actually, I've run into another possible implementation - it's using private subclass wrapping. I don't really like it (that's why I was trying to do it on my own) because there are "fatal-error-must-be-subclassed" methods in superclass's implementations, which don't contribute to clean code. Also, I'm not sure how I could implement this functionality by the initializer of CustomAnySequence (I've only found it possible by making a static method). Despite all that, that's the code:
class CustomAnySequence<Element>: Sequence {
class Iterator: IteratorProtocol {
func next() -> Element? {
fatalError("Must be overriden")
}
}
func makeIterator() -> Iterator {
fatalError("Must be overriden")
}
}
private final class CustomAnySequenceImplementation<S: Sequence>: CustomAnySequence<S.Element> {
final class IteratorImplementation: Iterator {
var wrapped: S.Iterator
init(_ wrapped: S.Iterator) {
self.wrapped = wrapped
}
override func next() -> S.Element? {
return wrapped.next()
}
}
var sequence: S
init(_ sequence: S) {
self.sequence = sequence
}
override func makeIterator() -> IteratorImplementation {
return IteratorImplementation(sequence.makeIterator())
}
}
extension CustomAnySequence {
static func make<S: Sequence>(_ sequence: S) -> CustomAnySequence<Element> where S.Element == Element {
return CustomAnySequenceImplementation<S>(sequence)
}
}
func printInts(_ sequence: CustomAnySequence<Int>) {
for element in sequence {
print(element)
}
}
printInts(CustomAnySequence.make([1, 2, 3]))
printInts(CustomAnySequence.make(Set([4, 5, 6])))
It actually does work, but it looks a bit like a boilerplate. At least, if you realize how to improve it by using an initializer, please let me know. Thank you in advance!
The problem with the first implementation is that
let sequence = CustomAnySequence([1, 2, 3])
does not satisfy the constraint in
init<S: Sequence>(_ sequence: S) where S.Iterator == Iterator
[1, 2, 3]
is a sequence, but its iterator type is not your CustomAnyIterator
. What you really want is to pass a sequence with the same element type, not the same iterator type:
init<S: Sequence>(_ sequence: S) where S.Element == Element
and pass sequence.makeIterator()
to the init method of CustomAnyIterator
.
Note also the inner class can inherit the Element
type placeholder from the outer class, and that the type aliases are not really needed.
final class CustomAnySequence<Element>: Sequence {
class CustomAnyIterator: IteratorProtocol {
private let _next: () -> Element?
init<I: IteratorProtocol>(_ iterator: I) where I.Element == Element {
var iterator = iterator
_next = { iterator.next() }
}
func next() -> Element? {
return _next()
}
}
private let _makeIterator: () -> CustomAnyIterator
init<S: Sequence>(_ sequence: S) where S.Element == Element {
_makeIterator = { CustomAnyIterator(sequence.makeIterator()) }
}
func makeIterator() -> CustomAnyIterator {
return _makeIterator()
}
}
You may also consider to use a struct
instead of a class
.