0

I am using SQLite.swift library.

I have several Expressions,

let idColumn = Expression<Int>("id")
let nameColumn = Expression<String>("name")

I would like to have a dictionary host them, So, I did:

let columns: [String: Expression<AnyObject>] = [
   "id": idColumn,
   "name": nameColumn
]

But I get compiler error:

Cannot convert value of type 'Expression<Int>' to expected dictionary value type 'Expression<AnyObject>'

Why is this error? Why Int type can’t be AnyObject?

I also tried Any instead of AnyObject:

let columns: [String: Expression<Any>] = [
       "id" : idColumn,
       "name": nameColumn
    ]

Similar error shows:

Cannot convert value of type 'Expression<Int>' to expected dictionary value type 'Expression<Any>'

I don't understand this... Could someone please explain to me?

Hamish
  • 78,605
  • 19
  • 187
  • 280
Leem
  • 17,220
  • 36
  • 109
  • 159
  • `Expression` and `Expression` are different types and casting doesn't work on restricted generic types. Why don't you declare both `idColumn` and `nameColumn` as `Expression`? – Dávid Pásztor Nov 15 '17 at 15:09
  • I have no experience with SQLite.swift (or SQLite), but this looks like what their [`cast(_:)`](https://github.com/stephencelis/SQLite.swift/blob/master/Sources/SQLite/Typed/Expression.swift#L141) function is for. Generics [are invariant in the general case though](https://stackoverflow.com/q/41976844/2976878). – Hamish Nov 15 '17 at 15:48
  • 1
    Because generics in general are not covariant on their parameterized type. Inheritance / polymorphism does not apply. – matt Nov 15 '17 at 17:34

2 Answers2

3

Your expectations are wrong; what you expect is not how the Swift language works. I'll just repeat the example I give in my book:

A generic type specialized to a subtype is not polymorphic with respect to the same generic type specialized to a supertype. For example, suppose we have a simple generic struct along with a class and its subclass:

struct Wrapper<T> {
}
class Cat {
}
class CalicoCat : Cat {
}

Then you can’t assign a Wrapper specialized to CalicoCat where a Wrapper specialized to Cat is expected:

let w : Wrapper<Cat> = Wrapper<CalicoCat>() // compile error

Technically, we say that generics are not covariant on their parameterized type. There are exceptions to that rule — Optional is the obvious one — but they have to be baked into the language itself.

matt
  • 515,959
  • 87
  • 875
  • 1,141
0

Naked Int and String types can be converted to AnyObject. But this does not apply when they are wrapped in a generic type: Expression<Int> can not be converted to Expression<AnyObject>. That's how Swift works today (Swift 4):

// OK
let i: Int = 1
let i2 = i as AnyObject

// Error: cannot convert value of type 'S<Int>' to type 'S<AnyObject>' in coercion
struct S<T> { }
let s = S<Int>()
let s2 = s as S<AnyObject>

Besides, even if it would, the expressions you would extract from the dictionary would have lost their column type: you could not use them directly in SQLite.swift, as exemplified by your other question.

Gwendal Roué
  • 3,949
  • 15
  • 34