SE-0242: Synthesize default values for the memberwise initializer

  • What is your evaluation of the proposal?

+1

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

I think so. It's a fairly simple addition that allows a bit more flexibility without extra cost to the mental model.

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

Definitely. IMO this is how synthesized initializers should have worked from the start.

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

Lots of languages have default values and lots of them have implicit constructors, but I can't think of a language now that has both that work in concert. Still, it feels more consistent.

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

I read the proposal and participated a bit in the implementation review.

Great work, @Alejandro!

5 Likes

The proposal does not make it clear what happens concerning immutable properties. I can wait until the toolchain to test it out, but I think it would be best for the proposal to make it clear, for future reference:

struct Dog {
    let name: String = "Fuki"
    var age: Int
}

let dog1 = Dog(age: 8)
let dog2 = Dog(name: "Smelly", age: 8)

struct Cat {
    private(set) var name: String = "Gizmo"
    var age: Int
}

let cat1 = Cat(age: 11)
let cat2 = Cat(name: "Kitty-Cat", age: 11)
4 Likes

Actually it does: “ Note that we can only synthesize values for variables that have declared default initializers and not constants .”

Allowing constants to be initialized with a non-default value is something I would like to see. The core team indicated support for at least considering it in the rationale for deferring SE-0018. But changing this is a much larger and more complex discussion than the simple incremental change proposed here. I think the author made the right choice in keeping this focused and simple, saving more complex discussions for a time when we revisit the broader memberwise init discussion.

3 Likes

let properties that are initialized at the declaration are not included in synthesized initializers today, and this isn't changing.

1 Like

What is your evaluation of the proposal?

Positive!

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

Yes! I still write much too many initializers.

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

Plain data structs are a major Swift tool, as it is in all languages that support them. I'm happy that we can raise even higher the benefits / energy spent ratio.

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

A good read of the proposal, and of this thread.

I appreciate @rex-remind's posts. I also like it when non-trivial structs are explicitly initialized with all their important components, so that the intent is crystal clear. However this proposal does not prevent this explicit coding style: just do not give default values to those variables.

My bad! Makes sense.

What is your evaluation of the proposal?

+1

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

I found myself wishing for this feature a couple of times in the past, and it's definitely something that I expected to be able to do already.

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

After using Swift for quite a while, I feel like this is how memberwise initializers were supposed to work from the beginning. The mental model created by default values makes this a very welcome improvement.

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

Read the proposal, implementation and the forum thread.

1 Like

Definite +1. This has stung me a lot, especially recently. I have a lot of boilerplate this will knock out.

Yes, this seems like a bug to new users and makes it confusing why default values even exist on Structs in the first place.

Yes

N/A

Quick reading.

The proposal should address what happens in this case:

func zeroWithSideEffects() -> Int {
  print("hello!")
  return 0
}

struct IntWrapper {
  var value: Int = zeroWithSideEffects()
}

let x = IntWrapper()

Does this print "hello!" once or twice? I believe the answer is "twice" given how Swift currently implements initializers, and I think that's the only thing we can do right now, and I believe it's going to be fairly rare for people to rely on this (and that they probably ought not to)
but it should be documented in the proposal.

(Of course, someone who is concerned with this behavior for a specific struct can manually implement any initializers they want, so it's not like they're locked in. The memberwise init also stays internal in this proposal, which is important because default arguments of public functions have stricter requirements on their expressions than initial values of public properties.)

15 Likes
  • What is your evaluation of the proposal?
    Huge +1
  • Is the problem being addressed significant enough to warrant a change to Swift?
    Yes, the current behavior is not intuitive and feels like a bug for new Swift developers who generally expect this to "just work".
  • Does this proposal fit well with the feel and direction of Swift?
    Yes
  • If you have 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?
    I've wanted this feature for a long time, I'm glad someone finally proposed it.
1 Like

I agree that this is probably the necessary answer given how Swift currently implements initializers and that it will be fairly rare for people to rely on it. This is of course what happens if people write the initializer manually. However, I don’t think this is what most people would intuitively expect or desire.

Hopefully this could be changed to “once” if / when we are able to revisit SE-0018. Changing the answer to this aligns nicely with re-interpreting instance constants as constant after initialization with the assigned value as a default initializer argument. The core team indicated some support for this direction in the rationale provided for the SE-0018 deferral.

5 Likes

I agree. Is it desirable that we document this double evaluation of side effects as a guaranteed behavior, when it is just the side effect of implementation details?

Is it possible that we document the observed behavior (because some people will look for a precise answer), along with "but this may change in a future Swift release" (so that we don't put a lock on a surprising behavior)?

4 Likes

+1 to this

Except for optionals right?

struct S {
    var f: Float
    var i: Int?
}
let s = S(f: 3.14) // s.i is set to nil
2 Likes

I want to make a joke about my previous comment saying 'some "default"', but I can't think of a good one...

But that's an interesting question... I don't think the proposal explicitly calls out defaulting to .none when it's an optional.

I don't think we really have a choice here, given this already works:

struct S {
    var i: Int?
}
let s = S() // s.i is set to nil
1 Like

Conversely, this does not work:

struct S {
  var i: Int?
  var j: String
}
let s = S(j: "j") // error: missing argument for parameter 'i' in call

Edit:

I agree that making it work should be included in this proposal, but just to be abundantly clear, your example demonstrates the zero-argument “all properties have a default value” initializer, not the memberwise initializer.

I know. I was trying to point out that since it works in the zero-argument case it'd be a bit silly to not make it work here. (You'd have to make the distinction between implicit and explicit default value, and then allow implicit default values for the zero-argument initializer but disregard them when setting the default values for the member wise initializer.)

1 Like

What is your evaluation of the proposal?

I am very supportive. This will let me get rid of most boilerplate initializers in my code.

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

This is not a big problem, but it's a recurring annoyance. I think the impact will be a bit similar to automatic implementation of Equatable and Hashable conformances: less boilerplate code everywhere.

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

I think it does. I see it as a refinement of having a synthesized default initializer structs where all members have a default value.

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

This is similar to aggregate initialization in C++. But it's so much better here since we can avoid inventing a new syntax for it.

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

Read of the proposal and the review thread. I also recall the previous threads about this a few years ago so maybe I participated then.

1 Like

Yeah I think this points out bigger problems for initialisers on structs in general, whether we implement manually or implemented by the compiler, so this doesn’t seem like a valid reason not to do this. I agree this issue should be noted in the proposal and stated something like “this behaviour is not intended but a current limitation”, to give the flexibility to update the behavior later.