What is the difference between SequenceType and CollectionType in swift?
GeneratorType (IteratorProtocol in Swift 3): Generators
is something that can give the next
element of some sequence, if there is no element it returns nil
. Generators
encapsulates iteration state and interfaces for iteration over a sequence.
A generator works by providing a single method, namely – next()
, which simply returns the next value from the underlying sequence
.
Following classes Adopt GeneratorType Protocol:
DictionaryGenerator, EmptyGenerator, more here.
SequenceType (Sequence in Swift 3): A Sequence
represent a series of values. Sequence
is a type that can be iterated with a for...in
loop.
Essentially a sequence is a generator factory; something that knows how to make generators for a sequence.
Following classes Adopt SequenceType Protocol:
NSArray, NSDictionary, NSSet and more.
CollectionType (Collection in Swift 3): Collection
is a SequenceType
that can be accessed via subscript and defines a startIndex
and endIndex
. Collection
is a step beyond a sequence; individual elements of a collection can be accessed multiple times.
CollectionType
inherits from SequenceType
Following classes Adopt CollectionType Protocol:
Array, Dictionary, Set, Range and more.
Form more information you can see this, this, and this
CollectionType Index Tracks Corresponding SequenceType Generator
The answer is 'yes'. The comment in the Swift code defining CollectionType
is:
/// The sequence view of the elements is identical to the collection
/// view. In other words, the following code binds the same series of
/// values to `x` as does `for x in self {}`::
///
/// for i in startIndex..<endIndex {
/// let x = self[i]
/// }
Since for x in self
invokes the GeneratorType
interface, the orders generated through the two interfaces must be identical. Implementors of new CollectionTypes are, at best it seems, to use this as guidance.
CollectionType Index Behavior
The answer is, from the Apple forums, summarizing: The observed behavior:
- is not a requirement
- should not be relied upon
- for value types, it may be difficult to throw an exception
Why isn't a Swift tuple considered a collection type?
I don't have inside knowledge of the motivations, but I can hopefully present some deeper understanding.
Type Safety
One of the primary goals of Swift is to enforce programming best practices to minimize human mistakes. One of these best practices is to ensure we always use specific types when we know them. In Swift, variables and constants always have explicit types and collections prefer to hold, say String
s, than AnyObject
s if you know that it will be storing String
s.
Notably, every collection type in Swift may only hold one type of element. This includes Array
, Dictionary
, and Set
.
Tuples are Compound Types
In Swift, there are two types of types: Named Types and Compound Types. Most types, including collection types, are named. Compound types, on the other hand, contain multiple named types in unique combinations. There are only two Compound Types: Function Types and Tuple Types. Tuple Types are different from collections in that:
- Multiple Named Types can be bundled together in a Tuple Type without using
AnyObject
. - Each position can be given a textual label.
- We can always anticipate how many values a tuple will be holding because we declared each position.
For more information, see the Types chapter of The Swift Programming Language.
Tuples Represent Objects
We typically don't think of a collection type as an object in itself. Instead, we think of it as a collection of objects. A tuple's labeled positions and multi-type functionality enable it to function more like an object than any collection. (Almost like a primitive Struct)
For example, you might consider an HTTP error to be a tuple with an error code (Int
) and a description (String
).
Tuples are often used as primitive approaches to temporary objects, such as returning multiple values from a function. They can be quickly dissected without indexing.
Additionally, each Tuple type has its own explicit type. For example, (Int, Int)
is an entirely different type from (Int, Int, Int)
, which is an entirely different type from (Double, Double, Double)
.
For more on this, see "Tuples" in the The Basics chapter of The Swift Programming Language.
Tuples are Used in Fundamental Language Syntax
When we think of collection types, we think again of collections. Tuples, however, are used in fundamental places in the language that make Compound Type a more fitting title for them. For example, the Function Type is a Compound Tye that is used anytime you specify a function or closure. It is simply a tuple of parameters and a tuple of return values. Even Void is just a typealias for (), an empty tuple.
Additionally, tuples are in language syntax to temporarily bind values. For example, in a for-in loop you might use a tuple to iterate over a dictionary:
let dict = [1: "A", 2: "B", 3: "C"]
for (_, letter) in dict {
doSomething()
}
Here we use a tuple to iterate over only the values of the dictionary and ignore the keys.
We can do the same in Switch statements (excerpt from The Swift Programming Language):
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) is at the origin")
case (_, 0):
print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// prints "(1, 1) is inside the box”
Because tuples are used in such fundamental places in language syntax, it wouldn't feel right for them to be bundled together with collection types.
SequenceType
& CollectionType
One last thing to note is that a fundamental feature of CollectionType
, which inherits much of this from SequenceType
, is the ability to provide a safe Generator for iteration. For example, a for-in loop is possible because collections define a way to get the next item. Because tuples do not consist of the same types, and have a guaranteed number of items (after declaration), using them on the right of a for-in loop (instead of a traditional collection) would be less than intuitive.
If using it in a for-in loop, something designed to thrive with collections, seems less-than-intuitive for a tuple, than a tuple may deserve a different category.
For details on CollectionType
, check out SwiftDoc.org. Note that a Generator providing iteration is required. Iterating a tuple would not be type safe and impose many unnecessary complexities, but making a tuple a collection is an interesting concept. It just might be too fundamental for that.
Circular dependencies between generic types (CollectionType and its Index/Generator, e.g.)
While Airspeed Velocity's answer applies to the most common cases, my question was asking specifically about the special case of generalizing CollectionType
indexing in order to be able to share a single Index
implementation for all thinkable kinds of binary trees (whose recursive nature makes it necessary to make use of a stack for index-based traversals (at least for trees without a parent pointer)), which requires the Index
to be specialized on the actual BinaryTree
, not the Element
.
The way I solved this problem was to rename MyCollection
to MyCollectionStorage
, revoke its CollectionType
conformity and wrap it with a struct that now takes its place as MyCollection
and deals with conforming to CollectionType
.
To make things a bit more "real" I will refer to:
MyCollection<E>
asSortedSet<E>
MyCollectionStorage<E>
asBinaryTree<E>
MyIndex<T>
asBinaryTreeIndex<T>
So without further ado:
struct SortedSet<Element>: CollectionType {
typealias Tree = BinaryTree<Element>
typealias Index = BinaryTreeIndex<Tree>
subscript(i: Index) -> Element { … }
func generate() -> IndexingGenerator<SortedSet> {
return IndexingGenerator(self)
}
}
struct BinaryTree<Element>: BinaryTreeType {
}
struct BinaryTreeIndex<BinaryTree: BinaryTreeType>: BidirectionalIndexType {
func predecessor() -> BinaryTreeIndex { … }
func successor() -> BinaryTreeIndex { … }
}
This way the dependency graph turns from a directed cyclic graph into a directed acyclic graph.
In Swift, what is a 'progression'?
The first mention I see of "progression" besides the for-in documentation is in the comments of the swift framework where the stride methods are defined.
func stride<T : Strideable>(from start: T, to end: T, by stride: T.Stride) -> StrideTo<T>
Return the sequence of values (
start
,start + stride
,start + stride + stride
, ... last) where last is the last value in the progression that is less thanend
.
func stride<T : Strideable>(from start: T, through end: T, by stride: T.Stride) -> StrideThrough<T>
Return the sequence of values (
start
,start + stride
,start + stride + stride
, ... last) where last is the last value in the progression less than or equal toend
. Note:: There is no guarantee thatend
is an element of the sequence.
So in short, "progression" refers to the Strideable
protocol similar to how "collection" refers to the CollectionType
protocol and the classes and structs that conform to it.
Numeric types (Int, Double, Float, UnsafePointer, Bit, etc...) tend to conform to this protocol so they may be incremented/decremented for for in
loops. Full inheritance graph for the Strideable
protocol found here.
How to convert code to Swift 4 because of undeclared types?
The thing here is that I wanted Weak Reference Set. But I found another solution. If anyone needs it here it is:
class WeakObject<T: AnyObject>: Equatable, Hashable {
weak var object: T?
init(object: T) {
self.object = object
}
var hashValue: Int {
if var object = object { return UnsafeMutablePointer<T>(&object).hashValue }
return 0
}
static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
return lhs.object === rhs.object
}
}
class WeakObjectSet<T: AnyObject> {
var objects: Set<WeakObject<T>>
init() {
self.objects = Set<WeakObject<T>>([])
}
init(objects: [T]) {
self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
}
var allObjects: [T] {
return objects.compactMap { $0.object }
}
func contains(_ object: T) -> Bool {
return self.objects.contains(WeakObject(object: object))
}
func addObject(_ object: T) {
self.objects.formUnion([WeakObject(object: object)])
}
func addObjects(_ objects: [T]) {
self.objects.formUnion(objects.map { WeakObject(object: $0) })
}
}
Define a Swift protocol which requires a specific type of sequence
There are two sides to this problem:
Accepting an arbitrary sequence of ints
Returning or storing an arbitrary sequence of ints
In the first case, the answer is to use generics. For example:
func iterateOverInts<SeqInt: SequenceType where SeqInt.Generator.Element == Int>(xs: SeqInt) {
for x in xs {
print(x)
}
}
In the second case, you need a type-eraser. A type-eraser is a wrapper that hides the actual type of some underlying implementation and presents only the interface. Swift has several of them in stdlib, mostly prefixed with the word Any
. In this case you want AnySequence
.
func doubles(xs: [Int]) -> AnySequence<Int> {
return AnySequence( xs.lazy.map { $0 * 2 } )
}
For more on AnySequence
and type-erasers in general, see A Little Respect for AnySequence.
If you need it in protocol form (usually you don't; you just need to use a generic as in iterateOverInts
), the type eraser is also the tool there:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
But seq
must return AnySequence<Int>
. It can't return [Int]
.
There is one more layer deeper you can take this, but sometimes it creates more trouble than it solves. You could define:
protocol HasSequenceOfInts {
typealias SeqInt : IntegerType
var seq: SeqInt { get }
}
But now HasSequenceOfInts
has a typealias
with all the limitations that implies. SeqInt
could be any kind of IntegerType
(not just Int
), so looks just like a constrained SequenceType
, and will generally need its own type eraser. So occasionally this technique is useful, but in your specific case it just gets you basically back where you started. You can't constrain SeqInt
to Int
here. It has to be to a protocol (of course you could invent a protocol and make Int
the only conforming type, but that doesn't change much).
BTW, regarding type-erasers, as you can probably see they're very mechanical. They're just a box that forwards to something else. That suggests that in the future the compiler will be able to auto-generate these type-erasers for us. The compiler has fixed other boxing problems for us over time. For instance, you used to have to create a Box
class to hold enums that had generic associated values. Now that's done semi-automatically with indirect
. We could imagine a similar mechanism being added to automatically create AnySequence
when it's required by the compiler. So I don't think this is a deep "Swift's design doesn't allow it." I think it's just "the Swift compiler doesn't handle it yet."
Related Topics
Downloading Firebase Storage Files Device Issue
Swift Format Text Field When User Is Typing
How to Increment Exponentially in Swift
Elegant 'Bounded' Methodology in Swift
App Window on Top of All Windows Including Others App Windows
Replace Multiple Words from a String Based on the Values in an Array
How to Take a Substring to the First Index of a Character
Learning Swift: Expressions Are Not Allowed at the Top Level
Swift Sorting Array of Structs by String Double
Swift Issue in Passing Variable Number of Parameters
Color Ouput with Swift Command Line Tool
Receiving Data from Nsinputstream in Swift
Animated Curve Line in Swift 3
How to Find a Maximum Value in a Swift Dictionary
(Cross-)Compiling Swift for Raspberry Pi
Swift Generics and Protocols Not Working on Uikit [Possible Bug]