[Review] SE-0018 Flexible Memberwise Initialization

Thank you all for the discussion on this proposal. The core team met to discuss it today and I have a boat load of notes to propagate. The “short" version is that the proposal is not accepted as-is. I’ll write them up the notes on rationale and the changes requested when I have time, hopefully by the end of tomorrow.

-Chris

···

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * 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 you 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?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Here are some long and poorly organized notes from the core team meeting to discuss this feature. Many, many, thanks to Alex Martini for capturing many of these details, but the mess I’ve made of this is not his fault. :-)

--- Metapoints ---

First, some meta points that came up from several members of the core team:

- Less is more. Many folks (core team and other community members both) commented that the mere presence of the expansive “future directions” section made the proposal off-putting.

- “Pure sugar” proposals need to have strong justification for their existence since they do not add new capabilities, but they do add new complexity to the language.

- This feature included justification based on the fact that the existing memberwise feature has problems, but some core team folks thought that this wasn’t good enough justification. They commented that perhaps we should just remove the existing memberwise init for structs and call it a day. (others strongly disagreed :-)

- The team wanted to evaluate “all” of the conceivable different approaches to make sure that this approach was the right one in general. I captured notes about some of that discussion below. On the balance, they agree that a variant of this proposal is the right way to go.

- We didn’t have time to bikeshed the fine details of the proposal (e.g. the “memberwise” keyword, the … sigil, etc), just discussing the general semantics and shape of what an acceptable solution would look like.

MOST SIGNIFICANTLY: there was strong pushback about considering this proposal *at all* right now: this is a pure sugar proposal with a broad design space. It doesn’t have long term ABI implications at all, and we have other serious semantic problems in the language to clean up and address. We can tackle this sort of sugar feature at any time, including in Swift 4. The core team would like to defer talking about this for a couple of months until the other things (property behaviors, resilience, and other big changes) are behind us. We need to focus on the important things, because getting syntactic niceties into Swift 3 but missing on ABI stability would be really unfortunate. We have seriously constrained design and engineering bandwidth. This feedback applies to other pure-sugar proposals as well.

--- Rationale ---

I explained three bits of rationale for why we should tackle this:

1) Our existing feature is half-baked and unsatisfying. This causes me personal angst but, as pointed out above, the simplest solution is to just remove what we have until we can do it right.

2) Memberwise init sugar strongly benefits “POD” types and other “bags of property” types (e.g. “Vec4"), and many of the C struct types that Cocoa has (CGRect etc). In these cases, clients often want to initialize all of the fields explicitly and a memberwise init proposal eliminates this boilerplate. This case is what our existing feature attempts to service.

3) Memberwise init sugar can replace some really common “builder” patterns. These cases often have a bunch of configurable state in a class, where almost all of it has default values, but where clients want to be able to only specify deltas from the default. In the cases where you have a type with a ton of data members, a memberwise init is very very appealing because it eliminates a ton of boilerplate to set up a builder manually, and it also allows the captured state to be immutable (if we allow memberwise init of lets, which Matthew is a huge proponent of). This case can often be a class derived from something else (e.g. a Kit class), explaining the desire to specify super.init, add custom parameters, etc.

#3 is my distillation/abstraction of many of the arguments that Matthew has made on the list and in the proposal (e.g. his very helpful FontPicker example), but I don’t claim that this captures all of his or any one else’s motivation. This is simply the rationale that the team discussed.

--- Common feedback and points ---

Getting to points common to any “memberwise initializer” proposal, the goals of the core team are:

- Provide a predictable model. It was concerning to many people that you could accidentally reorder properties and break a memberwise init without knowing/thinking about it. This directly contravenes the resilience model. API diff’ing tools and “synthesized header” views in Xcode ameliorate this, but don’t totally solve it.

- After discussion, the desire to support memberwise init of let properties was found to be a strong goal. The entire win of providing a builder-like pattern in an init method (vs doing initialize to default and reassign over the value later, C# style) is the immutability benefits.

- We would like for memberwise init to be orthogonal to property behaviors, access control, attributes, and all the other stuff you’d want to do on your properties. Also, ideally, you shouldn’t be limited to what kinds of properties can participate.

- The issues around whether properties with a default value (both let and var) can be replaced by a memberwise init is a really important semantic decision we need to nail down. Many of the proposals include the problem/fact that something like:

class C {
  var x : Int = foo()

  memberwise init(…) {}
}

would not run the foo() side effect when initialized with "C(42)”. Similarly, in a case like:

class C {
  let x : Int = 42
  memberwise init(…) {}
}

it is still very concerning to people that the apparent axiom that “x is 42” could be violated by a memberwise init. Allowing lets have other implementation issues, such as suddenly making “x” require storage, where it could otherwise be optimized out.

OTOH, my previous objection about being able to write a memberwise init manually is bogus, since you could write the above out like this:

class C {
  let x : Int // initial value removed.
  init(x: Int) { self.x = x } // manually written memberwise init.
  init() { x = 42 } // move the inline initial value here.
}

--- Approaches discussed ---

Here are the rough approaches we discussed, along with pros and cons. For consistency, most of them are modeling the analog of sugarizing an example case like this:

class C : Derived { // has base class
    var x: Int // no default value.
    var y = 4.0 // default value
    var z = “foo" // default value

     // has an explicit “q” parameter as well as all the members as parameters.
     init(q: Int, x: Int, y: Double = 4.0, z: String = "foo”) {
        self.x = x
        self.y = y
        self.z = z
        // your custom code can go here
        super.init(q)
    }
}

I ordered this so that the most promising ones are at the end, to keep you all in suspense :-)

Approach: C# model

As pointed out on the list, we could eliminate the notion of a synthesized memberwise init, and move the complexity to the caller side. C# uses syntax along the lines of:

  new Foo(){x=42, y=17.0}

pro) No memberwise initializer concept
con) Requires all fields to be default initialized, which breaks non-nullable pointers etc.
con) Requires mutability, since you’re initializing, and then overwriting a field.
con) This specific syntax would be problematic with trailing closures etc.

Core team was strongly opposed to this approach.

Approach: Scala (among others) memberwise init as part of class decl

This would give us something like this:

class C(var x: Int, y: Float = 4.0, var z : String = “foo") : Derived {
    var extra = 4.0 // extra, non-memberwise fields supported.
    init(q: Int, ...) { // all initializers get memberwise stuff added, allows bodies.
       extra = 17
        // your custom code can go here
        super.init(q)
     }
}

pro) Very predictable model, makes it very clear what is subject to memberwise init, and that the order matters
pro) This supports indicating that some fields don’t participate in memberwise init.
pro?) Syntax looks vaguely like the parameter list of the init
con) The syntax isn’t actually parameter list syntax, it is property syntax. You’d want to accept public/private, let/var, behaviors, attributes, and lots of other stuff in there. This would introduce confusion about parameter lists.
con) This is sticking more junk into the class decl, which is already populated with class attributes/modifiers, subclasses/protocols, etc.
con) This means we’ve have two very different ways to declare stored properties in a type.

Core team was strongly opposed to this approach.

Approach: Extract the members from the parameters of an initializer decl

This would give us something like this:

class C : Derived {
    // members come from the memberwise init.
     memberwise init(x: Int, y: Double = 4.0, z: String = "foo”) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Syntactically very terse.
con) This only supports one memberwise init.
con) Two ways to declare stored properties
con) Conflates the property grammar (behaviors etc) with parameter grammar, many of the same problems as approach right above does.

Core team was strongly opposed to this approach.

Approach: barely extend our existing model:

We could put default values onto our existing memberwise init, and allow you to write "memberwise init(...)” to explicitly request it to be synthesized, but not allow a body. We could bring the existing level of feature to classes.

pro) simple, solves some problems with the existing model.
pro) brings memberwise init to root classes and classes that derive from something with a init() to chain to (e.g. NSObject).
con) doesn’t solve enough of the problem to be worth it
con) feels like we are perpetuating a hack.
con) we’d have to extend this further at some point, this is just kicking the can down the road.

Core team was strongly opposed to this approach. Better to either rip out what we have or do a bigger approach.

Approach: Magic “members” tuple (or other generics/variadics features)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

    init(p : … Members) {
       self.members = p
    }

    // compiler synthesizes.
    typealias Members = (Int, Float, String)
    var members : Members { get {..} set {..}}
}

pro) seems like a more generally useful feature that could pay for itself in other ways
con) doesn’t work with default values, so it doesn’t solve the builder problem at all (and no, we are not adding default values back to tuple types :-)

Core team was strongly opposed to this as an approach for memberwise init, but can always consider doing this for other things if it is motivated by them.

Approach: Introduce sugar for “self.x = x"

David Owens ][ mentioned that perhaps we can go with something simpler, like this (concrete syntax is just a strawman):

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     init(q: Int, self.x, self.y, self.z) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body.

pro) Does not require introducing the notion of a memberwise init. Any init could use this.
pro) Makes it very clear what members are being initialized and what the API for the init is.
pro) Makes it easy to pick and choose which members you want to make available to clients.
con) This isn’t sweet enough sugar to be worthwhile, particularly for the builder pattern case, which would require lots of boilerplate
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird. Making this keyword driven (e.g. "memberwise” at least gives people something to google and lets them know they’re looking at something magic).
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach, but agree that something like this could be composed on top of another proposal if that made sense.

Approach: Introduce sugar for “self.x = x”, and add “memberwise and …"

Extending David Owens ][’s model a bit, we could keep the behavior he indicates and add memberwise/… to handle cases with default values:

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, self.x, ...) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body. The presence of memberwise/… would forward all the properties with default values.

pro) Since default values can be provided by a callee in any order, this approach can be made stable against reordering of properties.
pro) Compared to the base proposal above, this would be great for the builder pattern case.
con) The POD case still requires you to duplicate all your properties, so this doesn’t really address that use-case.
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird.
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach.

