Skip to content

Instantly share code, notes, and snippets.

@dabrahams
Last active November 7, 2017 08:29
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dabrahams/b6b79f19c2bf9b2a0083 to your computer and use it in GitHub Desktop.
Save dabrahams/b6b79f19c2bf9b2a0083 to your computer and use it in GitHub Desktop.
Status of Swift property guidelines discussion as of 2016-01-27

Properties

Conclusions After Much Discussion

  • Things that produce a logical transformation of the whole value are methods. That includes transformed views such as the result of reverse() on a bidirectional collection.

  • Things that mutate state (even state outside the receiver) are methods.

  • Things that are “intrinsic” to the receiver are properties

  • Proxies that confer a different set of capabilities or behaviors, like "x.lazy," are properties

  • Specific decisions:

    • Properties:

      • description/debugDescription (someone said: it is based on the intrinsic state, and not transformed)
      • first (because it is intrinsic to the thing?)
      • hashValue
      • optional.unsafelyUnwrapped
      • anyAdapter.base
      • unsafePointer.pointee
      • int.bigEndian, int.littleEndian, int.byteSwapped
      • string.utf8.nulTerminatedUTF8
      • customReflectable.customMirror
    • Methods:

      • collection.min()
      • sequence.iterator() because? Someone said: “An array does not care if it has a live iterator.”
      • index.successor(), index.predecessor() (return transformed versions of self)
      • string.uppercased(), string.lowercased(), ditto
      • sequence.enumerated(), ditto
      • sequence.reverse(), ditto

Survey of Current Standard Library Non-Mutating Nullary Methods and Public Properties

This section reflects the state of the library that was discussed and that led to the conclusions above. In other words, those conclusions have not been incorporated here.

Non-Mutating Nullary Methods

Methods That Return Transformed Versions of Sequences

extension SequenceType where Generator.Element : SequenceType {
  /// A concatenation of the elements of `self`.
  func flatten() -> FlattenSequence<Self>
}

extension Sequence {
  /// Return an `Array` containing the elements of `self` in reverse
  /// order.
  ///
  /// Complexity: O(`self.length`).
  func reverse() -> [Iterator.Element]
}

extension Collection where Index : BidirectionalIndex {
  /// Return the elements of `self` in reverse order.
  ///
  /// - Complexity: O(1)
  func reverse() -> ReverseCollection<Self>
}

extension Sequence where Iterator.Element : Comparable {
  /// Return an `Array` containing the sorted elements of `self`.
  ///
  /// The sorting algorithm is not stable (can change the relative order of
  /// elements that compare equal).
  ///
  /// - Requires: The less-than operator (`func <`) defined in
  ///   the `Comparable` conformance is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings)
  ///   over the elements in `self`."""
  func sorted() -> [Iterator.Element]
}

extension Sequence {
  /// Return an `Array` containing the elements of `self`, sorted according
  /// to `isOrderedBefore`.
  ///
  /// The sorting algorithm is not stable (can change the relative order of
  /// elements for which `isOrderedBefore` does not establish an order).
  ///
  /// - Requires: `isOrderedBefore` is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings)
  ///   over the elements in `self`."""
  func sorted(
    isOrderedBefore: (Iterator.Element, Iterator.Element) -> Bool
  ) -> [Iterator.Element]
}

Other Method Families

These are families where at least one member takes a parameter.

extension Sequence {
  /// Returns the minimum element in `self` or `nil` if the sequence is empty.
  ///
  /// - Complexity: O(`self.length`).
  /// - Requires: `isOrderedBefore` is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings).
  ///   over `self`.
  func min(
    isOrderedBefore: (Iterator.Element, Iterator.Element) throws -> Bool
  ) rethrows -> Iterator.Element?

  /// Returns the maximum element in `self` or `nil` if the sequence is empty.
  ///
  /// - Complexity: O(`self.length`).
  /// - Requires: `isOrderedBefore` is a
  ///   [strict weak ordering](http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings).
  ///   over `self`.
  func max(
    isOrderedBefore: (Iterator.Element, Iterator.Element) throws -> Bool
  ) rethrows -> Iterator.Element?

}

extension Sequence where Iterator.Element : Comparable {
  /// Returns the minimum element in `self` or `nil` if the sequence is empty.
  ///
  /// - Complexity: O(`self.length`).
  func min() -> Iterator.Element? // Iterator.Element : Comparable

  /// Returns the maximum element in `self` or `nil` if the sequence is empty.
  ///
  /// - Complexity: O(`self.length`).
  func max() -> Iterator.Element? // Iterator.Element : Comparable
}

