SE-0258: Property Delegates

  • What is your evaluation of the proposal?

+1 generally, with some reservations about naming/syntax

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes, IMO an extensible declaration feature similar to Annotations is one of the biggest things currently missing in Swift. This addresses one of the clearest use cases as is shown by how many things it could replace in Swift itself, and could also be used to implement a number of often desired capabilities that would normally require a change to the language or that do not belong in the standard library but are currently impossible/impractical to implement.

Syntax Reservations

1. I'm not sure I see a need to add syntax rules ($propertyName) for something where I would expect the use to be, if anything, discouraged. Is there a reason propertyName.backingStorage or something of the like can't be used?
2. I'm not a fan of @propertyDelegate being the naming. At the very least I think the "@" should be dropped as it seems to hold no current function other than to look similar to the thing being implemented, but more generally I'm not sure delegate should be used as a keyword.

Given this type of syntax could (and hopefully will) be added to functions/methods and parameters (e.g. oft-requested improvements to [weak self] in closures), I would prefer a well planned out naming scheme that nicely includes the likely future use cases. Things that come to mind are declarationProtocol and identityProtocol, or if property is desired, propertyProtocol.

That's not to say it should be a protocol. I don't think their current capabilities could properly support this type of feature in a clear and concise manner.

  • Does this proposal fit well with the feel and direction of Swift?

Yes, in the functionality it enables, no in the ways previously expressed. I'm hopeful it could enable better support for meta-programming and possibly some QOL features e.g. @CodingKey(name: ":custom_field:") for Codable implementations...just a thought :wink: )

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I've used Annotations in Java/Kotlin a decent amount. Those are pretty different in scope than this, but seems to be solving some of the same problems in a similar way.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

semi-detailed read through and a long-time desire for this type of feature.

3 Likes

Sorry it wasn't clear. Here's an update to make it clearer: [SE-0258] Clarify that property delegates cannot start with a lowercase letter by DougGregor Ā· Pull Request #1027 Ā· apple/swift-evolution Ā· GitHub

Doug

2 Likes

I believe the custom-attributes proposal says that @Attribute is resolved by looking up Attribute, which has to resolve to a type with a special builtin attribute on it that declares the type to be usable as a custom attribute. @propertyDelegate would be one such attribute, but that set can be arbitrarily extended in the future.

You can (equivalently for well-formed code) consider this to be a lookup in a separate namespace of all custom attributes, in which case an attribute like @propertyDelegate would make the declaration visible in both namespaces.

In either case, there's arbitrary room for expansion in the future.

3 Likes

Weak +1. This is an important feature for Swift and one that will be used frequently. I love the concept and appreciate the thoughtfulness that have gone into both the proposal and the discussion.

However, it seems fair to say that the community hasn't yet reached a common perspective on exactly what this feature should look like. The review seems a bit premature, even after multiple rounds of discussion. And I say this knowing full well how frustrating it must be for Doug Gregor and Joe Groff to read, given that they've put so much detailed analysis into the current proposal and have so carefully responded to commentary.

At heart, my reservations about the proposal are aesthetic. It's kind of clunky. This should be a marquee feature that shows off the elegance and power of Swift. But it feels like we're on the verge of settling for a Frankenstein's monster of magical @attributes, secret variables, and naming conventions formerly unknown to science. It seems perfectly functional; it just doesn't feel very designed. Nobody's going to look at these property delegates and think, "Oh, how beautiful."

Or at least, that's my subjective impression. I would love to get this out into the wild and see how it actually feels in daily use. I hope there's still room for this kind of experimentation in the Swift development process.

Yes, absolutely. We need this in some form.

Kind of? See above. :slight_smile:

I have read the proposals and participated in the previous discussions. But there are clearly many subtleties to this feature. I'm sure many of the nuances are going right over my head.

13 Likes

I've been following the discussions on this with growing excitement. It will be a very powerful feature that reduces a lot of repeated work. The $ storage access works for me. There's a precedent from closures for Swift to make available special entities prefixed with $.

Minor syntax note: I was fine with the Kotlin-esque by syntax (or via, an alternative from the pitch thread), and I actually find the advantages described in the proposal to be a convincing argument for it! The novelty of the keyword alone doesn't feel to me like a persuasive enough reason to reject it. I acknowledge this ship may have already sailed, and if so, I'm fine with the proposed syntax.

Edit: I meant to reply to the first post.

1 Like

@John_McCall Do you mind linking it? I can only find a pitch, and old discussion, I couldnā€™t find anything progressed enough to be the foundation of this proposal.

This doesn't block off future uses of @+Uppercase names for other custom attributes. When you have an @Foo attribute, we go look up the name Foo via the normal name lookup rules. It should come to a type (or the program is ill-formed), and if the type is not a @propertyDelegate then the program is ill-formed. We can add other ways to state that a given type can be used as a custom attribute. Perhaps a given type cannot opt in to being, say, both a property delegate type and a runtime attribute type, but I think that's a reasonable restriction that doesn't limit our evolution too much.

Doug

2 Likes

I just mean the pitch. I don't think we've explicitly spelled out this as a common concept across several pitches/proposals yet, but I think that's what's developing.

1 Like

Here's the custom attributes proposal: https://github.com/hartbit/swift-evolution/blob/static-custom-attributes/proposals/XXXX-static-custom-attributes.md

Doug

3 Likes

Thanks, looks interesting, I think similar has been one of the more powerful features of Java and C# that Iā€™ve seen.

Like this proposal Iā€™m +100 for the general idea of it. Although it seems to me that a lot of the confusion/reviews in this proposal are because weā€™re reviewing this one first, yet this one is a dependency of the custom attributes proposal.

Itā€™s probably okay, but the community might come up with an alternative syntax or approach for custom attributes, then this proposal would be in conflict.

1 Like

Very positive, I've been waiting for the property behaviours proposal for years :smiley: (this and some form of newtype!)

And let me say how much I love the proposal giving examples and offering a toolchain to try, makes it so nice to review!

Yes, I love languages that implement their amazing features at user level. Swift has a lot of this already but is still missing in some aspects. This proposal makes some of this stuff being able to be in a library. And I like the proposal mentions some of them already.

That said there are some things I would like to note:

  • Referencing the enclosing 'self' in a delegate type: I think this is a future direction that we should pursue soon, as without this there are a bunch of powerful abstractions that can't be done.

  • Similarly Delegating to an existing property seems necessary for some examples: Check out Kotlin's Storing Properties in a Map, I don't think we can do that with the current proposal.

  • Talking about Kotlin examples: the Observable example is really nice with Kotlin syntax but I don't think would be that nice in Swift. Passing the closures to the annotation would be really awful. Is there any alternative or idea to improve this? (I know this example is not useful because we have compiler magic for didSet, but still, would be nice to allow this to happen at user level)

  • Another note about initialization syntax: Could there be a way to separate the initialization of the value from configuration of the Delegate? For example I would like to write:

@Printer(prefix: "pre")
    var number = 1

but that doesn't work: // Property 'number' with attached delegate cannot initialize both the delegate type and the property.
I understand that with the design based on a type this is not possible, but honestly it's something that will bother me a little every time I use it ^^'
I don't mind not having the by Delegate syntax from Kotlin, but seems like having that syntax allows for nicer usages.

Absolutely, I even like that the proposal mentions the usage of attributes, and links to the conversation in the forum about exposing attribute creation to user space also.

I've recently had to use Atomic properties, and being able to use them with something as simple as @Atomic would be really nice. It would avoid having to pollute the code with atomic.value all over the place.

It also will help closing the gap with late initialisation, something that Kotlin has and is really useful. I love that Swift has lazy but the being compiler magic has made it impossible to port lateinit to Swift. Now we can.

The usage of $ I find it good but unfortunate. I think is good because it mimics the $0 in closures, and for me that means " $something is for compiler generated variables" and I can understand that easily. But is unfortunate because I'm not a fan of having to use $var to access the API of the Delegate. I understand that otherwise the namespaces could clash, so I'm not that opposed to it.

Kotlin has this feature and is really nice. It allows for nice APIs and quite powerful customisation at the user level.

I've been following this since was first suggested in the old mailing list ^^ I've read the proposal multiple times and tried the toolchain.


Without affecting the proposal itself, I have a question

  • Are there any performance implications on moving lazy out from the compiler?

and some comments about the toolchain, I guess the issues are just because is an early implementation:

  • The Delayed Initialization example doesnā€™t compile. There is some keywords mistyped in the proposal text.
  • I haven't been able to compile the @Ref(read: ā€¦, write: ā€¦). If I pass a closure there it crashes the compiler.
  • Same for Box: ā€œprops.rectangle.getter : props.Rectangleā€, referenced from:
  • This last error happens with many examples that I try to write.

Cheers

2 Likes

The casing restriction does feel a bit at odds with the rest of the language design, where we so far have avoided making distinctions about identifiers based on case or other identifier characteristics. Case is also a Western script centric concept; is ꇒ or ŁƒŲ³ŁˆŁ„ a valid property delegate name?

To keep user-defined attributes separated from future expansion of compiler builtin attributes, it would make sense to me to say that the builtin attributes live in the Swift standard library module, and allow user-defined attributes to shadow them, like other identifiers work today. You could then write @Swift.futureBuiltinAttribute to use an attribute we add as a language builtin in the future.