Approach: Matthew’s proposal SE-0018 (the actual formal proposal :)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
unclear) I’m not delving into it here, but the access control aspects were very controversial. We didn’t discuss it fully.
con) compared to the Scala approach, doesn’t handle the ability to opt a member out of memberwise init (we’d need some thing like @nomemberwise)
con) Doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).
con) Lots of concerns about unclarity that the order of the properties matter. In a large type, the memberwise init could be “at the end” of the class, after a bunch of other initializers and methods, and the properties could be “up at the top”. It could be very unclear to people that memberwise init is even happening, and changing a property could have surprising effects.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.

Core team thought this was very close, but the problems were significant enough to think it won’t fly, and suggest pursuing the variant below.

Approach: "Opt-in" version of Matthew’s proposal

Matthew has mentioned on list that he likes opt-in models, and after discussion the core team agreed that it is more promising than the base proposal.

In addition to the memberwise declmodifier, *also* introduce a declmodifier on the properties in question. Here I’ll use “mwi” as a strawman for “memberwise initializerable”, but it is obviously not the right thing, and I’m sure it will be replaced with something else brilliant :-)

class C : Derived {
    mwi var x: Int
    mwi var y = 4.0
    mwi var z = “foo"
    var extra = 4.0 // Doesn’t participate in memberwise inits.

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
pro) as with the Scala approach it handles the ability to have members be both memberwise and non-memberwise participating.
pro) Works with let and var initializer semantics.
pro) Looking at a property, you know immediately that it participates in memberwise init, and that you should go update the memberwise inits if you move it or change it. We could warn/error if you have a “mwi” property and no memberwise init, or a memberwise init with no mwi property.
pro) because this is an opt-in model, we can eliminate the controversial access control aspect of SE-0018.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.
con) more verbose than Matthew’s proposal. OTOH, the verbosity isn’t “boilerplate" since we’re getting something out of it. Simple POD cases can be written as (e.g.) “mwi var x, y, z, w : Double” which isn’t bad.

Adding the modifier on the affected property decls helps improve the intentionality and eliminate some of the "spooky action at a distance" problem, but the major breakthrough (IMO) is that it allows us to include lets into the model, and gives us a simple answer for why side effects are squashed on vars. Consider this example:

class C {
  mwi let a = 42
  mwi var b : Int = sideeffect()

  memberwise init(…) {}
}

A call site would look like this, giving A a value of 75 and would not run the side effect:
let tmp = C(a: 75, b: 12)

We’re ok with the side effect being squashed, and the let axiom being broken given that there is a decl modifier on the property decl. This is because you can tell locally by looking at the property that a memberwise initializer provides a different value. From an implementation perspective, this is also pretty great, because this leads to a very straight-forward codegen model that cleanly meshes with what we already have.

--- A path forward ---

So that’s where we left it. Coming back to the meta point above though, the core team really doesn’t want to discuss this right now, given that this is purely a sugar proposal and we need to stay focused on the primary Swift 3 goals. If Matthew or another community member wants to pick this up and keep running with it, that is perfectly fine, but we should defer more formal discussion about the proposal until at least march/april-ish. Likewise, other pure sugar proposals should similarly be deferred at this point, unless they have some strong motivation that aligns with a Swift 3 goal.

I want to thank Matthew in particular for passionately pushing this area forward!

-Chris

···

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

(this is mostly a repost of a message I sent to the "[draft]" thread for this proposal, with some light editing to better match terminology in the proposal)

What is your evaluation of the proposal?

I like this proposal. I think it will bring some much-needed ease-of-use.

I have reservations about the "..." placeholder for the memberwise initialization parameters, though. I know this was suggested by Chris Lattner, so I'm inclined to defer to his judgement. But, here are my thoughts:

First, it's very close to the varags syntax (e.g. "Int...") which can also appear in initializer parameter lists.

Second, and I think more important, I'm not sure that it's all that useful. It's presence isn't necessary for triggering memberwise initialization synthesis; that is already done by the "memberwise" keyword.

The primary example given in the proposal is:

memberwise init(anInt: Int, anotherInt: Int, ...) {
  /* code using anInt and anotherInt */
}

That is, it's used to indicate where the synthesized parameters appear in the parameter list if there are also custom (non-memberwise) parameters.

My question is, could the memberwise initialization parameters always be last? That would eliminate the need for the placeholder.

I don't think I've seen a compelling case for embedding the "..." within a list of custom arguments, like:

memberwise init(anInt: Int, ..., anotherInt: Int) {
  /* code using anInt and anotherInt */
}

It's been mentioned several times in the discussion of this proposal that this behavior is purely optional. If it turns out that there are rare cases where placing the memberwise params in the middle is useful, authors can use manual initialization.

Hi Alex, thanks for your review.

The initial draft of the proposal did exactly what you suggest - it did not include the placeholder and always placed the memberwise parameters last. Personally, I believe the placeholder adds clarity and really liked the idea when Chris suggested it.

Aside from personal preference, I like that this proposal introduces a “synthesized parameter placeholder” syntax. Similar syntax will also be used in the parameter forwarding proposal mentioned in the future enhancements section of this proposal.

I think the `…` works really well. That said, I don’t mind if people wish to bikeshed on it. If that discussion starts it is worth noting that one thing I like about the `…` is that it combines with an identifier cleanly. For example : `…memberwise`.

Combining the placeholder with an identifier allows more than one placeholder to be used in the same parameter list. For example, if you are forwarding memberwise parameters exposed by a super init you might also have `…super`.

That said, I don’t want the review thread to get distracted with discussions around general parameter forwarding so please just consider this as a preview of how this syntax might scale to future applications. For now, lets keep this thread focused on the review of the current proposal. :)

Matthew

···

On Jan 6, 2016, at 5:26 PM, Alex Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Wed, Jan 6, 2016 at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

        https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

        * 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 you 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?

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--

Alex Johnson | Engineering Lead

Quick Left, Inc. <https://quickleft.com/&gt;
Boulder | Denver | Portland | San Francisco
1 (844) QL-NERDS
@nonsensery

<https://github.com/quickleft&gt; <Facebook; <https://twitter.com/quickleft&gt; <https://instagram.com/quick_left/&gt; <https://www.flickr.com/photos/quickleft&gt; <https://vimeo.com/quickleft&gt;

<>
What's it like to work with us? TrainingPeaks, iTriage, and Ping Identity share their stories in this short video A Client's View <https://vimeo.com/92286352&gt;\.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-1 for me.

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

  * What is your evaluation of the proposal?

I’m unconvinced that the value is warranted for the complexity that is being brought to the table. Couple this with the fact that it’s very easy to accidentally make breaking changes by simply re-ordering your members, adding new ones, or even assigning a default value. I just find it far too fragile.

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

I don’t think so.

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

I suppose. However, it has a lot of impact on the resiliency of APIs, so I’d want to delay this feature until those issues are worked out first. The other concern is there is a lot of magic involved, especially when some of the future enhancements are looked at.

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

I’ve used tools to do this in past, such as MACROs or snippet-like items. Those seemed to be more useful and less constrained. Also, I make use of Swift’s implicit initializers for structs for internal code. While that is nice, I’ve not been convinced that moving this functionality to public is a good thing.

I know this won’t influence your opinion David, but I want to make it clear to any other readers: this proposal does not introduce any implicit public initializers. It simply enhances the existing memberwise initializer functionality by allowing more control and making it available to classes as well as structs.

···

On Jan 6, 2016, at 7:05 PM, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

I’ve been following the iterations and partaking in some of the conversations.

-David

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?

It’s okay.

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

I’m lukewarm about that. I have never found writing out the initializers I want to be a significant burden, and I find my code is better when they’re explicit. Every new feature increases the language's complexity and surface area, and I fear this one is not going to pay its way.

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

Yes, but I worry that it may be too early to add it. Other features in this space, like truly generic variadics, may well obsolete anything we do today. I’m not sure we should be designing convenience features that are likely to overlap with more general features coming down the road unless the inconvenience is very painful… which I personally don’t find it to be.

It isn’t clear to me how generic variadics might obsolete the functionality of this proposal. Can you elaborate on that?

···

On Jan 6, 2016, at 6:04 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

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

The proposal is more elegant than how you’d do this in Python, but on the other hand the mechanisms you’d use in Python are not single-purpose language features directed at memberwise initialization; they definitely pay their way because they can be used for much more.

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

A glance, admittedly.

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Meta: most reviews have the review dates in the Status field of their document, this one doesn't. I was a little confused at first.

For the proposal itself:

# What is your evaluation of the proposal?

+1

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

Yes, I write a lot of code just to initialize code. This will probably make some of that code clearer.

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

Probably?

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

I frequently write C++ and Python and neither of them have it. This is especially annoying for short programs.

# C# has an initializer syntax that allows you to assign properties at creation time, and it is handy:

Foo foo = new Foo() {
  Bar = "bar",
  Baz = 1,
};

However, this pattern wouldn't be of much help in Swift, where most types don't have a reasonable default value.

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

I followed the first week and a half of the proposal.

With all this said, I am confused with the way the proposal is written. The Proposed Solution section talks about opt-in memberwise initialization with the memberwise modifier on properties, but it is listed as a future direction and the Detailed Design makes no mention of it. I would like to be clear that my current appreciation of the proposal only extends to the Detailed Design, and not necessarily to the future directions.

Félix

···

Le 6 janv. 2016 à 19:04:41, Dave Abrahams via swift-evolution <swift-evolution@swift.org> a écrit :

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?

It’s okay.

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

I’m lukewarm about that. I have never found writing out the initializers I want to be a significant burden, and I find my code is better when they’re explicit. Every new feature increases the language's complexity and surface area, and I fear this one is not going to pay its way.

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

Yes, but I worry that it may be too early to add it. Other features in this space, like truly generic variadics, may well obsolete anything we do today. I’m not sure we should be designing convenience features that are likely to overlap with more general features coming down the road unless the inconvenience is very painful… which I personally don’t find it to be.

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

The proposal is more elegant than how you’d do this in Python, but on the other hand the mechanisms you’d use in Python are not single-purpose language features directed at memberwise initialization; they definitely pay their way because they can be used for much more.

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

