tl;dr
Here's the best tl;dr
I can come up with for those that don't want to read the longer explanation. If you haven't used a statically typed language with generic collection interfaces, this question is not for you. So here goes:
Since we can't create a property of type SequenceType
in Swift, what do we do instead if we want to hide the underlying type of our sequence? Also, how do we do this for mutable collections?
E.g., this doesn't work:
class FrogBox {
var _frogs = [Frog]()
var frogs: SequenceType {
return _frogs
}
}
So what do we do instead if we want to hide the fact that we are using an array, especially since we don't want the user to directly modify the array?
The Long Explanation
In a not-so-distant phase of my career, I wrote lots of C#, though Objective-C and Ruby are much more common now. In C#, if I want to expose an iterable collection as a property of a class, I'd declare as IEnumerable<T>
, e.g.,
class FrogBox {
private readonly ArrayList<Frog> _frogs = new ArrayList<Frog>();
public IEnumerable<Frog> frogs { get { return _frogs; } }
}
In Objective-C, on the other hand, the usual way is just to use the Swiss Army knives NSArray
or NSMutableArray
for everything, e.g.,
@interface FrogBox
@property (nonatomic, readonly) NSArray *frogs;
@end
@implementation FrogBox {
NSMutableArray *_frogs;
}
@dynamic frogs;
- (instancetype)init {
self = [super init];
if (self) {
_frogs = [NSMutableArray array];
}
return self;
}
- (NSArray*)frogs {
return _frogs;
}
@end
In Swift, we have the opportunity to do something more along the lines of C#, which I prefer because it allows us to encapsulate how the frogs are actually stored. Since our (fairly useless) FrogBox
only allows us to iterate the frogs, why do we care how they are stored?
This turns out to be a bit harder to achieve in Swift. Some protocols can only be used as generic type constraints because they have associated type constraints (using typealias
) or use Self
in their definition. SequenceType
does both, so we can't say var frogs: SequenceType
in our implementation of FrogBox
in Swift.
This leaves us with two choices. First, we could simply abandon encapsulation and use an array, in the manner of Objective-C. Or we could use SequenceOf<T>
as follows:
class FrogBox {
private var _frogs = [Frog]()
var frogs: SequenceOf<Frog> {
return SequenceOf<Frog>(_frogs)
}
}
The documentation for SequenceOf<T>
says that it "[f]orwards operations to an arbitrary underlying sequence with the same Element type, hiding the specifics of the underlying sequence type.". This is great, but what's this about an "arbitrary underlying sequence"? Does this mean my elements are copied to an arbitrary underlying sequence? What's the overhead of that? Or is it saying that the "arbitrary underlying sequence" is the one I give it? (I think the latter is more likely, though they should have said the "given underlying sequence".) SequenceOf<T>
may be the best answer, but I hesitate to use it until I get clarification. I could roll my own without too much trouble, but this wouldn't be standard.
So, what is the best practice in Swift? Stick with the "Swift Army Knife" that is Array<T>
(and risk exposing a writeable interface for something that should just be a sequence) or use SequenceOf<T>
or perhaps some other methodology I hadn't thought of? Also, how do we go above and beyond simple sequences while still hiding the underlying implementation? There appears to be no CollectionOf<T>
for indexable collections. Nor is there any equivalent of C#'s IList<T>
that I'm aware of, to encapsulate a list that can be modified. (Correction: It appears that Swift has MutableCollectionType
to which Array<T>
conforms. However, there is no MutableCollectionOf<T>
, and MutableCollectionType
is a pretty anemic interface.)
How to achieve all of this in Swift?