[swift-evolution] Proposal: Closures capture weak by default

Gwendal Roué gwendal.roue at gmail.com
Tue Dec 8 12:48:17 CST 2015


>> I’m really not found at all of having `let` properties captured by closures when used without explicit `self`.
>> 
>> Three reasons […]
>> 
>> REASON 2. What happens when a property starts its life as `let`, but turns `var` eventually? For example, the property eventually becomes lazy. OK now it’s is illegal to use the property without explicit `self` in closures, and you break a bunch of code. And you need to bump the major version of your package. Just because you turn a `let foo: String` into `lazy var foo: String = …`.
>> 
>> That’s not good at all.
> 
> These are good points, but re: reason 2, turning a public 'let' into a 'var' is already a non-resilient, semantics-breaking change, since you're taking back a promise you made about immutability. You should publish a public 'var' with a private setter if you want to reserve the right to publish mutability in the future.

Is it so sure? I don’t see any difference, API-wise, between `let foo` and `lazy var foo`. Maybe I forgot the private(set), but in this case please be nice and follow me: I’m talking about a public getter that starts its life as `let` and ends its life as `lazy var`. There is no API difference in this case, and no reason to bump the major version of a package that would expose such a getter.

Besides, a non-lazy property that starts its life as `let foo` and ends as `private(set) var foo` is in the same category: there is *no* difference in the public API, and *no* reason for bumping the major version of the lib.

And this is why I keep standing against your proposal of having the presence or absence of `self` change the capture mode of such properties in closures, despite the fact it looks smart at 1st sight.

>> REASON 3. It’s simply not clear. Clever, terse, smart, brilliant, but not clear.
>> 
>> Compare to:
>> 
>> 	// Obviously only `foo` is captured, and not self:
>> 	let foo = self.foo
>> 	performClosure { foo }
> 
> As for reason 3, the only noticeable difference between capturing an immutable property and capturing its container is that the container's lifetime might be shorter than it otherwise needs to be, so you save some memory, and weak references maybe null out sooner. If you were going to capture a weak reference to break a cycle anyway, the latter isn't a problem, and capturing the immutable value directly is preferable, since instead of disappearing, it will still be independently held by the closure.

This does not address my point. My point is that the current state of the language makes it crystal clear about the captured objects, when your proposal makes it 1. unclear, and 2. breaks the fact that the non-mandatory `self` should remain *discretionary*.

I’ve given symmetry examples above. Here is one another:

	let foo = self.foo	// discretionary foo
	self.foo = nil
	// Now self.foo is nil and can expose its new clean state to other objects, for example an eventual delegate.
	// Yet foo is still usable, for whatever purpose.

Without the discretionary self, we’d have to use ugly variable names like `theFoo`, `aFoo`, `myFoo`, or whatever funny names that are nor required today:

	let theFoo = foo
	foo = nil

To sum up: non-mandatory `self.` should remain discretionary. Hence any idea that is based on the absence or presence of an explicit `self.` is a bad idea.

Gwendal Roué



More information about the swift-evolution mailing list