I was playing around with some code in swift and encountered one interesting case.
Lets start with a little preamble: suppose you create some optional variables:
let a: String? = "abcd"; let b: Int? = 4
print(
"Type of \(a) is \(type(of: a))" //Type of Optional("abcd") is Optional<String>
"Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Int>
separator: "\n"
)
Then you force unwrap so types of au and bu are not optional.
let au = a!; let bu = b!
print(
"Type of \(au) is \(type(of: au))", //Type of abcd is String
"Type of \(bu) is \(type(of: bu))", //Type of 4 is Int
au + String(bu), //abcd4
separator: "\n"
)
Seem reasonable, but things start to go weird, when you try to apply same code to Optional<Any>:
let a: Any? = "abcd"; let b: Any? = 4
let au = a!; let bu = b!
print(
"Type of \(a) is \(type(of: a))", //Type of Optional("abcd") is Optional<Any>
"Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Any>
"Type of \(au) is \(type(of: au))", //Type of abcd is String
"Type of \(bu) is \(type(of: bu))", //Type of 4 is Int
//au + String(bu),
separator: "\n"
)
But now if you try to to do same concatenation au + String(bu), swift will produce compilation error, even though these two variables are known to be of some concrete type, as reported by swift itself.
The error is:
error: protocol type 'Any' cannot conform to 'LosslessStringConvertible' because only concrete types can conform to protocols
This certainly looks like a bug, isn't it. Please, share your opinion.
It seems not a bug. type(of:) returns the dynamic type in runtime, but the left and right operands of + operator must be resolved at compile time. The compiler cannot find a function that takes Any and String as parameters, so it just won't compile.
If you write (au as! String) + String(bu), it will compile.
Because Swift is designed as an ahead-of-time compilation language. I assume you are from a language like Python or something else with a JIT, but this is a fundamental difference you need to be accustomed to.
Hmm, interesting. So some other functions, can do runtime execution, or cant they? Was it intentional for some reason, or is it a limitation as of now?
Class methods can be overridden, and dispatched dynamically (this is polymorphism), and I think that protocol witnesses can be dynamically dispatched as well, though I'm quite fuzzy on when.
I assume you are talking about dynamic dispatch behavior in Swift, which seems a different issue from the problem the author is encountering here. The author is arguing about why Swift method cannot be resolved at runtime totally while ignoring compile time type mismatch.
You may be correct. Even when dynamic dispatch is done, the compiler will still enforce, at compile time, that some method signature matches. This is a basic feature of statically-typed languages.
You are talking about dynamic dispatch bahavior here (a AOT language can still do runtime things). But in your original post the problem is about compile time type mismatch.
Yeap, type(of:) got me confused. I initialy thought that force unwrapping Optional<Any> produces concrete type.
I also was thinking that when, swift cannot guarantee a compile-time validation, it instead checks constraints in runtime. For example:
let a: Any = 5; let b: Any = 4
func add <T, U>(_ a: T, _ b: U) where T: BinaryInteger, U: BinaryInteger {
print(
Int(a) + Int(b)
)
}
add(a, b) //9
This would cause crash or worse if the constraints are not satisfied at runtime. The designers of swift enforce compile time type matching to ensure we write safe code :)