Compiler cannot infer the type of a generic method: cannot specialize a non-generic definition

Defining a generic method with a return type as the placeholder type results in an error. Here is an example:

protocol Database {
    func loadObjects<T>(matching: String) -> [T]
}

Using the Database protocol works with a type inference only.

Example:

class MyClass {
    let database: Database
    
    init(database: Database) {
        self.database = database
    }
    
    func fetch() {
        let value: [Int] = database.loadObjects(matching: "query db") // 1 - Type inference -- compiles
        let anotherValue: [Int] = database.loadObjects<Int>(matching: "query db") // 2 - Type inference with placeholder -- compile-time error
        let lastValue = database.loadObjects<Int>(matching: "query db") // 3- Placeholder -- compile-time error
    }
}

Why does it work only with type inference? Shouldn't it work with the second and third case?

Cases 2 and 3 are syntax errors ("Cannot explicitly specialize a generic function") because you can't legally put <Int> there.

In case 2, it's redundant, since the inference proceeds from the explicit type on the LHS.

For case 3, you can force the inference like this:

        let lastValue = database.loadObjects(matching: "query db") as [Int]

Alternatively (since inference on return values seems to be out of favor), you can pass the type into the generic method:

func loadObjects<T>(ofType: T.Type, matching: String) -> [T]
…
        let lastValue = database.loadObjects(ofType: Int.self, matching: "query db") 
1 Like

Agree!

I can't understand why I need to cast it to the return type, while the following makes it sufficient for the compiler to infer the return type. But as you mentioned, it's illegal to put there, which is I also can't understand why I can't.

database.loadObjects<Int>(matching: "query db")

FWIW, I don't think it's unreasonable to suggest an Evolution proposal for the explicit syntax. It's not a syntax error, it's a semantic error. At parse time, there's no difference between

let x = foo<Int>()

and

let arr = Array<Int>()
1 Like

Generic manifesto specifically mentions this as a possible future enhancement. It is not unreasonable to ask for this. The problem is: Given the available resources, it is unlikely to ever happen. Unless, of course you (or someone else) cares enough about it to implement it and create an evolution proposal for its inclusion in the language.

I'd argue this change isn't particularly difficult to implement -- it would likely mean adding an extra few constraints per function call and disabling the diagnostic that currently bans the behavior. I'd be willing to take a crack at it and potentially put up a proposal.

The current preferred way to force a specific type parameter is to make a generic and take a parameter of the generic's metatype.

func unsafeBitCast<Source, Dest>(_ value: Source, to type: Dest.Type) -> Dest
unsafeBitCast(5.0, to: Int.self)

I agree that it is reasonable to write:

let x = foo<Int>()

instead of:

let x = foo() as Int
// or
let x: Int = foo()
// or
let x = foo(Int.self)

But I am personally satisfied with the current options and am neutral about adding explicit function specialization.

The only thing that I don't like about the current syntax, is that we always have to use .self suffix to refer to a type as value. I think compiler should be able to infer when we intend to use type as value without .self suffix, at least in most cases. So I would be happier if we could omit .self:

let x = foo(Int)

That is the subject of the deferred proposal SE-0090.

I remember that. I would love to revive the discussion but I don't have time to pursue it myself.

I think the solution to the issues raised in the rationale for differing the proposal is not very complicated. We can continue to support .self and not abolish it altogether, just let us omit it when there is no ambiguity of the kind that was mentioned there.