SE-0258: Property Delegates

The review of SE-0258: Property Delegates begins now and runs through April 23rd, 2019.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager via email or direct message on the forums. If you send me email, please put "SE-0258" somewhere in the subject line.

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

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

  • What is your evaluation of the proposal?

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

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

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

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

Thank you for contributing to Swift.

John McCall
Review Manager

13 Likes

What is your evaluation of the proposal?

+1

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

I actually do not know. This introduces a lot of more complexity to the language specially with the introduction of $ auto synthesized storage variable. This brings me back when I was learning obj-c and was totally confused about synthesized getters and setters.

I think the name doesn't fit the feature as proposed. The only part that would fit the name is the "Delegating to an existing propety" which is not part of the proposal but if it was, would we need $ ?
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-delegates.md#delegating-to-an-existing-property

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

The latest swift proposal have been trying to introduce a lot of features that would make the language more complex (some aka opaque) and I feel that this one is also in the same complexity area.

It would seem to me that swift its trying really hard to be like rust but if I wanted swift to be like rust I would just go use rust. I've been using golang and It reminds me of swift a lot but lately i feel that it will become so complicated that I might as well learn rust for another project. I have a dream of using Swift outside of iOS.

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?

Read both pitches

3 Likes

:tada: :raised_hands: :ok_hand: A huge +1

I would say yes. Additions to the language that allows you move compiler implemented features to the standard library (in the case of IBAction or IBOutlet) or removing large amounts of boilerplate (in the case of lazy or Copy-on-Write) are things we should strive for in the evolution of Swift.

Yes. The proposed solution feels ergonomic to me. The proposed attribute syntax fits very well with the current state of Swift in my opinion.

I like the proposed $propertyName storage access, since synthesized values (such as closure arguments) already use the $ delimiter.

N/A

I followed both forum threads and re-read the proposal before responding here,

1 Like
  • What is your evaluation of the proposal?
    “I am a patriotic supporter of this proposal” (cc @Ben_Cohen )
    Although I feel delegateValue is a little bit too much magic for Swift, I can live with that.

  • Is the problem being addressed significant enough to warrant a change to Swift?
    I would say yes as it would allow us to reduce a lot of painful boilerplate code and hopefully simplify things such as IBOutlet.

  • Does this proposal fit well with the feel and direction of Swift?
    After a lot of iteration from the original proposal I would say yes.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    Read the proposal like 100 times and participated in both related pitch threads.

  • What is your evaluation of the proposal?

Very much a +1 overall. I think another usage of this which I haven't seen anyone mention is dependency injection, which I'm looking forward to using.

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

Yes. I believe this makes code more concise while also making it easier to reason about.

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

Yes, we already have similar behavior that's baked in which we'll now be able to use ourselves.

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

Participated a bit in the pitch, but followed it all the way through. I still have two concerns, but they're not blockers:

Access Control
I'm still concerned that we didn't conclude the access control discussion, or at least make the default for storage private. Take the BehaviorRelay example, where the explanation of wanting the storage to be internal was the ability to call $storage.observe(). That has the unfortunate downside of exposing all the methods on $storage, which would include giving outside classes the ability to write to the BehaviorRelay - something you generally never want to do.

I feel like if you want methods on the storage exposed, you should instead vend an object that has the methods exposed and leave storage private. For example, if I want to give outside classes the ability to read my value and observe it, the var's type should be a class that exposes value T and func observe<T>() instead of being type T itself. This gives the author more control over what is exposed.

$ prefix for storage
Not trying to bikeshed, but I feel like an operator should mean something consistent. For example, when I see @ going forward, regardless of if it's the static attribute or a property delegate, the @ is signifying to me that i'm giving the class/var/whatever a new attribute of some kind which may or may not alter it's behavior.

On the other hand, the only other time I see $ is with closure paramater shorthand, i.e. $0, $1 etc. I'm struggling to come up with a connecting theme across $ if it's used here as the storage for the propertyDelegate of a var. It feels like the motivating reason to use $ is it happens to currently be reserved by Swift and I'm not sure I feel that's a good enough reason.

3 Likes
  • What is your evaluation of the proposal?

+1. This allows for simpler code by reducing boilerplate and also allows to remove some compiler magic.

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

Yes. This enables many new features in Swift.

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

The final design looks very simple/elegant and yet powerful.
The only thing I find a bit clunky are the "accessing self" ideas in "future directions". As such it was a good idea to leave that out of the current design. (It is hard to beat the ergonomics of a simple lazy keyword for sure)

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

Nope

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

Followed both threads daily and re-read the final proposal carefully.

PS: Does this now make SE-0153 obsolete?

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

Absolutely. Property delegates represent an important generalization of the language and its core access patterns and will enable a leap forward in the kinds of functionality that framework authors provide to their clients.

  • What is your evaluation of the proposal?

-1, but I must qualify this heavily.

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

The idea of a delegating property is one of those things that I think is so clever and useful that it is inevitable that it will find its way into Swift. It will clearly enable a new level of integration with frameworks that provide conceptually complex behaviors on top of existing types that, up until now, have required hacks or DSLs to achieve. One of the things I especially love about this feature is that it allows us to lower the conceptual entropy of consuming and interacting with behavior-oriented frameworks by allowing the framework author to shoulder the burden of implementation entirely - the wealth of really cool examples bear this out in spades. That's a really good place to be for a feature.

The reason I have to vote it down, then, is because its syntax is conceptually heavy, ugly, and does not fit the language. My concerns stem entirely from my belief that we have all the tools and concepts we need to put this feature into user's hands while also minimizing the amount of new syntactic capital we have to burn to introduce it into The Next Swift.

First, the use of @propertyDelegate to mark types. Swift uses @-tributes to signal compiler directives which does fit with requirement number one:

  1. The property delegate type must be defined with the attribute @propertyDelegate . The attribute indicates that the type is meant to be used as a property delegate type, and provides a point at which the compiler can verify any other consistency rules.

In particular, the last sentence which indicates that we may emit additional diagnostics while verifying the structural integrity of a property delegate than we would otherwise for a plain aggregate. However, it's the second rule that concerns me

  1. The property delegate type must have a property named value , whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the delegate instance.

We have a way to signal these kinds of requirements in the language already: a protocol. We could bless a PropertyDelegate protocol into the standard library and teach the compiler to perform any additional structural checks a conformance would require. That hypothetically involves special-casing its declaration in order to fit requirement number one, but I doubt it would involve more special-casing than implementing the attribute.

But that's just an attribute. I think the majority of my concerns are about the syntax-ization of user-defined types in what was previously a namespace for compiler-only concepts. By doing so, we've effectively lost the ability to introduce new @-tributes without having to go through a source-compat morass. This is no longer relevant: see this edit). More than that, the concept is so out of left field for this language where other compiler-level directives accept user-defined identifiers/values. It makes me think that we could have spelled this

@delegating(to: Atomic)
var x: T

Or

#delegating(to: Atomic)
var x: T

This hews closer to existing attributes and directives (#sourceLocation(...), @convention(...), @available(...), etc.), reads cleaner ("x has type T delegating to Atomic"), and signals clearly that the user intends for delegation. The proposal even provides similar syntax as a future direction.

Again, I have to qualify this: I think there is room in the language for a discussion of user-defined attributes, especially with respect to runtime introspection. However, I strongly believe we do not need this proposal to drive them forward if we already have the language design elements we need to retain its functionality.

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

I think this feature is pretty unique in its stylization. To express it, most other languages with meta programming facilities would probably use those. A functional language might choose to express this with a (state) monad plus some additional modality. Since Swift does not have those things, it makes sense that we would provide a standard in-language expansion.

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

I've been following along with this idea on and off since Joe's first Property Behaviors proposal.

17 Likes

Also a -1 (for different reasons than above).

I don't think this feature has gone through anywhere near the required level of prototyping or field-testing for a language that claims 'world domination' as a goal. This speaks to a larger problem with swift-evolution rather than this proposal specifically.

If you read the pitch thread, you'll see that the proposed syntax changed pretty wildly - for one thing there used to be a requirement that property delegates be implemented by a type with a single generic parameter. That seems to have been a requirement less than a month ago. If you went on holiday or had a busy week or two, you could come back and find something totally different to when you left.

Swift needs an "experimental" mode where we can test these kinds of features, and those features need a long-enough incubation period that we can find the difficult edge-cases. It is absolutely bonkers that the server work-group has a more thorough evolution process than the language which it targets.

When such big features can go from pitch to radical changes to proposal in less than a month, it makes Swift seem like some kind of mickey-mouse language. The first pitch thread is literally 25 days old.

18 Likes

I still don't understand why $foo is a better choice than _foo for the storage of a delegated property foo.

5 Likes

-1 mainly for the same reasons as @Karl.

And a strong -1 for private feedbacks on this or any other proposal. Why something about a public and open source language can be discussed privavely and we can not even know or argue about what is being discussed?

The purpose of these reviews is for the Core Team to gather feedback from the community. People are welcome to provide that feedback any way they like.

8 Likes

I was hoping you would respond that this was because of companies and even countries that retaliate people whose freedom of speech is not assured. If that were the case, I would be with you.

About what you argued, I understand the point but then it is an illusion to think that the quarrels here are important as they appear to be.

That's not correct. Property delegates are forced to start with an uppercase letter, as are custom attributes in the related pitch. So the compiler and Standard Library still owns all lowercase starting names.

3 Likes

The Core Team is always willing to accept feedback privately; we don't require you to have a good reason for it, or in fact to give a reason at all.

I think that's enough of this topic for here, though. If you'd like to continue to debate this policy, please start a new thread.

6 Likes

Even without that restriction, it would make sense to me to say that builtin attributes all live in the Swift. module namespace, so you could qualify references if you needed to, should we generalize user attribute names in the future.

6 Likes

I noticed a selling point here is "reducing compiler magic". I'm a bit confused on that. What would we call Codable and CaseIterable synthesis? How is it different from lazy ? Why are the former good and the latter bad?

3 Likes

For reference, there's a brief discussion of why a PropertyDelegate protocol doesn't work in the proposal. I feel that trying address those fundamental issues (like dealing with mutability) within protocols isn't something we want to tackle generally: protocols are meant to have very specific API contracts that are ABI and can't be made "fuzzy" to deal with mutating vs. non mutating getter/setters and such.

Additionally, I think the trend for Swift has been moving away from using concrete protocols and toward more ad hoc protocols, because protocols aren't (and won't ever be, IMO) expressive enough for our syntactic tie-ins. String interpolation, dynamic callable, dynamic member lookup---all of these are using ad hoc protocols because we want more-general overloading than can be expressed by a protocol. Given how hamstrung we are by the unfortunate designs of, e.g., the ExpressibleByIntegerLiteral and ExpressibleByFloatingPointLiteral protocols now being baked into the ABI, I really wish we had an ad hoc protocol there instead, which allows more evolutionary improvements within the constraints of existing ABI.

FWIW, we're specifically keeping @_underscoredAndLowercaseNames for compiler-defined attributes. It's the @Uppercase names that are provided for general extensibility, which lines up with the dominant convent for type names. That seems like the right expressivity / future evolution compromise.

As I said in the pitch thread, I suspect this is one of those concerns that will fade away immediately once people start using the feature. It assumes that the name of the delegate type doesn't convey the meaning of the annotation, but that's not the case: @Atomic var means the variable will be atomic. You can command-click on the Atomic if you want to learn the details of how it's done. Heck, that's better than you get with lazy.

The custom attributes discussion is happening. I suppose one could argue that that discussion could result in the whole idea getting radically revamped, so we should serialize the proposal reviews, but the level of enthusiasm for the custom-attribute direction has been high enough that I doubt the approach would get radically revamped.

Doug

4 Likes
  • What is your evaluation of the proposal?
    +1

  • Is the problem being addressed significant enough to warrant a change to Swift?
    Yes, it’s the most common case I see where an error prone pattern is repeatedly implemented.

  • Does this proposal fit well with the feel and direction of Swift?
    I believe so, however I think the syntax could be adjusted:

Have a protocol or two instead of the attribute on the delegate type. IMO this is more consistent with other Swift types.

protocol PropertyDelegate {
  associatedtype Value
  init(initialValue:Value)
  var value: Value { get }
}
protocol MitablePropertyDelegate: PropertyDelegate {
  var value: Value { get set }
}

Have a fixed attribute name so the attribute keywords don’t get polluted by user names:

@propertyDelegate(Lazy)
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    I’ve followed the discussion, and used similar patterns for years.
1 Like

Oh, then I withdraw that criticism. But I don’t recall reading it in the proposal. Is there language making this casing restriction clear @Douglas_Gregor?

It’s not just the semantic side of things - I mean, both approaches convey that same information and you could jump-to the definition by clicking the (meta)type in the other spelling. It’s that this proposal is spending the entire @-tribute syntax on this feature. Taking it as-is, we’re saying we don’t envision needing @+Uppercase for anything else in the future.