Methods That Mutate by Reference

I think it is uncontroversial that these should all be methods. They are technically not labeled mutating because they don't mutate the receiver.

extension UnsafeMutablePointer {
  /// Retrieve the `pointee`, returning the referenced memory to an
  /// uninitialized state.
  ///
  /// Equivalent to `{ defer { deinitializePointee() }; return pointee }()`, but
  /// more efficient.
  ///
  /// - Requires: The pointee is initialized.
  ///
  /// - Postcondition: The memory is uninitialized.
  func take() -> Pointee
}

extension Unmanaged {
  /// Perform an unbalanced release of the object.
  func release()

  /// Perform an unbalanced autorelease of the object.
  func autorelease() -> Unmanaged

  /// Perform an unbalanced retain of the object.
  func retain() -> Unmanaged

  /// Get the value of this unmanaged reference as a managed
  /// reference and consume an unbalanced retain of it.
  ///
  /// This is useful when a function returns an unmanaged reference
  /// and you know that you're responsible for releasing the result.
  func takeRetainedValue() -> Instance

  /// Get the value of this unmanaged reference as a managed
  /// reference without consuming an unbalanced retain of it.
  ///
  /// This is useful when a function returns an unmanaged reference
  /// and you know that you're not responsible for releasing the result.
  func takeUnretainedValue() -> Instance
}

Things That Clearly Could Be Properties

enum UnicodeDecodingResult {
  /// Return true if `self` indicates no more unicode scalars are
  /// available.
  func isEmptyInput() -> Bool
}
extension Mirror {
  func superclassMirror() -> Mirror?
}

extension Sequence {
  /// Return a value less than or equal to the number of elements in
  /// `self`, **nondestructively**.
  ///
  /// - Complexity: O(N).
  func underestimateLength() -> Int
}

Questionable Methods

These definitely seem like they warrant some discussion.

extension CustomReflectable {
  /// Return the `Mirror` for `self`.
  ///
  /// - Note: If `Self` has value semantics, the `Mirror` should be
  ///   unaffected by subsequent mutations of `self`.
  @warn_unused_result
  func customMirror() -> Mirror
}

extension CustomPlaygroundQuickLookable {
  /// Return the `Mirror` for `self`.
  ///
  /// - Note: If `Self` has value semantics, the `Mirror` should be
  ///   unaffected by subsequent mutations of `self`.
  @warn_unused_result
  func customPlaygroundQuickLook() -> PlaygroundQuickLook
}

extension Sequence {
  /// Return an *iterator* over the elements of this *sequence*.
  ///
  /// - Complexity: O(1).
  func iterator() -> Iterator
}

extension IteratorProtocol {
  /// Advance to the next element and return it, or `nil` if no next
  /// element exists.
  func next() -> Element?
}

extension Unmanaged {
  /// Unsafely turn an unmanaged class reference into an opaque
  /// C pointer.
  ///
  /// This operation does not change reference counts.
  ///
  ///     let str: CFString = Unmanaged.fromOpaque(ptr).takeUnretainedValue()
  func toOpaque() -> OpaquePointer
}

Methods That Are Likely to Trap

The thinking here is that users should feel free to explore an instance's properties without causing a fault.

extension ForwardIndex {
  /// Return the next consecutive value in a discrete sequence of
  /// `${Self}` values.
  ///
  /// - Requires: `self` has a well-defined successor.
  func successor() -> ${Self}
}

extension BidirectionalIndex {
  /// Return the previous consecutive value in a discrete sequence of
  /// `${Self}` values.
  ///
  /// - Requires: `self` has a well-defined predecessor.
  func predecessor() -> ${Self}
  mutating func _predecessorInPlace()
}

extension Optional {
  /// - Returns: `nonEmpty!`.
  ///
  /// - Requires: `nonEmpty != nil`.  In particular, in -O builds, no test
  ///   is performed to ensure that `nonEmpty` actually is non-nil.
  ///
  /// - Warning: Trades safety for performance.  Use `unsafeUnwrap`
  ///   only when `nonEmpty!` has proven to be a performance problem and
  ///   you are confident that, always, `nonEmpty != nil`.  It is better
  ///   than an `unsafeBitCast` because it's more restrictive, and
  ///   because checking is still performed in debug builds.
  func unsafeUnwrap() -> Wrapped
}

Properties

