It works perfectly fine if I create a new instance of AnyCacheable as -
let cacheable:AnyCacheable = AnyCacheable("Swift")
I don't understand why I don't have to explicitly specify the concrete type of 'T' like -
let cacheable:AnyCacheable = AnyCacheable<String>("Swift")
How does Swift infer the concrete type for 'T'? From the initializer -
init<U:Cacheable>(_ cacheable:U) where U.CacheType == T {
self._encode = cacheable.encode
self._decode = cacheable.decode
}
Swift can infer the type for 'U' from the initializer argument (in this case a String type). Since a 'where' statement implies a constraint how does Swift infer the type of 'T' to determine if it equals U.CacheType?
Thanks. So the compiler pretty much ignores the constraint checkU.CacheType == T because like you said 'T' cannot be anything else once U.CacheType has been inferred as 'String' / 'StringLiteralType' correct?
It doesn't really ignore it – that constraint is fundamental to how your AnyCacheable type works. It is playing a role in fixing what T is. It means, for example, that you couldn't write this:
// presumably you have some other form of caching in mind in other circumstances
var cacheables: [AnyCacheable<Int>] = []
cacheables.append(AnyCacheable("Swift"))
// error: Cannot convert value of type 'AnyCacheable<String>' to expected argument type 'AnyCacheable<Int>'
Oh, absolutely. I actually meant to ask if that constraint really got morphed into an assignment. I couldn't understand how the compiler could possible evaluate that expression to a Bool value if the type for 'T' isn't explicitly passed and also couldn't be implicitly inferred from anywhere else. The only block of code that provides any sort of context for 'T' is that 'where' clause - where U.CacheType == T that itself needs a prior deduction of 'T' to a concrete type before any attempt to evaluate that expression can be made.
Type inference is beautiful in most cases but actually takes a whole lot longer in some situations like this (At least for me) to understand which makes me totally hate it (until I figure out what actually is happening behind the curtains) :-)
What you're seeing here is the result of associated type inference. The type of String's CacheType is inferred based on the requirement for decode from Cacheable and the definition of decode from String.
By comparing the signatures the complier can determine that CacheType for String is String and then based on your constraint that U.CacheType == T, infer T has to be String in this instance since U == String (which is a result of your calling the initializer with a String).