SE-0258: Property Wrappers (second review)

As @anandabits says it, I'd rather go towards the simpler language feature: especially as creating a type to vend those properties doesn't feel like a very cumbersome solution.

2 Likes

As pointed out here, a var stored property is a variable, as defined in the official language reference.

6 Likes

I'm hoping to write up a more comprehensive response to the proposal generally before the review period ends, but in the meantime I'd like to just chime in about this particular point on naming things:

To my simple mind, if this feature will continue to be called "property wrappers," then it seems a no-brainer that there should be a property and a wrapper.

While $ is very terse and an interesting design direction, it's also completely inscrutable as a standalone name.

6 Likes

The only issue I have with this approach is that the projection really isn't the wrapper. Otherwise I like it.

I also think it's possible we would later regret the decision if we burn this identifier when we don't need to. I don't have anything specific in mind but I don't think a single-character identifier like this should be used without a better reason than I see here.

1 Like

I'm not sure what I think about $ here, but I think it's very unlikely that we would ever name something $ that wasn't somehow about building up an identifier with $ in it.

One example of a potential use would be the ability to use $ to refer to the parameters of a closure as a tuple (when none are inout). This might be handy in a future where we have variadic generics, tuple splatting, etc.

I see, building off the existing use of $ with anonymous closure parameters. I don't think that would at all justify the shorthand, but I concede that it's a potential use.

For the wrapper use-case I would suggest something more like var $value, where value stands in for the actual name of the property, and where the wrapper type could also declare something like var value$widget to get a property$widget name in the scope which declares the wrapped variable.

Let’s take a step back and think about the goals and limitations we are working with here.

Let’s say the enclosing type is MyClass, with a @State property called xyz, of type Int.

  1. We need to expose the synthesized storage of xyz (of type State), as a private or higher access level property on MyClass.
  2. It is desirable that the wrapper author can choose other properties on State, to be easily reachable
  3. In particular it is very important that users of SwiftUI can pass down a Binding Corresponding to xyz to “subviews”.
  4. We can’t really use an operator or function on xyz for solving neither 1 nor 2, since xyz is just a simple Double. Sure, the compiler can probably keep track of its special origin but that seems like a very bad idea.

Now 1 can be solved by prefixing xyz with $, and one way to access the types in 2 is as sub properties of $xyz. This is standard swift, no magic needed. The downside is of course that you need two additional pieces on top of “xyz” to do this, e.g. $xyz.binding.

On the other hand, the original solution, with the unfortunately named “wrapperValue” being exposed as $xyz and the storage being demoted to $$xyz has at least two downsides - it doesn’t scale past 1 extra property, and by implementing wrapperValue the name of the storage changes, which may cause confusion.

This solution works very well for the SwiftUI use case though, where the Binding is what you want to access most of the time.

Now Chris’ solution does scale, and allows for one privileged exposed type, which solves SwiftUI’s needs. I’m not sure if Apple would be happy if they have to tell everyone that the syntax from the KeyNote and all the sessions has changed, but that’s not for us to discuss I guess.

Nevertheless, I can’t help feeling that it basically just replaces . with $, it’s a very small win to be able to type xyz$wrapper.blabla rather than $xyz.wrapper.blabla that I definitely don’t think it is worth introducing more magic naming (callAsFunction is bad enough, we don’t want more of that). The same will be true for any solution that seeks to expose multiple “wrapperValue”s.

I wonder if we can’t solve it with protocols? At least it seems to me that the SwiftUI case can be handled with a BindingConvertible protocol, that requires a property binding: Binding<Element>. For Bindings this would simply return self, for State it would return the same thing that wrapperValue returns now. Given how much work has gone into SwiftUI, I’m sure they already thought of and dismissed this solution. It might not even work, I’m not by a computer so I can’t look closer at it.

In conclusion, since I don’t think it’s important to be able to directly more than one “wrapperValue”, I believe the best solution is to use underscore for the storage, which as noted before essentially is a convention already, people will find it without you telling them. That leaves us $xyz to give to wrapperValue, and if you want to expose anything else you simply do that on your storage type, and people will have to use _xyz.somethingUseul. Underscore is not vey elegant but again it’s aleady very common, and the storage will rarely be used outside MyClass anyway.

Yep, building off of the existing shorthand. It may never carry it's weight (hard to say without seeing other enhancements first), it's just an example of something else we might want to do.

I'd rather see us stick to either prefix or postfix use of $ (with postfix possibly leading to infix as suggested by Chris). I think mixing prefix and postfix would be a mistake here. If support for n projections is deemed important then we have to use postfix so it would be var value$.

