Generic dictionary extension error - ambiguous reference to subscript - swift

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.

Related

How can make a custom extension for Dictionary in Swift?

I am trying to add a function to Dictionary, this func called add, which has 2 input values (key and value). I tried to make the func be generic, that means I want key and value be able take any Type. I end it up to this down non-working code. What I should do next to make this func work?
extension Dictionary {
func add<Key, Value>(key: Key, value: Value) {
self[Key] = Value
}
}
First, you are trying to assign a type instead of an instance to the dictionary.
Type of expression is ambiguous without more context):
Second, you need to declare your method as mutating.
Cannot assign through subscript: subscript is get-only).
Third, you are creating two new generic types that have no relation with the Dictionary generic Key and Value types.
Cannot convert value of type 'Key' (generic parameter of instance method 'add(key:value:)') to expected argument type 'Key' (generic parameter of generic struct 'Dictionary').
extension Dictionary {
mutating func add(key: Key, value: Value) {
self[key] = value
}
}
var dict: [String: Int] = [:]
dict.add(key: "One", value: 1)
dict // ["One": 1]

Swift: mutate cast parameter (Error: Cannot use mutating member on immutable value of type 'MyObjectType' (aka 'Dictionary<Int, Int>')

I have a functions where I passed in Dictionary [Int : Int] as inout T and would need to write into the Dictionary. This function is included as a function of a protocol.
However, after casting T into MyObjectType (Dictionary), it becomes immutable.
Question: How can I make a generic parameter (it can be passed in as struct, class, array or dictionary) mutable after casting?
typealias MyObjectType = [Int : Int] // Key is a enum with Int as RawValue
static func myGenericFunction<T>(_ object: inout T) {
(object as! MyObjectType).updateValue(0, forKey: 1)
}
I have tried casting it to NSMutableDictionary as below which compile successfully but crash when running with simulator.
(object as! NSMutableDictionary)[Int(pMetric.selectedUnit)] = Int(pMetric.metricType)
Thanks for any good solution!

Using KeyPaths on associatedtype objects

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.

Why are where clauses only valid on functions with generic parameters?

It seems absurd that this method signature does not compile in Swift 4:
class Bar<ValueType> {
func version() throws -> String where ValueType == [String: Any] { ... }
}
(Error: where clause cannot be attached to a non-generic declaration)
but this compiles fine:
class Bar<ValueType> {
func version<T>(_ foo: T? = nil) throws -> String where ValueType == [String: Any] { ... }
}
Anyone have insight as to why this is the case?
Because ValueType has nothing to do with this method (in the first example). It would be wrong to put such a method in a type (class/struct/enum), since it's not really a true member of that type. It's conditionally a member of that type, depending on the truth value of the where clause.
To achieve this, you would want to put this method in an extension of your type, with the where clause you want. E.g.
extension YourType where ValueType == [String: Any] {
func version() throws -> String { ... }
}
That has been finally allowed in Swift 5.3. See Contextual Where Clauses in the official Language Guide.
Citing from there:
You can write a generic where clause as part of a declaration that doesn’t have its own generic type constraints, when you’re already working in the context of generic types. For example, you can write a generic where clause on a subscript of a generic type or on a method in an extension to a generic type.
...
extension Container {
func average() -> Double where Item == Int {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
That compiles just fine in Xcode 12 beta 4, but won't work in Xcode 11.6 that is shipped with Swift 5.2.4.

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.