A glance, admittedly.

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

  * What is your evaluation of the proposal?

+1 to the basic proposal. I'm much more reserved about the "Future enhancements"; some I don't want at all, some sound plausible but probably need changes.

Thanks for your support Kevin!

As I stated last night, there are two primary things that I think need improving via enhancements of some kind. I hope you can agree with these points:

1. We need a way to specify a default value for memberwise parameters for `let` properties.
2. We need a little bit more control over which properties participate in memberwise initialization when the “automatic” rules don’t quite do the right thing for a particular use case.

Most of the enhancements listed show various ways we could address the second point. We don’t need all of them.

Under the current proposal there will be cases where memberwise initialization would be of great use, but the rules of the automatic model expose a property (probably a `let`) that shouldn’t be exposed. We won’t be able to use memberwise initialization at all in those cases if we don’t have a way to correct that behavior.

There may also be cases where a property doesn’t participate in memberwsie initialization when we would like it to. In those cases we can add an explicit parameter and manually initialize the property, continuing to use memberwise initialization for other properties.

Ideally we can find a simple enhancement that solves both visibility problems. I’m definitely open to any ideas on how we can handle this.

Also, a question and a concern about the basic proposal. The question: you state that the only impact this has on existing code is structs with private properties that have been getting an implicit internal memberwise initializer will have the initializer be private. That's fine, but assuming that the implicit memberwise initializer behaves identically to `memberwise init(...) {}`, surely this proposal also makes the implicit memberwise initializer gain defaulted arguments for any var properties with an initializer expression? Don't get me wrong, I think it's good to change that, but it should be explicitly noted.

That is a great point! It won’t break any existing code, but it will change behavior slightly. I will update the proposal and submit a PR with this change.

As for my concern, it's with the following rule:

If the initializer body assigns to a var property that received memberwise initialization synthesis report a warning. It is unlikely that overwriting the value provided by the caller is the desired behavior.

I understand why you put this in there, but this is a warning that cannot be suppressed and will make it impossible to use a memberwise initializer for perfectly legitimate cases where you do in fact want to mutate the property after it's been assigned to.

For normal initializers I agree with you. However, I think it’s a reasonable for callers to assume that if you expose a property via memberwise initialization the post-initialization value will match the value they provide. This warning is intended to alert you to the fact that you are violating that reasonable assumption.

Do you have an example of where you would want a caller to initialize a property, but then overwrite the value they provide during initialization?

···

On Jan 7, 2016, at 2:46 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

What might be a reasonable thing is a warning that occurs if you assign to the var property without ever having read from it (i.e. a dead store warning on the memberwise initialization of the property). That way if I mutate a property to contain a derived value it's fine, but if I simply write to it without ever reading it, it's a problem.

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

I think so. Writing initializers can be a large source of boilerplate in Swift, especially when using classes where you can't get the implicit memberwise initializer.

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

Yes.

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

I'm not aware of a similar feature in any language I'm familiar with.

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

I spent a few minutes reading over the whole proposal. I did not read any of the swift-evolution thread.

-Kevin Ballard
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I like the general notion of something along these lines but admittedly haven’t had time to dig into the proposal specifics yet.

I have some concerns about cross-interactions with other features that are either also under discussion or are at least very anticipatable.

First, I know there has already been some discussion of allowing definition of stored fields in (some) extensions (e.g., something like allowing definition of stored fields in extensions within the module that defines the type).

E.G., something like this may become possible (assume all files are compiled together):

   // in `ComplicatedClass.swift`
   class ComplicatedClass {
     let text: String

     // how will this get expanded,
     // given the extensions below?
     memberwise init(...)
   }

   // in `ComplicatedClass+Foo.swift`
   extension ComplicatedClass {
     var fooData: Foo? = nil
     // presumably stuff-involving-`fooData`
   }

   // in `ComplicatedClass+Bar.swift`
   extension ComplicatedClass {
     var barData: Bar = Bar.standardBar
     // presumably stuff-involving-`barData`
   }

It doesn't seem impossible to specify how the memberwise-initialization would interact with constructs like the above, but I'd worry a bit about it making a feature that's already looking *rather* complicated even more so.

Especially since, if I had to pick just one, I'd think the ability to define stored properties outside the initial definition is a bigger win than a nice memberwise-initialization construct, even though both seem handy.

I followed the stored-properties-in-extensions discussion reasonably closely. My understanding is that the extension will need to initialize its own properties, either with an initial value or with a `partial init`. Designated initializers would be required to call the `partial init` for any extension that defines one.

This being the case, memberwise initialization would not directly interact with this feature at all. Memberwise initializers declared in the main body of type itself would only expose stored properties defined in the type itself.

It would also be possible to support `partial memberwise init` in extensions which would expose the stored properties declared in the extension as part of a partial initializer.

I don’t think there are difficult complications here.

Secondly, I’m a bit unsure how this will interact with e.g. the property-behavior proposal if both wind up ratified. For `lazy`, the interaction with `memberwise` is easy — it is omitted from the list — but when you get into e.g. something like a hypothetical `logged` or `synchronized` or `atomic` — wherein there is custom behavior, but the field would still need initialization — you’d want them to be included in the
`memberwise` init.

My thought here is that a behavior would define whether a property allows and / or requires initialization in phase 1 or not. This is probably necessary independent of memberwise initialization. Properties that allow or require phase 1 initialization would be eligible for memberwise initialization. Properties that don’t allow phase 1 initialization would not be eligible for memberwise initialization.

It’s a bit unfair to bring up another proposal, but this proposal and something like the property-behavior proposal *would* need to work well together (if both are approved).

Agreed. That is why there is a rule that references property behaviors in the proposal.

Thirdly, I’m not sure what the current plans are (if any) for users to be able to specify the precise memory-layout of a struct; apologies if this is already a feature, I simply haven’t looked into it.

**Today**: I order stored-field declarations for ease-of-reading (e.g. grouped into logical groups, and organized for ease-of-reading).

**Under Proposal**: I sometimes will get to choose between the “ease-of-reading” declaration ordering and the “cleanest-reading memberwise init” declaration ordering. These may not always be identical.

Agree. This is something that could be addressed in a future enhancement if necessary. This proposal is focused on the basic mechanism.

Also, nothing in the proposal prevents you from continuing to write a manual initializer when the synthesized initializer will not do what you require. If you are already explicitly restating the property identifiers to specify parameter order you are already half way to a manual initializer implementation.

Granted, if you need more than one memberwise initializer you would have to duplicate that effort. But re-ordering is going to have a hard time providing enough value if the basic feature does what we need in the majority of cases.

···

On Jan 7, 2016, at 9:02 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

**Future?**: I may have to choose between the “ease-of-reading” declaration ordering, the “cleanest-reading member wise init” declaration ordering, and (perhaps?) the “intended memory-layout” declaration ordering.

I don’t want to make this proposal more-complicated than it already is, but I worry a bit about having too many things impacting the choice of how to order declarations in source files; it may be better to include a way to explicitly declare the ordering-for-memberwise:

E.G., some way of explicitly indicating the memberwise ordering, perhaps like this:

   // syntax example re-using `ComplicatedClass`
   class ComplicatedClass {
     @memberwise($parameterList)
     // ^ can use just @memberwise to get default ordering + the defaults from
     // the property declarations, but perhaps require the explicit listing
     // whenver the ordering is not well-defined (e.g. if you have properties
     // declared in extensions…then you need to order it yourself)
     //
     // @memberwise(text="Example",barData=,fooData)
     // - `text="Example"` => memberwise init has text="Example"
     // - `barData=` => memberwise init has `barData` w/out default
     // - `fooData` => memberwise init has `fooData` w/default if it has one
     //
     // …and e.g. the above would make:
     //
     // memberwise init(...)
     //
     // ...expand-to:
     //
     // init(text:String = "Example", barData: Bar, fooData:Foo?=nil)
     //
     // ...and with the @memberwise declaration supporting a `...` for `super`
     // placement, like so:
     //
     // // superclass members come before:
     // @memberwise(...,)
     // @memberwise(...,$parameterList)
     //
     // // superclass members come after
     // @memberwise(,...)
     // @memberwise($parameterList,...)
     //
     // ...perhaps with tweaked syntax (`@memberwise(...,$)` or `@memberwise(...,self)`)
     // to be bit easier to read when you don't have an explicit parameter list?
   }

...which of course potentially only-further complicates the feature in some ways, but avoids having this use of this feature *necessarily* impact how one might choose to order declarations?

On Jan 6, 2016, at 4:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * 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 you 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?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

The proposal says that "let" properties with inline initializers should be left out of the memberwise initialization, AIUI on the grounds that a manually-written initializer would not be allowed to override the inline initialization:

Yes, this is because Chris insisted that the proposal be pure sugar for something that could be manually written. This ensures that anyone using memberwise initialization can stop using it in any case where that becomes necessary.

class C {
  let x = 1738
  init(x: Int = 679) {
    self.x = x // Error, self.x already initialized
  }
}

However, this is also true for vars. Semantically, if you change 'x' to a var in the above example, you get an initialization followed by an assignment:

class C {
  let x = dump(1738)
  init(x: Int = dump(679)) {
    self.x = x
  }
}

      C() // dumps 1738, then 679

which, if the initialization has side effects, will likely be surprising. We could say that the memberwise initializer elides the inline initialization of `var`s, on the grounds that initializations ought not to have side effects, but then we're introducing a behavior change in inline initializers for `var`s in the face of `memberwise` initializers that also cannot be replicated by a manually-written initializer. If we make that behavior change for vars, I think it's reasonable, and more orthogonal, to extend the same grace to lets as well. That also simplifies the rules for what appears in the memberwise initializer—there's now only two rules (or one, if we also remove the access control filter, as I've suggested in another subthread).

I agree. The dual assignment for `var` seems wasteful in addition to potentially surprising and the limitation for `let` is unfortunate. Of course the same can be said for all initializers that might wish to assign a different value to a `let` or do assign a different value to a `var`.

