Last active
December 11, 2015 03:57
-
-
Save rnapier/3095ab4986fce8d4fe8d to your computer and use it in GitHub Desktop.
How does Apple do AnySequence?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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