Skip to content

Instantly share code, notes, and snippets.

@jckarter
Created January 13, 2016 22:03

Revisions

  1. jckarter created this gist Jan 13, 2016.
    1,385 changes: 1,385 additions & 0 deletions property-behavior-decl.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1385 @@
    # Property Behaviors

    * Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/proposals/NNNN-name.md)
    * Author(s): [Joe Groff](https://github.com/jckarter)
    * Status: **Review**
    * Review manager: TBD

    ## Introduction

    There are property implementation patterns that come up repeatedly.
    Rather than hardcode a fixed set of patterns into the compiler,
    we should provide a general "property behavior" mechanism to allow
    these patterns to be defined as libraries.

    ## Motivation

    We've tried to accommodate several important patterns for properties with
    targeted language support, but this support has been narrow in scope and
    utility. For instance, Swift 1 and 2 provide `lazy` properties as a primitive
    language feature, since lazy initialization is common and is often necessary to
    avoid having properties be exposed as `Optional`. Without this language
    support, it takes a lot of boilerplate to get the same effect:

    ```swift
    class Foo {
    // lazy var foo = 1738
    private var _foo: Int?
    var foo: Int {
    get {
    if let value = _foo { return value }
    let initialValue = 1738
    _foo = initialValue
    return initialValue
    }
    set {
    _foo = newValue
    }
    }
    }
    ```

    Building `lazy` into the language has several disadvantages. It makes the
    language and compiler more complex and less orthogonal. It's also inflexible;
    there are many variations on lazy initialization that make sense, but we
    wouldn't want to hardcode language support for all of them. For instance, some
    applications may want the lazy initialization to be synchronized, but `lazy`
    only provides single-threaded initialization. The standard implementation of
    `lazy` is also problematic for value types. A `lazy` getter must be `mutating`,
    which means it can't be accessed from an immutable value. Inline storage is
    also suboptimal for many memoization tasks, since the cache cannot be reused
    across copies of the value. A value-oriented memoized property implementation
    might look very different, using a class instance to store the cached value
    out-of-line in order to avoid mutation of the value itself. Lazy properties are
    also unable to surface any additional operations over a regular property, such
    as to reset a lazy property's storage to be recomputed again.

    There are important property patterns outside of lazy initialization.
    It often makes sense to have "delayed",
    once-assignable-then-immutable properties to support multi-phase initialization:

    ```swift
    class Foo {
    let immediatelyInitialized = "foo"
    var _initializedLater: String?

    // We want initializedLater to present like a non-optional 'let' to user code;
    // it can only be assigned once, and can't be accessed before being assigned.
    var initializedLater: String {
    get { return _initializedLater! }
    set {
    assert(_initializedLater == nil)
    _initializedLater = newValue
    }
    }
    }

    ```

    Implicitly-unwrapped optionals allow this in a pinch, but give up a lot of
    safety compared to a non-optional 'let'. Using IUO for multi-phase initialization
    gives up both immutability and nil-safety.

    We also have other application-specific property features like
    `didSet`/`willSet` and array addressors that add language complexity for
    limited functionality. Beyond what we've baked into the language already,
    there's a seemingly endless set of common property behaviors, including
    resetting, synchronized access, and various kinds of proxying, all begging for
    language attention to eliminate their boilerplate.

    ## Proposed solution

    I suggest we allow for **property behaviors** to be implemented within the
    language. A `var` or `let` declaration can specify its **behaviors** in square
    brackets after the keyword:

    ```swift
    var [lazy] foo = 1738
    ```

    which implements the property `foo` in a way described by the **property
    behavior declaration** for `lazy`:

    ```swift
    var behavior lazy<Value>: Value {
    var value: Value? = nil
    deferred initializer: Value
    get {
    if let value = value {
    return value
    }
    let initialValue = initializer
    value = initialValue
    return initialValue
    }
    set {
    value = newValue
    }
    }
    ```

    Property behaviors can control the storage,
    initialization, and access of affected properties, obviating the need for
    special language support for `lazy`, observers, addressors, and other
    special-case property features. Property behaviors can also provide additional
    operations on properties, such as `clear`-ing a lazy property, accessed with
    `property.behavior` syntax:

    ```swift
    extension lazy {
    mutating func clear() {
    value = nil
    }
    }

    foo.lazy.clear()
    ```

    ## Examples

    Before describing the detailed design, I'll run through some examples of
    potential applications for behaviors.

    ### Lazy

    The current `lazy` property feature can be reimplemented as a property behavior.

    ```swift
    // Property behaviors are declared using the `var behavior` keyword cluster.
    public var behavior lazy<Value>: Value {
    // Behaviors can declare storage that backs the property.
    private var value: Value?

    // Behaviors can declare that properties using the behavior require
    // a `deferred initializer` expression. When deferred, the
    // initializer expression is assumed to be evaluated after
    // initialization of the containing value, which allows it to refer
    // to `self`. If declared, `initializer` is bound in accessors and
    // methods of the behavior.
    deferred initializer: Value

    // Behaviors can declare initialization logic for the storage.
    // (Stored properties can also be initialized in-line.)
    init() {
    value = nil
    }

    // Inline initializers should also be supported, so `var value: Value? = nil`
    // would work.

    // Behaviors can declare accessors that implement the property.
    get {
    if let value = value {
    return value
    }
    //
    value = initializer
    }
    set {
    value = newValue
    }

    // Behaviors can also declare methods to attach to the property.
    public mutating func clear() {
    value = nil
    }
    }
    ```

    Properties declared with the `lazy` behavior are backed by the `Optional`-typed
    storage and accessors from the behavior:

    ```swift
    var [lazy] x = 1738 // Allocates an Int? behind the scenes, inited to nil
    print(x) // Invokes the `lazy` getter, initializing the property
    x = 679 // Invokes the `lazy` setter
    ```

    Visible members of the behavior can also be accessed under `property.behavior`:

    ```swift
    x.lazy.clear() // Invokes `lazy`'s `clear` method
    ```

    ### Memoization

    Variations of `lazy` can be implemented that are more appropriate for certain
    situations. For instance, here's a `memoized` behavior that stores the cached
    value indirectly, making it suitable for immutable value types:

    ```swift
    public var behavior memoized<Value>: Value {
    public class MemoizationBox<Value> {
    var value: Value? = nil
    public func getOrEvaluate(fn: () -> Value) -> Value {
    if let value = value { return value }
    // Perform the initialization in a thread-safe way.
    // Implementation of 'sync' not shown here.
    return sync {
    let initialValue = fn()
    value = initialValue
    return initialValue
    }
    }
    func clear() {
    value = nil
    }
    }

    let storage: MemoizationBox<Value> = MemoizationBox()

    deferred initializer: Value

    get {
    return storage.getOrEvaluate { initializer }
    }

    public func clear() {
    storage.clear()
    }
    }
    ```

    which can then be used like this:

    ```swift
    struct Location {
    let street, city, postalCode: String

    let [memoized] address = "\(street)\n\(city) \(postalCode)"
    }
    ```

    ### Delayed Initialization

    A property behavior can model "delayed" initialization behavior, where the DI
    rules for properties are enforced dynamically rather than at compile time.
    This can avoid the need for implicitly-unwrapped optionals in multi-phase
    initialization use cases. We can implement both a mutable variant, which
    allows for reassignment like a `var`:

    ```swift
    public var behavior delayedMutable<Value>: Value {
    private var value: Value? = nil

    get {
    guard let value = value else {
    fatalError("property accessed before being initialized")
    }
    return value
    }
    set {
    value = newValue
    }

    // Perform an explicit initialization, trapping if the
    // value is already initialized.
    public mutating func initialize(initialValue: Value) {
    if let _ = value {
    fatalError("property initialized twice")
    }
    value = initialValue
    }
    }
    ```

    and an immutable variant, which only allows a single initialization like
    a `let`:

    ```swift
    public var behavior delayedImmutable<Value>: Value {
    private var value: Value? = nil

    get {
    guard let value = value else {
    fatalError("property accessed before being initialized")
    }
    return value
    }

    // Perform an explicit initialization, trapping if the
    // value is already initialized.
    public mutating func initialize(initialValue: Value) {
    if let _ = value {
    fatalError("property initialized twice")
    }
    value = initialValue
    }
    }
    ```

    This enables multi-phase initialization, like this:

    ```swift
    class Foo {
    var [delayedImmutable] x: Int

    init() {
    // We don't know "x" yet, and we don't have to set it
    }

    func initializeX(x: Int) {
    self.x.delayedImmutable.initialize(x) // Will crash if 'self.x' is already initialized
    }

    func getX() -> Int {
    return x // Will crash if 'self.x' wasn't initialized
    }
    }
    ```

    #### Resettable properties

    There's a common pattern in Cocoa where properties are used as optional
    customization points, but can be reset to nil to fall back to a non-public
    default value. In Swift, properties that follow this pattern currently must be
    imported as ImplicitlyUnwrappedOptional, even though the property can only be
    *set* to nil. If expressed as a behavior, the `reset` operation can be
    decoupled from the type, allowing the property to be exported as non-optional:

    ```swift
    public var behavior resettable<Value>: Value {
    // A behavior can declare an *eager* initializer, which is available
    // during initialization of the property, but which cannot refer to
    // `self`.
    eager initializer: Value

    var value: Value = initializer

    get {
    return value
    }
    set {
    value = newValue
    }

    // Reset the property to its original initialized value.
    mutating func reset() {
    value = initializer
    }
    }
    ```

    For example:


    ```
    var [resettable] foo: Int = 22
    print(foo) // => 22
    foo = 44
    print(foo) // => 44
    foo.resettable.reset()
    print(foo) // => 22
    ```

    ### Property Observers

    A property behavior can also replicate the built-in behavior of `didSet`/`willSet`
    observers, by declaring support for custom accessors:

    ```swift
    public var behavior observed<Value>: Value {
    // For composability, a behavior can declare a `base` property. The
    // base can be inherited from another behavior or an overridden
    // superclass property; if neither of those are provided, a fresh stored
    // property is generated in the containing scope.
    base var value: Value

    // A behavior can also declare accessors, the implementations of which
    // must be provided by property declarations using the behavior.
    // The behavior may provide a default implementation of
    // the accessors, in order to make them optional.

    // The willSet accessor, invoked before the property is updated. The
    // default does nothing.
    accessor willSet(newValue: Value) { }

    // The didSet accessor, invoked before the property is updated. The
    // default does nothing.
    accessor didSet(oldValue: Value) { }

    get {
    return value
    }

    set {
    willSet(newValue)
    let oldValue = value
    value = newValue
    didSet(oldValue)
    }
    }
    ```

    Because the `observed` behavior declares a **base property**, it can also
    be composed on top of other behaviors:

    ```swift
    var [lazy, observed] observedLazy = expensiveExpression() {
    didSet { print("\(oldValue) => \(observedLazy)") }
    }
    ```

    or applied in subclasses that override base class properties, like `didSet`
    /`willSet` today:

    ```swift
    class Foo {
    var foo: Int
    }

    class NoisyFoo: Foo {
    override var [observed] foo: Int {
    didSet { print("\(oldValue) => \(foo)") }
    }
    }
    ```

    A common complaint with `didSet`/`willSet` is that the observers fire on
    *every* write, not only ones that cause a real change. A behavior
    that supports a `didChange` accessor, which only gets invoked if the property
    value really changed to a value not equal to the old value, can be implemented
    as a new behavior:

    ```swift
    public var behavior changeObserved<Value: Equatable>: Value {
    base var value: Value

    mutating accessor didChange(oldValue: Value) { }

    get {
    return value
    }
    set {
    let oldValue = value
    value = newValue
    if oldValue != newValue {
    didChange(oldValue)
    }
    }
    }
    ```

    For example:

    ```swift
    var [changeObserved] x = 1 {
    didChange { print("\(oldValue) => \(x)") }
    }

    x = 1 // Prints nothing
    x = 2 // Prints 1 => 2
    ```

    ### Synchronized Property Access

    Objective-C supports `atomic` properties, which take a lock on `get` and `set`
    to synchronize accesses to a property. This is occasionally useful, and it can
    be brought to Swift as a behavior. The real implementation of `atomic`
    properties in ObjC uses a global bank of locks, but for illustrative purposes
    (and to demonstrate referring to `self`) I'll use a per-object lock instead:

    ```swift
    // A class that owns a mutex that can be used to synchronize access to its
    // properties.
    public protocol Synchronizable: class {
    func withLock<R>(@noescape body: () -> R) -> R
    }

    // Behaviors can refer to a property's containing type using
    // `Self` (including to impose constraints on it).
    public var behavior synchronized<Value where Self: Synchronizable>: Value {
    base var value: Value

    get {
    return self.withLock {
    return value
    }
    }
    set {
    self.withLock {
    value = newValue
    }
    }
    }
    ```

    For example:

    ```swift
    // `Synchronizable` conformances not presented here
    class SharedState: Synchronizable {
    var [synchronized] attributes: [String: Any] = [:]
    }

    class LazySharedState: Synchronizable {
    var [lazy, synchronized] attributes: [String: Any]
    = loadStateFromDisk()
    }

    class State: NSObject {
    var attributes: [String: Any]
    }

    class SynchronizedState: State, Synchronizable {
    override var [synchronized] attributes: [String: Any]
    }
    ```

    ### `NSCopying`

    Many Cocoa classes implement value-like objects that require explicit copying.
    Swift currently provides an `@NSCopying` attribute for properties to give
    them behavior like Objective-C's `@property(copy)`, invoking the `copy` method
    on new objects when the property is set. We can turn this into a behavior:

    ```swift
    public var behavior copying<Value: NSCopying>: Value {
    base var value: Value

    get {
    return value
    }
    set {
    // Copy the value on reassignment.
    value = newValue.copy()
    }
    }
    ```

    This is a small sampling of the possibilities of behaviors. Let's look at the
    proposed design in detail:

    ## Detailed design

    ### Property behavior declarations

    A **property behavior declaration** is introduced by the `var behavior`
    contextual keyword cluster, which declares the (possibly generic) type of
    properties supported by the behavior.

    ```text
    property-behavior-decl ::=
    attribute* decl-modifier*
    'var' 'behavior' identifier generic-param-list? ':' type '{'
    property-behavior-member-decl*
    '}'
    ```

    Inside the behavior declaration, standard initializer, property, method, and
    nested type declarations are allowed, as are **core accessor** declarations
    `get` and `set`. Additional contextual declarations are
    supported for **initializer requirement declarations**,
    **base property declarations**, and
    **accessor requirement declarations**:

    ```text
    property-behavior-member-decl ::= decl
    property-behavior-member-decl ::= core-accessor-decl // get, set
    property-behavior-member-decl ::= property-behavior-initializer-decl
    property-behavior-member-decl ::= property-behavior-base-property-decl
    property-behavior-member-decl ::= property-behavior-accessor-decl // accessor foo(...)
    ```

    ### Bindings within Behavior Declarations

    Definitions within behaviors can refer to other members of the behavior by
    unqualified lookup, or if disambiguation is necessary, by qualified lookup
    on the behavior:

    ```swift
    var behavior foo: Int {
    var x: Int

    init() {
    x = 1738
    }

    mutating func update(x: Int) {
    foo.x = x // Disambiguate reference to behavior storage
    }
    }
    ```

    If the behavior includes an *initializer requirement declaration*, then
    `initializer` is bound as a computed get-only property that evaluates the
    property's initializer expression:

    ```swift
    var behavior foo<Value>: Value {
    deferred initializer: Value

    get {
    return initializer
    }
    }
    ```

    If the behavior includes *accessor requirement declarations*, then the
    declared accessor names are bound as functions with labeled arguments:

    ```swift
    var behavior fakeComputed<Value>: Value {
    accessor get() -> Value
    mutating accessor set(newValue: Value)

    get {
    return get()
    }
    set {
    set(newValue: newValue)
    }
    }
    ```

    Note that the behavior's own *core accessor* implementations are *not*
    referenceable this way.

    Inside a behavior declaration, `self` is implicitly bound to the value that
    contains the property instantiated using this behavior. For a freestanding
    property at global or local scope, this will be the empty tuple `()`, and
    for a static or class property, this will be the metatype. Within
    the behavior declaration, the type of `self` is abstract and represented by the
    implicit generic type parameter `Self`. Constraints can be placed on `Self`
    in the generic signature of the behavior, to make protocol members available
    on `self`:

    ```swift
    protocol Fungible {
    typealias Fungus
    func funge() -> Fungus
    }

    var behavior runcible<Value where Self: Fungible, Self.Fungus == Value>: Value {
    get {
    return self.funge()
    }
    }
    ```

    Lookup within `self` is *not* implicit within behaviors and must always be
    explicit, since unqualified lookup refers to the behavior's own members. `self`
    is immutable except in `mutating` methods, where it is considered an `inout`
    parameter unless the `Self` type has a class constraint. `self` cannot be
    accessed within inline initializers of the behavior's storage or in `init`
    declarations, since these may run during the container's own initialization
    phase.

    ### Nested Types in Behaviors

    Behavior declarations may nest type declarations as a namespacing mechanism.
    As with other type declarations, the nested type cannot reference members
    from its enclosing behavior.

    ### Properties and Methods in Behaviors

    Behaviors may include property and method declarations. Any storage produced
    by behavior properties is expanded into the containing scope of a property
    using the behavior.

    ```swift
    var behavior runcible: Int {
    var x: Int = 0
    let y: String = ""
    }
    var [runcible] a: Int

    // expands to:

    var `a.runcible.x`: Int
    let `a.runcible.y`: String
    var a: Int { ... }
    ```

    For public behaviors, this is inherently *fragile*, so
    adding or removing storage is a breaking change. Resilience can be achieved
    by using a resilient type as storage. The instantiated properties must also
    be of types that are visible to potential users of the behavior, meaning
    that public behaviors must use storage with types that are either public
    or internal-with-availability, similar to the restrictions on inlineable
    functions.

    The properties and methods of the
    behavior are accessible from properties using the behavior, if they
    have sufficient visibility.

    ```swift
    var behavior runcible: Int {
    private var x: Int = 0
    var y: String = ""

    func foo() {}
    }

    // In a different file...

    var [runcible] a: Int
    _ = a.runcible.x // Error, runcible.x is private
    _ = a.runcible.y // OK
    a.runcible.foo() // OK
    ```

    Method and computed property implementations have only immutable access to
    `self` and their storage by default, unless they are `mutating`. (As with
    computed properties, setters are `mutating` by default unless explicitly
    marked `nonmutating`).

    ### Base Property

    A behavior may declare no more than one **base property**, which can be
    used to compose and override with behaviors. A base property is declared
    using the contextual `base` modifier on a `var` or `let` declaration:

    ```text
    property-behavior-base-property-decl ::=
    attribute* (decl-modifier | 'base')* core-property-decl
    // core-property-decl refers to the existing property-decl syntax without
    // `attribute* decl-modifier*` prefix
    ```


    The base property must have the
    same type as the behavior. When a behavior declares a base property, a property
    using the behavior by itself instantiates the base property as a stored
    property, passing through any initialization:

    ```swift
    var behavior hasBase: Int {
    base var value: Int
    }

    var [hasBase] x: Int = 0 // `x.hasBase.base` instantiated as stored property
    var [hasBase] y: Int // `y.hasBase.base` instantiated as stored property
    y = 0 // Late initialization of `y`
    ```

    A property may use multiple behaviors, if all behaviors after the first
    use a base property. Each behavior sees the result of the previous behavior
    as its base.

    ```swift
    var behavior root: Int { ... }

    var [root, hasBase] z: Int
    // Instantiates:
    // var [root] `z.hasBase.value`: Int
    // as the base for:
    // var [hasBase] z: Int
    ```

    If a subclass `override`-s a property, it may apply behaviors to the overridden
    property, which will be used as its `base`.

    ```
    class Foo {
    var x: Int = 0
    }
    class Bar: Foo {
    override var [hasBase] x: Int // uses `super.x` as `x.hasBase.value`
    }
    ```

    Inside a behavior declaration, the base property declaration cannot have an
    inline initializer or be initialized in an `init` declaration, since the
    underlying base property may not be owned by the behavior instantiation.
    A base property declaration also may not appear with an initializer
    requirement declaration.

    ### `init` in Behaviors

    The storage of a behavior must be initialized, either by inline initialization,
    or by an `init` declaration within the initializer:

    ```swift
    var behavior inlineInitialized: Int {
    var x: Int = 0 // initialized inline
    get { return x }
    }

    var behavior initInitialized: Int {
    var x: Int
    get { return x }

    init() {
    x = 0
    }
    }
    ```

    Behaviors can contain at most one `init` declaration, which must take no
    parameters. This `init` declaration cannot take a visibility modifier; it
    is always as visible as the behavior itself. Neither inline initializers nor
    `init` declaration bodies may reference `self`, since they will be executed
    during the initializing of a property's containing value. For the same
    reason, an `init` also may not refer to the `base` property, if any, nor
    may it invoke any accessor requirements or deferred initializer requirements.

    ### Accessor Requirement Declarations

    An *accessor requirement declaration* specifies that a behavior requires
    any property declared to use the behavior to provide an accessor
    implementation. An accessor requirement declaration is introduced by the
    contextual `accessor` keyword:

    ```swift
    property-behavior-accessor-decl ::=
    attribute* decl-modifier*
    'accessor' identifier function-signature function-body?
    ```

    An accessor requirement declaration looks like, and serves a similar role to,
    a function requirement declaration in a protocol. A property using the
    behavior must supply an implementation for each of its accessor requirements.
    The accessor names (with labeled arguments) are bound as functions within
    the behavior declaration:

    ```swift
    // Reinvent computed properties
    var behavior computed<Value>: Value {
    accessor get() -> Value
    mutating accessor set(newValue: Value)

    get { return get() }
    set { set(newValue: newValue) }
    }

    var [computed] foo: Int {
    get {
    return 0
    }
    set {
    // Parameter gets the name 'newValue' from the accessor requirement
    // by default, as with built-in accessors today.
    print(newValue)
    }
    }

    var [computed] bar: Int {
    get {
    return 0
    }
    set(myNewValue) {
    // Parameter name can be overridden as well
    print(myNewValue)
    }
    }
    ```

    Accessor requirements can be made optional by specifying a default
    implementation:

    ```swift
    // Reinvent property observers
    var behavior observed<Value>: Value {
    base var value: Value

    mutating accessor willSet(newValue: Value) {
    // do nothing by default
    }
    mutating accessor didSet(oldValue: Value) {
    // do nothing by default
    }

    get {
    return value
    }
    set {
    willSet(newValue: newValue)
    let oldValue = value
    value = newValue
    didSet(oldValue: oldValue)
    }
    }
    ```

    Accessor requirements cannot take visibility modifiers; they are always as
    visible as the behavior itself.

    Like methods, accessors are not allowed to mutate the storage of the behavior
    or `self` unless declared `mutating`. Mutating accessors can only be invoked
    by the behavior from other `mutating` contexts.

    ### Initializer Requirement Declarations

    A behavior can require an inline initializer expression with an *initializer
    requirement declaration*, specified by the contextual `initializer` keyword:

    ```text
    property-behavior-initializer-decl ::=
    ('eager' | 'deferred') 'initializer' ':' type
    ```

    The type of `initializer` must match the type of the behavior.
    The initializer requirement must be declared as `eager` or `deferred`,
    indicating where in the initialization process the initializer expression
    is used:

    - An **eager initializer** is used during the initialization of the behavior's
    state. An eager initializer can be used in the `init` implementation of
    a behavior, or in the inline initializer of one or more of its instantiated
    stored properties. A property using the behavior cannot refer to `self`
    within its initializer expression, as in a normal stored property.

    ```swift
    var behavior eagerInit: Int {
    eager initializer: Int

    var value: Int

    init() {
    value = initializer // Allowed for eager initializer
    }

    get {
    return value + initializer
    }
    }

    class Foo {
    var [eagerInit] foo: Int = self.bar // Not allowed
    var bar: Int
    }
    ```

    - A **deferred initializer** is used only after the initialization of the
    behavior's state. A deferred initializer cannot be referenced until
    the behavior's storage is initialized. A property using the behavior can
    refer to `self` within its initializer expression, as one would expect
    a `lazy` property to be able to.

    ```swift
    var behavior deferredInit: Int {
    eager initializer: Int

    var value: Int

    init() {
    value = initializer // Not allowed
    }

    get {
    return value + initializer
    }
    }

    class Foo {
    var [deferredInit] foo: Int = self.bar // OK
    var bar: Int
    }
    ```

    When a behavior declares an initializer requirement, the name `initializer`
    is bound within the behavior declaration as a computed get-only property that
    evaluates the property's initializer expression. A property behavior with a
    `base` property member cannot require an initializer. A property behavior
    with an initializer requirement also cannot be applied to a property
    declaration with a destructuring pattern.

    ```swift
    var [deferredInit] (a, b) = tuple // Error
    ```

    Initializer requirements do not take visibility modifiers; they are always
    as visible as the behavior.

    ### Core Accessor Declarations

    The behavior implements the property by defining its *core accessors*,
    `get` and optionally `set`. If a behavior only provides a getter, it
    produces read-only properties; if it provides both a getter and setter, it
    produces mutable properties (though properties that instantiate the behavior
    may still control the visibility of their setters). It is an error if
    a behavior declaration does not provide at least a getter.

    ### Using Behaviors in Property Declarations

    Property declarations gain the ability to declare behaviors,
    with arbitrary accessors:

    ```text
    property-decl ::= attribute* decl-modifier* core-property-decl
    core-property-decl ::=
    ('var' | 'let') behaviors? pattern-binding
    ((',' pattern-binding)+ | accessors)?
    behaviors ::= '[' visibility? decl-ref (',' visibility? decl-ref)* ']'
    pattern-binding ::= var-pattern (':' type)? inline-initializer?
    inline-initializer ::= '=' expr
    accessors ::= '{' accessor+ '}' | brace-stmt // see notes about disambiguation
    accessor ::= decl-modifier* decl-ref accessor-args? brace-stmt
    accessor-args ::= '(' identifier (',' identifier)* ')'
    ```

    For example:

    ```swift
    public var [behavior1, public behavior2] prop: Int {
    accessor1 { body() }
    behavior1.accessor2(arg) { body() }
    behavior2.accessor3 { body() }
    }
    ```

    If multiple properties are declared in the same declaration, the behaviors
    apply to every declared property. `let` properties cannot use behaviors
    in this proposal.

    When a property is declared with behaviors, the behaviors are instantiated
    left-to-right, with each instantiated property used as the base property of
    the next behavior instantiation. It is an error if any of the behaviors after
    the first don't declare a base property, or if the behavior cannot be
    instantiated for the property's type or `self` type. If the behaviors require
    accessors, the implementations for those accessors are taken from the
    property's accessor declarations, matching by name. If two behaviors require
    accessors of the same name, the accessor definitions can disambiguate using
    qualified names. If an accessor requirement takes parameters, but the
    definition in for the property does not explicitly name parameters, the
    parameter labels from the behavior's accessor requirement declaration are
    implicitly bound by default.

    ```swift
    var behavior foo: Int {
    accessor bar(arg: Int)
    get { return 0 }
    }

    var [foo] x: Int {
    bar { print(arg) } // `arg` implicitly bound
    }

    var [foo] x: Int {
    bar(myArg) { print(myArg) } // `arg` explicitly bound to `myArg`
    }
    ```

    If any accessor definition in the property does not match up to a behavior
    requirement, it is an error.

    To preserve the shorthand for get-only computed properties, if the accessor
    declaration consists of code like a function body, that code is used as
    the implementation of a single accessor named `get`:

    ```swift
    var [foo] x: Int {
    return 0
    }

    // is sugar for:

    var [foo] x: Int {
    get {
    return 0
    }
    }
    ```

    The parser resolves the ambiguity between implicit getter and named accessor
    syntax by reading ahead for `identifier ('.' identifier)* ('(' .* ')') '{'`,
    favoring parsing as a named accessor declaration if the lookahead succeeds.

    If a property with behaviors declares an inline initializer, it behaves as
    follows:

    - If the first behavior has an initializer requirement, the initializer
    expression is bound as the initializer when instantiating the behavior.
    An initializer requirement in a behavior can only be satisfied if the
    declaration is not destructuring.
    - If the first behavior declares a base property, and the base property is
    not satisfied by `override`-ing a super property, then the initializer
    expression is used to initialize the storage of the default stored base
    property.
    - Otherwise, it is an error to provide an inline initializer.

    ```
    var behavior noInitializer: Int {
    get { return 0 }
    }
    var behavior initializerReqt: Int {
    deferred initializer: Int
    get { return 0 }
    }
    var behavior baseProp: Int {
    base var value: Int
    get { return 0 }
    }
    var [baseProp] x = 0 // OK, base property initialized to 0
    var [initializerReqt] y = 0 // OK, satisfies initializer requirement
    var [initializerReqt] (a, b) = tuple // Error, behavior can't destructure
    var [noInitializer] z = 0 // Error, behavior doesn't support initializer
    ```

    If a behavior instantiates a default stored base property, it may be an
    error to leave out the initializer, if the property is not
    initialized before use.

    ### Accessing Behavior Members on Properties

    A behavior's properties and methods can be accessed on properties using the
    behavior under `property.behavior`:

    ```swift
    var behavior foo: Int {
    var storage: Int = 0
    func method() { }
    get { return storage }
    }

    var [foo] x: Int
    print(x.foo.storage)
    x.foo.method()
    ```

    To access a behavior member, code must have visibility of both the property's
    behavior, and the behavior's member. Behaviors are `private` by default,
    unless declared with a higher visibility. A behavior cannot be more visible
    than the property it applies to.

    ```swift
    // foo.swift
    var behavior foo: Int {
    private var storage: Int = 0
    func method() { }
    get { return storage }
    }

    // bar.swift
    var [foo] bar: Int
    var [internal foo] internalFoo: Int
    var [public foo] publicFoo: Int // Error, behavior more visible than property

    _ = bar.foo.storage // Error, `storage` is private to behavior
    bar.foo.method() // OK

    // bas.swift
    bar.foo.method() // Error, `foo` behavior is private
    internalFoo.foo.method() // OK
    ```

    Methods, properties, and nested types within the behavior can be accessed.
    It is not allowed to access a behavior's `init` declaration, initializer
    or accessor requirements, or core accessors from outside the behavior
    declaration.

    ### Extensions on Behaviors

    Property behaviors should support extensions, to allow new methods or computed
    properties to be added, or to provide categorization:

    ```swift
    var behavior foo: Int {
    base var value: Int
    get {
    return value
    }
    }

    extension foo {
    mutating func inc() {
    value += 1
    }
    }
    ```

    As with struct or enum extensions, behavior extensions cannot introduce
    additional storage. Since behaviors are not types, behavior extensions cannot
    declare protocol conformances. Behavior extensions can however constrain the
    generic parameters of the behavior, including `Self`, to make the members
    available only on a subset of property and container types.

    ## Impact on existing code

    By itself, this is (almost, see below) an additive feature that doesn't impact
    existing code. However, it potentially obsoletes `lazy`, `willSet`/`didSet`,
    and `@NSCopying` as hardcoded language features. We could grandfather these
    in, but my preference would be to phase them out by migrating them to
    library-based property behavior implementations. (Removing them should be its
    own separate proposal, though.)

    One potentially breaking change is the disambiguation rule for implicit
    getter accessors. The rule favors parsing as a named accessor, which would
    break code that intends to define a getter whose first statement uses trailing
    closure syntax:

    ```swift
    var foo: Int {
    doSomething { // parsed as named `doSomething` accessor
    }
    return 0
    }
    ```

    The potential impact is amplified if qualified accessor names with explicit
    arguments are considered:

    ```swift
    var foo: Int {
    x.doSomething(arg) { // still parsed as named `x.doSomething` accessor
    }
    return 0
    }
    ```

    which strikes me as problematic. Ideas for a better disambiguation rule are
    welcome. An alternative to consider would be requiring non-core accessor
    implementations to be decorated with an `accessor` keyword, like the
    requirement declarations are in the behavior:



    It's also worth exploring whether property behaviors could replace the
    "addressor" mechanism used by the standard library to implement `Array`
    efficiently. It'd be great if the language only needed to expose the core
    conservative access pattern (`get`/`set`/`materializeForSet`) and let all
    variations be implemented as library features. Note that superseding
    `didSet`/`willSet` and addressors completely would require being able to apply
    behaviors to subscripts in addition to properties, which seems like a
    reasonable generalization.

    ## Alternatives considered

    ### Using a protocol (formal or not) instead of a new declaration

    A previous iteration of this proposal used an informal instantiation
    protocol for property behaviors, desugaring a behavior into function calls, so
    that:

    ```swift
    var [lazy] foo = 1738
    ```

    would act as sugar for something like this:

    ```swift
    var `foo.lazy` = lazy(var: Int.self, initializer: { 1738 })
    var foo: Int {
    get {
    return `foo.lazy`[varIn: self,
    initializer: { 1738 }]
    }
    set {
    `foo.lazy`[varIn: self,
    initializer: { 1738 }] = newValue
    }
    }
    ```

    There are a few disadvantages to this approach:

    - Behaviors would pollute the namespace, potentially with multiple global
    functions and/or types.
    - In practice, it would require every behavior to be implemented using a new
    (usually generic) type, which introduces runtime overhead for the type's
    metadata structures.
    - The property behavior logic ends up less clear, being encoded in
    unspecialized language constructs.
    - Determining the capabilities of a behavior relied on function overload
    resolution, which can be fiddly, and would require a lot of special case
    diagnostic work to get good, property-oriented error messages out of.
    - Without severely complicating the informal protocol, it would be difficult to
    support eager vs. deferred initializers, or allowing mutating access to
    `self` concurrently with the property's own storage without violating `inout`
    aliasing rules. The code generation for standalone behavior decls can hide
    this complexity.

    Making property behaviors a distinct declaration undeniably increases the
    language size, but the demand for something like behaviors is clearly there.
    In return for a new declaration, we get better namespacing, more
    efficient code generation, clearer, more descriptive code for their
    implementation, and more expressive power with better diagnostics. I argue that
    the complexity can pay for itself, today by eliminating several special-case
    language features, and potentially in the future by generalizing to other kinds
    of behaviors (or being subsumed by an all-encompassing macro system). For
    instance, a future `func behavior` could conceivably provide Python
    decorator-like behavior for transforming function bodies.

    ### Declaration syntax

    Alternatives to the proposed `var [behavior] propertyName` syntax include:

    - A different set of brackets, `var (behavior) propertyName` or
    `var {behavior} propertyName`. Parens have the problem of being ambiguous
    with a tuple `var` declaration, requiring lookahead to resolve. Square
    brackets also work better with other declarations behaviors could be extended
    to apply to in the future, such as subscripts or functions
    - An attribute, such as `@behavior(lazy)` or `behavior(lazy) var`.
    This is the most conservative answer, but is clunky.
    - Use the behavior function name directly as an attribute, so that e.g. `@lazy`
    works.
    - Use a new keyword, as in `var x: T by behavior`.
    - Something on the right side of the colon, such as `var x: lazy(T)`. To me
    this reads like `lazy(T)` is a type of some kind, which it really isn't.
    - Something following the property name, such as `var x«lazy»: T` or `var
    x¶lazy: T` (picking your favorite ASCII characters to replace `«»¶`). One
    nice thing about this approach is that it suggests `self.x«lazy»` as a
    declaration-follows-use way of accessing the backing property.

    ### Syntax for accessing the backing property

    The proposal suggests `x.behaviorName` for accessing the underlying backing
    property of `var (behaviorName) x`. The main disadvantage of this is that it
    complicates name lookup, which must be aware of the behavior in order to
    resolve the name, and is potentially ambiguous, since the behavior name could
    of course also be the name of a member of the property's type. Some
    alternatives to consider:

    - Reserving a keyword and syntactic form to refer to the backing property, such as
    `foo.x.behavior` or `foo.behavior(x)`. The problems with this are that reserving
    a keyword is undesirable, and that `behavior` is a vague term that requires
    more context for a reader to understand what's going on. If we support multiple
    behaviors on a property, it also doesn't provide a mechanism to distinguish between
    behaviors.
    - Something following the property name, such a `foo.x«lazy»` or `foo.x¶lazy`
    (choosing your favorite ASCII substitution for `«»¶`).
    - Doing member lookup in both the property's type and its behaviors (favoring
    the declared property when there are conflicts). If `foo` is known to be
    `lazy`, it's attractive for `foo.clear()` to Just Work without additional
    syntax. This has the usual ambiguity problems of overloading, of course; if
    the behavior's members are shadowed by the fronting type, something would be
    necessary to disambiguate.

    ### Core accessors for property behavior implementation

    The proposal as written specifies `get` and `set` as the only accessors that
    can be used to implement a property behavior. For efficiency, we may want
    to also allow the standard library to use addressors to implement standard
    behaviors, though it would be nice to design a fundamental core accessor model
    that subsumes addressors too. It'd also be a reasonable extension to
    allow a property behavior declaration to itself instantiate behaviors, and
    define itself in terms of those behaviors' accessors.

    ### Behaviors for immutable `let` properties

    A previous revision of this proposal allowed for behaviors to apply to
    `let` properties. Since we don't have an effects system (yet?), `let`
    behavior implementations have the potential to invalidate the immutability
    assumptions expected of `let` properties, and it would be the programmer's
    responsibility to maintain them. We don't support computed `let`s for the
    same reason, so I suggest leaving `let`s out of property behaviors for now.
    `let behavior`s could be added in the future when we have a comprehensive
    design for immutable computed properties and/or functions.

    ## TODO

    **Resilience rules for behaviors**: IMO, storage for behaviors should be
    fragile, since otherwise they require the equivalent of type metadata to
    abstract their layout across resilience boundaries. Other optimizations
    are possible depending on how much flexibility we want to allow in
    property behavior evolution. Some questions to answer include:

    - Can a behavior add new accessor requirements (with defaults)?
    - Can a behavior introduce references to `self` if it doesn't already use
    it?
    - Can a behavior freely change its private, non-stored-property members?
    - Can a behavior relax its generic constraints, on either the property type or
    on `Self`?

    Some fundamental constraints:

    - A behavior can't resiliently add, take away, or change the eagerness of an
    initializer requirement.
    - A behavior can't change whether it has a base property.
    - A behavior can't remove accessor requirements.