What you suggest is exactly how I wanted the proposal to work. Chris was opposed to this approach. I would be very happy with this change if you can sell Chris on it! I think many others would be as well.

In addition to this not being possible to implement manually, Chris explained to me that it is important to the optimizer to be able to assume a `let` with an inline initializer will never have any other value (especially when that inline initializer is a constant value). Allowing an instance member to be optimized away when initialized with a constant value enables it to be referenced with convenient syntax due to implicit `self`. It would have never occurred to me to make a true constant value an instance property, but apparently this is a common practice. And with a guaranteed optimization is makes sense to take advantage of the implicit `self`.

At the same time, IMO the inability to provide a default value for a property that is only used when it is not initialized manually is a more important issue. I wish the “inline initializer” worked this way.

I know the implementation would be a bit more complex - the compiler would have to analyze the body of non-memberwise initializers before synthesizing the inline-initialization if necessary. But it already emits an “uninitialized member” error message so it already knows which properties are initialized and which are not. That information may not be available in the right part of the compiler but we do not how to determine this.

This would solve the init / assign problem for `var` and the no overridable default problem for `let` in both existing and memberwise initializers. It would also mean that memberwise initializers can work this way while still being pure sugar for code that can be written manually. Unfortunately this seems like something that is not open for change.

Matthew

···

On Jan 7, 2016, at 8:55 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

(Aside, a small nitpick, but it really bugs me: initialization has O(M+N) complexity, not O(M×N) complexity. One doesn’t initialize every member with every parameter.)

FWIW I believe it’s meant to be interpreted as "M lines of boilerplate per initializer" x "N initializers” => ~ MN lines of initializer-related boilerplate per type.

···

On Jan 8, 2016, at 11:03 AM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 4:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * 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 you 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?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

What is your evaluation of the proposal?

I have reservations about it. I think it should be deferred in search of a more generic solution, perhaps to be resurrected if the search for generality fails.

I appreciate the tremendous care that has obviously gone into the proposal. The dual attention to use cases and corner cases is admirable. The proposal present a thorough understanding of the problem that includes considerations I certainly hadn’t thought of!

Thanks Paul!

Still, I can’t shake unease about the proposed solution. As I read the examples, they’re not quite self-explanatory: a lot of magic, but the result doesn’t feel quite magical. Without being able to see what the compiler synthesizes, it’s often not obvious what will happen. As I read the detailed rules, they all sound quite sensible, but taken together feel like they’ll be hard to keep track of, and will lead to a lot of frustrated experimenting with the compiler. Tricky questions lurk all over. For example, what happens when I have a 7-member struct, all 7 parameters use memberwise initialization, but then I want to add some custom logic for the initialization of member 3? I think I have to either reorder the params or abandon … altogether? I feel like those tricky questions should melt away once I grasp the underlying principle, but there isn’t one to grasp; it’s just a bunch of tricky cases.

On reflection, it comes down to this: the feature to functionality ratio is too high.

Would you propose removing the current implicit memberwise initializer for structs on the same grounds? This proposal effectively fleshes that feature out giving it more functionality. The only fundamental complexity it adds is the access control rules, which I feel are pretty important to enforce.

That probably requires some explanation. By “feature,” I mean a specific behavior the software implements for which users will need a mental model. By “functionality,” I mean the set of things one can do with the software, and more importantly the nature of the user’s experience doing them.

An example of a high feature/functionality payoff is Swift’s as-if-immutable structs. There’s a fairly large set of relevant features: the let/var distinction for declarations, the “mutating” keyword, the interaction of “let” with setters and mutating methods, and the way “let” recursively affects structs inside of structs. However, all of that folds into a mental model with a nice unifying principle: Swift structs behave semantically as if they are all always immutable and mutations create a new struct and reassign it to the variable; however, they perform better than that, because the compiler will optimize that into a direct mutation when possible. The features pay huge functionality dividends across a wide variety of situations: defensive programming, passing data across threads, isolating responsibility … it goes on.

(Really, “functionality” is a qualitative thing, more about the experience than just “what can I do,” and so talking about the “feature to functionality ratio” is only a metaphorical gesture. It captures the right feeling, though.)

In this proposal, there’s a similarly large set of new features: the “memberwise” keyword, the new use of …, multiple interacting rules about memberwise property eligibility, synthesized initialization, rules about how that interacts with manual initialization. For all that, though, the functionality gain is limited: we shed some senseless repetition — that’s good! — but only for copying of parameters to properties, only when they’re in the same order, and only in initializers.

The proposal reads like a set of specific use cases translated directly into specific features, without a good eye to the bigger picture.

It feels to me like this functionality should come from a feature set that is more general, more arbitrarily composable, and pays greater dividends in a wider variety of situations. As a simple example, what if I want to write an updateFrom(other: Self) method that does a mass copy of all properties? Why doesn’t this proposal help with that, too? Because the … placeholder and the synthesized copying are tightly coupled (1) to each other and (2) to initialization.

I’m not sure what the better answer is, but it’s out there. I didn’t follow the whole discussion, but I did notice Joe Groff’s proposal for a Members tuple; that seems to me to be getting much warmer. I’d much prefer something along those lines, even if it were slightly more verbose.

I think the direction suggested by Joe (and Dave) is interesting. But they haven’t explained how it would handle some important use cases this proposal addresses (default parameter values, subset of members without using a struct, etc). If we are going to reject this proposal in hope of a more general solution I would at least like to see a path forward that might be able to address these use cases.

More importantly, the general features on their own would not address the problems addressed by this proposal. There would still need to be initializer-specific magic. Joe hinted at what that might be but has not fleshed out all the details yet. Maybe it would be a simpler model but we would need to see more specific details.

I don’t believe a fully generalized solution is possible. There are a lot of initialization-specific constraints that must be met (definitive initialization, single initialization of `let` properties, etc).

(Aside, a small nitpick, but it really bugs me: initialization has O(M+N) complexity, not O(M×N) complexity. One doesn’t initialize every member with every parameter.)

MxN is members x initializers.

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

It’s certainly worth considering. Mindless parameter copying is a source of low-value verbosity, and can lead to errors.

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

It does in that it aims to reduce developer error and verbosity by providing an idiom to solve a common problem.

It does not in that the idiom is so narrow. The language is moving away from that in other realms (e.g. lazy).

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

I’ve never heard of a language with such a high specialized tool for doing just this.

Ruby and Javascript allow programmatic traversal of both arguments and properties, and provide enough reflection to automate that. Developers in both languages do write ad hoc solutions to do almost exactly what this proposal does, though they’re not widely used. The most common is probably variations on this:

def initialize(**args)
  args.each do |prop, value|
    obj.send("#{prop}=", value)
  end
end

…which uses key/value pairs from an argument array to set properties.

Being able to convert arguments to a dictionary is something Swift certainly might investigate.

We should have a safe solution that allows the compiler to enforce type checking and definitive initialization rules.

···

On Jan 8, 2016, at 11:03 AM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

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

I read the proposal carefully, but only lightly skimmed the discussion and other reviews.

Cheers,

Paul

On Jan 6, 2016, at 4:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * 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 you 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?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I had another thought last night about this proposal. Since it appears to be working as syntactic sugar, if I write something like

var x: Int
memberwise init(...) {
   // what does x refer to here?
}

Inside of the memberwise init, does the bare identifier `x` refer to the property, or to the implicit parameter? I think it should refer to the property, because making it refer to the implicit parameter means you'd be referring to a variable that was never visibly declared anywhere.
AFAIK the only precedent for that is the implicit argument `newValue` to setters, `oldValue` to didSet observers, and `error` to catch blocks, but those are all constant identifiers that mean the same thing in every such context, as opposed to memberwise init where the implicit parameters are different for each init. More generally, we should avoid exposing such non-declared variables without a compelling reason.

The other argument in favor of making `x` refer to the property is because that's just generally more useful. All the properties match the values of the parameters on the first line of the user-supplied body to the memberwise init, so there's no benefit to be had from leaving the parameters accessible to user code.

As for how to actually accomplish this, assuming this feature is implemented as an AST transformation, ideally we'd have some way to synthesize identifiers in the AST that cannot possibly be referenced by user code (e.g. some form of identifier namespacing if it exists, or some form of gensym-like behavior). Alternatively, if there's no particularly good way to do this, we could just use something like `__name` as the internal parameter name.

Thanks for bringing this up Kevin. I agree it should refer to the property. Not sure what, but for some reason I didn’t think to state this explicitly in the proposal. In fact, my examples accidentally implied otherwise because they didn’t show the compiler using a mangled name of some kind for the internal parameter name in the expansions.

The only downside is that it changes behavior if you transition from a memberwsie initializer to a manual initializer.

Matthew

···

On Jan 8, 2016, at 1:05 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

-Kevin Ballard
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi, Matthew. (Hi, everyone.) Thanks for putting together a detailed and well-thought-out proposal. I had some smaller comments:

Hi Jordan, thanks for your review.

- Given that these initializers are based on access control, is "public memberwise init(...)" different from "internal memberwise init(...)"? Can I put both in the same type?

If you have at least one internal property they would result in different signatures so would not be a duplicate declaration. The public memberwise init would obviously need to initialize the internal property somehow.

- Why not just put the "memberwise" in the parameter list, and drop the decl modifier? This came up in the draft discussion but I didn't see any follow-up.

Good question. The decl modifier was initially Chris’s idea and it seemed reasonable to me so I ran with it.

- I kind of agree now that "memberwise" is the wrong name, but I don't have a better one.

It is a bit verbose, and in many ways has the same issues as `associatedtype`. I used it because it is the terminology in use in Swift today and also don’t have anything better to replace it with.

…but after reading through the formal review discussion, I have to say I don't think this is a good feature for Swift. I tend to agree with David Owens and others that this is too much magic in one keyword. (I picked David because I think his responses most closely match my concerns out of the dissenters.)

Imagine trying to explain this feature in The Swift Programming Language. It starts off very simple with the perfect use case:

