[Pitch] Allow Property Wrappers on Let Declarations

Hello @amritpan,

I had missed this proposal early on. I know I've certainly come across places where the let restriction on property wrappers is annoying, and I think it's reasonable to consider lifting the restriction.

I have two concerns with this proposal at this point, one philosophical and one procedural.

The philosophical concern is that a let constant is immutable. It can't be reassigned, yes, but if the type of the let constant is of a value type then that value cannot be changed no matter what. Applying a property wrapper to that feels like it weakens the immutability guarantee, because of the ability to introduce a backing reference type. Your WrapperClass property wrapper, for example, lets us write

_value.wrappedValue = 17

after the let has been initialized, because _value has reference semantics. It's possible that the benefit from allowing property wrappers on let's outweighs the downsides of potential mutability surprises, but we should be cognizant of the guarantees we're losing.

The procedural concern I have is that there are two other proposals in the Swift 5.9 time frame, one accepted and one under review now, that interact with this proposal:

One of the stated goals of the second proposal is to get closer to the point where macros can subsume the uses of property wrappers. That goal wasn't really understood at the time you pitched your proposal, because macros (and especially accessor macros) were really just starting to come into focus then. However, your proposal would take a step away from that goal if property wrappers could be used with let but accessor macros could not. Therefore, please consider broadening your proposal to encompass accessor macros as well, or if it doesn't work, add an Alternatives Considered section that details why it doesn't work.

I had one last thought... the init accessors proposal (SE-0400) under review allows us expression let-like single initialization behaviors for computed properties:

var onlySetOnce: Int {
  init { /* set it */ }
  get { /* get it */ }
  // no setter!
}

... which is very similar to how your proposal lets you run code on initialization, and run code when getting a value, without allowing one to directly mutate the property.

Doug

6 Likes