Allow stored properties in extensions

I don't think this is a good idea, for a few reasons:

Primarily, I feel like it trades a win for a loss in a way that is at best a wash, and probably a net loss. While it is nice to declare variables locally to their use, it's also nice to go to a type and answer, in one place, "what is this thing". That can be done if you declare all the storage in one place.

My preference for this comes from working on the standard library, back in the days when significant refactors were frequent. I would often find myself wanting to go to a type and ask "what is this thing made up of". But I couldn't: the type's storage declarations were scattered throughout the file in exactly the way proposed here – Stored properties were declared just above where they were first used by some methods. Note some methods, because often that storage was then used much later down in the file too, after some other unrelated stuff. This worked because of another stylistic approach at the time, which was to declare (nearly) all the protocols a type conformed to at the top, instead of grouping them later with extensions.

I found this so confusing that I went through and reorganized most types in the standard library to do two things:

  1. Declare the minimum possible things (really just stored properties and some type aliases and the most fundamental of inits) and then close the definition.
  2. Everything else, either protocol conformances, inner type definitions, or just other methods, were grouped together into short extensions.

Having done this, the standard library code became far easier to work on, at least for me.

Now, I realize this proposal is in part motivated by #2 here. If you break conformances up into extensions, and just declare storage with the extension that needs it, then this makes those extensions more coherent.

But I think it would be a big loss, at least for the standard library code, to not be able to go to the struct definition and see "what is this type made out of – what is the essense of this thing". This is a really important question to answer, especially for low-level code. Is Array just a pointer to a buffer? I can tell you that easily because the declaration of Array is nice and minimal.[1] Same goes for Slice, or String or String.Index.

I admit this is a stylistic preference, but I'd really push to keep any codebase written in this style. I don't think we want to offer more choice in this area.

I might be biased by the fact that I don't tend to work on much business logic code, where perhaps storage required by conformances is more common than in the std lib. But I would also fear that on a sprawling codebase, making storage declaration easier would lead to bloated types where you don't even realize how big the types are getting. And it's also the case that often, storage might be primarily associated with some protocol conformance, but also touched in other places. And when that happens, you're back to the question "what is this type and what does it contain" and I wouldn't want that smeared all over the file.

Secondly (and probably much less importantly) I think this will cause confusion when people try to do this outside the file. A frequently requested feature is to be able to add storage to protocols, or to add storage in extensions even outside the type's module. This is often coming from folks used to a language where everything is a pointer, and they're looking for for sugar for some kind of global lookup table based on the class reference (asking "how would adding storage to Int work?" usually helps). By allowing the syntax in a specific circumstance, you're likely going to upset people hoping for this feature even more.


  1. OK not as nice and minimal as you might wish if you work on it... but it used to be so much worse. ↩ī¸Ž

39 Likes