Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Suggestion for behaviours proposal

Basic syntax

public behaviour lazy<Value>: Value {
  private var storage: Value?

  // Only `init()` allowed for now
  init() {
    storage = nil
  }
  
  mutating get {
    if let containedValue = storage {
      return containedValue
    } else {
      storage = initialValue()
      return storage!
    }
  }

  // Note `mutating`
  mutating set {
    storage = newValue
  }
  
  // `initialValue` is a special case of accessor
  // If `initialValue` accessor is defined, then such variable
  // will need to have an initializer. Otherwise, it will need not to have one. 
  accessor initialValue() -> Value
}

func basicUsage() {
  @lazy var num: Int = 42
  print(num)
}

Example with observed

public behavior observed<Value>: Value {
  var value = initialValue()
  
  accessor initialValue() -> Value
  
  mutating accessor willSet(newValue: Value) { }
  mutating accessor didSet(oldValue: Value) { }

  get {
    return value
  }

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

Explanation

As Chris Lattner stated, declaration form of the current proposal public let [lazy] _: Value { ... } is a precedent. All Swift type declarations (besides typealiases) have form of MODIFIERS DATATYPE_KIND NAME<TEMPLATE_PARAMS> BODY. We can easily follow this pattern. I find this syntax cleaner and more readable, although some may disagree.

var keyword is not really needed in the current proposal. No support for let properties is suggested right now. For a future direction, I suggest that let properties with behaviours differ in that mutable members cannot be called on them. (Hence mutable set, for more customization.)

One reason why I moved Value type parameter to generic parameters is because it's a type, on which the behavious is parametrized, and those types have always been declared like that in Swift. (Consistency, that is.) Behaviours must have exactly one generic parameter.

The other reason is that as a future direction, it's possible to make behaviours accept more generic parameters. I don't include it here, as it goes too far from the current state of things.

Future directions

  1. Naming type of a variable together with its behaviour: @lazy Int or lazy<Int>
  2. Functions accepting behaviours: func acceptLazyVar(lazyInt: @lazy Int)
  3. Accessing behaviour and its methods: acceptLazyVar(lazyInt.@lazy), lazyInt.@lazy.clear()
  4. Multiple behaviours: @synchronized @lazy var shared: Resource
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.