The compiler is ensuring the behaviour is the same everywhere.
public func description<T>(of something: T) -> String {
return "I donāt know what it is."
}
public func description(of text: String) -> String {
return "It says, ā\(text)ā"
}
public func describe<T>(_ something: T) {
print(description(of: something)) // ā always the T variant.
}
public func demonstrate() {
// Selecting between overloads
print(description(of: 0)) // I donāt know what it is.
print(description(of: String)) // It says, āHello, world!āā
// Calling a generic function
describe(0) // I donāt know what it is.
describe("Hello, world!") // I donāt know what it is.
// ā Here the compiler does have access to the information
// it would have needed to select the other overload,
// but it chooses not to for consistency.
}
Now, in another module, we copy and past that last function identically (but Iāll fix the comments to describe what happens):
import BaseModule
public func demonstrate() {
// Selecting between overloads
print(description(of: 0)) // I donāt know what it is.
print(description(of: String)) // It says, āHello, world!āā
// Calling a generic function
describe(0) // I donāt know what it is.
describe("Hello, world!") // I donāt know what it is.
// ā Here the compiler has no way of knowing the internals
// of `describe(_:)`ās implementation, so it cannot inline it,
// and is therefore also incapable of shifting its implementation
// to call a different overload.
}
Notice that the result of the function is the same no matter which module you write it in. That would no longer be the case if the compiler were to attempt āspecializingā the function the way you describe. And it would be very confusing if the same code did different things in different places.
The compiler will never select an overload based on knowledge outside the current scope, because it knows that knowledge will not always be available.
If you want the implementation to change based on the dynamic type, then you will have to either use a class, a protocol conformance, or embed the dynamism in the function itself. In your case, you can do this:
struct Foo<A> {
var value: A? {
get {
getter()
return nil
}
set { setter() }
}
func getter() {
if A.self == String.self {
print("specific getter A")
} else {
print("general getter A")
}
}
func setter() {
if A.self == String.self {
print("specific setter A")
} else {
print("general setter A")
}
}
}
With dynamic dispatch, the compiler doesnāt have to worry about consistency, because it knows that regardless of what information is or isnāt available to it at compile time, everything will be known when it comes time to actually make the decision while the program is running. The compiler is still capable of inlining when it has access to the implementation, and then optimizing away the unreachable half of the if, but this way it ensures that the same thing still happens even where it canāt do any inlining.