Table of contents
Abstract
All classes, structs and enums in Swift are named and they are concrete in the sense that there are no abstract members, like abstract methods or properties. Especially for classes with very little functionality, it would sometimes be handy to have a lightweight approach to define a single instance of a class or struct. This post introduces a Swift-specific design pattern for creating such lightweight anonymous class and struct instances.
Anonymous class design pattern
Implementing small classes and structs
Let’s assume we would want to implement Java-like iterators in Swift. Swift’s approach to iterators is defined in terms of the GeneratorType
protocol:
protocol GeneratorType { typealias Element mutating func next() -> Element? }
This protocol defines a function next
which returns the elements of a collection of objects incrementally. If no elements are left, next
returns nil
. All of Swift’s collection classes, like Array<T>
, Set<T>
, and Dictionary<K, V>
, return generators for iterating over their elements. This mechanism is also used for implementing the for
loop in Swift.
Java-like iterators are a little more expressive: they also define a hasNext
method which returns true if there are more elements left. We could define a corresponding protocol, which is compatible to Swift’s GeneratorType
protocol, in the following way:
protocol IteratorType: GeneratorType { var hasNext: Bool { get } }
This protocol defines a hasNext
property and inherits function next
from GeneratorType
.
If we would have to implement iterators for a large number of collection classes, we would have to define a lot of small, very similar classes or structs which implement the IteratorType
protocol for a specific collection class.
Here’s an example showing how we can implement an iterator over arrays in Swift:
struct ArrayIterator<T>: IteratorType { let array: [T] var i = 0 init(_ array: [T]) { self.array = array } var hasNext: Bool { return i < array.count } mutating func next() -> T? { return i < array.count ? array[i] : nil } }
We can now equip the Array<T>
struct with a method iterator()
which returns an iterator over the array elements:
extension Array { func iterator() -> ArrayIterator<Element> { return ArrayIterator(self) } }
Minimizing overhead without giving up readability
The previous section showed the boilerplate code that needed to be written to create an iterator implementation and extend a struct to return iterators. We can reduce this implementation overhead with an idea that was briefly described already in the post on abstract classes in Swift: we implement a generic implementation of iterators by delegating the methods to individual closures that are provided via init
.
Here’s a minimal implementation of such a generic implementation:
struct Iterator<T>: IteratorType { private let _hasNext: () -> Bool private let _next: () -> T? init(hasNext: () -> Bool, next: () -> T?) { self._hasNext = hasNext self._next = next } var hasNext: Bool { return _hasNext() } func next() -> T? { return _next() } }
The following code shows how we can now implement an iterator over arrays without defining a specific ArrayIterator
:
extension Array { func iterator() -> Iterator<Element> { var i = 0 return Iterator( hasNext: { i < self.count }, next: { return i < self.count ? self[i++] : nil } ) } }
This approach has the following advantages:
- No Array-specific class or struct needs to be defined.
- The implementation is completely encapsulated within the
iterator()
method ofArray<T>
. - The definition of the iterator for
Array
is intuitive; it really looks like
an anonymous class instantiation in Java. This is due to the fact that we gave the parameter
labels in theIterator
constructor the same name like the methods/properties
they implement.
There is one caveat that needs to be considered: even though we used a struct to define Iterator<T>
, all instances of Iterator<T>
will have reference semantics. This is because the state that this iterator encapsulates is contained in the context of the closures that are passed to the init
method. Variable i
in line 3 of the listing above is a good example for such implicitly captured state.
Since the struct itself doesn’t directly contain mutable state, we also do not need to tag the next()
method with modifier mutating
. Nevertheless, next()
does mutate indirectly captured state. This is also why I would consider the usage of modifier mutating
in Swift highly misleading.
Encoding variants
If there are common patterns for implementing an iterator, for instance by using other existing methods, this can be easily supported in terms of additional init
methods. The following code shows how we could generically support iterators that are defined in terms of a standard Swift generator:
struct Iterator<T>: IteratorType { private let _hasNext: () -> Bool private let _next: () -> T? init(hasNext: () -> Bool, next: () -> T?) { self._hasNext = hasNext self._next = next } init(_ generate: () -> T?) { var lookahead: T? = generate() self.init( hasNext: { return lookahead != nil }, next: { if lookahead != nil { let res = lookahead lookahead = generate() return res } return lookahead } ) } var hasNext: Bool { return _hasNext() } func next() -> T? { return _next() } }
This second init
method accepts as its parameter a next()
function of a regular Swift generator. It implements both hasNext
and next()
in terms of this function. This gives us now a second way to quickly implement iterators for all collection classes which have a standard Swift generator already. Here’s how we could make use of this in class Array<T>
:
extension Array { func iterator() -> Iterator<Element> { var generator = generate() return Iterator { generator.next() } } }
This code compiles and works as expected. Nevertheless, it’s not the shortest possible way. My first attempt to eliminate the unnecessary closure, which gets passed to the init
method of Iterator
, does not seem to work:
extension Array { func iterator() -> Iterator<Element> { var generator = generate() return Iterator(generator.next) } }
This code results in the following compiler error message: “Partial application of ‘mutating’ method is not allowed”. I’m puzzled that this isn’t allowed given that a programmer can easily wrap the full application of the mutating method into a closure and achieve the exact same thing.
My very first attempt to stuff everything into a single line, does not work for yet another reason:
extension Array { func iterator() -> Iterator<Element> { return Iterator(generate().next) } }
Here, the compiler complains with the message: “Value of type ‘IndexingGenerator<[Element]>’ has no member ‘next’. This must be a compiler bug, or a misleading compiler error message, because IndexingGenerator<[Element]>
clearly does have a member next
.
Summary
- Swift does not have a lightweight approach to define small classes and structs which
implement a small protocol. - As a workaround, it is often sufficient to define a generic implementation of the protocol
which forwards the methods and properties of the protocol to closures that are provided by
clients instantiating the generic implementation. - By using the method/property names of the protocol as labels in the
init
method of the generic implementation, we can enable an intuitive syntax for instantiating
the implementation that is very similar to the anonymous class syntax in Java.