something that’s been on my wishlist for a while has been the ability to categorize the stored properties of a type separately from the computed properties. it is currently very difficult to sort through all of the instance properties from the generated documentation to understand what a type’s actual representation is.
i did some investigation into this idea today, and discovered that lib/SymbolGraphGen currently classifies all instance properties as stored properties (vp).
it is also not possible to deduce this from the presence of accessors ({ get }, { get set }, etc.) in the emitted symbol declaration, since those also appear for stored public private(set) properties.
what would it take for lib/SymbolGraphGen to emit this information?
Note that it is very much by design that Swift-the-language does not distinguish stored and computed properties by default. Changing representations is supposed to be a source-compatible change.
right, but when you have a type with a large population of instance properties, it becomes a problem when “important” properties get lost in a long list of computed properties. this is really just about the readability of the generated docs.
nothing about surfacing “storeness” in generated documentation is going to have any impact on source compatibility. if a stored property becomes a computed property (or vice versa), the only observable change in the documentation is the symbol would move to a different heading on the same page.
Hmmm, I think then it would be better to organize the properties into headings regarding which ones are "important" or not.
There could be important computed properties, and unimportant stored ones. I'm not sure if "is stored or not" would be a useful distinction from a documentation perspective. And that matches the OO philosophy on the matter, that storage is an implementation detail which shouldn't be exposed.
Do you have any examples that illustrate the issue?
right, i think this is an important difference between class-heavy styles of programming and struct-heavy styles.
here’s an small example:
from looking at the type’s source code, you can see it is just a glorified (UInt16, UInt16, UInt16) tuple.
but if you browse its documentation, it actually has five instance properties.
var description: String
var major: UInt16
var minor: UInt16
var patch: UInt16
var rawValue: Int64
but the description and the rawValue are not integral to the type, they are hooks for protocol conformances. as the type matures, it would likely gain many more such “conformance” or “convenience” properties, and the only reason it doesn’t have more already is because i am consciously trying to avoid this problem by limiting the amount of API this type exposes.
Somewhat related, I needed to know the actual layout of various structures earlier this evening - e.g. UnsafeMutableBufferPointer - and I ultimately had to dig up some gyb templates buried deep in the Swift stdlib sources (and then read very carefully to ensure I'd found all the stored properties, since their declarations can appear practically anywhere in the file). If you don't know to specifically go to GitHub and specifically search Apple's "Swift" repo and to fiddle with the language settings to get it to actually surface those template files, you'll never find them. Search engines work really hard to not show GitHub source code results.
If they'd been simply listed in the documentation, even if just in some appendix or footnote, I'd have been much happier.
Note that in this case the order is important, too. I need to know the full layout in order to map that to registers and the like.
Even frozen types don’t necessarily make all of their stored properties public, so having a “stored properties” section there could still seem like it’s providing more information than it really is.
What are exact guarantees about frozen types? Could this change happen or not?
// old version
@frozen public struct S {
var x: Int
}
// new version
@frozen public struct S {
private var y: Int
var x: Int {
get { y &- 1 }
set { y = newValue &+ 1 }
}
}
it is a basic tradeoff when going from class inheritance to protocol-oriented patterns that you lose the ability to reverse-map declarations to protocol conformances.
for example, a type might expose a description property that witnesses multiple protocol requirements, or might witness a conformance declared in a downstream module.
i came across a somewhat more motivating example that doesn’t have anything to do with protocol conformances in the form of this datetime formatter type i have:
var day: Int32
var DD:String
var hh: String
var hour: Int32
var minute: Int32
var mm: String
var MM:String
var month: Int32
var second: Int32
var ss: String
var year: Int32
var yyyymmdd: String
var yyyymmddThhmmssZ: String
Funny enough, I just ran into a case at work (in Ruby) where I had to optimize a class that represents a SemVer Version, which changed its layout.
I changed it from having 3 seperate fields for major/minor/patch, to a single bit-packed Integer. This allowed us to greatly speed up the < operator, which our app used a lot.
This is a perfect example of where decoupling internal storage from API is useful, regardless if you're doing traditional class-oriented OOP or not. If the docs put an emphasis on the stored properties, then the docs for major/minor/patch would have been de-emphasized, and the docs for this random packedInt would have been emphasized instead.
It would be better if major/minor/public fields were highlighted intentionally+explicitly as members of a section, rather than implicitly because they happened to be stored properties instead of computed.
ha, i had a similar issue with an IP address type, except in my case it was the ==(_:_:) operator.
the thing is i don’t think the “Topics” system is mature enough where we can lean on that as a curation mechanism. my main issue with “Topics” lists is that you need to know the symbol hashes to create them, and the symbol hashes are just dreadful to work with, particularly if you’re not using XCode.
probably stored-ness is not the best heuristic to use here. but i think we still do need one.
yeah but that’s fundamentally an IDE/editor issue and any improvements there would have to come from the VSCode plugin side. it’s not apparent to me what documentation tooling can do here.