Using KeyPaths on associatedtype objects - swift

I am making a backend using Vapor 3 and Swift 5.
On some part I'm trying to use a KeyPath on an associatedtype like so:
protocol MyProtocol {
associatedType T: Model & Parameter
// Some more declarations
}
extension MyProtocol where T.ID: Parameter {
func myFunction(_ req: Request) throws -> Future<T> {
return try req.content.decode(T.self).flatMap { t in
//Do some stuff
let myId = t[keyPath: T.ID] //Value of type Self.T has no subscripts
//Cannot create a single-element tuple with an element label
//Do some more stuff
}
}
}
But I get that Value of type Self.T has no subscripts error. I looked and according to this: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md the keyPath subscript is defined on an extension to Any, and I would asume a T should also be an Any.
In Any way (<- I had to šŸ˜), I'm sure I've seen keyPaths being used on generics (I don't fully understand how generics and associatedtypes are different). So does anyone have an idea what might cause the keyPath subscript to be unavailable in this case? Is it just because it is an associatedtype, is there any way I can make this work?
Update
If I erase the type of t by casting it to Any I still get the no subscripts error, if I cast it to AnyObject I don't get the subscripts error but I still get the Cannot create a single-element tuple which I don't understand, since I am not trying to make a tuple, I'm using a subscript.

Related

Storing a generic conforming to an associated type inside A collection

I am trying to store a generic who uses an an associated type, however when trying to create a type which should conform to the generic type I describe in the generic list at the top of class A, but I get the error.
"Cannot invoke 'append' with an argument list of type '(B)'"
How can I properly declare the generic so that this code works?
class A<DataType: Any, AssociatedType: Runable> where
AssociatedType.DataType == DataType {
var array = Array<AssociatedType>()
func addAssociatedValue(data: DataType) {
array.append(B(data: data))
}
func runOnAll(with data: DataType) {
for item in array {
item.run(with: data)
}
}
}
class B<DataType>: Runable {
init(data: DataType) { }
func run(with: DataType) { }
}
protocol Runable {
associatedtype DataType
func run(with: DataType)
}
I am also using Swift 4.2 so if there is a solution that uses one of the newer Swift features that will also work as a solution.
B conforms to Runnable, yes, but you can't put it into an array that's supposed to store AssociatedTypes. Because the actual type of AssociatedType is decided by the caller of the class, not the class itself. The class can't say, "I want AssociatedType to always be B". If that's the case, you might as well remove the AssociatedType generic parameter and replace it with B. The caller can make AssociatedType be Foo or Bar or anything conforming to Runnable. And now you are forcing to put a B in.
I think you should rethink your model a bit. Ask yourself whether you really want AssociatedType as a generic parameter.
You could consider adding another requirement for Runnable:
init(data: DataType)
And add required to B's initializer. This way, you could write addAssociatedValue like this:
func addAssociatedValue(data: DataType) {
array.append(AssociatedType(data: data))
}

What is "Element" type?

Reading Swift Programming Language book I've seen number of references to type Element, which is used to define type of collection items. However, I can't find any documentation on it, is it class, protocol? What kind of functionality/methods/properties it has?
struct Stack<Element>: Container {
// original Stack<Element> implementation
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
...
If we tried trace the idea of how we do even get Element when working with collections, we would notice that it is related to the Iterator protocol. Let's make it more clear:
Swift Collection types (Array, Dictionary and Set) are all conforms to Collection protocol. Therefore, when it comes to the Collection protocol, we can see that the root of it is the Sequence protocol:
A type that provides sequential, iterated access to its elements.
Sequence has an Element and Iterator associated types, declared as:
associatedtype Element
associatedtype Iterator : IteratorProtocol where Iterator.Element == Element
You could review it on the Sequence source code.
As shown, Iterator also has an Element associated type, which is compared with the sequence Element, well what does that means?
IteratorProtocol is the one which does the actual work:
The IteratorProtocol protocol is tightly linked with the Sequence
protocol. Sequences provide access to their elements by creating an
iterator, which keeps track of its iteration process and returns one
element at a time as it advances through the sequence.
So, Element would be the type of returned element to the sequence.
Coding:
To make it simple to be understandable, you could implement such a code for simulating the case:
protocol MyProtocol {
associatedtype MyElement
}
extension MyProtocol where MyElement == String {
func sayHello() {
print("Hello")
}
}
struct MyStruct: MyProtocol {
typealias MyElement = String
}
MyStruct().sayHello()
Note that -as shown above- implementing an extension to MyProtocol makes MyElement associated type to be sensible for the where-clause.
Therefore sayHello() method would be only available for MyProtocol types (MyStruct in our case) that assign String to MyElement, means that if MyStruct has been implemented as:
struct MyStruct: MyProtocol {
typealias MyElement = Int
}
you would be not able to:
MyStruct().sayHello()
You should see a compile-time error:
'MyStruct.MyElement' (aka 'Int') is not convertible to 'String'
The same logic when it comes to Swift collection types:
extension Array where Element == String {
func sayHello() {
print("Hello")
}
}
Here is the definition in the Apple documentation
Element defines a placeholder name for a type to be provided later.
This future type can be referred to as Element anywhere within the
structureā€™s definition.
Element is usually used as the generic type name for collections, as in
public struct Array<Element> { ... }
so it is what you construct your array from, and not something predefined by the language.
Element is a purpose-built (and defined) placeholder for structures. Unlike some of the answers/comments have suggested, Element cannot always be substituted for T because T without the proper context is undefined. For example, the following would not compile:
infix operator ++
extension Array {
static func ++ (left: Array<T>, right: T) -> Array {
...
}
}
The compiler doesn't know what T is, it's just an arbitrary letterā€”it could be any letter, or even symbol (T has just become Swift convention). However, this will compile:
infix operator ++
extension Array {
static func ++ (left: Array<Element>, right: Element) -> Array {
...
}
}
And it compiles because the compiler knows what Element is, a defined placeholder, not a type that was arbitrarily made up.

Generic dictionary extension error - ambiguous reference to subscript

I'm playing with generics in Swift 3 (Xcode 8.2.1) and I don't understand why this won't compile. I also tried self.updateValue... and that fails also.
extension Dictionary {
mutating func mergeWith<K: Hashable, V: AnyObject> (a: [K:V]) -> [K:V] {
for (k,v) in a {
self[k] = v // compile error: Ambiguous reference to member 'subscript'
}
}
}
I'm trying to limit the types of generics K and V to what works with a Dictionary, but that doesn't seem to work?
It's not a particularly helpful error, but the problem is that you're introducing new local generic placeholders K and V in your method ā€“ which need not be related in any way to the Dictionary's Key and Value types (remember that generic placeholders are satisfied by the caller, not the callee).
So just simply remove them and use the existing generic placeholders Key and Value instead, i.e take a [Key : Value] parameter. Or better still, take advantage of the fact that Swift automatically infers the generic placeholders of a generic type when you refer to it inside of itself, and just type the parameter as Dictionary (which will resolve to Dictionary<Key, Value>).
extension Dictionary {
mutating func merge(with dict: Dictionary) {
for (key, value) in dict {
self[key] = value
}
}
}
Also mutating methods usually don't return the mutated instance, so I removed the return type from your method.

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
}

Defining typeliases declared in other protocols

I'm creating a protocol that extends from CollectionType, however, I'm introducing new typealiases that eliminate the need for Element in CollectionType (or rather, they allow me to compute it).
I'll use a simple MapType protocol as an example:
protocol MapType : CollectionType, DictionaryLiteralConvertible {
typealias Key
typealias Value
func updateValue(theNewValue:Value, forKey theKey:Key) -> Value?
}
In the above example, what I really need to be able to do is redefine Element to be the tuple (Key, Value), but I'm not sure how to do this in a protocol rather than a structure or class.
Simply adding typealias Element = (Key, Value) produces no errors, but also doesn't appear to actually do anything within the context of the protocol, for example, the following won't work:
extension MapType {
var firstKey:Key? { return self.generate().next()?.0 }
}
This produces an error, as the generator isn't recognised as returning a tuple (i.e- it has no member .0).
What is the best way to define Element as (Key, Value) in this case, such that I can use it within protocol extensions? Is this even possible?
We can't necessarily force that the Element type inherited from the CollectionType protocol is necessarily a tuple made up of the Key and Value types from the MapType.
However, we can limit our protocol extension to only add the firstKey method to those that do conform to the protocols in such a way using a where statement.
Consider this simplified example:
protocol Base {
typealias Element
func first() -> Element?
}
protocol Child: Base {
typealias Key
typealias Value
func last() -> (Key, Value)?
}
extension Child where Self.Element == (Self.Key, Self.Value) {
var firstKey:Key? { return self.first()?.0 }
}
struct ChildStruct: Child {
func first() -> (String, Int)? {
return ("Foo", 1)
}
func last() -> (String, Int)? {
return ("Bar", 2)
}
}
let c = ChildStruct()
let first = c.first()
let firstKey = c.firstKey
You're basically trying to create a where clause inside of a protocol. That's not possible in Swift today. You can not constrain associated types based on other associated types. Someday maybe, but not today.
You'll need to rethink how you're attacking the problem. The likely solution is to use a generic struct rather than a protocol (otherwise you tend to wind up with a lot of duplicated where clauses all over your code). You can see this recent dotSwift talk for more detailed examples.