public struct Point {
  public var x: Double
  public var y: Double
  public memberwise init(...) {}
}

…but then spends the rest of the chapter going over everything in the proposal: why you can't provide a default for 'let' properties, why hiding a setter affects the presence of a member, and what to do if you want to change it but preserve backwards binary compatibility. It seems like a big, complicated feature that "does what you mean" only when it's first added and then becomes an impediment to any later change.

I can certainly sympathize with this. It is the big drawback of the automatic model. At the same time, this proposal builds on the existing memberwise init for structs and doesn’t really make the rules that much more complex. But it does make the feature more prevalent and visible, thus making it more important to understand the rules. And of course it could be argued that the current memberwise init is too complex as it is.

For the record, here is a concise list of how this proposal expands the current memberwise init:

1. Allow the memberwise initializer to be used in classes
2. Allow default parameter values for `var` properties
3. Fix the problem the current memberwise initializer has with lazy properties
4. Use the `set` rather than `get` visibility for `var` properties
5. Allow you to request the memberwise initializer, including:
  i. Add additional parameters
  ii. include an initializer body
  iii. Specify access level, which will result in omission of memberwise parameters for more-private properties (it would be reasonable to limit this to private and internal if concerns about allowing it to be public are a significant factor)

I am curious to hear your thoughts on which of these points are not desirable, and what your opinion is about the existing rules for the implicit memberwise init for structs.

I'm motivated to solve the tiny value struct case, the public C-like struct case, but I don't think I want this creeping into the interface of a public class. I'd rather go with the "annotation on parameters" solution (which would be a separate proposal, of course).

public struct Point {
  public var x: Double
  public var y: Double
  public init(self x: Double, self y: Double) {}
}

or

  public init(@assigned x: Double, @assigned y: Double) {}
  public init(self.x: Double, self.y: Double) {}
  // something else

I’ll ask you the same question I asked David about this approach: if the parameter must be explicitly related to a property for assignment, why should we repeat the type information (and default value information if it is a var)? Doesn’t it seem reasonable to omit the type?

···

On Jan 8, 2016, at 5:18 PM, Jordan Rose <jordan_rose@apple.com> wrote:

For comparison with other languages, the "memberwise initializer" proposal is closest to C's brace-initialization, but C structs aren't resilient to change in the way that classes are, and they won't break if you reorder the members, while this will. The "annotation on parameters" idea is precedented in CoffeeScript, but I don't know of any other languages that do it.

This proposal is very practical and covers many common use cases, but I just don't see it as naturally fitting into the language.

Best,
Jordan

On Jan 6, 2016, at 14:47, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * 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 you 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?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

* What is your evaluation of the proposal?

-1 in its current form

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

Yes and no. First of all, I do not believe that writing initialisers is that much of a hassle to justify the proposal. Also, it makes the language even more idiosyncratic and introduces additional compiler magic. We should strive to make Swift simpler, not adding more syntactic constructs with idiosyncratic behaviour.

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

IMO, no. See above

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

No

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

Quick reading, nothing in-depth

···

On 10 Jan 2016, at 19:38, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

* What is your evaluation of the proposal?

At first glance it looks like it should be a simple thing, but a closer look lead me to believe it's conflating two things:

1. stored properties initialization
2. user-visible property values

Stored properties are the internal state, while user-visible properties is the exposed state. Initialization is about setting the internal state. While it's common to have initializers accept values as arguments, in the general case the initializer has to convert them from the external representation to the internal representation while setting the internal state.

For a plain type with only raw stored properties, internal state and exposed state are the same thing, and this proposal is simple and very appealing.

But for more complex types where the internal state does not map one-on-one to the exposed state, the proposal seems suboptimal. What you really want is to call all the publicly available setters, not set the raw values. But that goes against how things work in an initializer, where setters are skipped and the raw values are set directly.

I understand the proposal is trying to work around this problem by inferring things from the visibility attributes, but the rules seems complex, bug prone (because it bypasses the setters), the feature seems incomplete (missing computed properties), and in the end unnecessary (because you can use the setters). I think we'd be better served if there was simply a way to set properties in batch after initialization, perhaps like this:

  var object = MyObject()
  object .= (property1: 1, property2: "a")

(Note: you can *almost* implement this already through reflection and a custom operator taking a tuple.)

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

This proposal is a good way to get back the default memberwise initializer that structs with no initializer get for free, and allow it for classes when it makes sense. It'd be nice to have that.

I'm also of the opinion that it's worth addressing the verbosity of setting multiple properties at once. But for complex types that hide their internal state, I don't think the added complexity for determining the parameter list and the default values is worth it.

Also, I see no reason why a type should have to opt-in (by providing a memberwise initializer) so its users have a convenient way to set properties: it'd be much better to have a general solution that works everywhere.

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

Not sure.

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

I'm not aware of any.

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

I read the proposal, and a lot of what was discussed on the list (but not everything).

--
Michel Fortin
https://michelf.ca

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Especially after reviewing the associated proposals, I can't avoid the impression that the topic of initialization steers towards a complexity that is hard to to justify.
There are whole programming languages that are easier to explain than the rules that are laid out here and in the other threads — and I mean it literally:
Just take Scheme or Tcl as an example.

I already suggested to delay the decision, but now I'm convinced this would be way better than to force an issue.
There are alternatives that have been mentioned but did not yet receive a fair treatment, and there might be better solutions no one has been talking about.
Initialization isn't that bad right now, so despite Matthews haste, there is imho no need to choose a solution without a neutral comparison of all options.

Since this has been mentioned a few times now I'd like to add that I would support that as well.

I'm not really sure enough yet as to how the reviews work, so I'll ask here:

Is it possible for a subset or modified version of a proposal to be accepted or would the proposal be rejected while asking for a reduced/modified follow-up proposal?

Also are proposals always accepted or rejected, or could a proposal be returned as "needs more work, submit again later" instead of a flat out rejection?

- Janosch

···

On 11 Jan 2016, at 07:37, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

  * What is your evaluation of the proposal?

It’s a well-considered and well-written proposal. I agree with the semantics of memberwise initializers (+1 to adding a reasonable implicit memberwise initializer for classes, and the ability to use default arguments in that implicit memberwise initializer). However, I would prefer to accept the semantics as improvements to the creation of the implicit memberwise initializer, so it’s a -1 to the “memberwise” specifier and “…” placeholder syntax.

I want to thank the core team for putting so much thought and consideration into their feedback. This was clearly given extensive discussion. I also want to appologize if it proved to be a distraction from more important goals.

I agree with the recommendation the team has made. I began to be concerned about the "complexity" of the “automatic" model prior to the start of the review and those concerns proved to be shared by many. Using an "opt-in" approach will provide much-needed clarity. I am especially happy to see that the core team has decided that supporting default parameter values for `let` properties is an important aspect of an eventual solution.

There is one thing that isn't clear to me in the feedback. Does the core team believe the implicit memberwise initializer should:

1) Remain in its current form
2) Remain in a slightly enhanced form (i.e. receive default parameter values)
3) Remain in an enhanced form and also be extended to classes
4) Be removed in favor of making all initilaizers explicitly declared

As requested, I will defer work on a modified formal proposal until the time is right. Chris, please let me know when you're ready to take this topic up again. I will draft a proposal based on the "opt-in" approach (with no future enhancements!).

In the meantime, anyone who is interested in seeing an outline of possible options for an "opt-in" approach that builds on more general initialization features should keep an eye out for the second (first complete) draft of my Partial Initializers proposal. At John McCall's request I am including an a section that describes how partial initializers could be related to memberwise intialization. I hope to have the draft ready later today.

Thank you again to everyone who participated in the discussion and review of my proposal. It has been a very productive conversation. My thinking about the topic has been refined and clarified significantly by this process.

- Matthew

···

On Jan 13, 2016, at 1:24 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Here are some long and poorly organized notes from the core team meeting to discuss this feature. Many, many, thanks to Alex Martini for capturing many of these details, but the mess I’ve made of this is not his fault. :-)

--- Metapoints ---

First, some meta points that came up from several members of the core team:

- Less is more. Many folks (core team and other community members both) commented that the mere presence of the expansive “future directions” section made the proposal off-putting.

- “Pure sugar” proposals need to have strong justification for their existence since they do not add new capabilities, but they do add new complexity to the language.

- This feature included justification based on the fact that the existing memberwise feature has problems, but some core team folks thought that this wasn’t good enough justification. They commented that perhaps we should just remove the existing memberwise init for structs and call it a day. (others strongly disagreed :-)

- The team wanted to evaluate “all” of the conceivable different approaches to make sure that this approach was the right one in general. I captured notes about some of that discussion below. On the balance, they agree that a variant of this proposal is the right way to go.

- We didn’t have time to bikeshed the fine details of the proposal (e.g. the “memberwise” keyword, the … sigil, etc), just discussing the general semantics and shape of what an acceptable solution would look like.

MOST SIGNIFICANTLY: there was strong pushback about considering this proposal *at all* right now: this is a pure sugar proposal with a broad design space. It doesn’t have long term ABI implications at all, and we have other serious semantic problems in the language to clean up and address. We can tackle this sort of sugar feature at any time, including in Swift 4. The core team would like to defer talking about this for a couple of months until the other things (property behaviors, resilience, and other big changes) are behind us. We need to focus on the important things, because getting syntactic niceties into Swift 3 but missing on ABI stability would be really unfortunate. We have seriously constrained design and engineering bandwidth. This feedback applies to other pure-sugar proposals as well.

--- Rationale ---

I explained three bits of rationale for why we should tackle this:

1) Our existing feature is half-baked and unsatisfying. This causes me personal angst but, as pointed out above, the simplest solution is to just remove what we have until we can do it right.

