Swift Protocol Extension with AssociatedType Constrained to Collection, Can't Use Subscript - swift

I'm trying to write a protocol that conforms to the Collection Protocol, and it has an associatedType - Object and a property object.
protocol DDCDataSource: Collection
{
associatedtype Object
var object: Object {get set}
}
I want to add some default functionality for the case where Object also conforms to the Collection protocol, namely just directly return Object's implementation of these required Collection properties and functions. It seems like it all works except for Collection's requirement for a subscript.
Cannot subscript a value of type 'Self.Object' with an index of type 'Self.Object.Index'
extension DDCDataSource where Object: Collection
{
typealias Index = Object.Index
var startIndex: Object.Index {
get {
return object.startIndex
}
}
var endIndex: Object.Index {
get {
return object.endIndex
}
}
subscript(position: Object.Index) -> Element
{
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}

Short answer: Change the return type of the subscript method
to Object.Element
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
or add a type alias (in a similar way as you did for the Index type)
typealias Element = Object.Element
subscript(position: Object.Index) -> Element {
return object[position]
}
That makes the code compile and run as expected.
Explanation: The subscript method of Collection is declared as
subscript(position: Self.Index) -> Self.Element { get }
where Self.Index and Self.Element are associated types
of `Collection. With your code
subscript(position: Object.Index) -> Element {
return object[position]
}
the compiler infers Self.Index to be Object.Index, but there
is no relation between Self.Element and Object.Element (which is
returned by object[position]). The error becomes more apparent
if you add an explicit cast:
subscript(position: Object.Index) -> Element {
return object[position] as Element
}
Now the compiler complains
error: 'Self.Object.Element' is not convertible to 'Self.Element'; did you mean to use 'as!' to force downcast?
The correct solution is not the forced cast but to make the compiler
know that Self.Element is Object.Element, by adding a type alias
or by changing the return type
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
so that the compiler infers DDCDataSource.Element to be Object.Element.
Full self-contained example: (Swift 4, Xcode 9 beta 6)
(Note that you can omit the get keyword for read-only computed
properties.)
protocol DDCDataSource: Collection {
associatedtype Object
var object: Object { get set }
}
extension DDCDataSource where Object: Collection {
var startIndex: Object.Index {
return object.startIndex
}
var endIndex: Object.Index {
return object.endIndex
}
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}
struct MyDataSource: DDCDataSource {
var object = [1, 2, 3]
}
let mds = MyDataSource()
print(mds[1]) // 2
for x in mds { print(x) } // 1 2 3

Firstly, I think you should define Element,
Secondly, you use object[position], Object Conforms To Collection , but it is not of Collection Types . Obviously it is not Array.
As apple
says: array
conforms to CustomDebugStringConvertible / CustomReflectable /
CustomStringConvertible / CVarArg /Decodable / Encodable /
ExpressibleByArrayLiteral /MutableCollection /RandomAccessCollection /
RangeReplaceableCollection
I think extension DDCDataSource where Object: Array is better.
And the element in array shall be Element defined. Just tips.

Try this:
subscript(position:Object.Index) -> Element
{
var element: Element
guard let elementObject = object[position] else {
//handle the case of 'object' being 'nil' and exit the current scope
}
element = elementObject as! Element
}

Related

Type() -> Int cannot conform to BinaryInteger

I am trying to find the number of item bigger than a certain item as follows. However, I am getting the following issue : Type() -> Int cannot conform to BinaryInteger.
I am wondering how I could able to fix it?
extension Array where Element: FloatingPoint {
var biggerItem : Element {
return numberBiggerItems()
}
private func numberBiggerItems() -> Element {
return Element { filter({$0 > 100.0}).count }
}
}
There are three problems in your code:
The return type of your method should be Int, which is what thecount of a collection returns.
The literal 100.0 is interpreted as a Double. You have to use 100 so that it can be a literal for the generic FloatingPoint type.
The return value of filter(...).count must not be wrapped in a closure (this is what causes the seen error message).
Putting it together:
extension Array where Element: FloatingPoint {
var biggerItem : Int {
return numberBiggerItems()
}
private func numberBiggerItems() -> Int {
return filter({ $0 > 100 }).count
}
}
If you really want to return the integer count as an Element then it can be converted with an Element(...) constructor:
extension Array where Element: FloatingPoint {
var biggerItem : Element {
return numberBiggerItems()
}
private func numberBiggerItems() -> Element {
return Element(filter({ $0 > 100 }).count)
}
}

Redundant duplication of typealiase declarations when conforms to a protocol

protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
associatedtype Target: Collection
static var reference: KeyPath<Self, Target> { get }
}
extension PathCollection {
private var target: Target { self[keyPath: Self.reference] }
var startIndex: Index { target.startIndex }
var endIndex: Index { target.endIndex }
subscript(index: Index) -> Element {
get { target[index] }
}
func index(after i: Index) -> Index {
target.index(after: i)
}
}
It's pretty useful protocol which helps us to reduce boilerplate code when creating custom collections.
Suppose our struct wraps a dictionary. And we want it to be a collection just like that dictionary.
We should provide keyPath to the dictionary property and apply to the protocol. And it works!
Example of usage and my question:
protocol Graph: PathCollection where Target == [String: Int] {
var storage: [String: Int] { get set }
}
extension Graph {
static var reference: KeyPath<Self, [String: Int]> { \.storage }
}
struct UndirectedGraph: Graph {
typealias Element = Dictionary<String, Int>.Element // Why should we again declare this typealias!?
typealias Index = Dictionary<String, Int>.Index // Why should we again declare this typealias!?
var storage: [String: Int]
}
It perfectly works. But why should we redeclare Element and Index typealiases!? At the very first line of code of this post we explicitly defines Element and Index:
protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
and then:
protocol Graph: PathCollection where Target == [String: Int] {
If I remove that redeclarations I get an compilation error, which I don't understand:
'PathCollection' requires the types 'Slice' and
'Dictionary<String, Int>.Element' (aka '(key: String, value: Int)') be
equivalent

RangeReplaceableCollection conformance doesn't require... actually anything

According to the documentation:
To add RangeReplaceableCollection conformance to your custom
collection, add an empty initializer and the replaceSubrange(_:with:)
method to your custom type.
But in practice it's not required! (except for empty initializer)
// Just stubs for minimal reproducible code
struct Category: Hashable {}
struct Product {}
struct ProductCollection {
typealias DictionaryType = [Category : [Product]]
// Underlying, private storage
private var products = DictionaryType()
// Enable our collection to be initialized with a dictionary
init(products: DictionaryType = DictionaryType()) {
self.products = products
}
}
extension ProductCollection: Collection {
// Required nested types, that tell Swift what our collection contains
typealias Index = DictionaryType.Index
typealias Element = DictionaryType.Element
// The upper and lower bounds of the collection, used in iterations
var startIndex: Index { return products.startIndex }
var endIndex: Index { return products.endIndex }
// Required subscript, based on a dictionary index
subscript(index: Index) -> Iterator.Element {
get { return products[index] }
}
// Method that returns the next index when iterating
func index(after i: Index) -> Index {
return products.index(after: i)
}
}
extension ProductCollection: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (Category, [Product])...) {
self.init(products: .init(uniqueKeysWithValues: elements))
}
}
extension ProductCollection: RangeReplaceableCollection {
init() {
products = DictionaryType()
}
// func replaceSubrange<C: Collection, R: RangeExpression>(_ subrange: R, with newElements: C)
// where Self.Element == C.Element, Self.Index == R.Bound {
// }
}
The code is taken from a great (but not related to the post's topic) John Sundell article.
This code compiles even though replaceSubrange function is not provided.
One more question. Why should I provide an empty initializer explicitly in this situation? I can initialize the struct like ProductCollection() without having that initializer. I can do this for many reasons: 1) products property has initializing value provided 2) main initializer has default value provided 3) there is also a ExpressibleByDictionaryLiteral initializer which can be used to initialize an empty object.
So why I have to provide one more empty initializer explicitly?
But please, the first question about replaceSubrange function is more important :)
That is a bug which has also been discussed in the Swift forum:
SR-6501 RangeReplaceableCollection default implementations cause infinite recursion
Compiler lets me use incomplete RangeReplaceableCollection
Using Swift
The reason is that there is an overload of the replaceSubRange() method (taking a RangeExpression as the first argument) which the compiler erroneously accepts as satisfying the protocol requirement.
But note that even if the code compiles without implementing the required method, it does not work and leads to an infinite loop. Here is a short example:
struct MyCollection : MutableCollection {
private var storage: [Int] = []
init(_ elements: [Int]) { self.storage = elements }
var startIndex : Int { return 0 }
var endIndex : Int { return storage.count }
func index(after i: Int) -> Int { return i + 1 }
subscript(position : Int) -> Int {
get { return storage[position] }
set(newElement) { storage[position] = newElement }
}
}
extension MyCollection: RangeReplaceableCollection {
init() { }
}
var mc = MyCollection([0, 1, 2, 3, 4, 5])
mc.replaceSubrange(0..<3, with: [2, 3, 4])
Running that code leads to an “infinite” loop and eventually crashes with EXC_BAD_ACCESS due to a stack overflow.

Referencing instance method 'stringify()' on 'Collection' requires the types 'Int' and 'Stringify' be equivalent

I made an protocol Stringify to convert Types that implement the protocol to a String.
protocol Stringify {
func stringify() -> String
}
extension Collection where Iterator.Element == Stringify {
/// changes all the elements in the collection to a String
func stringify() -> [String] {
var strings = [String]()
if let elements = self as? [Stringify] {
for element in elements {
strings.append(element.stringify())
}
}
return strings
}
}
extension Int: Stringify {
func stringify() -> String {
return String(self)
}
}
extension Double: Stringify {
func stringify() -> String {
return String(self)
}
}
let test = [5,6,7]
/// does work
[6,5,34].stringify()
/// does not work -> Error alert
test.stringify()
But when I set a collection of Ints to a property and using then stringify() on it, it does not work.
Error:
Referencing instance method 'stringify()' on 'Collection' requires the
types 'Int' and 'Stringify' be equivalent
If I use it directly everything is going fine.
What is the problem here?
extension Collection where Iterator.Element == Stringify
has a “same type requirement” and defines an extension for collections whose elements are of the type Stringify. But test is an array of Int, i.e. the elements conform to the Stringify protocol. So what you want is
extension Collection where Iterator.Element : Stringify
or, equivalently,
extension Collection where Element : Stringify
The reason that
/// does work
[6,5,34].stringify()
compiles with your original definition is that the compiler infers the type of the array as [Stringify] from the context.
let test: [Stringify] = [5,6,7]
test.stringify()
would compile as well.
Note that there is no need to cast self in the extension method. You can simplify the implementation to
func stringify() -> [String] {
var strings = [String]()
for element in self {
strings.append(element.stringify())
}
return strings
}
or just
func stringify() -> [String] {
return self.map { $0.stringify() }
}

conforming to Sequence and IteratorProtocol in Swift

I am trying to write my own version of IndexingIterator to increase my understanding of Sequence. I haven't assign any type to associatetype Iterator in my struct. However, the complier doesn't complain about that and I get a default implementation of makeIterator.
Following are my codes:
struct __IndexingIterator<Elements: IndexableBase>: Sequence, IteratorProtocol {
mutating func next() -> Elements._Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
// this works and returns an instance of __IndexingIterator<Array<String>>. why?
iterator.makeIterator()
I think there must be some extensions on Sequence which add the default implementation. Thus, I searched it in Sequence.swift and only found this.
extension Sequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
public func makeIterator() -> Self {
return self
}
}
I thought it would be like this:
extension Sequence where Self: IteratorProtocol {
typealias Iterator = Self
...
}
Did I miss something or I misunderstood the extension?
It looks like Alexander's answer is correct. Here's a boiled down version, without using Sequence:
protocol MySequence {
associatedtype Iterator: IteratorProtocol
func maakeIterator() -> Iterator
}
extension MySequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
func maakeIterator() -> Self {
return self
}
}
struct __IndexingIterator<Element>: MySequence, IteratorProtocol {
mutating func next() -> Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
iterator.maakeIterator()
You can write your own Iterator which confrom to IteratorProtocol first, then write what you need confrom to Sequence.
Make sure that you have to implement requried func.
struct IteratorTest : IteratorProtocol {
typealias Element = Int
var count : Int
init(count :Int) {
self.count = count
}
mutating func next() -> Int? {
if count == 0 {
return nil
}else {
defer {
count -= 1
}
return count;
}
}
}
struct CountDown : Sequence {
typealias Iterator = IteratorTest
func makeIterator() -> IteratorTest {
return IteratorTest.init(count: 10)
}
}
The type alias isn't necessary, because the Element associated type is inferred from your implementation of next().
Here's a simple example:
protocol ResourceProvider {
associatedtype Resoruce
func provide() -> Resoruce;
}
struct StringProvider {
func provide() -> String { // "Resource" inferred to be "String"
return "A string"
}
}