extension LazyFilterIterator {
  /// The underlying iterator whose elements are being filtered.
  var base: Base { get }
}
extension Mirror {
  let children: Children { get }
  /// Suggests a display style for the reflected subject.
  let displayStyle: DisplayStyle?
}
extension Collection {
  /// Returns the number of elements.
  ///
  /// - Complexity: O(1) if `Index` conforms to `RandomAccessIndex`;
  ///   O(N) otherwise.
  var length: Index.Distance { get }
  
  /// The collection's "past the end" position.
  ///
  /// `endIndex` is not a valid argument to `subscript`, and is always
  /// reachable from `startIndex` by zero or more applications of
  /// `successor()`.
  var endIndex: Any${Traversal}Index { get }

  /// The position of the first element in a non-empty collection.
  ///
  /// In an empty collection, `startIndex == endIndex`.
  var startIndex: Any${Traversal}Index { get }

  /// The first element of `self`, or `nil` if `self` is empty.
  var first: Iterator.Element? { get }

  /// Return the range of valid index values.
  ///
  /// The result's `endIndex` is the same as that of `self`.  Because
  /// `Range` is half-open, iterating the values of the result produces
  /// all valid subscript arguments for `self`, omitting its `endIndex`.
  var indices: Range<Index> { get }

  /// Returns `true` iff `self` is empty.
  var isEmpty: Bool { get }

  /// A collection with contents identical to `self`, but on which
  /// normally-eager operations such as `map` and `filter` are
  /// implemented lazily.
  ///
  /// - See Also: `LazySequenceProtocol`, `LazyCollectionProtocol`.
  var lazy: LazyCollection<Self> { get }
}

extension Collection where Index : BidirectionalIndex {
  var last: Iterator.Element?
}
/// A collection whose elements are all identical.
extension Repeated {
  /// The value of every element in this collection.
  let repeatedValue: Element
}
struct Mirror {
  /// The static type of the subject being reflected.
  ///
  /// This type may differ from the subject's dynamic type when `self`
  /// is the `superclassMirror()` of another mirror.
  let subjectType: Any.Type
}
extension UnsafeBufferPointer {
  /// A pointer to the first element of the buffer.
  var baseAddress: UnsafePointer<Element>
}
extension Int {
  /// Returns the big-endian representation of the integer, changing the
  /// byte order if necessary.
  var bigEndian: Int  { get }
  
  /// Returns the little-endian representation of the integer, changing the
  /// byte order if necessary.
  var littleEndian: Int { get }

  /// Returns the current integer with the byte order swapped.
  var byteSwapped: Int { get }
}
protocol Boolean {
  /// The value of `self`, expressed as a `Bool`.
  var boolValue: Bool { get }
}
extension ManagedBufferPointer {
  /// Return the object instance being used for storage.
  var buffer: AnyObject { get }
}
extension Array {
  /// The number of elements the `Array` can store without reallocation.
  var capacity: Int { get }
}
extension String {
  /// Return `self` converted to lower case.
  ///
  /// - Complexity: O(n)
  var lowercased: String { get }

  /// Return `self` converted to upper case.
  ///
  /// - Complexity: O(n)
  var uppercased: String { get }

  /// A UTF-8 encoding of `self`.
  var utf8: UTF8View { get set }
  
  /// A UTF-16 encoding of `self`.
  var utf16: UTF16View { get set }

  /// A collection of `Characters` representing the `String`'s
  /// [extended grapheme
  /// clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster).
  var characters: CharacterView { get set }

  /// The value of `self` as a collection of [Unicode scalar values](http://www.unicode.org/glossary/#unicode_scalar_value).
  var unicodeScalars : UnicodeScalarView { get set }
}