2) Memberwise init sugar strongly benefits “POD” types and other “bags of property” types (e.g. “Vec4"), and many of the C struct types that Cocoa has (CGRect etc). In these cases, clients often want to initialize all of the fields explicitly and a memberwise init proposal eliminates this boilerplate. This case is what our existing feature attempts to service.

3) Memberwise init sugar can replace some really common “builder” patterns. These cases often have a bunch of configurable state in a class, where almost all of it has default values, but where clients want to be able to only specify deltas from the default. In the cases where you have a type with a ton of data members, a memberwise init is very very appealing because it eliminates a ton of boilerplate to set up a builder manually, and it also allows the captured state to be immutable (if we allow memberwise init of lets, which Matthew is a huge proponent of). This case can often be a class derived from something else (e.g. a Kit class), explaining the desire to specify super.init, add custom parameters, etc.

#3 is my distillation/abstraction of many of the arguments that Matthew has made on the list and in the proposal (e.g. his very helpful FontPicker example), but I don’t claim that this captures all of his or any one else’s motivation. This is simply the rationale that the team discussed.

--- Common feedback and points ---

Getting to points common to any “memberwise initializer” proposal, the goals of the core team are:

- Provide a predictable model. It was concerning to many people that you could accidentally reorder properties and break a memberwise init without knowing/thinking about it. This directly contravenes the resilience model. API diff’ing tools and “synthesized header” views in Xcode ameliorate this, but don’t totally solve it.

- After discussion, the desire to support memberwise init of let properties was found to be a strong goal. The entire win of providing a builder-like pattern in an init method (vs doing initialize to default and reassign over the value later, C# style) is the immutability benefits.

- We would like for memberwise init to be orthogonal to property behaviors, access control, attributes, and all the other stuff you’d want to do on your properties. Also, ideally, you shouldn’t be limited to what kinds of properties can participate.

- The issues around whether properties with a default value (both let and var) can be replaced by a memberwise init is a really important semantic decision we need to nail down. Many of the proposals include the problem/fact that something like:

class C {
  var x : Int = foo()

  memberwise init(…) {}
}

would not run the foo() side effect when initialized with "C(42)”. Similarly, in a case like:

class C {
  let x : Int = 42
  memberwise init(…) {}
}

it is still very concerning to people that the apparent axiom that “x is 42” could be violated by a memberwise init. Allowing lets have other implementation issues, such as suddenly making “x” require storage, where it could otherwise be optimized out.

OTOH, my previous objection about being able to write a memberwise init manually is bogus, since you could write the above out like this:

class C {
  let x : Int // initial value removed.
  init(x: Int) { self.x = x } // manually written memberwise init.
  init() { x = 42 } // move the inline initial value here.
}

--- Approaches discussed ---

Here are the rough approaches we discussed, along with pros and cons. For consistency, most of them are modeling the analog of sugarizing an example case like this:

class C : Derived { // has base class
    var x: Int // no default value.
    var y = 4.0 // default value
    var z = “foo" // default value

     // has an explicit “q” parameter as well as all the members as parameters.
     init(q: Int, x: Int, y: Double = 4.0, z: String = "foo”) {
        self.x = x
        self.y = y
        self.z = z
        // your custom code can go here
        super.init(q)
    }
}

I ordered this so that the most promising ones are at the end, to keep you all in suspense :-)

Approach: C# model

As pointed out on the list, we could eliminate the notion of a synthesized memberwise init, and move the complexity to the caller side. C# uses syntax along the lines of:

  new Foo(){x=42, y=17.0}

pro) No memberwise initializer concept
con) Requires all fields to be default initialized, which breaks non-nullable pointers etc.
con) Requires mutability, since you’re initializing, and then overwriting a field.
con) This specific syntax would be problematic with trailing closures etc.

Core team was strongly opposed to this approach.

Approach: Scala (among others) memberwise init as part of class decl

This would give us something like this:

class C(var x: Int, y: Float = 4.0, var z : String = “foo") : Derived {
    var extra = 4.0 // extra, non-memberwise fields supported.
    init(q: Int, ...) { // all initializers get memberwise stuff added, allows bodies.
       extra = 17
        // your custom code can go here
        super.init(q)
     }
}

pro) Very predictable model, makes it very clear what is subject to memberwise init, and that the order matters
pro) This supports indicating that some fields don’t participate in memberwise init.
pro?) Syntax looks vaguely like the parameter list of the init
con) The syntax isn’t actually parameter list syntax, it is property syntax. You’d want to accept public/private, let/var, behaviors, attributes, and lots of other stuff in there. This would introduce confusion about parameter lists.
con) This is sticking more junk into the class decl, which is already populated with class attributes/modifiers, subclasses/protocols, etc.
con) This means we’ve have two very different ways to declare stored properties in a type.

Core team was strongly opposed to this approach.

Approach: Extract the members from the parameters of an initializer decl

This would give us something like this:

class C : Derived {
    // members come from the memberwise init.
     memberwise init(x: Int, y: Double = 4.0, z: String = "foo”) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Syntactically very terse.
con) This only supports one memberwise init.
con) Two ways to declare stored properties
con) Conflates the property grammar (behaviors etc) with parameter grammar, many of the same problems as approach right above does.

Core team was strongly opposed to this approach.

Approach: barely extend our existing model:

We could put default values onto our existing memberwise init, and allow you to write "memberwise init(...)” to explicitly request it to be synthesized, but not allow a body. We could bring the existing level of feature to classes.

pro) simple, solves some problems with the existing model.
pro) brings memberwise init to root classes and classes that derive from something with a init() to chain to (e.g. NSObject).
con) doesn’t solve enough of the problem to be worth it
con) feels like we are perpetuating a hack.
con) we’d have to extend this further at some point, this is just kicking the can down the road.

Core team was strongly opposed to this approach. Better to either rip out what we have or do a bigger approach.

Approach: Magic “members” tuple (or other generics/variadics features)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

    init(p : … Members) {
       self.members = p
    }

    // compiler synthesizes.
    typealias Members = (Int, Float, String)
    var members : Members { get {..} set {..}}
}

pro) seems like a more generally useful feature that could pay for itself in other ways
con) doesn’t work with default values, so it doesn’t solve the builder problem at all (and no, we are not adding default values back to tuple types :-)

Core team was strongly opposed to this as an approach for memberwise init, but can always consider doing this for other things if it is motivated by them.

Approach: Introduce sugar for “self.x = x"

David Owens ][ mentioned that perhaps we can go with something simpler, like this (concrete syntax is just a strawman):

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     init(q: Int, self.x, self.y, self.z) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body.

pro) Does not require introducing the notion of a memberwise init. Any init could use this.
pro) Makes it very clear what members are being initialized and what the API for the init is.
pro) Makes it easy to pick and choose which members you want to make available to clients.
con) This isn’t sweet enough sugar to be worthwhile, particularly for the builder pattern case, which would require lots of boilerplate
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird. Making this keyword driven (e.g. "memberwise” at least gives people something to google and lets them know they’re looking at something magic).
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach, but agree that something like this could be composed on top of another proposal if that made sense.

Approach: Introduce sugar for “self.x = x”, and add “memberwise and …"

Extending David Owens ][’s model a bit, we could keep the behavior he indicates and add memberwise/… to handle cases with default values:

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, self.x, ...) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body. The presence of memberwise/… would forward all the properties with default values.

pro) Since default values can be provided by a callee in any order, this approach can be made stable against reordering of properties.
pro) Compared to the base proposal above, this would be great for the builder pattern case.
con) The POD case still requires you to duplicate all your properties, so this doesn’t really address that use-case.
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird.
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach.

Approach: Matthew’s proposal SE-0018 (the actual formal proposal :)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
unclear) I’m not delving into it here, but the access control aspects were very controversial. We didn’t discuss it fully.
con) compared to the Scala approach, doesn’t handle the ability to opt a member out of memberwise init (we’d need some thing like @nomemberwise)
con) Doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).
con) Lots of concerns about unclarity that the order of the properties matter. In a large type, the memberwise init could be “at the end” of the class, after a bunch of other initializers and methods, and the properties could be “up at the top”. It could be very unclear to people that memberwise init is even happening, and changing a property could have surprising effects.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.

Core team thought this was very close, but the problems were significant enough to think it won’t fly, and suggest pursuing the variant below.

Approach: "Opt-in" version of Matthew’s proposal

Matthew has mentioned on list that he likes opt-in models, and after discussion the core team agreed that it is more promising than the base proposal.

In addition to the memberwise declmodifier, *also* introduce a declmodifier on the properties in question. Here I’ll use “mwi” as a strawman for “memberwise initializerable”, but it is obviously not the right thing, and I’m sure it will be replaced with something else brilliant :-)

class C : Derived {
    mwi var x: Int
    mwi var y = 4.0
    mwi var z = “foo"
    var extra = 4.0 // Doesn’t participate in memberwise inits.

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
pro) as with the Scala approach it handles the ability to have members be both memberwise and non-memberwise participating.
pro) Works with let and var initializer semantics.
pro) Looking at a property, you know immediately that it participates in memberwise init, and that you should go update the memberwise inits if you move it or change it. We could warn/error if you have a “mwi” property and no memberwise init, or a memberwise init with no mwi property.
pro) because this is an opt-in model, we can eliminate the controversial access control aspect of SE-0018.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.
con) more verbose than Matthew’s proposal. OTOH, the verbosity isn’t “boilerplate" since we’re getting something out of it. Simple POD cases can be written as (e.g.) “mwi var x, y, z, w : Double” which isn’t bad.

Adding the modifier on the affected property decls helps improve the intentionality and eliminate some of the "spooky action at a distance" problem, but the major breakthrough (IMO) is that it allows us to include lets into the model, and gives us a simple answer for why side effects are squashed on vars. Consider this example:

class C {
  mwi let a = 42
  mwi var b : Int = sideeffect()

  memberwise init(…) {}
}

A call site would look like this, giving A a value of 75 and would not run the side effect:
let tmp = C(a: 75, b: 12)

We’re ok with the side effect being squashed, and the let axiom being broken given that there is a decl modifier on the property decl. This is because you can tell locally by looking at the property that a memberwise initializer provides a different value. From an implementation perspective, this is also pretty great, because this leads to a very straight-forward codegen model that cleanly meshes with what we already have.