In terms of placeholders, I think combining your idea here with the suggestion by @xwu would be good. var property and var property$ (or if we reskin this to "variable wrapper", then var variable and var variable$.

I like the graphical feel of:

// for wrappedValue:
foo

// for defaultWrapper:
|foo|

// for wrapper[dbhandle]:
|foo.dbwrapper|
1 Like

I understand your point here, and I don't disagree that it would be better if we stuck with one pattern, but I think it would be very tough to convince people within Apple that we should invalidate quite a lot of Swift UI code (in examples, documentation, tutorials, blogposts, etc.) by moving a character from prefix to postfix in order to make the feature generalize in ways that Swift UI doesn't particularly care about. So I think the question has to be this: what is the best design we can come up with that doesn't break existing use sites? And I, personally, think that allowing $property as an alternative to property$ (as directed by the wrapper type, not implicitly accepted as an alias) is a tolerable outcome; but if you disagree, I would recommend putting energy towards finding alternatives that fit around $property. That is the reality of where we stand today.

2 Likes

Uff that feels like a direct hit in the face. Don't get me wrong, I actually expect the SwiftUI and all the related code to property wrappers and function builders to change to reflect a generalized and fine-tuned solution that went though weeks of SE talks. Otherwise I don't see why we're having these long SE talks, we could just have said that the design is locked to what was presented at WWDC.

I do understand that we cannot change all the WWDC video sessions, but all the documented and written out code online can change and that would be for good.

This is just critics, I'm not flaming or pointing fingers at anyone.

12 Likes

There is enormous room to change both this feature and function builders; it's just that that realistically doesn't exist to changing things like the common-case syntax at use sites. For property wrappers, that means (1) the spelling of simple wrapper attributes like @State and (2) $property as a way of referencing an "associated property". For function builders, that means the basic idea of a closure (1) not being explicitly marked and (2) primarily consisting of a sequence of expression-statements whose value is somehow collected.

1 Like

Understood. That said, I think it's disappointing that the evolution process is boxed into this corner. I imagine it may be beyond the control of the Apple employees on the core team at this point. Ideally that would not happen.

This makes the Swift evolution feel like it's optional for Apple. If Apple wants to build frameworks in secret on top of unfinished language features it should be willing to encounter this kind of turbulence. If it isn't then Swift isn't truly developed in the open. Which is perfectly within Apple's right to do of course, but is not what we have been led to believe in the past.

On a broader level, I think it's really important that proposals be evaluated strictly on their own merit, not in terms of a demand for compatibility with a beta framework that was only revealed to the world a couple weeks ago. Evolution decisions should be made in the best interests of long-term planning on the order of a decade. We won't be able to do that if Apple isn't willing to play fair with the evolution process.

I'm not convinced we actually need the infix version and if we don't then the prefix we already have is fine. As @hartbit pointed out upthread, a single projection can go a very long way. The only thing can be accomplished with multiple projections that can't be accomplished with a single projection is to have a foo$ available that does not directly contain the other projections. It isn't clear to me how important that is.

So maybe in this case a prefix $foo is fine without mixing it with infix syntax in the future. Regardless, I hope we don't get boxed into a corner like this again in the future.

19 Likes

PS: I also understand that the core team will dictate the final result which is likely to be influenced by apple at some extent, but I didn't expect that the time and effort the community went through for these features would be kindly speaking 'ignored' by something like "we don't want to change/update it, it's too much, let's stick to an early build". This feels really demotivating.

3 Likes

Again, the community has already provided a lot of really valuable feedback that has significantly changed the design and expressive capabilities of both of these features (or will, for function builders). Some syntactic suggestions are not really in the cards, but that's not unusual, either.

2 Likes

This is what I was concerned about in the discussion thread about evolution for these new features:

If this is the case, and it sounds like it is, aspects that the Core Team will not be open to feedback on should be clearly communicated to the community as part of the evolution process so that members to not find themselves wasting breath on things that the Core Team is unlikely to consider. I myself find the magic and relatively unreadable nature of the wrapperValue/$foo syntax a big downside of property wrappers, but would not have continued to voice my concern if I had known that this aspect was already decided.

10 Likes

Come on, Apple is a profit driven company and they have spent enormous amounts of money and energy into creating Swift and making it amazing. They have never said that Swift will be completely controlled by the community. It’s developed by Apple but as we have seen they take community input very seriously, and Swift is much more open than anyone thought when they mentioned they would open source it. So it’s quite unbecoming to complain.

SwiftUI is by far the coolest thing Apple has given to developers since 2014, and you can understand that they wanted to develop it in secret. This also means that they had to make a few decisions about Swift along the way, without consulting the community.

But you really have to ask yourself, if you had to choose between SwiftUI and complete community control, would you really choose the latter? I know I wouldn’t. I think it’s a perfect balance, Apple funds and develops all these amazing things for us, AND we get to participate in the development.

6 Likes

You’re making it seem like it’s all or nothing. Apple doesn’t have to develop in a vacuum, they choose to.

1 Like

I think it's important to know that some of the discussion from above did not just suggested some syntactic sugar. $property or property$ are two different things. By the proposed design $property is a single identifier, while property$ is not because it's property identifier that trigger to use a different accessor with a totally different type. This makes the latter form definitely more flexible and more importantly extensible in the future (e.g. property$view). $property would not provide that capability and as noted by others you will need to use a wrapper to workaround the issue, which from my point of view comes with some drawbacks.

Anyway I'm curious to see what the outcome of this review will be. I would like to take the chance and thank everyone who participated and pushed this topic forward including the core team of course. :slight_smile:

4 Likes