Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active December 11, 2015 03:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rnapier/3095ab4986fce8d4fe8d to your computer and use it in GitHub Desktop.
Save rnapier/3095ab4986fce8d4fe8d to your computer and use it in GitHub Desktop.
How does Apple do AnySequence?
// Reimplementing AnySequence with a closure is pretty easy,
// and I believe this technique can be applied to any type eraser:
struct MyClosureAnySequence<Element> : SequenceType {
let generator: () -> AnyGenerator<Element>
init<S : SequenceType where S.Generator.Element == Element>(_ base: S) {
generator = { anyGenerator(base.generate()) }
}
init<G : GeneratorType where G.Element == Element>(_ makeUnderlyingGenerator: () -> G) {
generator = { anyGenerator(makeUnderlyingGenerator()) }
}
func generate() -> AnyGenerator<Element> {
return generator()
}
}
// But this isn't how Apple does it (why not?). They have a
// _SequenceBox<SequenceType> inside of AnySequence. Based on the
// debugger output, this is the closest I've found to how they might be
// implementing it. It's pretty complicated, and even this style of boxing
// I believe could be done better (_AnySequenceBox could easily be a protocol
// rather than a class). But (a) I might not have correctly reverse-engineered
// it, (b) there may be subtlties I'm not aware of, and (c) this may be
// old enough that there is "compiler history" to consider.
//
// One possible "subtlties I'm not aware of" is memory use. This seems to
// focus on keeping overhead very small. Using closures requires a property
// for every function rather than a single property for everything, and makes
// the struct 16 bytes rather than 8 even for one property. The use
// of a class for _AnySequenceBox rather than a protocol also saves something
// like 32 bytes I believe.
class _MyAnySequenceBox {
func generate() -> AnyObject {
fatalError()
}
}
final class _MySequenceBox<Seq: SequenceType>: _MyAnySequenceBox {
let _base: Seq
init(_ base: Seq) { _base = base }
override func generate() -> AnyObject {
return anyGenerator(_base.generate())
}
}
struct _MyClosureBasedSequence<G: GeneratorType>: SequenceType {
let _makeUnderlyingGenerator: () -> G
init(_ makeUnderlyingGenerator: () -> G) {
_makeUnderlyingGenerator = makeUnderlyingGenerator
}
func generate() -> G {
return _makeUnderlyingGenerator()
}
}
struct MyAnySequence<Element> : SequenceType {
let _box: _MyAnySequenceBox
init<S : SequenceType where S.Generator.Element == Element>(_ base: S) {
_box = _MySequenceBox(base)
}
init<G : GeneratorType where G.Element == Element>(_ makeUnderlyingGenerator: () -> G) {
_box = _MySequenceBox(_MyClosureBasedSequence(makeUnderlyingGenerator))
}
func generate() -> AnyGenerator<Element> {
return _box.generate() as! AnyGenerator<Element>
}
}
let x = [1,2,3]
let myanyx = MyAnySequence(x.generate)
for e in myanyx {
print(e)
}
let anyx = AnySequence(x.generate)
for e in anyx {
print(e)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment