SE-0258: Property Delegates

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

What is your evaluation of the proposal?

50/50

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

If it is decided to never have atomic in Swift, I’m afraid we have no choice. So, yes.

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

Not sure, the new syntax i$ very confusing.

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

N/A

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

I read through both pitches.

+1
I read the proposal and have followed the various threads that have lead to this.

I agree that the syntax is heavier than most of the features that we have seen thus far but I don't know that the proposed solutions are much better or are workable.

I regret that delegates aren't protocols and can't be combined.

Even in a simple use case I've needed combinations of delegates: I coded an app where the user could read and edit documents, just that. When a document was opened, its data was represented by objects which were either read from the file, and so were lazy, either new objects created by the user, and so were fully initialized. So the storage of a single property could be either normal, either lazy, and in both cases I needed KVO.

According to the proposal, KVO delegates may be handled in the future.

With protocols we can make operators to make those combinations, but without them we're left with glue code.

+1 for the proposal, with one caveat - I'm not a fan of the $myVar.prop syntax.

Conceptually it seems to me that "myVar" is the thing, and "myVar.etc" are the properties. The $ signifies that the variable name refers to something that's related to, but not actually my variable, which seems odd.

myVar.$prop would be preferable to me. I didn't see it mentioned in the 'Alternatives considered' section, although I don't know if it would conflict with other language features.

myVar.Lazy.prop ?

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

A quick reading.
Thanks.

1 Like

+1. This feature will allow me to remove a lot of boilerplate code. I also really like how it transformed through two pitch threads. Now it feels very swifty.

I think we need another round of revision on this feature before it’s ready for prime time.

• • •

First, the compiler-synthesis aspect is essentially sugar to reduce boilerplate. We can already write computed properties that trivially forward access to another property as the proposal describes. Sure, doing so manually takes a few extra lines of code, but it is certainly a pattern that people can employ today.

So…are they?

Is this pattern in common use? Do major projects define their own UserDefaults type in this manner? Or DelayedImmutable or CopyOnWrite or Atomic or any of the other examples shown in the proposals?

If the pattern of encapsulating behavior in a delegate type is in such high demand to warrant special syntax and inclusion in the standard library, then I would expect to see it in regular use already even without the sugar.

• • •

Second, although the proposal calls itself “property delegates”, it does not actually enable programmers to delegate one property to another. Instead, it is shaped much more like “property behaviors lite”.

The dollar-sign spelling for the delegate seems entirely magical, and goes against the existing precedent of using an underscore. I would prefer to see the synthesized property either spelled with an underscore and always private, or else invisible, unnamed, and inaccessible.

In the case where a different name or visibility is required, I would expect it to be possible for a programmer to manually declare the delegate property, with some way to indicate that the main declaration delegates to it. After all, if we are going to introduce property delegates, the core feature should be providing a convenient way to delegate one property to another.

• • •

If instead what we actually want is property behaviors, then we should start with a clear roadmap so we know where we want to end up. It seems clear that access to self is important, and it is not part of the current proposal.

The “future directions” section talks at length about making self available, which indicates that the authors recognize its importance. Yet the proposal still does not offer that functionality.

I do not think we should pursue this in fits and starts. Instead, we should prepare a single cohesive vision for it. Perhaps multiple proposals will still be required, but regardless of that we should know where we are planning to go.

• • •

Furthermore, with a significant change like adding custom @ attributes, I strongly believe we should have more time with the toolchain so that people can try out the feature, gain real-world experience with it, and work through the kinks.

I would not want to review this feature until I had the opportunity to use it hands-on for an extended amount of time. After all, there could be many different designs in this space, and we should be highly confident we have found the right one before permanently adding it to the language.

9 Likes