--- A path forward ---

So that’s where we left it. Coming back to the meta point above though, the core team really doesn’t want to discuss this right now, given that this is purely a sugar proposal and we need to stay focused on the primary Swift 3 goals. If Matthew or another community member wants to pick this up and keep running with it, that is perfectly fine, but we should defer more formal discussion about the proposal until at least march/april-ish. Likewise, other pure sugar proposals should similarly be deferred at this point, unless they have some strong motivation that aligns with a Swift 3 goal.

I want to thank Matthew in particular for passionately pushing this area forward!

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Many thanks to you and the core team for this incredibly detailed rationale and the decision. I very much like your favored approach and reasoning!
And many thanks to Matthew for pushing this forward with a thorough look at all the details of this area!

-Thorsten

···

Am 13.01.2016 um 08:24 schrieb Chris Lattner via swift-evolution <swift-evolution@swift.org>:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Here are some long and poorly organized notes from the core team meeting to discuss this feature. Many, many, thanks to Alex Martini for capturing many of these details, but the mess I’ve made of this is not his fault. :-)

--- Metapoints ---

First, some meta points that came up from several members of the core team:

- Less is more. Many folks (core team and other community members both) commented that the mere presence of the expansive “future directions” section made the proposal off-putting.

- “Pure sugar” proposals need to have strong justification for their existence since they do not add new capabilities, but they do add new complexity to the language.

- This feature included justification based on the fact that the existing memberwise feature has problems, but some core team folks thought that this wasn’t good enough justification. They commented that perhaps we should just remove the existing memberwise init for structs and call it a day. (others strongly disagreed :-)

- The team wanted to evaluate “all” of the conceivable different approaches to make sure that this approach was the right one in general. I captured notes about some of that discussion below. On the balance, they agree that a variant of this proposal is the right way to go.

- We didn’t have time to bikeshed the fine details of the proposal (e.g. the “memberwise” keyword, the … sigil, etc), just discussing the general semantics and shape of what an acceptable solution would look like.

MOST SIGNIFICANTLY: there was strong pushback about considering this proposal *at all* right now: this is a pure sugar proposal with a broad design space. It doesn’t have long term ABI implications at all, and we have other serious semantic problems in the language to clean up and address. We can tackle this sort of sugar feature at any time, including in Swift 4. The core team would like to defer talking about this for a couple of months until the other things (property behaviors, resilience, and other big changes) are behind us. We need to focus on the important things, because getting syntactic niceties into Swift 3 but missing on ABI stability would be really unfortunate. We have seriously constrained design and engineering bandwidth. This feedback applies to other pure-sugar proposals as well.

--- Rationale ---

I explained three bits of rationale for why we should tackle this:

1) Our existing feature is half-baked and unsatisfying. This causes me personal angst but, as pointed out above, the simplest solution is to just remove what we have until we can do it right.

2) Memberwise init sugar strongly benefits “POD” types and other “bags of property” types (e.g. “Vec4"), and many of the C struct types that Cocoa has (CGRect etc). In these cases, clients often want to initialize all of the fields explicitly and a memberwise init proposal eliminates this boilerplate. This case is what our existing feature attempts to service.

3) Memberwise init sugar can replace some really common “builder” patterns. These cases often have a bunch of configurable state in a class, where almost all of it has default values, but where clients want to be able to only specify deltas from the default. In the cases where you have a type with a ton of data members, a memberwise init is very very appealing because it eliminates a ton of boilerplate to set up a builder manually, and it also allows the captured state to be immutable (if we allow memberwise init of lets, which Matthew is a huge proponent of). This case can often be a class derived from something else (e.g. a Kit class), explaining the desire to specify super.init, add custom parameters, etc.

#3 is my distillation/abstraction of many of the arguments that Matthew has made on the list and in the proposal (e.g. his very helpful FontPicker example), but I don’t claim that this captures all of his or any one else’s motivation. This is simply the rationale that the team discussed.

--- Common feedback and points ---

Getting to points common to any “memberwise initializer” proposal, the goals of the core team are:

- Provide a predictable model. It was concerning to many people that you could accidentally reorder properties and break a memberwise init without knowing/thinking about it. This directly contravenes the resilience model. API diff’ing tools and “synthesized header” views in Xcode ameliorate this, but don’t totally solve it.

- After discussion, the desire to support memberwise init of let properties was found to be a strong goal. The entire win of providing a builder-like pattern in an init method (vs doing initialize to default and reassign over the value later, C# style) is the immutability benefits.

- We would like for memberwise init to be orthogonal to property behaviors, access control, attributes, and all the other stuff you’d want to do on your properties. Also, ideally, you shouldn’t be limited to what kinds of properties can participate.

- The issues around whether properties with a default value (both let and var) can be replaced by a memberwise init is a really important semantic decision we need to nail down. Many of the proposals include the problem/fact that something like:

class C {
  var x : Int = foo()

  memberwise init(…) {}
}

would not run the foo() side effect when initialized with "C(42)”. Similarly, in a case like:

class C {
  let x : Int = 42
  memberwise init(…) {}
}

it is still very concerning to people that the apparent axiom that “x is 42” could be violated by a memberwise init. Allowing lets have other implementation issues, such as suddenly making “x” require storage, where it could otherwise be optimized out.

OTOH, my previous objection about being able to write a memberwise init manually is bogus, since you could write the above out like this:

class C {
  let x : Int // initial value removed.
  init(x: Int) { self.x = x } // manually written memberwise init.
  init() { x = 42 } // move the inline initial value here.
}

--- Approaches discussed ---

Here are the rough approaches we discussed, along with pros and cons. For consistency, most of them are modeling the analog of sugarizing an example case like this:

class C : Derived { // has base class
    var x: Int // no default value.
    var y = 4.0 // default value
    var z = “foo" // default value

     // has an explicit “q” parameter as well as all the members as parameters.
     init(q: Int, x: Int, y: Double = 4.0, z: String = "foo”) {
        self.x = x
        self.y = y
        self.z = z
        // your custom code can go here
        super.init(q)
    }
}

I ordered this so that the most promising ones are at the end, to keep you all in suspense :-)

Approach: C# model

As pointed out on the list, we could eliminate the notion of a synthesized memberwise init, and move the complexity to the caller side. C# uses syntax along the lines of:

  new Foo(){x=42, y=17.0}

pro) No memberwise initializer concept
con) Requires all fields to be default initialized, which breaks non-nullable pointers etc.
con) Requires mutability, since you’re initializing, and then overwriting a field.
con) This specific syntax would be problematic with trailing closures etc.

Core team was strongly opposed to this approach.

Approach: Scala (among others) memberwise init as part of class decl

This would give us something like this:

class C(var x: Int, y: Float = 4.0, var z : String = “foo") : Derived {
    var extra = 4.0 // extra, non-memberwise fields supported.
    init(q: Int, ...) { // all initializers get memberwise stuff added, allows bodies.
       extra = 17
        // your custom code can go here
        super.init(q)
     }
}

pro) Very predictable model, makes it very clear what is subject to memberwise init, and that the order matters
pro) This supports indicating that some fields don’t participate in memberwise init.
pro?) Syntax looks vaguely like the parameter list of the init
con) The syntax isn’t actually parameter list syntax, it is property syntax. You’d want to accept public/private, let/var, behaviors, attributes, and lots of other stuff in there. This would introduce confusion about parameter lists.
con) This is sticking more junk into the class decl, which is already populated with class attributes/modifiers, subclasses/protocols, etc.
con) This means we’ve have two very different ways to declare stored properties in a type.

Core team was strongly opposed to this approach.

Approach: Extract the members from the parameters of an initializer decl

This would give us something like this:

class C : Derived {
    // members come from the memberwise init.
     memberwise init(x: Int, y: Double = 4.0, z: String = "foo”) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Syntactically very terse.
con) This only supports one memberwise init.
con) Two ways to declare stored properties
con) Conflates the property grammar (behaviors etc) with parameter grammar, many of the same problems as approach right above does.

Core team was strongly opposed to this approach.

Approach: barely extend our existing model:

We could put default values onto our existing memberwise init, and allow you to write "memberwise init(...)” to explicitly request it to be synthesized, but not allow a body. We could bring the existing level of feature to classes.

pro) simple, solves some problems with the existing model.
pro) brings memberwise init to root classes and classes that derive from something with a init() to chain to (e.g. NSObject).
con) doesn’t solve enough of the problem to be worth it
con) feels like we are perpetuating a hack.
con) we’d have to extend this further at some point, this is just kicking the can down the road.

Core team was strongly opposed to this approach. Better to either rip out what we have or do a bigger approach.

Approach: Magic “members” tuple (or other generics/variadics features)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

    init(p : … Members) {
       self.members = p
    }

    // compiler synthesizes.
    typealias Members = (Int, Float, String)
    var members : Members { get {..} set {..}}
}

pro) seems like a more generally useful feature that could pay for itself in other ways
con) doesn’t work with default values, so it doesn’t solve the builder problem at all (and no, we are not adding default values back to tuple types :-)

Core team was strongly opposed to this as an approach for memberwise init, but can always consider doing this for other things if it is motivated by them.

Approach: Introduce sugar for “self.x = x"

David Owens ][ mentioned that perhaps we can go with something simpler, like this (concrete syntax is just a strawman):

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     init(q: Int, self.x, self.y, self.z) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body.

pro) Does not require introducing the notion of a memberwise init. Any init could use this.
pro) Makes it very clear what members are being initialized and what the API for the init is.
pro) Makes it easy to pick and choose which members you want to make available to clients.
con) This isn’t sweet enough sugar to be worthwhile, particularly for the builder pattern case, which would require lots of boilerplate
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird. Making this keyword driven (e.g. "memberwise” at least gives people something to google and lets them know they’re looking at something magic).
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach, but agree that something like this could be composed on top of another proposal if that made sense.

Approach: Introduce sugar for “self.x = x”, and add “memberwise and …"

Extending David Owens ][’s model a bit, we could keep the behavior he indicates and add memberwise/… to handle cases with default values:

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, self.x, ...) {
        // your custom code can go here
        super.init(q)
    }
}

