SE-0242: Synthesize default values for the memberwise initializer

The review of SE-0242: Synthesize default values for the memberwise initializer begins now and runs through February 26, 2019.

The proposal is written by @Alejandro.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager via email or direct message on the forums. If you send me email, please put "SE-0242" somewhere in the subject line.

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

  • 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 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?

Thank you for contributing to Swift!

Ted Kremenek
Review Manager

26 Likes

+1
While personally, I would like to see expanded memberwise initialization, I understand it's a fairly heated topic that was put on the back burner for now. I think this fixes an existing annoyance that all Swift devs experience at some point, and most would expect to "just work".

The most pertinent question I think that needs answering for this one is, "Does this proposal fit well with the feel and direction of Swift?". I believe this does. As I stated previously, memberwise initialization is a complex topic, with many possible expansions and discussions to be had. That being said, this feels like a natural extension of the existing rules.

10 Likes

Big -1

Default initializers are clear to code readers and helpful to implementors as they currently stand as the type checker always guarantees that the implementor considered the value of every attribute on a struct before initialization. If there is something that could be left out it semantically is not that struct. In the very rare cases where I want default members I can add new initializers myself.

By contrast this proposal is more similar to how initializers function in go and it has had a negative impact on code clarity and code design (especially when working with more junior engineers) in my experience. Since this adds complexity to something simple it also does not fit well with the feel and direction of Swift.

However, there is one single case where I find this annoying and that’s when constructing stub types. Yet, I think this feature would be better served addressing that problem directly. Possibly having a @defaultvaluememberwiseinitializer or @stubinitializer attribute or something of the sort aiding in clarity and not implicitly taking away the advantages previously described. This could be used in a private extension on the type in a test file for example. This specific example I find warranting change but that’s not the proposal as currently defined.

I read the thread and proposal in entirety.

1 Like

Could you comment on some examples of negative impact in your experience?

To my mind this seems a very limited and sensible addition to the language. Given that the the synthesized initializer requires labels for everything, I’m afraid I don’t see how the result of adding this feature could be confusing.

19 Likes

The two things that come to mind are 1. Implementors thinking they should carry data in a struct which is a subset of the values provided by the struct even though semantically the two data sets are used differently. I’ve seen this especially when transforming data over pipelines (Go). If there is a different shape to the struct it’s probably not the same struct. These subtle differences grow in complexity as the code base grows. 2. Even though all parameters are labelled how do you know how many labelled values go into Dog by reading it? In a 500,000 line code base are you always going to go look at the type and know this value has been fully defined? These are difficult assumptions to work with on large code bases, even worse with large teams of varying skill set. It’s even more challenging when the struct is defining something more esoteric so you can’t make any assumptions about what it means for it to be fully initialized.

I know some of this may sound vague but I’ve seen the consequences working on large Go codebases, which is where this is coming from. I can’t imagine it won’t cause headaches in Swift down the road either.

Rex, with all due respect, this reads to me like a lot of handwaving, but I can not parse it as any reasonable argument against the feature as proposed. Would you like to clarify some more?

3 Likes

I'm not sure how this Go specific issue carries over into Swift land. If can elaborate with Swift examples of this kind of problem, that'd be helpful.

You should always have some form of the declaration of the struct inits available to you. Ideally you would have an IDE that can help you out here. But failing all that, the compiler will work out if you've gotten something wrong. And it's gotten pretty good at identifying the issue and offering a fixit.

I'm assuming you're talking about structs that might have some kind of logic built around (some) of their properties, yes? If that's the case, yes, I could see a beginner/lazy programmer just sticking a default onto a new property, when said property really needs some additional logic before a "correct" value can be put it. But IMO that kind of programmer mistake should be caught via code reviews. Where it should be pointed out that unless a property has a sensible default value, you should opt-out of memberwise initialization and provide your own inits.

2 Likes

Just so I'm clear, is this an argument against default values altogether?

7 Likes

The story I’m trying to paint, and that I have experienced, is one of headache as reviewer and reader of a very large codebase with default values accepted implicitly throughout the language. There is a cost at scale that is tough for me at least to communicate concretely. I’m not trying to be handwavy but I can see how it my seem that way.

One thing I will posit though is what use case is actually being solved here? I did propose one, with an alternative solution. I may be misreading but I do not see one in the proposal.

Implicitly defined default values on member wise initializers as proposed. Experience has found that as is, where they are not implicitly defined, they are more rarely used and usually in selective cases where it adds value / makes sense.

  • What is your evaluation of the proposal?

It’s a small but valuable addition to the existing memberwise initializers.

I do have one question though. Does this proposal have an implementation? I didn’t see a link in the proposal itself or in the review announcement.

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

Yes, losing the default value in the synthesized initializer has been a long-time frustration for many Swift programmers.

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

Very much. As the author of SE-0018 I would like to see Swift continue to build out memberwise initialization to be more robust. This is a small but important step in that direction.

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

N/A

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

I have studied memberwise initialization in great depth.

5 Likes

It does, you can find it here: https://github.com/apple/swift/pull/19743 . @nnnnnnnn has started a toolchain build, so when that's done I'll post a link to it in here.

5 Likes

Thank you!

Hmmm... your question made me question my mental model. Is this actually opt-in via = 0? If that’s the case I completely flip on my position.

7 Likes

Yes, this is about the compiler still generating a default initializer with defaults grabbed from the property definitions. The compiler isn't going to be choosing some "default" if it wasn't given one.

5 Likes

My bad :sweat_smile:

If that’s the case I’m +1. Seems like the correct way they should operate in actuality.

14 Likes

To save some face this is how Go works which has biased me... apologies for not reading this proposal correctly.

No worries, I wasn't aware that was how Go works. And indeed if that behavior was being proposed, even I would be strongly against that.

5 Likes

Right, such a design was explicitly rejected for Swift from the get-go and is not proposed here.

4 Likes

+1. With a struct that has default values declared for all properties, Swift already synthesises an init() using those default values, and a parametric init(paramA:paramB:paramC:...) allowing you to specify value for each property. It follows that you should be able to elide some of these parameters. I can't conceive an argument in favour of the all or nothing status quo.

8 Likes