Variables in externsion

Forgive me if its a noob question but I was very curious
What is the exact technical reason for which extensions cannot have variables
How come methods become a part of the class but not variables
What give's Swift/Objective C that advantage that other languages don't have (Eg Java)

If I were to extend Int, where would you store those variables?

2 Likes

I am quite sure that the question covers that aspect too.

I am quite sure that the question covers that aspect too.

That was meant rhetorically. So let’s expand on it. Supposing a reasonably modern 64-bit platform, the ABI of Int is a single 64-bit word of storage. All existing code making use of that ABI assumes word-sized loads and word sized stores. If an extension were to add storage for, say, an extra Bool field, where among the bitpatterns for an Int could we possibly attach this bit? We certainly cannot stick it among the 64 bits in use by Int’s ABI since all 64 are needed for the full space of representable integer bitpatterns. We also cannot simply add more storage to accommodate the new bit, or we would break the layout expected by every existing client of Int that was built against the word-sized ABI.

Okay, but maybe that’s not convincing enough. After all, we could totally alter the ABI of Int to add our new bit, but say only change the ABI in the module we’re building. Now we export a public API:

extension Int {
  public var isSpecial: Bool = true
}

Alright, so now every client of our module needs to know about our special Int too to use it properly, so let’s use our compiler to propagate the new ABI up and out to dependent modules that import us. Let’s repeat the extension process in another module for good measure

extension Int {
  public var isFancy: Bool = true
}

Again, we’ll ignore the ABI propagation problem here. Suppose both extensions were imported together. Now we have to have a stable way to sort extensions adding storage so clients see the proper ABI at each level. It’s not infeasible, but it’s certainly difficult to juggle all of these disparate ABIs dependent entirely upon the set of modules imported into any one Swift file. We’d also need to make sure that we emit thunks between ABIs so code in our module can, say, call standard library APIs that assume the original 64-bit ABI. Imagine that, the act of importing a module automatically incurs a code size and runtime performance impact!

This system is... a mess. Its consequences would be disastrous both from an implementation perspective and from a user perspective. It’s not impossible to imagine a world where we allowed this through some kind of side channel akin to associated objects, but every choice has consequences. And associated objects, for example, come with a steep runtime cost.

But why can you add functions then? Well, they’re not subject to the same ABI-altering concerns from before. A new member function in isolation adds a symbol (modulo thunks) to the module it is defined in, but that symbol is not a part of the ABI of the original type - its presence or absence does not change the fact that an Int is 64 bits.

8 Likes

Perhaps a higher-level summary might be a useful starting point.

The Swift compiler builds knowledge of the size and layout of types (structs, enums and classes) into the code it generates from your source.

That means you cannot change the size and layout of a type without recompiling everything in your app that uses the changed type.

Recompiling everything isn't really a viable choice, because it would prevent us from having libraries (of code that's already compiled).

2 Likes

Functions are different because they're not part of the size and layout of types.

When you add a function in an extension, the Swift compiler can arrange for new source code to call the new function — without invalidating the old source code that doesn't know about the new function.

2 Likes

Thanks, @QuinceyMorris @codafi that answers my query

Given that extensions are used for multiple purposes, especially as a code organization feature with commonly several extensions in the same source file, the issues raised here don't seem to be an issue in that case. I won't call it a major problem but I do sometimes want to add a property to a type in an extension in the same file that all the rest of the code for that type exists. The restrictions mentioned don't seem relevant in that case.

2 Likes