… where self.x in the parameter list is sugar that says it takes whatever the x property is (incl type and initial value if present), and does "self.x = x” in the body. The presence of memberwise/… would forward all the properties with default values.

pro) Since default values can be provided by a callee in any order, this approach can be made stable against reordering of properties.
pro) Compared to the base proposal above, this would be great for the builder pattern case.
con) The POD case still requires you to duplicate all your properties, so this doesn’t really address that use-case.
con) the self.x syntax is weird and surely can be improved, but anything we came up with would be magic and weird.
con) doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).

Core team was weakly opposed to this approach.

Approach: Matthew’s proposal SE-0018 (the actual formal proposal :)

class C : Derived {
    var x: Int
    var y = 4.0
    var z = “foo"

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
unclear) I’m not delving into it here, but the access control aspects were very controversial. We didn’t discuss it fully.
con) compared to the Scala approach, doesn’t handle the ability to opt a member out of memberwise init (we’d need some thing like @nomemberwise)
con) Doesn’t address the problems of replacing the var/let initial values (zapping a side effect in either of them, and breaking ‘let’ axioms).
con) Lots of concerns about unclarity that the order of the properties matter. In a large type, the memberwise init could be “at the end” of the class, after a bunch of other initializers and methods, and the properties could be “up at the top”. It could be very unclear to people that memberwise init is even happening, and changing a property could have surprising effects.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.

Core team thought this was very close, but the problems were significant enough to think it won’t fly, and suggest pursuing the variant below.

Approach: "Opt-in" version of Matthew’s proposal

Matthew has mentioned on list that he likes opt-in models, and after discussion the core team agreed that it is more promising than the base proposal.

In addition to the memberwise declmodifier, *also* introduce a declmodifier on the properties in question. Here I’ll use “mwi” as a strawman for “memberwise initializerable”, but it is obviously not the right thing, and I’m sure it will be replaced with something else brilliant :-)

class C : Derived {
    mwi var x: Int
    mwi var y = 4.0
    mwi var z = “foo"
    var extra = 4.0 // Doesn’t participate in memberwise inits.

     memberwise init(q: Int, ...) {
        // your custom code can go here
        super.init(q)
    }
}

pro) Solves the POD case as well as the builder case.
pro) Keeps properties the way they are, composes with behaviors etc.
pro) Supports adding custom parameters to the initializer, supports custom chaining to super.init, etc.
pro) Supports multiple different memberwise inits in a class, e.g. that want to chain to different super.init’s.
pro) Like the clarity of having a keyword on the init, and a sigil in the parameter list saying that something is going on.
pro) as with the Scala approach it handles the ability to have members be both memberwise and non-memberwise participating.
pro) Works with let and var initializer semantics.
pro) Looking at a property, you know immediately that it participates in memberwise init, and that you should go update the memberwise inits if you move it or change it. We could warn/error if you have a “mwi” property and no memberwise init, or a memberwise init with no mwi property.
pro) because this is an opt-in model, we can eliminate the controversial access control aspect of SE-0018.
con) Not clear from the memberwise init declaration what the extra parameters are, you have to look at a synthesized header or something.
con) more verbose than Matthew’s proposal. OTOH, the verbosity isn’t “boilerplate" since we’re getting something out of it. Simple POD cases can be written as (e.g.) “mwi var x, y, z, w : Double” which isn’t bad.

Adding the modifier on the affected property decls helps improve the intentionality and eliminate some of the "spooky action at a distance" problem, but the major breakthrough (IMO) is that it allows us to include lets into the model, and gives us a simple answer for why side effects are squashed on vars. Consider this example:

class C {
  mwi let a = 42
  mwi var b : Int = sideeffect()

  memberwise init(…) {}
}

A call site would look like this, giving A a value of 75 and would not run the side effect:
let tmp = C(a: 75, b: 12)

We’re ok with the side effect being squashed, and the let axiom being broken given that there is a decl modifier on the property decl. This is because you can tell locally by looking at the property that a memberwise initializer provides a different value. From an implementation perspective, this is also pretty great, because this leads to a very straight-forward codegen model that cleanly meshes with what we already have.

--- A path forward ---

So that’s where we left it. Coming back to the meta point above though, the core team really doesn’t want to discuss this right now, given that this is purely a sugar proposal and we need to stay focused on the primary Swift 3 goals. If Matthew or another community member wants to pick this up and keep running with it, that is perfectly fine, but we should defer more formal discussion about the proposal until at least march/april-ish. Likewise, other pure sugar proposals should similarly be deferred at this point, unless they have some strong motivation that aligns with a Swift 3 goal.

I want to thank Matthew in particular for passionately pushing this area forward!

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi Matthew,

Thanks for the explanation.

Before getting into a deeper discussion, I'd like to try to enumerate the
reasons for adding the placeholder as I understand them:

   1. Add clarity by visually distinguishing memberwise initializers from
   normal initializers.
   2. Introduce a "synthesized parameters placeholder" syntax that might be
   useful in other places.
   3. Allow some control over where the synthesized memberwise parameters
   end up in the initializer signature.

Does that seem accurate?

~ Alex

···

On Wed, Jan 6, 2016 at 3:48 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 6, 2016, at 5:26 PM, Alex Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

(this is mostly a repost of a message I sent to the "[draft]" thread for
this proposal, with some light editing to better match terminology in the
proposal)

*What is your evaluation of the proposal?*

I like this proposal. I think it will bring some much-needed ease-of-use.

I have reservations about the "..." placeholder for the memberwise
initialization parameters, though. I know this was suggested by
Chris Lattner, so I'm inclined to defer to his judgement. But, here are my
thoughts:

First, it's very close to the varags syntax (e.g. "Int...") which can
also appear in initializer parameter lists.

Second, and I think more important, I'm not sure that it's all that
*useful*. It's presence isn't necessary for triggering memberwise
initialization synthesis; that is already done by the "memberwise" keyword.

The primary example given in the proposal is:

memberwise init(anInt: Int, anotherInt: Int, ...) {

  /* code using anInt and anotherInt */

}

That is, it's used to indicate where the synthesized parameters appear in
the parameter list if there are also custom (non-memberwise) parameters.

My question is, *could the memberwise initialization parameters always be
last?* That would eliminate the need for the placeholder.

I don't think I've seen a compelling case for embedding the "..." *within*
a list of custom arguments, like:

memberwise init(anInt: Int, ..., anotherInt: Int) {
  /* code using anInt and anotherInt */
}

It's been mentioned several times in the discussion of this proposal that
this behavior is purely optional. If it turns out that there are rare cases
where placing the memberwise params in the middle is useful, authors can
use manual initialization.

Hi Alex, thanks for your review.

The initial draft of the proposal did exactly what you suggest - it did
not include the placeholder and always placed the memberwise parameters
last. Personally, I believe the placeholder adds clarity and really liked
the idea when Chris suggested it.

Aside from personal preference, I like that this proposal introduces a
“synthesized parameter placeholder” syntax. Similar syntax will also be
used in the parameter forwarding proposal mentioned in the future
enhancements section of this proposal.

I think the `…` works really well. That said, I don’t mind if people wish
to bikeshed on it. If that discussion starts it is worth noting that one
thing I like about the `…` is that it combines with an identifier cleanly.
For example : `…memberwise`.

Combining the placeholder with an identifier allows more than one
placeholder to be used in the same parameter list. For example, if you are
forwarding memberwise parameters exposed by a super init you might also
have `…super`.

That said, I don’t want the review thread to get distracted with
discussions around general parameter forwarding so please just consider
this as a preview of how this syntax might scale to future applications.
For now, lets keep this thread focused on the review of the current
proposal. :)

Matthew

On Wed, Jan 6, 2016 at 2:47 PM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs
through January 10th. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

        https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

        * 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 you 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?

More information about the Swift evolution process is available at

        https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris
Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--

*Alex Johnson | Engineering Lead*

*Quick Left, Inc. <https://quickleft.com/&gt;\*
*Boulder **|* *Denver* *|* *Portland** |** San Francisco*
1 (844) QL-NERDS
@nonsensery

<https://github.com/quickleft&gt; <Facebook;
<https://twitter.com/quickleft&gt; <https://instagram.com/quick_left/&gt;
<https://www.flickr.com/photos/quickleft&gt; <https://vimeo.com/quickleft&gt;

*What's it like to work with us? **TrainingPeaks, iTriage, and Ping
Identity share their stories in this short video** A Client's View
<https://vimeo.com/92286352&gt;\*\.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Not sure if this is exactly what Dave has in mind, but an idea that comes to mind: we could say that structs and classes have a magic "members" tuple and typealias:

struct Foo {
  var x, y: Int

  // Implicit members
  typealias Members = (x: Int, y: Int)
  var members: Members {
    get { return (x: x, y: y) }
    set { (x, y) = newValue }
  }
}

With plausible future features for forwarding tuples as arguments, then the memberwise initializer could be implemented like this:

      // Say that a parameter declared 'x...' receives a tuple of arguments labeled according to its type,
      // like '**x' in Python
init(members...: Members) {
  self.members = members
}

And I think all your other use cases could be covered as well.

-Joe

···

On Jan 6, 2016, at 5:23 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 6:04 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 6, 2016, at 2:47 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "Flexible Memberwise Initialization" begins now and runs through January 10th. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?

It’s okay.

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

I’m lukewarm about that. I have never found writing out the initializers I want to be a significant burden, and I find my code is better when they’re explicit. Every new feature increases the language's complexity and surface area, and I fear this one is not going to pay its way.

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

Yes, but I worry that it may be too early to add it. Other features in this space, like truly generic variadics, may well obsolete anything we do today. I’m not sure we should be designing convenience features that are likely to overlap with more general features coming down the road unless the inconvenience is very painful… which I personally don’t find it to be.

It isn’t clear to me how generic variadics might obsolete the functionality of this proposal. Can you elaborate on that?