[Pitch] Refining Property-wrapper-related Initialization

Hello Swift Community!

This thread posed the question:

struct MyStruct {
  @Wrapper var property = 0
}
MyStruct($property: Wrapper()) // ❌ Why isn't this allowed?

func acceptWrapped(@Wrapper property: Int = 0) { ... }
acceptWrapped($property: Wrapper()) // ✅ But this is?

So @amritpan, @Jumhyn and I co-authored the following pitch. Enjoy:


Introduction

SE 0258 introduced property wrappers and SE 0293 expanded them with function-like declarations. Today, property wrapper initialization exhibits inconsistencies due to its growing versatility. Specifically, memberwise initializers use complex, poorly documented rules and projection initialization remains limited. This proposal will simplify synthesized memberwise initialization for types with wrapped properties and extend projection value initialization to include global, type, and local wrapped properties.

Please read the rest here.


The source compatibility section outlines a novel "soft deprecation" approach, where your feedback would be especially valuable. Let us know what you think!

14 Likes

This definitely feels like a logical next step in the refining and generalisation of property wrapper initialization logic.

Would it make sense to also include the Allow Property Wrappers with Multiple Arguments to Defer Initialization when wrappedValue is not Specified proposal in this proposal? I feel like it's also part of refining property wrapper initialization – I know that one of the authors of this proposal, @amritpan, is working on an implementation for it. I'm also curious on how these two proposals play with each other, or whether they might even conflict with each other.

Otherwise, +1 for me.

Great question! So far, in my cursory understanding, there are no conflicts between this pitch and the Defer Initialization for Multiple Arguments pitch. We'll be working on the implementation for this pitch next, will better understand whether there are any issues, and can consider their combination at that time.

3 Likes

Yeah, this is my understanding as well. Once the following from the linked pitch becomes legal code:

struct Model {
    @Argument(argument: "hello") var property: Int
}

this proposal would straightforwardly synthesize the memberwise init as:

init(@Argument(argument: "hello") property: Int) {
  self._property = _property
}

which would desugar to something like:

init(property: Int) {
  var _property: Argument = Argument(wrappedValue: property, argument: "hello")
  var property: Int {
    get { _property.wrappedValue }
    set { _property.wrappedValue = newValue }
  }

  self._property = _property
}
6 Likes

Hi guys!

May I ask what is actual status of this initiative?
I'm working on the framework implementing the way of the API contract description and I'm really interested in the proposed changes!
They would allow to do things like

struct FindArtistRequest {
    @Path("artist_name") var artistName: String
    @Query("app_id") var appId: String
}

instead of

struct FindArtistRequest {
    @Path("artist_name") var artistName: String = ""
    @Query("app_id") var appId: String = ""
}

and

struct LoginRequest {
    @JsonBody(encoder: .iso8601) var body: Credentials

    struct Credentials: Encodable {
        let email: String
        let password: String
    }
}

which is currently almost impossible!

Thanks in advance!

@Jumhyn @amritpan Sorry for bothering you, but I'm really interested to bring this topic up. Is there any plans or roadmap regarding this small, but nice improvement?