[swift-evolution] [Review] SE-0030 Property Behaviors

Brent Royal-Gordon brent at architechies.com
Sat Feb 20 00:16:09 CST 2016

> For instance, allow it to be a struct if you need to implement your own storage:
> 	struct DelayedStorage<Value> {
> 		var storage: Value?
> 		var value: Value {
> 			get {
> 				guard let theValue = storage else {
> 					fatalError("delayedImmutable property read before initialization")
> 				}
> 				return theValue
> 			}
> 			set {
> 				guard storage == nil else {
> 					fatalError("delayedImmutable property rewritten after initialization")
> 				}
> 				storage = newValue
> 			}
> 		}
> 		init() {
> 			storage = nil
> 		}
> 	}
> 	@DelayedStorage var outlet: UIView

Some of the points in the "Using a protocol (formal or not) instead of a new declaration" alternative discussed in the proposal (https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#using-a-protocol-formal-or-not-instead-of-a-new-declaration) are relevant to this suggestion too:

> 	• 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.

In particular, note the point about runtime overhead. Behaviors are carefully designed to be basically equivalent to writing the same code yourself every time you need that semantic. If we weren't aiming for that, the design would be much simpler: we'd have an `init(initialValue:)` that could handle deferred initialization by using an `@autoclosure` and storing it in a property, for instance. But compared to writing the same thing yourself, that would waste memory on an extra property to store the closure, so we don't want to do that.

> And, just teasing... but maybe there's a way a behavior could be attached as a type modifier instead of a property modifier. This would allow you to write `var delayedObjects: [@DelayedStorage NSObject]` and not have to access the value by appending `.value` every time.

I think this is fraught with difficulties that will make it impractical at best.

Suppose you have a property `var foo: @Lazy Foo`, and you assign it to a variable like so: `let bar = foo`. What is the type of `bar`? Does `@Lazy` follow it somehow? What does it even mean if it does?

Suppose you have an `Array<@DelayedStorage NSObject>` and a method which expects an `Array<NSObject>`. Can you pass the array to the method? Is this answer correct for all possible behaviors?

Suppose you have an `Array<NSObject>` and a property which contains an `Array<@DelayedStorage NSObject>`. Can you assign the array to the property? Is this answer correct for all possible behaviors?

Suppose you have an `Array<@DelayedStorage NSObject>`. What type should `Element` be in each of these Array members? What is the algorithm by which you can produce the correct result for each one programmatically, or the syntax by which you could annotate them to get the correct result?

	subscript(index: Int) -> Element { get set }
	var first: Element?
	func indexOf(element: Element) -> Int?
	mutating func append(newElement: Element)
	mutating func removeLast() -> Element
	func minElement(@noescape isOrderedBefore: (Element, Element) throws -> Bool) rethrows -> Element?
	mutating func replaceRange<C : CollectionType where C.Generator.Element == Element>(subRange: Range<Int>, with newElements: C)
	mutating func withUnsafeMutableBufferPointer<R>(@noescape body: (inout UnsafeMutableBufferPointer<Element>) throws -> R) rethrows -> R

Ultimately, my sense is that trying to make behaviors-as-implicit-types in this fashion ends up with a lot of the problems of implicitly unwrapped optionals: essentially, it's hard to tell how far type inference will propagate the implicit types, or when the special implicit logic is invoked. We want less of that, not more.

Brent Royal-Gordon

More information about the swift-evolution mailing list