Optional properties get unwanted default values in memberwise initializers

extracting from another thread. i believe this behaviour is broken and needs fixing:

struct S {
    var a: Int
    var b: Int?
    var c: Int? = nil
}
let x = S(a: 0, b: nil, c: nil) // ok
let z = S(a: 0, b: nil) // ok
let y = S(a: 0, c: nil) // *** HHHMMM???
let w = S(a: 0) // *** HHHMMM???

this is because the synthesized memberwise initializer is generated as:

init(a: Int, b: Int? = nil, c: Int? = nil)

i believe this is a mistake and it should be generated as:

init(a: Int, b: Int?, c: Int? = nil)

note that initializer is generated correctly with the full Optional form:

struct S {
    var a: Int
    var b: Optional<Int>
    var c: Optional<Int> = nil
}

in order to "opt out" of argument default value i need to change Int? to Optional - and that's not obvious at all, as the Int? feels just a short hand syntax for Optional (same way as [Int:Int] is a short hand syntax to Dictionary<Int, Int>)

please note, to keep the scope of this issue minimal i suggest to keep this issue distinct and separate to the infamous "Pre-pitch: remove the implicit initialization of Optional variables" et al. this is to only "fix one thing at a time", especially given that the other thing might not be broken. so, "var b: Int?" is still treated as if it was written "var b: Int? = nil" for all other intents and purposes, just when it comes to memberwise initializer the following two will be errors:

let y = S(a: 0, c: nil) // error: value for b is missing
let w = S(a: 0) // error: value for b is missing

and the actual initializer generated would be:

init(a: Int, b: Int?, c: Int? = nil)

this is a breaking change, will need a fix-it.

5 Likes

I think this is all one thing. You’re showing one of several reasons why it’d be better if there were no special rule for implicit initialization using the ? operator. It doesn’t make sense to me to add another special rule to treat the default value as being present except for the synthesized initializer. Let’s keep this all on one thread and solve the actual problem.

5 Likes

@tera, you'll get the behavior you are after with this change in the definition of S:

struct S {
    var a: Int
    var b: Optional<Int> // Instead of Int?
    var c: Int? = nil
}
let x = S(a: 0, b: nil, c: nil)
let z = S(a: 0, b: nil)
let y = S(a: 0, c: nil) // error: Missing argument for parameter 'b' in call
let w = S(a: 0) // error: Missing argument for parameter 'b' in call

Surprised? You're not alone. This topic is indeed currently discussed in Pre-pitch: remove the implicit initialization of Optional variables

i don't see it exactly this way, for the following reason:

foo(param: Int?)

doesn't allow me to call it without parameter foo()

so it is not treated internally as if was: foo(param: Int? = nil)
but is exactly foo(param: Optional<Int>)

this shows that there are already parts of compiler that treats Int? as Optional<Int> (without = nil). i propose to use the same treatment in memberwise initialisers.

What I’m saying is that the specific member either does or does not have a default value. Swift currently has a special rule that members typed T? have an implicit default value. The problem you illustrate flows directly from that one issue—this is no other special rule about memberwise initializers at play.

I understand that you’re proposing the addition of another special rule surrounding memberwise initializers to address a specific consequence of the existing special rule surrounding implicit default values for members typed T?. That it makes one “thing” into two “things” doesn’t mean that it was two “things” to begin with. We want fewer special rules, not to compound them.

2 Likes

I had this same opinion a year back. Ever since, I've come to understand the issue as @xwu poses it, and arrived to the conclusion that it's better to remove implicit behavior altogether, in order to comply with Swift's core ideal of clarity over brevity. Introducing more rules to the exception is not the way to tackle this issue. We ought to get rid of the exception in the first place.

4 Likes

David, thanks for bringing this up. please correct your link to point to the first post of that thread (if technically possible).

1 Like

I think that

var c:Int? = nil 

is not as helpful as much people feel. Often when I am writing an Optional = nil default value, I am ending up writing down a full init function later anyway.

the reason i spinned this thread off (and the adjacent optionals-in-json related thread) is the consideration that swift would be a better language with this issue fixed, and even people who don't like the idea of removing implicit optional initialization in the other thread could (hopefully) agree on this particular one.

wrong thread.... please use the other one. otherwise we may end up with two equally heated discussions leading to nowhere as we've already seen a few times.

1 Like

Sorry, I will post on the other thread in the future.

huh, as i feared this didn't fix the link attached to the head of this topic.. attempting to fix: https://forums.swift.org/t/unexpected-memberwise-inits-for-all-optional-structs

Well. I do like the idea of removing implicit optional initialization, and I do not agree with this idea of adding additional special rules.

4 Likes

It's no big deal, the reference is there anyway :slight_smile:

1 Like