Any? incorrect semantics?

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.

1 Like

Why cant this code au + String(bu) be performed at runtime as well?

Operators are not dispatched dynamically.

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.

2 Likes

Hmm, interesting.:thinking: 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?

I thought it was a kind of a mix.

Can you give example to this argument?

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.

1 Like

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.

1 Like

I belive initializers can be pointed as example. Because in some cases in order to parametrize a type it has to lookup its metadata at runtime.

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

Just nitpicking, but you can combine let into a single statement.

let a: Any = 5, b: Any = 4

:)

1 Like

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 :)

2 Likes

Got it.