[swift-evolution] lazy keyword vs lazy initialization pattern

Matthew Johnson matthew at anandabits.com
Sun Dec 6 08:36:22 CST 2015


Property delegates do look very nice, but I also wonder whether they would cover the `delayed let x : T` case.  They also don’t know *when* the property is getting set.

I would really like to see something like `delayed let x : T` to handle the second phase of initialization problem, although I would add the requirement that it *must* be initialized in the second phase and it *cannot* be read until it is set (as far as the compiler can detect this through control flow analysis).  This would behave quite similarly to a simple `let x : T` member but with the caveat that it is possible to blow up if you attempt to read before setting it in the second phase and the compiler cannot detect the read attempt when performing control flow analysis.  

If Swift’s property delegates were able to both support let members and require initialization during the second phase (so the instance is guaranteed to be fully initialized and work correctly after initialization completes) then they would be a great solution.  I just can’t imagine how they could enforce this semantic.  I’m looking forward to reading Joe’s proposal and will be delighted if his design is able to support the desired semantics.

Chris, do you agree that this semantic is desirable to support one way or another?  

Thorsten’s attempt to implement something like this using a property delegate looks really dangerous to me.  A var member that blows up when somebody attempts to set it a second time seems extremely fragile.  It’s like an IUO but rather than blowing up when read it also blows up when set more than once.

Matthew


> On Dec 6, 2015, at 6:32 AM, Thorsten Seitz via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hi all,
> 
> I like the property delegates feature from Kotlin, too, but I don’t see how it can replace the „delayed let x : T“ idea with initialization taking place in e.g. awakeFromNib, because a let-property cannot be set. With a var it would probably look roughly like follows, but with „let x : T by Delayed()“ the setValue method would not be allowed (at least in Kotlin):
> 
> var x : T by Delayed()
> 
> with
> 
> class Delayed<T> {
> 	var value : T!
> 	func getValue(this: Any?, property: Property<T>) -> T { return value }
> 	func setValue(this: Any?, property: Property<T>, value: T) {
> 		guard x == nil else { abort(„Property \(property) of \(this) has already been initialized when trying to initialize with value \(value)“) }
> 		self.value = value
> 	}
> }
> 
> Some remarks
> * I chose abort() in this particular case instead of throwing because repeated initialization should be a logical error
> * Are there uses cases where throwing would be the right thing in getValue or setValue? I guess not, just like throwing is not possible in a computed property.
> * Is the compiler able to optimize away the overhead of going through getValue on each access?
> * I was a bit surprised that Kotlin does not require an interface for the delegates to implement. I would expect delegates to be required to implement appropriate interfaces (or protocols) like PropertyReader/PropertyAccessor, probably with a type parameter for the property owner as well (instead of Any?). The latter would allow 
> 
> -Thorsten
> 
>> Am 06.12.2015 um 11:00 schrieb David Hart via swift-evolution <swift-evolution at swift.org>:
>> 
>> This property delegates feature from Kotlin sounds fantastic! As the examples in Kotlin show, it could bring a very powerful solution to many problems in Swift right now: lazy being narrow, cache missing, native KVO :D
>> 
>> This has just become my #1 feature proposal!
>> 
>> Super excited David!
>> 
>>> On 06 Dec 2015, at 08:27, Chris Lattner <clattner at apple.com> wrote:
>>> 
>>> 
>>>> On Dec 4, 2015, at 5:40 AM, David Hart <david at hartbit.com> wrote:
>>>> 
>>>> In Objective-C, I often used the lazy initialization pattern to implement a cache for expensive operations. For exemple, here is an often used scenario in a project where objects behind foreign keys in a database ORM are only fetched when necessary:
>>>> 
>>>> When thinking about it, I came to the conclusion that the use cases of lazy seem very narrow compared to how useful the lazy initialization pattern was in Objective-C.
>>>> I want your opinion on three alternatives:
>>>> 
>>>> 1- Do nothing, and use the slightly uglier Swift example when using a cache.
>>>> 2- Modify lazy semantics to re-calculates when nil (I think this is the worst solution).
>>>> 3- Add a cache modifier that re-calcualtes when nil.
>>> 
>>> Hi David,
>>> 
>>> My preference is:
>>> 4) Kick lazy out of the compiler and make it a standard library feature instead, allowing it to be extended in lots of ways.
>>> 
>>> The existing lazy behavior was added as a very narrow solution to a common scenario that would otherwise required optional machinations everywhere.  It succeeded in that goal, but it is super narrow, and the implementation has a number of bugs (e.g. you can’t have a lazy local variable).
>>> 
>>> In terms of its narrowness, it has the limitations you describe, but it also doesn’t solve other common problems.  For example, many people complain about properties that are initialized only in a second phase of initialization (e.g. awakeFromNib) and right now, most folks resort to using typing their properties as ImplicitlyUnwrappedOptional to avoid using ! at every use site.  A natural solution to that is to introduce a concept of a “delayed var x : T” which does not need initialization in init(), and runtime traps if it is read before initialized.  This provides similar semantics to T!, but without infecting the type system.  Better than T!, a “delayed let x : T” would allow exactly one runtime initialization of the property.
>>> 
>>> The problem with this line of thinking is that - if we kept going down this path - we’d end up adding a ton of very narrow solutions for specific problems, adding language complexity all the way.  This is pretty dissatisfying for a number of reasons. :-)
>>> 
>>> A different approach is to introduce a single language feature that would allow you to define customization behavior for properties in a library, which would be a general solution to these sorts of issues.  Joe Groff is working on a proposal to do just this, similar to the idea of Kotlin property delegates (https://kotlinlang.org/docs/reference/delegated-properties.html).
>>> 
>>> We’ve been a bit busy lately :-) but I’m hoping that he’ll send this proposal out in the next few weeks.  Assuming that proposal works out, I expect lazy to get ripped out of the language, and *that* discussion can reopen detailed debate about what the replacement should look like.
>>> 
>>> -Chris
>>> 
>>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list