diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0001.md b/api-guidelines/Sources/APIGuidelines.docc/API-0001.md new file mode 100644 index 0000000..0c7a60c --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0001.md @@ -0,0 +1,115 @@ +# (API-0001) Write a documentation comment for every declaration + +Insights gained by writing documentation can have a profound impact on your design, so don't put it off. + +## Overview + +* **Use Swift's [dialect of Markdown](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/).** + +* **Begin with a summary** that describes the entity being declared. + Often, an API can be completely understood from its declaration and + its summary. + + ```swift + /// **Returns a "view" of `self` containing the same elements in** + /// **reverse order.** + func reversed() -> ReverseCollection + ``` + + * **Focus on the summary**; it's the most important part. Many + excellent documentation comments consist of nothing more than a + great summary. + + * **Use a single sentence fragment** if possible, ending with a + period. Do not use a complete sentence. + + * **Describe what a function or method *does* and what it + *returns***, omitting null effects and `Void` returns: + + ```swift + /// **Inserts** `newHead` at the beginning of `self`. + mutating func prepend(_ newHead: Int) + + /// **Returns** a `List` containing `head` followed by the elements + /// of `self`. + func prepending(_ head: Element) -> List + + /// **Removes and returns** the first element of `self` if non-empty; + /// returns `nil` otherwise. + mutating func popFirst() -> Element? + ``` + + Note: in rare cases like `popFirst` above, the summary is formed + of multiple sentence fragments separated by semicolons. + + * **Describe what a subscript *accesses***: + + ```swift + /// **Accesses** the `index`th element. + subscript(index: Int) -> Element { get set } + ``` + + * **Describe what an initializer *creates***: + + ```swift + /// **Creates** an instance containing `n` repetitions of `x`. + init(count n: Int, repeatedElement x: Element) + ``` + + * For all other declarations, **describe what the declared entity *is***. + + ```swift + /// **A collection that** supports equally efficient insertion/removal + /// at any position. + struct List { + + /// **The element at the beginning** of `self`, or `nil` if self is + /// empty. + var first: Element? + ... + ``` + +* **Optionally, continue** with one or more paragraphs and bullet + items. Paragraphs are separated by blank lines and use complete + sentences. + + The following example shows the summary, additional discussion, + parameters section, and symbol commands: + + ```swift + /// Writes the textual representation of each // ← Summary + /// element of `items` to the standard output. + /// // ← Blank line + /// The textual representation for each item `x` // ← Additional discussion + /// is generated by the expression `String(x)`. + /// + /// - **Parameter separator**: text to be printed // ⎫ + /// between items. // ⎟ + /// - **Parameter terminator**: text to be printed // ⎬ Parameters section + /// at the end. // ⎟ + /// // ⎭ + /// - **Note**: To print without a trailing // ⎫ + /// newline, pass `terminator: ""` // ⎟ + /// // ⎬ Symbol commands + /// - **SeeAlso**: `CustomDebugStringConvertible`, // ⎟ + /// `CustomStringConvertible`, `debugPrint`. // ⎭ + public func print( + _ items: Any..., separator: String = " ", terminator: String = "\n") + ``` + + * **Use recognized + [symbol documentation markup](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW1) + elements** to add information beyond the summary, whenever + appropriate. + + * **Know and use recognized bullet items with + [symbol command syntax](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW13).** Popular development + tools such as Xcode give special treatment to bullet items that + start with the following keywords: + + | [Attention](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Attention.html) | [Author](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Author.html) | [Authors](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Authors.html) | [Bug](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Bug.html) | + | [Complexity](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Complexity.html) | [Copyright](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Copyright.html) | [Date](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Date.html) | [Experiment](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Experiment.html) | + | [Important](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Important.html) | [Invariant](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Invariant.html) | [Note](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Note.html) | [Parameter](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Parameter.html) | + | [Parameters](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Parameters.html) | [Postcondition](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Postcondition.html) | [Precondition](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Precondition.html) | [Remark](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Remark.html) | + | [Requires](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Requires.html) | [Returns](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Returns.html) | [SeeAlso](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/SeeAlso.html) | [Since](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Since.html) | + | [Throws](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Throws.html) | [ToDo](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Todo.html) | [Version](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Version.html) | [Warning](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/Warning.html) | diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0002.md b/api-guidelines/Sources/APIGuidelines.docc/API-0002.md new file mode 100644 index 0000000..bdf1846 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0002.md @@ -0,0 +1,24 @@ +# (API-0002) Include words to avoid ambiguity + +**Include all the words needed to avoid ambiguity** for a person reading code where the name is used. + +## Overview + +✅ For example, consider a method that removes the element at a +given position within a collection. + +```swift +extension List { + public mutating func remove(at position: Index) -> Element +} +employees.remove(at: x) +``` + +⛔ If we were to omit the word `at` from the method signature, it could +imply to the reader that the method searches for and removes an +element equal to `x`, rather than using `x` to indicate the +position of the element to remove. + +```swift +employees.remove(x) // unclear: are we removing x? +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0003.md b/api-guidelines/Sources/APIGuidelines.docc/API-0003.md new file mode 100644 index 0000000..0454237 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0003.md @@ -0,0 +1,25 @@ +# (API-0003) Omit needless words + +Every word in a name should convey salient information at the use site. + +## Overview + +More words may be needed to clarify intent or disambiguate meaning, but those that are redundant with information the reader already possesses should be omitted. In particular, omit words that *merely repeat* type information. + +⛔ In this case, the word `Element` adds nothing salient at the call site: + +```swift +public mutating func removeElement(_ member: Element) -> Element? + +allViews.removeElement(cancelButton) +``` + +✅ This API would be better: + +```swift +public mutating func remove(_ member: Element) -> Element? + +allViews.remove(cancelButton) // clearer +``` + +Occasionally, repeating type information is necessary to avoid ambiguity, but in general it is better to use a word that describes a parameter's *role* rather than its type. See for details. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0004.md b/api-guidelines/Sources/APIGuidelines.docc/API-0004.md new file mode 100644 index 0000000..474ebaa --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0004.md @@ -0,0 +1,40 @@ +# (API-0004) Name according to roles + +**Name variables, parameters, and associated types according to their roles,** rather than their type constraints. + +## Overview + +⛔ Repurposing a type name fails to optimize clarity and expressivity: + +```swift +var **string** = "Hello" +protocol ViewController { + associatedtype **View**Type : View +} +class ProductionLine { + func restock(from **widgetFactory**: WidgetFactory) +} +``` + +✅ Instead, strive to choose a name that expresses the entity's *role*: + +```swift +var **greeting** = "Hello" +protocol ViewController { + associatedtype **ContentView** : View +} +class ProductionLine { + func restock(from **supplier**: WidgetFactory) +} +``` + +If an associated type is so tightly bound to its protocol constraint +that the protocol name *is* the role, avoid collision by appending +`Protocol` to the protocol name: + +```swift +protocol Sequence { + associatedtype Iterator : Iterator**Protocol** +} +protocol Iterator**Protocol** { ... } +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0005.md b/api-guidelines/Sources/APIGuidelines.docc/API-0005.md new file mode 100644 index 0000000..c78cfea --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0005.md @@ -0,0 +1,26 @@ +# (API-0005) Compensate for weak type information + +**Compensate for weak type information** to clarify a parameter's role. + +## Overview + +Especially when a parameter type is `NSObject`, `Any`, `AnyObject`, +or a fundamental type such as `Int` or `String`, type information and +context at the point of use may not fully convey intent. In this +example, the declaration may be clear, but the use site is vague. + +⛔ + +```swift +func add(_ observer: NSObject, for keyPath: String) + +grid.add(self, for: graphics) // vague +``` + +✅ To restore clarity, **precede each weakly typed parameter with a +noun describing its role**: + +```swift +func add**Observer**(_ observer: NSObject, for**KeyPath** path: String) +grid.addObserver(self, forKeyPath: graphics) // clear +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0006.md b/api-guidelines/Sources/APIGuidelines.docc/API-0006.md new file mode 100644 index 0000000..c7a82a5 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0006.md @@ -0,0 +1,30 @@ +# (API-0006) Prefer grammatical English phrases + +**Prefer method and function names that make use sites form grammatical English phrases.** + +## Overview + +✅ + +```swift +x.insert(y, at: z) // "x, insert y at z" +x.subviews(havingColor: y) // "x's subviews having color y" +x.capitalizingNouns() // "x, capitalizing nouns" +``` + +⛔ + +```swift +x.insert(y, position: z) +x.subviews(color: y) +x.nounCapitalize() +``` + +It is acceptable for fluency to degrade after the first argument or +two when those arguments are not central to the call's meaning: + +```swift +AudioUnit.instantiate( + with: description, + **options: [.inProcess], completionHandler: stopProgressBar**) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0007.md b/api-guidelines/Sources/APIGuidelines.docc/API-0007.md new file mode 100644 index 0000000..07525e5 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0007.md @@ -0,0 +1,3 @@ +# (API-0007) Begin factory method names with "make" + +**Begin names of factory methods with "`make`",** e.g. `x.makeIterator()`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0008.md b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md new file mode 100644 index 0000000..59e99df --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0008.md @@ -0,0 +1,34 @@ +# (API-0008) Initializer and factory method first arguments + +The first argument to initializer and factory method calls should not form a phrase starting with the base name, e.g. `x.makeWidget(cogCount: 47)` + +## Overview + +For more on factory methods, see [Factory method pattern](https://en.wikipedia.org/wiki/Factory_method_pattern). + +✅ For example, the first arguments to these calls do not read as part of the same +phrase as the base name: + +```swift +let foreground = **Color**(red: 32, green: 64, blue: 128) +let newPart = **factory.makeWidget**(gears: 42, spindles: 14) +let ref = **Link**(target: destination) +``` + +⛔ In the following, the API author has tried to create grammatical +continuity with the first argument. + +```swift +let foreground = **Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)** +let newPart = **factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)** +let ref = **Link(to: destination)** +``` + +In practice, this guideline along with those for +argument labels (see ) means the first argument will +have a label unless the call is performing a +[value preserving type conversion](). + +```swift +let rgbForeground = RGBColor(cmykForeground) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0009.md b/api-guidelines/Sources/APIGuidelines.docc/API-0009.md new file mode 100644 index 0000000..6888cd1 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0009.md @@ -0,0 +1,65 @@ +# (API-0009) Name according to side-effects + +**Name functions and methods according to their side-effects.** + +## Overview + +* Those without side-effects should read as noun phrases, + e.g. `x.distance(to: y)`, `i.successor()`. + +* Those with side-effects should read as imperative verb phrases, + e.g., `print(x)`, `x.sort()`, `x.append(y)`. + +* **Name Mutating/nonmutating method pairs** consistently. + A mutating method will often have a nonmutating variant with + similar semantics, but that returns a new value rather than + updating an instance in-place. + + * When the operation is **naturally described by a verb**, use the + verb's imperative for the mutating method and apply the "ed" or + "ing" suffix to name its nonmutating counterpart. + + |Mutating|Nonmutating| + |-|-| + |`x.sort()`|`z = x.sorted()`| + |`x.append(y)`|`z = x.appending(y)`| + + * Prefer to name the nonmutating variant using the verb's past + [participle](https://en.wikipedia.org/wiki/Participle) (usually + appending "ed"): + + ```swift + /// Reverses `self` in-place. + mutating func reverse() + + /// Returns a reversed copy of `self`. + func revers**ed**() -> Self + ... + x.reverse() + let y = x.reversed() + ``` + + * When adding "ed" is not grammatical because the verb has a direct + object, name the nonmutating variant using the verb's present + [participle](https://en.wikipedia.org/wiki/Participle), by + appending "ing." + + ```swift + /// Strips all the newlines from `self` + mutating func stripNewlines() + + /// Returns a copy of `self` with all the newlines stripped. + func strip**ping**Newlines() -> String + ... + s.stripNewlines() + let oneLine = t.strippingNewlines() + ``` + + * When the operation is **naturally described by a noun**, use the + noun for the nonmutating method and apply the "form" prefix to + name its mutating counterpart. + + |Nonmutating|Mutating| + |-|-| + |`x = y.union(z)`|`y.formUnion(z)`| + |`j = c.successor(i)`|`c.formSuccessor(&i)`| diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0010.md b/api-guidelines/Sources/APIGuidelines.docc/API-0010.md new file mode 100644 index 0000000..f5dbfdb --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0010.md @@ -0,0 +1,3 @@ +# (API-0010) Boolean methods read as assertions + +**Uses of Boolean methods and properties should read as assertions about the receiver** when the use is nonmutating, e.g. `x.isEmpty`, `line1.intersects(line2)`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0011.md b/api-guidelines/Sources/APIGuidelines.docc/API-0011.md new file mode 100644 index 0000000..40b8cbf --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0011.md @@ -0,0 +1,3 @@ +# (API-0011) Protocols describing "what" read as nouns + +**Protocols that describe *what something is* should read as nouns** (e.g. `Collection`). diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0012.md b/api-guidelines/Sources/APIGuidelines.docc/API-0012.md new file mode 100644 index 0000000..f8b7eb6 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0012.md @@ -0,0 +1,3 @@ +# (API-0012) Protocols describing capability use suffixes + +**Protocols that describe a *capability* should be named using the suffixes `able`, `ible`, or `ing`** (e.g. `Equatable`, `ProgressReporting`). diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0013.md b/api-guidelines/Sources/APIGuidelines.docc/API-0013.md new file mode 100644 index 0000000..3065b45 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0013.md @@ -0,0 +1,3 @@ +# (API-0013) Other names read as nouns + +The names of other **types, properties, variables, and constants should read as nouns.** diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0014.md b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md new file mode 100644 index 0000000..dd3c2be --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0014.md @@ -0,0 +1,13 @@ +# (API-0014) Avoid obscure terms + +**Avoid obscure terms** if a more common word conveys meaning just as well. + +## Overview + +**Term of Art** +: *noun* - a word or phrase that has a precise, specialized meaning + within a particular field or profession. + +Don't say "epidermis" if "skin" will serve your purpose. +Terms of art are an essential communication tool, but should only be +used to capture crucial meaning that would otherwise be lost. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0015.md b/api-guidelines/Sources/APIGuidelines.docc/API-0015.md new file mode 100644 index 0000000..1085334 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0015.md @@ -0,0 +1,17 @@ +# (API-0015) Stick to the established meaning + +**Stick to the established meaning** if you do use a term of art. + +## Overview + +The only reason to use a technical term rather than a more common +word is that it *precisely* expresses something that would +otherwise be ambiguous or unclear. Therefore, an API should use +the term strictly in accordance with its accepted meaning. + +* **Don't surprise an expert**: anyone already familiar with the term + will be surprised and probably angered if we appear to have + invented a new meaning for it. + +* **Don't confuse a beginner**: anyone trying to learn the term is + likely to do a web search and find its traditional meaning. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0016.md b/api-guidelines/Sources/APIGuidelines.docc/API-0016.md new file mode 100644 index 0000000..3315080 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0016.md @@ -0,0 +1,8 @@ +# (API-0016) Avoid abbreviations + +**Avoid abbreviations.** Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms. + +## Overview + +> The intended meaning for any abbreviation you use should be +> easily found by a web search. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0017.md b/api-guidelines/Sources/APIGuidelines.docc/API-0017.md new file mode 100644 index 0000000..7549567 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0017.md @@ -0,0 +1,22 @@ +# (API-0017) Embrace precedent + +**Embrace precedent.** Don't optimize terms for the total beginner at the expense of conformance to existing culture. + +## Overview + +It is better to name a contiguous data structure `Array` than to +use a simplified term such as `List`, even though a beginner +might grasp the meaning of `List` more easily. Arrays are +fundamental in modern computing, so every programmer knows—or +will soon learn—what an array is. Use a term that most +programmers are familiar with, and their web searches and +questions will be rewarded. + +Within a particular programming *domain*, such as mathematics, a +widely precedented term such as `sin(x)` is preferable to an +explanatory phrase such as +`verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)`. +Note that in this case, precedent outweighs the guideline to +avoid abbreviations: although the complete word is `sine`, +"sin(*x*)" has been in common use among programmers for decades, +and among mathematicians for centuries. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0018.md b/api-guidelines/Sources/APIGuidelines.docc/API-0018.md new file mode 100644 index 0000000..59f9f15 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0018.md @@ -0,0 +1,10 @@ +# (API-0018) Document computed property complexity + +**Document the complexity of any computed property that is not O(1).** + +## Overview + +People often assume that property access involves no +significant computation, because they have stored properties as a +mental model. Be sure to alert them when that assumption may be +violated. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0019.md b/api-guidelines/Sources/APIGuidelines.docc/API-0019.md new file mode 100644 index 0000000..47e0b98 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0019.md @@ -0,0 +1,23 @@ +# (API-0019) Prefer methods and properties to free functions + +**Prefer methods and properties to free functions.** Free functions are used only in special cases. + +## Overview + +1. When there's no obvious `self`: + + ``` + min(x, y, z) + ``` + +2. When the function is an unconstrained generic: + + ``` + print(x) + ``` + +3. When function syntax is part of the established domain notation: + + ``` + sin(x) + ``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0020.md b/api-guidelines/Sources/APIGuidelines.docc/API-0020.md new file mode 100644 index 0000000..4ac7117 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0020.md @@ -0,0 +1,22 @@ +# (API-0020) Follow case conventions + +**Follow case conventions.** Names of types and protocols are `UpperCamelCase`. Everything else is `lowerCamelCase`. + +## Overview + +[Acronyms and initialisms](https://en.wikipedia.org/wiki/Acronym) +that commonly appear as all upper case in American English should be +uniformly up- or down-cased according to case conventions: + +```swift +var **utf8**Bytes: [**UTF8**.CodeUnit] +var isRepresentableAs**ASCII** = true +var user**SMTP**Server: Secure**SMTP**Server +``` + +Other acronyms should be treated as ordinary words: + +```swift +var **radar**Detector: **Radar**Scanner +var enjoys**Scuba**Diving = true +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0021.md b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md new file mode 100644 index 0000000..04e2a71 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0021.md @@ -0,0 +1,68 @@ +# (API-0021) Methods can share a base name + +**Methods can share a base name** when they share the same basic meaning or when they operate in distinct domains. + +## Overview + + + +✅ For example, the following is encouraged, since the methods do essentially +the same things: + +```swift +extension Shape { + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Point**) -> Bool { ... } + + /// Returns `true` if `other` is entirely within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **Shape**) -> Bool { ... } + + /// Returns `true` if `other` is within the area of `self`; + /// otherwise, `false`. + func **contains**(_ other: **LineSegment**) -> Bool { ... } +} +``` + +✅ And since geometric types and collections are separate domains, +this is also fine in the same program: + +```swift +extension Collection where Element : Equatable { + /// Returns `true` if `self` contains an element equal to + /// `sought`; otherwise, `false`. + func **contains**(_ sought: Element) -> Bool { ... } +} +``` + +⛔ However, these `index` methods have different semantics, and should +have been named differently: + +```swift +extension Database { + /// Rebuilds the database's search index + func **index**() { ... } + + /// Returns the `n`th row in the given table. + func **index**(_ n: Int, inTable: TableID) -> TableRow { ... } +} +``` + +⛔ Lastly, avoid "overloading on return type" because it causes +ambiguities in the presence of type inference. + +```swift +extension Box { + /// Returns the `Int` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> Int? { ... } + + /// Returns the `String` stored in `self`, if any, and + /// `nil` otherwise. + func **value**() -> String? { ... } +} +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0022.md b/api-guidelines/Sources/APIGuidelines.docc/API-0022.md new file mode 100644 index 0000000..3b8346b --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0022.md @@ -0,0 +1,30 @@ +# (API-0022) Choose parameter names to serve documentation + +**Choose parameter names to serve documentation.** Even though parameter names do not appear at a function or method's point of use, they play an important explanatory role. + +## Overview + +Choose these names to make documentation easy to read. + +✅ For example, these names make documentation read naturally: + +```swift +/// Return an `Array` containing the elements of `self` +/// that satisfy `**predicate**`. +func filter(_ **predicate**: (Element) -> Bool) -> [Generator.Element] + +/// Replace the given `**subRange**` of elements with `**newElements**`. +mutating func replaceRange(_ **subRange**: Range, with **newElements**: [E]) +``` + +⛔ These, however, make the documentation awkward and ungrammatical: + +```swift +/// Return an `Array` containing the elements of `self` +/// that satisfy `**includedInResult**`. +func filter(_ **includedInResult**: (Element) -> Bool) -> [Generator.Element] + +/// Replace the **range of elements indicated by `r`** with +/// the contents of `**with**`. +mutating func replaceRange(_ **r**: Range, **with**: [E]) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0023.md b/api-guidelines/Sources/APIGuidelines.docc/API-0023.md new file mode 100644 index 0000000..8f1f581 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0023.md @@ -0,0 +1,62 @@ +# (API-0023) Take advantage of defaulted parameters + +**Take advantage of defaulted parameters** when it simplifies common uses. Any parameter with a single commonly-used value is a candidate for a default. + +## Overview + +Default arguments improve readability by hiding irrelevant information. + +⛔ For example: + +```swift +let order = lastName.compare( + royalFamilyName**, options: [], range: nil, locale: nil**) +``` + +✅ can become the much simpler: + +```swift +let order = lastName.**compare(royalFamilyName)** +``` + +Default arguments are generally preferable to the use of method +families, because they impose a lower cognitive burden on anyone +trying to understand the API. + +✅ + +```swift +extension String { + /// *...description...* + public func compare( + _ other: String, options: CompareOptions **= []**, + range: Range? **= nil**, locale: Locale? **= nil** + ) -> Ordering +} +``` + +⛔ The above may not be simple, but it is much simpler than: + +```swift +extension String { + /// *...description 1...* + public func **compare**(_ other: String) -> Ordering + /// *...description 2...* + public func **compare**(_ other: String, options: CompareOptions) -> Ordering + /// *...description 3...* + public func **compare**( + _ other: String, options: CompareOptions, range: Range) -> Ordering + /// *...description 4...* + public func **compare**( + _ other: String, options: StringCompareOptions, + range: Range, locale: Locale) -> Ordering +} +``` + +Every member of a method family needs to be separately documented +and understood by users. To decide among them, a user needs to +understand all of them, and occasional surprising relationships—for +example, `foo(bar: nil)` and `foo()` aren't always synonyms—make +this a tedious process of ferreting out minor differences in +mostly identical documentation. Using a single method with +defaults provides a vastly superior programmer experience. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0024.md b/api-guidelines/Sources/APIGuidelines.docc/API-0024.md new file mode 100644 index 0000000..dd3c0f2 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0024.md @@ -0,0 +1,3 @@ +# (API-0024) Locate parameters with defaults toward the end + +**Prefer to locate parameters with defaults toward the end** of the parameter list. Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0025.md b/api-guidelines/Sources/APIGuidelines.docc/API-0025.md new file mode 100644 index 0000000..1a4a072 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0025.md @@ -0,0 +1,10 @@ +# (API-0025) Prefer #fileID in production + +**If your API will run in production, prefer `#fileID`** over alternatives. + +## Overview + +`#fileID` saves space and protects developers' privacy. Use `#filePath` in +APIs that are never run by end users (such as test helpers and scripts) if +the full path will simplify development workflows or be used for file I/O. +Use `#file` to preserve source compatibility with Swift 5.2 or earlier. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0026.md b/api-guidelines/Sources/APIGuidelines.docc/API-0026.md new file mode 100644 index 0000000..e7c9187 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0026.md @@ -0,0 +1,3 @@ +# (API-0026) Omit labels for indistinguishable arguments + +**Omit all labels when arguments can't be usefully distinguished**, e.g. `min(number1, number2)`, `zip(sequence1, sequence2)`. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0027.md b/api-guidelines/Sources/APIGuidelines.docc/API-0027.md new file mode 100644 index 0000000..0672826 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0027.md @@ -0,0 +1,48 @@ +# (API-0027) Omit first label for value-preserving type conversions + +**In initializers that perform value preserving type conversions, omit the first argument label**, e.g. `Int64(someUInt32)` + +## Overview + +The first argument should always be the source of the conversion. + +``` +extension String { + // Convert `x` into its textual representation in the given radix + init(**_** x: BigInt, radix: Int = 10) // ← Note the initial underscore +} + +text = "The value is: " +text += **String(veryLargeNumber)** +text += " and in hexadecimal, it's" +text += **String(veryLargeNumber, radix: 16)** +``` + +In "narrowing" type conversions, though, a label that describes +the narrowing is recommended. + +```swift +extension UInt32 { + /// Creates an instance having the specified `value`. + init(**_** value: Int16) // ← Widening, so no label + /// Creates an instance having the lowest 32 bits of `source`. + init(**truncating** source: UInt64) + /// Creates an instance having the nearest representable + /// approximation of `valueToApproximate`. + init(**saturating** valueToApproximate: UInt64) +} +``` + +> A value preserving type conversion is a +> [monomorphism](https://en.wikipedia.org/wiki/Monomorphism), i.e. +> every difference in the +> value of the source results in a difference in the value of the +> result. +> For example, conversion from `Int8` to `Int64` is value +> preserving because every distinct `Int8` value is converted to a +> distinct `Int64` value. Conversion in the other direction, however, +> cannot be value preserving: `Int64` has more possible values than +> can be represented in an `Int8`. +> +> Note: the ability to retrieve the original value has no bearing +> on whether a conversion is value preserving. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0028.md b/api-guidelines/Sources/APIGuidelines.docc/API-0028.md new file mode 100644 index 0000000..76529e2 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0028.md @@ -0,0 +1,25 @@ +# (API-0028) Label prepositional phrase arguments + +When the first argument forms part of a prepositional phrase, give it an argument label. The argument label should normally begin at the preposition, e.g. `x.removeBoxes(havingLength: 12)`. + +## Overview + +For more on prepositional phrases, see [prepositional phrase](https://en.wikipedia.org/wiki/Adpositional_phrase#Prepositional_phrases). For more on prepositions, see [preposition](https://en.wikipedia.org/wiki/Preposition). + +An exception arises when the first two arguments represent parts of +a single abstraction. + +⛔ + +```swift +a.move(**toX:** b, **y:** c) +a.fade(**fromRed:** b, **green:** c, **blue:** d) +``` + +✅ In such cases, begin the argument label *after* the preposition, to +keep the abstraction clear. + +```swift +a.moveTo(**x:** b, **y:** c) +a.fadeFrom(**red:** b, **green:** c, **blue:** d) +``` diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0029.md b/api-guidelines/Sources/APIGuidelines.docc/API-0029.md new file mode 100644 index 0000000..efec303 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0029.md @@ -0,0 +1,29 @@ +# (API-0029) Omit label when first argument forms a grammatical phrase + +**Otherwise, if the first argument forms part of a grammatical phrase, omit its label**, appending any preceding words to the base name, e.g. `x.addSubview(y)` + +## Overview + +This guideline implies that if the first argument *doesn't* form +part of a grammatical phrase, it should have a label. + +✅ + +```swift +view.dismiss(**animated:** false) +let text = words.split(**maxSplits:** 12) +let studentsByName = students.sorted(**isOrderedBefore:** Student.namePrecedes) +``` + +⛔ Note that it's important that the phrase convey the correct meaning. +The following would be grammatical but would express the wrong +thing. + +```swift +view.dismiss(false) // Don't dismiss? Dismiss a Bool? +words.split(12) // Split the number 12? +``` + +Note also that arguments with default values can be omitted, and +in that case do not form part of a grammatical phrase, so they +should always have labels. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0030.md b/api-guidelines/Sources/APIGuidelines.docc/API-0030.md new file mode 100644 index 0000000..417f243 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0030.md @@ -0,0 +1,3 @@ +# (API-0030) Label all other arguments + +**Label all other arguments.** diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0031.md b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md new file mode 100644 index 0000000..10242f8 --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0031.md @@ -0,0 +1,32 @@ +# (API-0031) Label tuple members and name closure parameters + +**Label tuple members and name closure parameters** where they appear in your API. + +## Overview + +These names have +explanatory power, can be referenced from documentation comments, +and provide expressive access to tuple members. + +```swift +/// Ensure that we hold uniquely-referenced storage for at least +/// `requestedCapacity` elements. +/// +/// If more storage is needed, `allocate` is called with +/// **`byteCount`** equal to the number of maximally-aligned +/// bytes to allocate. +/// +/// - Returns: +/// - **reallocated**: `true` if a new block of memory +/// was allocated; otherwise, `false`. +/// - **capacityChanged**: `true` if `capacity` was updated; +/// otherwise, `false`. +mutating func ensureUniqueStorage( + minimumCapacity requestedCapacity: Int, + allocate: (_ **byteCount**: Int) -> UnsafePointer +) -> (**reallocated:** Bool, **capacityChanged:** Bool) +``` + +Names used for closure parameters should be chosen like +parameter names (see ) for top-level functions. Labels for +closure arguments that appear at the call site are not supported. diff --git a/api-guidelines/Sources/APIGuidelines.docc/API-0032.md b/api-guidelines/Sources/APIGuidelines.docc/API-0032.md new file mode 100644 index 0000000..ed595fe --- /dev/null +++ b/api-guidelines/Sources/APIGuidelines.docc/API-0032.md @@ -0,0 +1,50 @@ +# (API-0032) Take extra care with unconstrained polymorphism + +**Take extra care with unconstrained polymorphism** (e.g. `Any`, `AnyObject`, and unconstrained generic parameters) to avoid ambiguities in overload sets. + +## Overview + +⛔ For example, consider this overload set: + +```swift +struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(_ newElements: S) + where S.Generator.Element == Element +} +``` + +These methods form a semantic family, and the argument types +appear at first to be sharply distinct. However, when `Element` +is `Any`, a single element can have the same type as a sequence of +elements. + +⛔ + +```swift +var values: [Any] = [1, "a"] +values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? +``` + +✅ To eliminate the ambiguity, name the second overload more +explicitly. + +```swift +struct Array { + /// Inserts `newElement` at `self.endIndex`. + public mutating func append(_ newElement: Element) + + /// Inserts the contents of `newElements`, in order, at + /// `self.endIndex`. + public mutating func append(**contentsOf** newElements: S) + where S.Generator.Element == Element +} +``` + +Notice how the new name better matches the documentation comment. +In this case, the act of writing the documentation comment +actually brought the issue to the API author's attention. diff --git a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md index 087098b..40b52fe 100644 --- a/api-guidelines/Sources/APIGuidelines.docc/Documentation.md +++ b/api-guidelines/Sources/APIGuidelines.docc/Documentation.md @@ -1 +1,85 @@ # ``APIGuidelines`` + +## Overview + +Delivering a clear, consistent developer experience when writing Swift code is largely defined by the names and idioms that appear in APIs. +These design guidelines explain how to make sure that your code feels like a part of the larger Swift ecosystem. + +* **Clarity at the point of use** is your most important goal. + Entities such as methods and properties are declared only once but + *used* repeatedly. Design APIs to make those uses clear and + concise. When evaluating a design, reading a declaration is seldom + sufficient; always examine a use case to make sure it looks + clear in context. + +* **Clarity is more important than brevity.** Although Swift + code can be compact, it is a *non-goal* + to enable the smallest possible code with the fewest characters. + Brevity in Swift code, where it occurs, is a side-effect of the + strong type system and features that naturally reduce boilerplate. + +* **Write a documentation comment** + for every declaration. Insights gained by writing documentation can + have a profound impact on your design, so don't put it off. + +> Warning: +> If you are having trouble describing your API's +> functionality in simple terms, **you may have designed the wrong API.** + +## Topics + +### Fundamentals + +- + +### Naming — Promote Clear Usage + +- +- +- +- + +### Naming — Strive for Fluent Usage + +- +- +- +- +- +- +- +- + +### Naming — Use Terminology Well + +- +- +- +- + +### Conventions — General + +- +- +- +- + +### Conventions — Parameters + +- +- +- +- + +### Conventions — Argument Labels + +- +- +- +- +- + +### Special Instructions + +- +-