I'm trying to model the relatively easy idea of returning an instance of a type that conforms to a given protocol.
Here's the thing: Let's say there is a protocol Theme and I model two types of themes: Theme1: Theme and Theme2: Theme.
Now, I have an instance of instance of type either Theme1 or Theme2 that is a value of current_theme.
let current_theme = Theme1()
I want to build a generic function that can return current_theme variable, something like this:
func currentTheme<T: Theme>() -> T {
return current_theme
}
this is just a draft because it doesn't seem to work as I expect. Either I have to downcast to T, or T cannot be interred.
For a full example, please check this gist:
the most surprising for me is this form (it actually works):
func currentTheme<T>() -> T {
return current_theme as! T
}
My question is whether it is possible to build a generic function with constraints (without downcasting) where concrete type can be inferred from the returning value?
Disclaimer: I may have no idea what I'm talking about.
I think the problem isn't the type inference. It's the fact that current_theme can be one of several concrete types at runtime, and the compiler can't tell which one it actually will be at any point in time. Thus, you have to cast to the concrete type at runtime (using as! or as?), as you have done. Note that the compiler is able to determine what T you intend when you call currentTheme(), if there's enough context at the call site.
The problem is that your function is not really generic.
A generic type is actually a declaration of a family of related types: specifically, one type for each possible type satisfying the generic constraints. In your example, this declaration:
func currentTheme<T: Theme>() -> T {
return current_theme
}
can be thought of as declaring many functions, one for each T that conforms to Theme. The compiler will select the appropriate specific type based on the call site. For example, I (as the caller) am allowed to write this:
let x: Theme1 = currentTheme()
However, the implementation does not match that. That's because for all but one of the infinite possible conforming types this function is invalid and will not compile. If it could compile my runtime example would be gibberish and it shouldn't compile, but the rules of generics allow me to write exactly that code.
What you're trying to write, I think, is a function where the return type will definitely conform to Theme, but you don't want to write down what specific concrete implementation it will be at the function definition site. This is presumably because you have some non-local knowledge about what the concrete type will be, likely in a different file covered by some compile-time guards.
In this case, you should simply declare a typealias and use that as the return type. You can then keep the typealias declared near the underlying variable you're returning.
Another possibility is that you are changing the current theme at runtime. In this case, you need to return the Theme existential from this function.
since Kind is an arbitrary type, this function can't return any possible Theme, especially Theme that has Kind = Int. I kinda failed to make it generic enough, so it could anything that conforms to Theme.
tried that. Notice, that it can't return 'ThemeI' still. AnyTheme could store theme as Any, then it's not very easy to use. What I'd need is id<Theme> from ObjC "basically"
What you need is generalised existentials, really, because you're asking to work with a protocol irregardless of its associated types. That's just not possible in Swift today.
I think the question has to be: what are you actually trying to do? The Theme protocol would be useless, as it has only one property, defined by an associated type that you do not know at compile time. I assume your actual use-case is a bit more complex than this. So if we want to fix it, I think we'll need to go into the specifics.