14 Likes

Weak +1. Iā€™m somewhat concerned about composability with multiple delegates and access to self, but in principle I think this is generally a great direction.

Yes, manual handling on a case-by-case basis is error prone and a wholistic solution that is also declarative is a welcome addition to the language.

In general, yes. I feel like weā€™re running out of room with attributes, # based macroey things, though. My hope is the language can pull back from the flood of language features that mess up Swift code from its elegant beginnings.

N/A

A quick reading and followed the pitch threads.

Just want to put my support behind what @Joe_Groff just said:


In general I look forward to being able to use property delegates. Generic behaviour wrapper types have been in my code since the beginning, but have until now required extraneous code at the call site: print(object.behaviourModifier.value). It will be nice to reduce this.

-1

I think a complete solution should be capable of replacing today's lazy. Also not thrilled with the backing $ var, or the name itself propertyDelegate, which is easily confused with the pervasive delegate pattern to iOS developers (and we should acknowledge the 90% of Swift developers are iOS developers)

7 Likes

I like the proposed syntax for delegates. I especially like that types cannot be retroactively made property delegates behind the back of the original module that defined them.

I'm a bit miffed that delegates can't constrain (or get information about) the context they in which they are used -- i.e., the instance (or at least the type) that contains the property and a key path to the property that's being implemented. However, I'm happy to accept the proposed limitations as long as we have room to potentially eliminate them later. (Which it looks like we do.)

While the examples are illuminating, I find some of them unconvincing and/or questionable:

  • Atomic<T> makes no sense to me as a property delegate. The interface for atomics consists of the explicit load/store/compareExchange operations -- it would be a bad idea to hide those away behind a $ sign, especially in the current design where the storage property cannot even be made public.

    Swift should not emulate Objective-C's atomic properties. Encouraging people to pretend that Atomic<Int> values can be treated as normal get/set properties would lead to data races.

    @Atomic var foo: Int = 0       // BAD PATTERN, DO NOT USE
    ...
    foo += 1 // Unsafe; silent data race
    

    A regular standalone generic type (with type-specific extensions) would be much more appropriate:

    var bar: Atomic<Int> = 0
    ...
    // Operations are somewhat more verbose, but they are safe.
    // It's extremely clear what constitutes a single transaction, and
    // we have an easy way to expose all available functionality.
    bar.increment() 
    bar.increment(ordering: .relaxed)
    // If pressed, we can also add custom operator overloads:
    bar += 1 // Safe, if rather limited and (potentially) confusing
    

    (That said, it wouldn't necessarily be practical or desirable to implement atomics through a single generic type, either. I would actually prefer to add a short list of separate atomic types instead.)

  • Making UnsafeMutablePointer a property delegate makes for a cute demo, but would it be wise to actually do that? Is it solving a problem people are currently struggling with?

7 Likes

Is https://ci.swift.org/job/swift-PR-toolchain-osx/282/artifact/branch-master/swift-PR-23701-282-osx.tar.gz still the latest toolchain for this, or is there a newer one? I would like to use the feature before deciding.

Radical changes is an exaggeration. There was a minor change in syntax and the lifting of an unnecessary restriction. As for the timeline,

There have been several years of thought and discussion about the design of this feature, so I think concerns about process and timelines, and discussions about introducing an incubation process to Swift, might be more relevant elsewhere.

When reviewing a proposal, here are some questions to consider:

  • What is your evaluation of the proposal?

+1

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes

  • Does this proposal fit well with the feel and direction of Swift?

To an acceptable degree.

I echo and sympathize with the concerns/comments from @Karl, @codafi, and @GarthSnyder. I am not sure that the actual proposed syntax is the best possible incarnation. However, I also do not feel well qualified to debate this, and overall have significant trust in the core team to make a good decision.

The primary reason for my +1 is that I have worked on several large Swift codebases which would benefit considerably from this feature. My strongest feeling is that this feature, in roughly any form the core team would accept, is so compelling and important to simply get into the language, that that trumps the syntactical concerns.

I do strongly feel that Swift needs more experimental feature mechanisms, and I would like to see more attention paid to the technical mechanisms to safely evolve syntax, so that the community can become unblocked to explore the innovation possible with new features, without also having the feeling that we are potentially committing to a syntax for 10 years.

Ultimately, my belief is that if a better syntax becomes available, that Swift can/will find a way to evolve towards it. In the meantime, I think this feature is important enough to have access to for supporting large code bases and novel paradigms.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No direct experience.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Moderate.

2 Likes

For macos the proposal points to https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz