I need to implement a type-erasing wrapper for my own structure, very similar to SequenceOf
, GeneratorOf
, etc. So I started by trying to just re-implement the standard SequenceOf
myself.
I just copied & pasteed the the declaration for SequenceOf
, renamed it to MySequenceOf
, and filled in some stubs to get:
/// A type-erased sequence.
///
/// Forwards operations to an arbitrary underlying sequence with the
/// same `Element` type, hiding the specifics of the underlying
/// sequence type.
///
/// See also: `GeneratorOf<T>`.
struct MySequenceOf<T> : SequenceType {
/// Construct an instance whose `generate()` method forwards to
/// `makeUnderlyingGenerator`
init<G : GeneratorType where T == T>(_ makeUnderlyingGenerator: () -> G) {
fatalError("implement me")
}
/// Construct an instance whose `generate()` method forwards to
/// that of `base`.
init<S : SequenceType where T == T>(_ base: S) {
fatalError("implement me")
}
/// Return a *generator* over the elements of this *sequence*.
///
/// Complexity: O(1)
func generate() -> GeneratorOf<T> {
fatalError("implement me")
}
}
I get the compiler error: "Neither type in same-type refers to a generic parameter or associated type". So I assume that the Xcode-generated declaration of SequenceOf
's "where T == T
" constraint really means "where G.Element == T
", which gives me the following compilable struct:
struct MySequenceOf<T> : SequenceType {
init<G : GeneratorType where G.Element == T>(_ makeUnderlyingGenerator: () -> G) {
fatalError("implement me")
}
func generate() -> GeneratorOf<T> {
fatalError("implement me")
}
}
So now, easy enough, I just need to hang on to makeUnderlyingGenerator
from the initializer and invoke it from generate()
:
struct MySequenceOf<T> : SequenceType {
let maker: ()->GeneratorOf<T>
init<G : GeneratorType where G.Element == T>(_ makeUnderlyingGenerator: () -> G) {
self.maker = { return makeUnderlyingGenerator() }
}
func generate() -> GeneratorOf<T> {
return self.maker()
}
}
but that gives me the error: "'G' is not convertible to 'GeneratorOf'"
It does compiles if I force a cast:
struct MySequenceOf<T> : SequenceType {
let maker: ()->GeneratorOf<T>
init<G : GeneratorType where G.Element == T>(_ makeUnderlyingGenerator: () -> G) {
self.maker = { return makeUnderlyingGenerator() as GeneratorOf<T> }
}
func generate() -> GeneratorOf<T> {
return self.maker()
}
}
But then it crashes at runtime from the dynamic cast.
So how can type-erasure like this be implemented? It must be possible, because the Swift standard library does it a bunch (SequenceOf, GeneratorOf, SinkOf).
Try:
struct MySequenceOf<T> : SequenceType {
private let _generate:() -> MyGeneratorOf<T>
init<G : GeneratorType where G.Element == T>(_ makeUnderlyingGenerator: () -> G) {
_generate = { MyGeneratorOf(makeUnderlyingGenerator()) }
}
init<S : SequenceType where S.Generator.Element == T>(_ base: S) {
_generate = { MyGeneratorOf(base.generate()) }
}
func generate() -> MyGeneratorOf<T> {
return _generate()
}
}
struct MyGeneratorOf<T> : GeneratorType, SequenceType {
private let _next:() -> T?
init(_ nextElement: () -> T?) {
_next = nextElement
}
init<G : GeneratorType where G.Element == T>(var _ base: G) {
_next = { base.next() }
}
mutating func next() -> T? {
return _next()
}
func generate() -> MyGeneratorOf<T> {
return self
}
}
The basic strategy of implementing ProtocolOf<T>
is, like this:
protocol ProtocolType {
typealias Value
func methodA() -> Value
func methodB(arg:Value) -> Bool
}
struct ProtocolOf<T>:ProtocolType {
private let _methodA: () -> T
private let _methodB: (T) -> Bool
init<B:ProtocolType where B.Value == T>(_ base:B) {
_methodA = { base.methodA() }
_methodB = { base.methodB($0) }
}
func methodA() -> T { return _methodA() }
func methodB(arg:T) -> Bool { return _methodB(arg) }
}
Added to answering @MartinR in comment.
Is there a special reason that _generate is a closure and not the generator itself?
First of all, I think, It's a matter of specification or semantics.
Needless to say, the difference is "when to create the generator".
Consider this code:
class Foo:SequenceType {
var vals:[Int] = [1,2,3]
func generate() -> Array<Int>.Generator {
return vals.generate()
}
}
let foo = Foo()
let seq = MySequenceOf(foo)
foo.vals = [4,5,6]
let result = Array(seq)
The problem is: result
should be [1,2,3]
or [4,5,6]
? My MySequenceOf
and built-in SequenceOf
results the latter. I just matched the behaviors with built-in one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With