Computing Element Type When Extending CollectionType - swift

I'm trying to implement a protocol that conforms to/extends CollectionType, however it doesn't take a single generic type that would obviously be the type of element, so I'd like to be able to compute/force the type of Generator.Element.
I'll use a map protocol as an example:
protocol Map : CollectionType {
typealias Key
typealias Value
subscript(key:Key) -> Value? { get }
}
Is there a way that I can specify that Self.Generator.Element must be (Key, Value), other than documentation to type authors?

You want to define a subprotocol of CollectionType with additional constraint on CollectionType.Generator.Element nested type. This nested type belongs to CollectionType.Generator nested type which is constrained to be GeneratorType, so in the first place we need to introduce a subprotocol of GeneratorType with additional constraint:
protocol KeyValueGeneratorType: GeneratorType {
associatedtype Key
associatedtype Value
mutating func next() -> (Key, Value)?
}
Then we can introduce a subprotocol of CollectionType with additional constraint:
protocol KeyValueCollectionType: CollectionType {
associatedtype Generator: KeyValueGeneratorType
}
The Dictionary type does in fact conform to our protocol, so we need just a short declaration to manifest this:
extension DictionaryGenerator: KeyValueGeneratorType {}
extension Dictionary: KeyValueCollectionType {}

You have to create the type of generator element you want to conform to. For example.
protocol SpecialElement {
typealias key : Int { get }
typealias Value : Int { get }
}
And then:
extension CollectionType where Self.Generator.Element: SpecialElement {
func addValues() -> Int {
var total = 0
for item in self {
total += item.Value
}
return total
}
}

Related

Swift protocol conformance when returning a generic

Here's an example:
protocol Feed {
func items<T>() -> [T]? where T: FeedItem
}
protocol FeedItem {}
class FeedModel: Feed, Decodable {
func items<T>() -> [T]? where T : FeedItem {
return [FeedItemModel]() // Error: Cannot convert return expression of type '[FeedItemModel]' to return type '[T]?'
}
}
class FeedItemModel: FeedItem, Decodable {}
Why does it:
A) try to convert to T when T is a generic, not a type?
B) does not recognize FeedItemModel as conforming to FeedItem?
func items<T>() -> [T]? where T : FeedItem
This says that the caller can define T to be whatever they want, as long as T conforms to FeedItemModel, and this function will return an optional array of those.
FeedItemModel is something that conforms to FeedItem, but it is not promised to be the type T that the caller requested.
As an example, consider:
class OtherModel: FeedItem {}
According to your function signature, I can do this:
let ms: [OtherModel]? = FeedModel().items()
But your function won't then return [OtherModel]? to me. I suspect you don't actually mean this to be generic. I expect you mean:
func items() -> [FeedItemModel]?
or possibly
func items() -> [FeedItem]?
(Though I would think very hard before doing the latter one and make sure that the protocol existential is really doing useful work here.)
A)
T is a type, a homogenous concrete type specified at runtime.
Imaging T is class Foo : FeedItem it's obvious that FeedItemModel cannot be converted to Foo
B)
FeedItemModel is recognized as conforming to FeedItem but this is irrelevant.
It's often a mix-up of generics and protocols. Generic types are not covariant. If you need covariant types use an associated type.
Either you can ignore generics because because it only applies to that one function and it isn't needed since directly saying that the return type is [FeedItem]? yields the same result
protocol Feed {
func items() -> [FeedItem]?
}
class FeedModel: Feed, Decodable {
func items() -> [FeedItem]? {
return [OtherModel]()
}
}
If you on the other hand want a generic protocol then you should use a associated type
protocol Feed2 {
associatedtype T: FeedItem
func items() -> [T]?
}
class FeedModel2: Feed2, Decodable {
typealias T = FeedItemModel
func items() -> [T]? {
return [FeedItemModel]()
}
}

SwifT: Generic function that gets generic protocol as parameter not working

Suppose I have this simple generic protocol
protocol FooProtocol {
associatedtype Element: CustomStringConvertible
func echo(element: Element) -> Element
}
And this simple generic struct that implements it
struct FooStruct<Element: CustomStringConvertible>: FooProtocol {
func echo(element: Element) -> Element {
return element
}
}
Lastly, I have the following function:
func callEcho<T: FooProtocol>(container: T) {
container.echo(element: "some string")
}
This results in error: cannot invoke 'echo' with an argument list of type '(element: String)'
The solution is to change the function to
func callEcho<T: FooProtocol>(container: T) where T.Element == String {
container.echo(element: "some string")
}
My question is: why is the where T.Element == String constraint necessary? The compiler knows that T is some entity that implements FooProtocol, and the protocol only demands that Element implements CustomStringConvertible, which my string literal does. So why does it not work without the constraint?
Thanks!
I'm not sure why you say "the protocol only demands that Element implements CustomStringConvertible." The protocol demands that echo accept and return its Element, which may or may not be String. For example, I can implement this conforming type:
struct AnotherFoo: FooProtocol {
func echo(element: Int) -> Int { fatalError() }
}
So what should happen if I then call:
callEcho(container: AnotherFoo())
This would try to pass a string literal to a function that requires an Int.
container has type T, so that container.echo(element:) expects an argument of type T.Element – and that is not necessarily a string.
If the intention is to pass string literals to the method then T.Element must adopt ExpressibleByStringLiteral, not CustomStringConvertible:
protocol FooProtocol {
associatedtype Element: ExpressibleByStringLiteral
func echo(element: Element) -> Element
}
Now this compiles:
func callEcho<T: FooProtocol>(container: T) {
_ = container.echo(element: "some string")
}