extension String.UTF8View {
  /// A contiguously-stored nul-terminated UTF-8 representation of
  /// `self`.
  ///
  /// To access the underlying memory, invoke
  /// `withUnsafeBufferPointer` on the `ContiguousArray`.
  var nulTerminatedUTF8: ContiguousArray<UTF8.CodeUnit> { get }
}
extension CustomDebugStringConvertible {
  /// A textual representation of `self`, suitable for debugging.
  var debugDescription: String { get }
}
protocol CustomStringConvertible {
  /// A textual representation of `self`.
  var description: String { get }
}
extension LazySequenceProtocol {
  /// A sequence containing the same elements as this one, possibly with
  /// a simpler type.
  ///
  /// When implementing lazy operations, wrapping `elements` instead
  /// of `self` can prevent result types from growing an extra
  /// `LazySequence` layer.  For example,
  ///
  /// _prext_ example neeeded
  ///
  /// Note: this property need not be implemented by conforming types,
  /// it has a default implementation in a protocol extension that
  /// just returns `self`.
  var elements: Elements {get}
}
extension Interval {
  /// The `Interval`'s lower bound.
  ///
  /// Invariant: `start` <= `end`.
  var start: Bound { get }
  /// The `Interval`'s upper bound.
  ///
  /// Invariant: `start` <= `end`.
  var end: Bound { get }
  /// `true` iff `self` is empty.
  var isEmpty: Bool {get}
}
extension Sequence {
  /// Return a lazy `Sequence` containing pairs (*n*, *x*), where
  /// *n*s are consecutive `Int`s starting at zero, and *x*s are
  /// the elements of `self`:
  ///
  ///     > for (n, c) in "Swift".characters.enumerated {
  ///         print("\(n): '\(c)'")
  ///       }
  ///     0: 'S'
  ///     1: 'w'
  ///     2: 'i'
  ///     3: 'f'
  ///     4: 't'
  var enumerated: EnumeratedSequence<Self> { get }
}
protocol Collection : Indexable, Sequence {
  /// `true` iff `self` contains no characters.
  var isEmpty : Bool { get }
  /// Returns the first element of `self`, or `nil` if `self` is empty.
  var first: Iterator.Element? { get }
  /// Return the range of valid index values.
  ///
  /// The result's `endIndex` is the same as that of `self`.  Because
  /// `Range` is half-open, iterating the values of the result produces
  /// all valid subscript arguments for `self`, omitting its `endIndex`.
  var indices: Range<Index> { get }
}
extension StaticString {
  /// `true` iff `self` stores a pointer to ASCII or UTF-8 code units.
  var hasPointerRepresentation: Bool { get }
  /// `true` if `self` stores a pointer to ASCII code units.
  ///
  /// If `self` stores a single Unicode scalar value, the value of
  /// `isASCII` is unspecified.
  var isASCII: Bool { get }

  /// If `self` stores a pointer to ASCII or UTF-8 code units, the
  /// length in bytes of that data.
  ///
  /// If `self` stores a single Unicode scalar value, the value of
  /// `lengthInBytes` is unspecified.
  var lengthInBytes: Int

  /// The stored Unicode scalar value.
  ///
  /// - Requires: `self` stores a single Unicode scalar value.
  var unicodeScalar: UnicodeScalar { get }

  /// A pointer to the beginning of UTF-8 code units.
  ///
  /// - Requires: `self` stores a pointer to either ASCII or UTF-8 code
  ///   units.
  var utf8Start: UnsafePointer<UInt8> { get }
}
protocol FloatingPoint : Strideable {
  /// The IEEE 754 "class" of this type.
  var floatingPointClass: FloatingPointClassification { get }

  /// `true` iff `self` is negative.
  var isSignMinus: Bool { get }

  /// `true` iff `self` is normal (not zero, subnormal, infinity, or
  /// NaN).
  var isNormal: Bool { get }

  /// `true` iff `self` is zero, subnormal, or normal (not infinity
  /// or NaN).
  var isFinite: Bool { get }

  /// `true` iff `self` is +0.0 or -0.0.
  var isZero: Bool { get }

  /// `true` iff `self` is subnormal.
  var isSubnormal: Bool { get }

  /// `true` iff `self` is infinity.
  var isInfinite: Bool { get }

  /// `true` iff `self` is NaN.
  var isNaN: Bool { get }

  /// `true` iff `self` is a signaling NaN.
  var isSignaling: Bool { get }
}
extension Hashable {
  /// The hash value.
  ///
  /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`.
  ///
  /// - Note: The hash value is not guaranteed to be stable across
  ///   different invocations of the same program.  Do not persist the
  ///   hash value across program runs.
  var hashValue: Int { get }
}
extension Dictionary {
  /// A collection containing just the keys of `self`.
  ///
  /// Keys appear in the same order as they occur as the `.key` member
  /// of key-value pairs in `self`.  Each key in the result has a
  /// unique value.
  var keys: LazyMapCollection<Dictionary, Key> { get }

  /// A collection containing just the values of `self`.
  ///
  /// Values appear in the same order as they occur as the `.value` member
  /// of key-value pairs in `self`.
  var values: LazyMapCollection<Dictionary, Value> { get }
}
extension UnsafePointer {
  /// Access the `Pointee` instance referenced by `self`.
  ///
  /// - Requires: the pointee has been initialized with an instance of
  ///   type `Pointee`.
  var pointee: Pointee { get }
}
extension RawRepresentable {
  /// The corresponding value of the "raw" type.
  ///
  /// `Self(rawValue: self.rawValue)!` is equivalent to `self`.
  var rawValue: RawValue { get }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment