SE-0258: Property Delegates

Well, sure. The proposal has a second example based on storage arenas. If I were to tie that LongTermStorage delegate in with Ref (rather than its current UnsafeMutablePointer), I'd be able to choose between the reference-counted Box (with associated lifetime + ref-counting overhead) and the manually-manager LongTermStorage without any other changes to the implementation or client.

Yes to the first question. I think private is sufficient here; the "initialization only" restriction seems like an unnecessary extra restriction.

Doug

-1
I agree with @ptomaselli. My overriding concern with Swift's evolution is feature creep. While it's well presented and surely useful, this proposed feature seems to add significant new syntax and magic that could overwhelm a beginner or intermediate user of the language from fully understanding a codebase that used it liberally. And I'm not sure I see enough of a benefit to make it worth that. The language is already on the "large" side when you consider it against other Emerging Languages, or frankly other programming languages in general. In fact, I taught a class called "Emerging Languages" this semester and compared to other emerging languages we covered, with Swift there was just so much surface area to convey. I'm afraid Swift will go the way of C++ when I see proposals like this.

I'm not saying it's not useful and wouldn't simplify certain use cases. I'm saying that I agree with @ptomaselli that for many of those scenarios, a couple blessed types in the Standard Library could work well without adding yet more syntax to the language.

2 Likes

I donā€™t know why you say itā€™s a one-off when, by my estimate, itā€™s more like a 40-off.*

Swift already has many attributes, and they are all documented in the same way the @propertyDelegate attribute will be: in conceptual material like The Swift Programming Language. I would love to have doc comments for them too, and happily, this proposal takes an important step in that direction by allowing doc comments on delegate types. The compile-time attributes proposal would take another big step by allowing us to declare many built-in attributes, possibly including @propertyDelegate, in Swift code so that they can be documented in the same way.

This proposal gets us halfway to the kind of documentation you want, and another proposal that could get us all the way there is in sight. Is this really the proposal to reject because it doesnā€™t have Quick Help?


* I arrive at 40 because the highest declared DECL_ATTR identifier is 85, there are a few gaps, and I'd say about half of them are officially part of the language rather than implementation details.

1 Like

We're all very interested in the teachability of Swift, but I don't think "could someone cover the entire language in a survey class?" is ever going to be the right standard. A PL survey class should be discussing the ideas that make a language special. The "feature creep" proposals you're objecting to make sense as part of Swift's focus on enabling expressive and natural-feeling libraries, and while I think that's a very interesting part of Swift's design, it's probably not survey-class material. Even if I were teaching it, I'd want to spend that time talking about why it's interesting to be able to e.g. add new binary operators, not actually teaching them the details of the precedencegroup declaration.

I think precedencegroup is a good analogy, actually. What's important about precedencegroup is that (1) it can express what needs to be expressed without creating artificial problems and (2) nobody needs to know anything about it unless they're actually defining their own associative infix operators, not just using someone else's. Very few programmers will be defining their own custom property delegates, but they certainly might use some custom delegates from a library, and the important thing is that using those delegates feels as natural to them as using something that was actually part of the language, rather than being a whole different thing that they feel they have to learn.

12 Likes

I used the Emerging Languages class as one example, but I also teach Swift in an iOS Development course and as a book author. And my personal experience dealing with students has been that the size of the language is indeed an issue. When you deal in it every day as a software developer or a compiler author, I imagine it's very hard to put yourself back into the shoes of a 20 year old learning it for the first time who may have only a couple other college classes under their belt.

"Progressive disclosure" seems to be the general answer to this, but it's also a fairly hand wavy answer. One way you become a better programmer or learn a language is by reading other people's code. When a language is large enough that how the code you read will vary widely and require quite a large cognitive load to absorb all of its syntax, that hurts its teachability. Many learners also find the overwhelming amount that must be learned to reach mastery discouraging. I love Swift, my fear is that the sentiment of this thread for C++ can be what happens to any language that grows too large:
https://twitter.com/chadloder/status/1101281496214593538?s=11

I'll just add that you also setup a straw man. I never said the goal of the class was to cover the entire language. I said that Swift has more surface area to cover than most other languages. That means that to even do its interesting features justice, you have a lot more work to do.

2 Likes

The reason I asked about this restriction is because I thought part of the purpose of delegateValue was to hide the underlying storage. This approach defeats that. Maybe thatā€™s ok. But if weā€™re going to make the delegate directly available anyway I see even less reason to include the delegateValue sugar.

With this design, programmers need to understand whether the delegate they use declares a delegateValue or not. They also need to understand that when it does, $foo magically referees to that property and need to instead use $$foo to refer to the delegate itself. Further, if we introduce private(storage) that will only refer to $foo regardless of whether delegateValue exists or not. $$foo will always be private. This is a pretty subtle set of rules and will inevitably cause confusion for a lot of people.

One alternative to this magic is to just have users refer to the projected API explicitly $foo.ref (in the Box example). That is simple and wonā€™t confuse anybody.

I still think this is an interesting area to explore. But as I have said several times, delegateValue in particular feels like it would benefit from further design iteration. If nothing else, that would help us gain confidence that we have chosen the right design. But Iā€™m hopeful that we could find a solution that doesnā€™t have as many sharp edges, especially edges that are visible to users of property delegates.

2 Likes

The LongTermStorage example has an interesting signature for its initializer:

init(manager: StorageManager, initialValue: Value) {

I can imagine wanting to be able to separate the "configuration" from the initial value. Instead of writing:

@LongTermStorage(manager: manager, initialValue: "Hello")
var someValue: String

It might be interesting to be able to say:

@LongTermStorage(manager: manager)
var someValue = "hello"

This becomes even more interesting when out-of-line initialization is used:

@LongTermStorage(manager: manager)
var someValue: String

// ...

// elsewhere, perhaps in an initializer body:
someValue = "hello"

If we had sugar like this, the Ref example I posted could relatively easily be modified to accept different storage configurations without exposing the backing storage directly while still supporting direct initialization of the delegating property:

@Ref(storage: .box)
var boxRef: String = "hello"

@Ref(storage: .longTerm(manager))
var longTermRef: String = "hello"

I'm posting this primarily to show that there may be solutions other than delegateValue to solving some of the intended use cases.

Further, in reading the text of that example closely, it specifically states:

A property delegate type can choose to hide its instance entirely by providing a property named delegateValue

This sentence contradicts exposing a private $$foo to support out-of-line initialization of the instance. If delegateValue is intended to hide the instance completely then direct initialization is all that could be supported. If it isn't, then maybe some of the use cases suffer. I don't think we understand the use cases well enough yet to make a decision about which would be better.

1 Like

FWIW this was one of the quirks I found with the proposed design. I would love to have a better solution that.

The Core Team has elected to return this proposal for further development; any discussion should happen in that thread.

As always, thank you for helping to make Swift a better language.

2 Likes