why is this causing so much trouble? (protocols and typealiases on their associated types)

I'm trying to do something along the lines of this:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<T>
}
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolType.ArrayT) {
let _ = with.map { $0 }
// ^ compiler complains on this line
// "value of type ProtocolType.ArrayT has no member map"
}
}
where I define a convenience typealias ArrayT that uses the associatedtype T. It seems that when I try to use ArrayT like in doSomething(_:), I lose the Array type information of ArrayT.
Shouldn't ArrayT definitely be an Array and therefore a member of the Sequence protocol, exposing the map function? 🤔
the working solution I'm employing now is to just define a generic typealias outside of the protocol:
typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolArray<ProtocolType>) {
let _ = with.map { $0 } // no complaints
}
}
what am I missing here?
The line associatedtype ArrayT = Array<T> only tells the compiler that the default value of ArrayT is Array<T>. An adaption of the protocol can still change ArrayT like:
struct U: Protocol {
typealias T = UInt32
typealias ArrayT = UInt64 // <-- valid!
}
If you want a fixed type, you should use a typealias...
// does not work yet.
protocol Protocol {
associatedtype T
typealias ArrayT = Array<T>
}
But the compiler complains that the type is too complex 🤷. So the best you could do is constrain the ArrayT to be a Sequence / Collection / etc, and hope that the adaptors won't change the type themselves.
// still somewhat off
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
}
Note that, however, the Sequence can have any element type, but we want ArrayT's Element must be T. We cannot attach a where clause to the associatedtype:
// fail to compile: 'where' clause cannot be attached to an associated type declaration
associatedtype ArrayT: Sequence where Iterator.Element == T = [T]
Instead, you need to put this constraint every time you use the protocol:
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T {
Complete, working code:
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
// ^~~~~~~~~~
}
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
func doSomething(w: ProtocolType.ArrayT) {
let _: [ProtocolType.T] = w.map { $0 }
}
}
When you "initialize" an associatedtype, you're not defining it. That's not the point of associated types in the first place. Associated types are deliberately unbound ("placeholders") until the adopting class resolves them all. All you're doing there is giving it a default value, which a conforming class is allowed to override. To extend your example:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<Self.T>
func useSomeT(myFavoriteT: T)
func useSomeArrayT(myLeastFavoriteArrayT: ArrayT)
}
class Whatever: Protocol {
func useSomeT(myFavoriteT: Int) {
print("My Favorite Int: \(myFavoriteT)")
}
func useSomeArrayT(myLeastFavoriteArrayT: [Int: String]) {
print(myLeastFavoriteArrayT.map { $0.1 })
}
}
struct Struct<ProtocolType: Protocol> {
func doSomething(stuff: ProtocolType.ArrayT) {
print(type(of: stuff))
}
}
let x = Struct<Whatever>()
x.doSomething(stuff: [3: "Doggies"])
For your example, it seems all you really want is to declare with: Array<ProtocolType.T> (or simply with: [ProtocolType.T]) as the parameter type.

Swift: Any Kind of sequence as a function parameter

I have created my custom sequence type and I want the function to accept any kind of sequence as a parameter. (I want to use both sets, and my sequence types on it)
Something like this:
private func _addToCurrentTileset(tilesToAdd tiles: SequenceType)
Is there any way how I can do it?
It seems relatively straightforward, but I can't figure it out somehow. Swift toolchain tells me:
Protocol 'SequenceType' can only be used as a generic constraint because it has Self or associated type requirements, and I don't know how to create a protocol that will conform to SequenceType and the Self requirement from it.
I can eliminate the associatedType requirement with, but not Self:
protocol EnumerableTileSequence: SequenceType {
associatedtype GeneratorType = geoBingAnCore.Generator
associatedtype SubSequence: SequenceType = EnumerableTileSequence
}
Now if say I can eliminate self requirement, then already with such protocol definition other collectionType entities like arrays, sets won't conform to it.
Reference:
my custom sequences are all subclasses of enumerator type defined as:
public class Enumerator<T> {
public func nextObject() -> T? {
RequiresConcreteImplementation()
}
}
extension Enumerator {
public var allObjects: [T] {
return Array(self)
}
}
extension Enumerator: SequenceType {
public func generate() -> Generator<T> {
return Generator(enumerator: self)
}
}
public struct Generator<T>: GeneratorType {
let enumerator: Enumerator<T>
public mutating func next() -> T? {
return enumerator.nextObject()
}
}
The compiler is telling you the answer: "Protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements".
You can therefore do this with generics:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) {
...
}
This will allow you to pass in any concrete type that conforms to Sequence into your function. Swift will infer the concrete type, allowing you to pass the sequence around without lose type information.
If you want to restrict the type of the element in the sequence to a given protocol, you can do:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) where T.Element: SomeProtocol {
...
}
Or to a concrete type:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) where T.Element == SomeConcreteType {
...
}
If you don't care about the concrete type of the sequence itself (useful for mixing them together and in most cases storing them), then Anton's answer has got you covered with the type-erased version of Sequence.
You can use type-eraser AnySequence for that:
A type-erased sequence.
Forwards operations to an arbitrary underlying sequence having the same Element type, hiding the specifics of the underlying SequenceType.
E.g. if you will need to store tiles as an internal property or somehow use its concrete type in the structure of you object then that would be the way to go.
If you simply need to be able to use the sequence w/o having to store it (e.g. just map on it), then you can simply use generics (like #originaluser2 suggests). E.g. you might end up with something like:
private func _addToCurrentTileset<S: SequenceType where S.Generator.Element == Tile>(tilesToAdd tiles: S) {
let typeErasedSequence = AnySequence(tiles) // Type == AnySequence<Tile>
let originalSequence = tiles // Type == whatever type that conforms to SequenceType and has Tile as its Generator.Element
}

Self in protocol

I am learning swift and playing with Xcode.
and I always dig into the definitions. I have seen that:
public protocol GeneratorType {
typealias Element
#warn_unused_result
public mutating func next() -> Self.Element?
}
A struct that conforming this protocol:
public struct IndexingGenerator<Elements : Indexable> : GeneratorType, SequenceType {
public init(_ elements: Elements)
public mutating func next() -> Elements._Element?
}
I know 'Self' means that returning the conforming type. But what does 'Self.Element' mean?
and the function that implemented the requirement that returning 'Elements._Element?', I can’t see 'Elements._Element?' is equal to 'Self.Element?'.
Can anyone explain to me this?
and tell me more about this. thank you.
Self.Element refers to the concrete type that any type implementing GeneratorType protocol will declare as its Element typealias.
For example, in this generator of Fibonacci numbers:
struct Fibonacci: GeneratorType {
typealias Element = Int
private var value: Int = 1
private var previous: Int = 0
mutating func next() -> Element? {
let newValue = value + previous
previous = value
value = newValue
return previous
}
}
... you implement GeneratorType protocol and indicate what will be its Element typealias (Int in this case), and that's the type that generator's next() will be returning (well, actually the optional of that type).
Quite often, though, you would not have to explicitly specify typealiases when implementing parametrised protocols, as Swift is smart enough to infer them for you. E.g. for the Fibonacci numbers generator from the above example the following will also do:
struct Fibonacci: GeneratorType {
private var value: Int = 1
private var previous: Int = 0
mutating func next() -> Int? {
let newValue = value + previous
previous = value
value = newValue
return previous
}
}
... Swift knows from the signature of next() that it returns Int?, and that GeneratorType implementors also must have next() in their to-do list, and that these methods must return Element? types. So, Swift just puts 2 and 2 together, and infers that Element? must be the same thing as Int?, and therefore Element == Int.
About this:
public struct IndexingGenerator<Elements : Indexable> : GeneratorType, SequenceType {
public init(_ elements: Elements)
public mutating func next() -> Elements._Element?
}
Here we have four things going on:
We declare generic type IndexingGenerator that takes a parameter-type called Elements.
This Elements type has a constraint that it must implement Indexable protocol.
The generator that we implement is supposed to return values of the type that is accessible via Indexable interface of Elements, which is known to IndexingGenerator via dot-syntax as Elements._Element.
Swift infers that Element of IndexingGenerator is the same thing as Elements._Element.
So, essentially the above delclaration is equivalent to:
public struct IndexingGenerator<Elements : Indexable> : GeneratorType, SequenceType {
public typealias Element = Elements._Element
public init(_ elements: Elements)
public mutating func next() -> Element?
}
Finally, if curious why _Element and not just Element like in GeneratorType, here is what they write in the open-source Swift repository (under swift/stdlib/public/core/Collection.swift):
The declaration of _Element and subscript here is a trick used to break a cyclic conformance/deduction that Swift can't handle. We need something other than a CollectionType.Generator.Element that can be used as IndexingGenerator<T>'s Element. Here we arrange for the CollectionType itself to have an Element type that's deducible from its subscript. Ideally we'd like to constrain this Element to be the same as CollectionType.Generator.Element, but we have no way of expressing it today.