Make it Official: the "Undocumented/Private API" signifier

Problem

There is an undocumented convention in Swift, of appending an underscore "_" to a public symbol or attribute in order to communicate some special meaning.

However, you will not find any mention of this practice within Swift's own documentation on swift.org, nor any explanation of the often subtle differences in meaning of "_". (Please correct me if I'm wrong. I did several searches and came up with nothing, and several read-throughs.)

Examples:

  • _enclosingInstance (undocumented subscript method for property wrappers)
  • _FormatSpecifiable (protocol in SwiftUI)
  • @_exported
  • @_functionBuilder
  • @_implementationOnly
  • @_transparent
  • @_silgen_name ("foo")

In each case, "_" means something slightly different, e.g. (my interpretations, correct me if I'm wrong):

  • "Use of this symbol or attribute is unsupported by Apple."
  • "This is part of a feature that has not gone fully through the Swift evolution process yet, and thus, no guarantees are made about how much it might change in the next release of Swift."
  • "This feature is used internally for optimizability and resilience features of the Swift language, and is not intended for public use (unless you really know what you're doing)."

Proposed solution

We should canonicalize different ways to communicate these meanings in a clear way, rather than using the same symbol for each of these meanings.

Here are some examples of what could be more clear:

// Library author strongly suggests that you avoid subclassing or implementing. 
🚫FormatSpecifiable

// API is still under construction, subject to change.
@🚧functionBuilder

// Special capability for internal Swift toolchain development.
@🛠️silgen_name

This kind of convention would make it easy for people to add lint rules about which kinds of APIs and language features they're comfortable with allowing in their codebase. If I want to ban the use of undocumented APIs in Apple & third-party code, but I'm fine with using undocumented Swift features, I could ban :no_entry_sign:-prepended symbols but allow :construction:- and :hammer_and_wrench:-prepended ones in my lint rules.

Use of clear symbols would also make it obvious in a code-review when someone is using an undocumented feature.

The only substantial objection I could see to this would be that emojis can be a pain to type during code entry. However, this objection would be easy to solve via one or more of the following features added to your IDE:

  • IDE performs a smart-replace between a typed underscore character, and the appropriate emoji, based on autocomplete.
  • If you type _ after a space or on a new line, a contextual menu could appear that lets you select the appropriate symbol to prepend. (Considering that there would only need to be a handful of these, I don't see this as burdensome).
  • Use text macros like this forum has: :construction: becomes :construction:

We could also add a new compiler flag to throw a warning or error if :no_entry_sign: appears on any conformance declarations in your code, perhaps with the ability to set exceptions (to allow use of unsupported APIs from a specific library that's under development, while blocking the use of these from all other libraries).

Note:

If people are against the emoji idea, we should at least add proper documentation around the use of underscore so it's clear what it's supposed to mean in different contexts.

8 Likes

I think I'm generally in favor this, even though (or perhaps because) there's a high likelihood of things getting "a bit interesting" in the area of making the emoji easy to type.

I agree adding proper documentation!

Personally, I feel using emoji is a very interesting solution. But I don't agree using emoji is a proper way at least in standard library. With some themes of editor, emoji are hard to read due to their color. IMO restricting theme is what standard library shouldn't do.

(example) For me it's hard to read.

5 Likes

It always means the same thing, namely that, “This is secretly exposed so that a particular other thing has special access through some barrier. If you are not that particular other thing, do not touch it.”

It is like the key hidden under the flowerpot on the porch. Unless you are the one who put it there (or have permission from that person) do not use it or you could end up in all sorts of trouble.

9 Likes

The underscore means "this is private." It comes from Obj-C where nothing is really private. On method names in Obj-C Apple uses this convention to prevent subclasses from adding methods that unintentionally have the same names as private methods in the Base class. I think they may also use double underscores for some kinds of type names.

I avoid using _ as part of names in my swift code mostly.

I'm in favor of improving the documentation. Adding emojis as a naming convention, not so much. :-1:

2 Likes

There's been a few things in the standard library with underscores due to being still in development like @_functionBuilder, which is now becoming official as @resultBuilder. I

IMHO this is a different meaning than "do not touch"—it's just to say "touch at your own risk", which is IMHO a different meaning.

The other example I gave is the _enclosingInstance subscript on property wrappers, which was kind of a footnote of the original SE proposal that wound up being somewhat the "runt of the litter" of property wrapper features, but implemented anyway (presumably due to being needed by Apple for something). You can totally use this in your own features but just be aware it might get "upgraded" at some point to a "proper" version that could require a source change.

Good callout.

Seems like we would need to have a font that puts a boundary around emojis. I feel like fonts should probably give a border of the font-color to emojis, so that they will stand out against any background that the font stands out against. The problem you are illustrating seems like it's endemic to current Swift, since it already allows emojis in symbols.

Note also, apparently Apple already uses emojis in certain public interfaces to mark types that shouldn't be used by users, e.g.:

@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
public struct Label<Title, Icon> : SwiftUI.View where Title : SwiftUI.View, Icon : SwiftUI.View {
  public init(@SwiftUI.ViewBuilder title: () -> Title, @SwiftUI.ViewBuilder icon: () -> Icon)
  public var body: some SwiftUI.View {
    get
  }
  public typealias Body = @_opaqueReturnTypeOf("$s7SwiftUI5LabelV4bodyQrvp", 0) 🦸<Title, Icon>
}

See the :woman_superhero: :woman_superhero: emoji? Haha yeah there she is.

2 Likes

It is no longer used: [NFC] Change magic emoji to __ by davezarzycki · Pull Request #35734 · apple/swift · GitHub

4 Likes

Improving documentation is a definite +1. Using emojis however gives me chills as I imagine a Hungarian notation future where some symbol names start with three or four+ emojis.

2 Likes

For user-facing documentation, I don't think there's much to say here beyond "this is private", to borrow @phoneyDev's phrasing. There might be more to say for people interested in compiler and standard library development.

If you see an underscored API or language feature, one of the main things being signified is that you should not expect to find documentation about it, since the API or feature isn't developed enough yet.

1 Like

It is still “do not touch”. Apple needed a back door past the stable API, so it made an underscored attribute. Apple knows how to use it safely, because it is the one who has been implementing it and is aware of any remaining holes in the implementation. It was not intended for anyone else to use until it was complete and exposed as @resultBuilder.

Any underscored identifier that is still evident in your application when you submit it will be rejected if the App Store notices (aside from underscored identifiers of your own). Some attributes like @_functionBuilder or @_exported leave no traces after compilation is complete, enabling them to evade detection, but they are still not intended for public use.

The purpose of marking something with an underscore is to withhold it from the published API. (Xcode’s autocomplete and many documentation generators hide their existence.) Publishing why you are withholding an identifier effectively adds the identifier to the API by making a contract about its stability and intended use. That defeats the purpose of withholding it.

7 Likes

While I love how expressive the suggested emojis are, I have to say we added a couple of emojis in type and variable names a while back, and I regret it every time I have to type one of them. Perhaps not an issue for these symbols since they wouldn't be typed very often.

Far Be It From Me To Discourage The Use Of Emojis In Swift Code but actually yeah I would be very concerned about the potential accessibility impact of this change. Any Swift code which is part of the standard library / major packages ought to play well with screenreaders etc. not to mention a wide variety of input methods, editors, internationalization, so forth. Plus, emoji can be non‐obvious in composition (some are single codepoints, some are followed by a variation selector, some are zero‐width joiner sequences) all of which makes them confusing (and potentially fragile over decades; emoji are a young technology yet) in any part of a public interface (even if an undocumented/experimental one).

In one respect I think that this is a Style Guide issue and not something which necessarily needs to be “standardized” in a real sense at all (except that the standard library should have a convention which should be documented). Furthermore, since these are not things people ought to be typing often, I’m not sure what the argument is against just making them very verbose (_experimental_, _discouraged_, _pleaseDoNotUse_, etc.); I think brevity is unnecessary in this case.

If something more than that is needed, I feel like it should be more formal than a mere naming convention: for example a @tag("experimental") attribute which does not impact compilation but adds a tag readable via Quick Look, linters, etc. At this point I wonder if we are just reinventing documentation comments? But it could perhaps be useful.

3 Likes
Terms of Service

Privacy Policy

Cookie Policy