a few initializer proposals


(Mike Kluev) #1

hi. here are a few somewhat related initializer proposals for your
consideration.
they are designed as "opt-ins" so shall not break existing code.

***** proposal 1 *****
have an ability to keep default initializers in structs

struct S {
    var int: Int
    var float: Float
    var bool: Bool
}

here default initializer is currently generated:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

// autogenerated:
// init(int: Int, float: Float, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

so we can write:

let s = S(int: 0, float: 0, bool: false)

so far so good. now i want to add a "convenience" initializer. i don't want
the default initializer to go away though:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // *** ERROR. this
initializer is gone
    }
}

let s = S(x: 0)
let s = S(int: 0, float: 0, bool: false) // *** ERROR. this initializer is
gone

this may be convenient in some cases but not others.
proposal: introduce an opt-in construct to keep the default initializer
intact:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

    default init // explicit opt-in. suggest a better name

    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // still ok
    }
}

let s = S(x: 0) // ok
let s = S(int: 0, float: 0, bool: false) // still ok

***** proposal 2 *****
have the default initializer to have default nil values for all optional
parameters in structs

struct S {
    var int: Int
    var float: Float?
    var bool: Bool

// autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

in the extreme case:

struct S5 {
    var int: Int?
    var float: Float?
    var bool: Bool?

// autogenerated:
// init(int: Int? = nil, float: Float? = nil, bool: Bool? = nil) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

usage:
S(float: 3)
S()
S(int: 0, float: 0, bool: false)

note that this is still "opt-in" as the old code will be using the full
form anyway and not break.

***** proposal 3 *****
introduce a similar opt-in default initialiser for classes:

class C {
    var int: Int
    var float: Float?
    var bool: Bool
}

let c = C(int: 0, float: 0, bool: false) // *** ERROR

class C {
    var int: Int
    var float: Float?
    var bool: Bool

    default init // explicit opt-in. suggest a better name

// this is autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

let c = C(int: 0, bool: false) // ok

***** question 4 *****

this one is not a proposal, rather a question. probably there is a good
reason for it, i just want to know it: why there is no default initializer
for classes similar to what we have for structs?

···

=================================
thoughts and comments on any of these?

Mike


(Jon Shier) #2

1 can already be accomplished by moving the custom initializer into an extension.

Jon

···

On Jun 21, 2017, at 7:42 PM, Mike Kluev via swift-evolution <swift-evolution@swift.org> wrote:

hi. here are a few somewhat related initializer proposals for your consideration.
they are designed as "opt-ins" so shall not break existing code.

***** proposal 1 *****
have an ability to keep default initializers in structs

struct S {
    var int: Int
    var float: Float
    var bool: Bool
}

here default initializer is currently generated:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
// autogenerated:
// init(int: Int, float: Float, bool: Bool) {
// self.int <http://self.int/> = int
// self.float = float
// self.bool = bool
// }
}

so we can write:

let s = S(int: 0, float: 0, bool: false)

so far so good. now i want to add a "convenience" initializer. i don't want the default initializer to go away though:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // *** ERROR. this initializer is gone
    }
}

let s = S(x: 0)
let s = S(int: 0, float: 0, bool: false) // *** ERROR. this initializer is gone

this may be convenient in some cases but not others.
proposal: introduce an opt-in construct to keep the default initializer intact:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
    default init // explicit opt-in. suggest a better name
    
    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // still ok
    }
}

let s = S(x: 0) // ok
let s = S(int: 0, float: 0, bool: false) // still ok

***** proposal 2 *****
have the default initializer to have default nil values for all optional parameters in structs

struct S {
    var int: Int
    var float: Float?
    var bool: Bool
    
// autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int <http://self.int/> = int
// self.float = float
// self.bool = bool
// }
}

in the extreme case:

struct S5 {
    var int: Int?
    var float: Float?
    var bool: Bool?
    
// autogenerated:
// init(int: Int? = nil, float: Float? = nil, bool: Bool? = nil) {
// self.int <http://self.int/> = int
// self.float = float
// self.bool = bool
// }
}

usage:
S(float: 3)
S()
S(int: 0, float: 0, bool: false)

note that this is still "opt-in" as the old code will be using the full form anyway and not break.

***** proposal 3 *****
introduce a similar opt-in default initialiser for classes:

class C {
    var int: Int
    var float: Float?
    var bool: Bool
}

let c = C(int: 0, float: 0, bool: false) // *** ERROR

class C {
    var int: Int
    var float: Float?
    var bool: Bool
    
    default init // explicit opt-in. suggest a better name
    
// this is autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int <http://self.int/> = int
// self.float = float
// self.bool = bool
// }
}

let c = C(int: 0, bool: false) // ok

***** question 4 *****

this one is not a proposal, rather a question. probably there is a good reason for it, i just want to know it: why there is no default initializer for classes similar to what we have for structs?

=================================
thoughts and comments on any of these?

Mike

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


(Bastiaan Marinus van de Weerd) #3

Strongly +1 on proposals #1 and #2. Cautiously +1 on #3 (I’m not sure about
side effects there).

If #1 is already possible as Jon Shier says using an extension, I’d say it
deserves promotion to be part of the language (e.g. Mike’s proposed
`default init`), or at least should be made more discoverable (e.g. using a
Fix-It on an implementation equivalent to the default initializer).

–Bastiaan

···

On Wed, Jun 21, 2017 at 8:00 PM, Jon Shier via swift-evolution < swift-evolution@swift.org> wrote:

1 can already be accomplished by moving the custom initializer into an
extension.

Jon

On Jun 21, 2017, at 7:42 PM, Mike Kluev via swift-evolution < > swift-evolution@swift.org> wrote:

hi. here are a few somewhat related initializer proposals for your
consideration.
they are designed as "opt-ins" so shall not break existing code.

***** proposal 1 *****
have an ability to keep default initializers in structs

struct S {
    var int: Int
    var float: Float
    var bool: Bool
}

here default initializer is currently generated:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

// autogenerated:
// init(int: Int, float: Float, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

so we can write:

let s = S(int: 0, float: 0, bool: false)

so far so good. now i want to add a "convenience" initializer. i don't
want the default initializer to go away though:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // *** ERROR. this
initializer is gone
    }
}

let s = S(x: 0)
let s = S(int: 0, float: 0, bool: false) // *** ERROR. this initializer is
gone

this may be convenient in some cases but not others.
proposal: introduce an opt-in construct to keep the default initializer
intact:

struct S {
    var int: Int
    var float: Float
    var bool: Bool

    default init // explicit opt-in. suggest a better name

    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // still ok
    }
}

let s = S(x: 0) // ok
let s = S(int: 0, float: 0, bool: false) // still ok

***** proposal 2 *****
have the default initializer to have default nil values for all optional
parameters in structs

struct S {
    var int: Int
    var float: Float?
    var bool: Bool

// autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

in the extreme case:

struct S5 {
    var int: Int?
    var float: Float?
    var bool: Bool?

// autogenerated:
// init(int: Int? = nil, float: Float? = nil, bool: Bool? = nil) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

usage:
S(float: 3)
S()
S(int: 0, float: 0, bool: false)

note that this is still "opt-in" as the old code will be using the full
form anyway and not break.

***** proposal 3 *****
introduce a similar opt-in default initialiser for classes:

class C {
    var int: Int
    var float: Float?
    var bool: Bool
}

let c = C(int: 0, float: 0, bool: false) // *** ERROR

class C {
    var int: Int
    var float: Float?
    var bool: Bool

    default init // explicit opt-in. suggest a better name

// this is autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

let c = C(int: 0, bool: false) // ok

***** question 4 *****

this one is not a proposal, rather a question. probably there is a good
reason for it, i just want to know it: why there is no default initializer
for classes similar to what we have for structs?

=================================
thoughts and comments on any of these?

Mike

_______________________________________________
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


(Mike Kluev) #4

1 can already be accomplished by moving the custom initializer into an

extension.

it can indeed. although if you need to create an extension just for this
purpose it feels like a workaround, takes more lines and is less obvious,
imho. plus if #3 is accepted (the same construct for classes) it would be
more consistent to have it in structs as well.

Mike

···

On 22 June 2017 at 02:46, Jon Shier <jon@jonshier.com> wrote:


#5

I think an even stronger change should be made for #1: instead of explicitly opting into not discarding the default initializer, you should have to explicitly opt out of keeping it with something like @nosynthesizedinit. Unless I’m missing something and there is a compelling reason to remove the default initializer when you’ve provided another initializer.

I agree with #2 as is.

For #3, I imagine there’s some reason classes don’t get synthesized initializers. Probably because they’re meant to encapsulate a lot more functionality than merely maintaining state. Even if a class had an init with the “default” signature, it would almost certainly do more in the body than just assign them to instance variables. Since structs are so frequently used to simply hold onto state/keep data around, it makes sense for the compiler to provide an initializer that takes the entire initial state/set of data. In other words: if you find yourself using such an initializer for a class (an initializer that merely assigns the arguments to instance variables), you probably want to make that class a struct.

···

On Jun 22, 2017, at 9:38 AM, Bastiaan Marinus van de Weerd via swift-evolution <swift-evolution@swift.org> wrote:

Strongly +1 on proposals #1 and #2. Cautiously +1 on #3 (I’m not sure about side effects there).

If #1 is already possible as Jon Shier says using an extension, I’d say it deserves promotion to be part of the language (e.g. Mike’s proposed `default init`), or at least should be made more discoverable (e.g. using a Fix-It on an implementation equivalent to the default initializer).

–Bastiaan

On Wed, Jun 21, 2017 at 8:00 PM, Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:
1 can already be accomplished by moving the custom initializer into an extension.

Jon

On Jun 21, 2017, at 7:42 PM, Mike Kluev via swift-evolution <swift-evolution@swift.org> wrote:

hi. here are a few somewhat related initializer proposals for your consideration.
they are designed as "opt-ins" so shall not break existing code.

***** proposal 1 *****
have an ability to keep default initializers in structs

struct S {
    var int: Int
    var float: Float
    var bool: Bool
}

here default initializer is currently generated:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
// autogenerated:
// init(int: Int, float: Float, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

so we can write:

let s = S(int: 0, float: 0, bool: false)

so far so good. now i want to add a "convenience" initializer. i don't want the default initializer to go away though:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // *** ERROR. this initializer is gone
    }
}

let s = S(x: 0)
let s = S(int: 0, float: 0, bool: false) // *** ERROR. this initializer is gone

this may be convenient in some cases but not others.
proposal: introduce an opt-in construct to keep the default initializer intact:

struct S {
    var int: Int
    var float: Float
    var bool: Bool
    
    default init // explicit opt-in. suggest a better name
    
    /* convenience */ init(x: Int) {
        self.init(int: 0, float: 0, bool: false) // still ok
    }
}

let s = S(x: 0) // ok
let s = S(int: 0, float: 0, bool: false) // still ok

***** proposal 2 *****
have the default initializer to have default nil values for all optional parameters in structs

struct S {
    var int: Int
    var float: Float?
    var bool: Bool
    
// autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

in the extreme case:

struct S5 {
    var int: Int?
    var float: Float?
    var bool: Bool?
    
// autogenerated:
// init(int: Int? = nil, float: Float? = nil, bool: Bool? = nil) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

usage:
S(float: 3)
S()
S(int: 0, float: 0, bool: false)

note that this is still "opt-in" as the old code will be using the full form anyway and not break.

***** proposal 3 *****
introduce a similar opt-in default initialiser for classes:

class C {
    var int: Int
    var float: Float?
    var bool: Bool
}

let c = C(int: 0, float: 0, bool: false) // *** ERROR

class C {
    var int: Int
    var float: Float?
    var bool: Bool
    
    default init // explicit opt-in. suggest a better name
    
// this is autogenerated:
// init(int: Int, float: Float? = nil, bool: Bool) {
// self.int = int
// self.float = float
// self.bool = bool
// }
}

let c = C(int: 0, bool: false) // ok

***** question 4 *****

this one is not a proposal, rather a question. probably there is a good reason for it, i just want to know it: why there is no default initializer for classes similar to what we have for structs?

=================================
thoughts and comments on any of these?

Mike

_______________________________________________
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

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


(Xiaodi Wu) #6

These issues which you have raised have been discussed thoroughly on this
list some time ago. It was distilled into a very well written proposal,
SE-0018, that was evaluated and deferred. I would recommend that you and
all people who are interested in this topic review the initial pitch and
the subsequent discussions that took place.

At the conclusion of that process, any improvements for memberwise
initialization were deemed out of scope for Swift 3. Subsequently, all
sugar was out of scope for Swift 4 phase 1 and deemed to be extremely low
priority for Swift 4 phase 2. Whether the topic will be in scope again is
an open question.

The summary of the core team's decision is unusually long and
extraordinarily enlightening. The issues about this feature being out of
scope are discussed as "meta-points," and an extensive amount of text
explains the thought process behind that conclusion. Beyond that, however,
the decision also explores a truly remarkable set of possible solutions to
the underlying issue, and it raises very interesting (non-meta) points that
would have to be carefully considered before any revised proposal.

···

On Thu, Jun 22, 2017 at 16:14 Mike Kluev via swift-evolution < swift-evolution@swift.org> wrote:

On 22 June 2017 at 02:46, Jon Shier <jon@jonshier.com> wrote:

1 can already be accomplished by moving the custom initializer into an
extension.

it can indeed. although if you need to create an extension just for this
purpose it feels like a workaround, takes more lines and is less obvious,
imho. plus if #3 is accepted (the same construct for classes) it would be
more consistent to have it in structs as well.

Mike

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


(Mike Kluev) #7

thank you Brian.

i like the "default value" idea in those proposals. speaking of lazy vars,
maybe just to not have them in the default memberwise initializer param
list at all:

// this is NOT how the language behaves currently:

struct S {
    var a: Int
    var b: Int = 0
    var c: Int?
    var d: Int? = nil // same as c
    var e: Int!
    lazy var f: Int = { return 0 } ()

// expected "reasonable" default memberwise initializer autogenerated or
opt-in-ed:
//
// init(
// a: Int,
// b: Int = 0, // got default value #change
// c: Int? = nil, // got implicit default value #change
// d: Int? = nil, // got default value #change
// e: Int! // no default value - safer
// // f is absent due to lazy #change
// )
// {
// self.a = a
// self.b = b
// self.c = c
// self.d = d
// self.e = e
// // f is absent due to lazy #change
// }
}

speaking of private var members, if they have an implicit or explicit
default value:

private var g: Int = ...
private var h: Int?
private var i: Int? = ...
private var j: Int!
private var k: Int! = ...

they can be omitted from the parameter list of the default memberwise
initialiser (#change) similar to how lazy vars are omitted above.
if a single private var has no implicit / explicit default value then
default member wise initialiser is not created (#change) and attempt to
opt-in to have one gives an error. let's call it proposal #5. this
particular one is not a backward compatible change, the old code will have
to be modified.

Mike

···

On 23 June 2017 at 00:36, Brian King <brianaking at gmail.com <brianaking@gmail.com>> wrote:

A similar syntax has already been proposed and has been `deferred`. There
are a few other links to discussion in the proposal.

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


(Mike Kluev) #8

thanks. interesting reading indeed:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006469.html

"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. "

that was written in Jan 2016, a year and a half ago. time to resurrect?

personally i am not too mad about the "path forward" outcome at the bottom.
looks a bit too verbose, a bit too complicated and a bit too "over
engineered", but that's in my humble imho. personally i'd tend to more
simple measures, e.g. if there is "let x = 0" and it looks a problem if we
override it (as it does indeed) - just don't have those "let's" it in the
default memberwise initializer parameter list - done.

btw. don't blame syntax sugar, it's not without value.... when it helps
eliminating 200 or even 2 lines.

speaking of "kicking the can down the road" - i trust this is a valuable
approach still... it is very hard (and almost impossible) to do it right
straight away... whether there will be needed 10 iterations until it's
right or 3, or what particular swift version it is in - not that important.

Mike

···

On 23 June 2017 at 01:20, Xiaodi Wu <xiaodi.wu at gmail.com <xiaodi.wu@gmail.com>> wrote:

These issues which you have raised have been discussed thoroughly on this
list some time ago. It was distilled into a very well written proposal,
SE-0018, that was evaluated and deferred. I would recommend that you and
all people who are interested in this topic review the initial pitch and
the subsequent discussions that took place.

At the conclusion of that process, any improvements for memberwise
initialization were deemed out of scope for Swift 3. Subsequently, all
sugar was out of scope for Swift 4 phase 1 and deemed to be extremely low
priority for Swift 4 phase 2. Whether the topic will be in scope again is
an open question.

The summary of the core team's decision is unusually long and
extraordinarily enlightening. The issues about this feature being out of
scope are discussed as "meta-points," and an extensive amount of text
explains the thought process behind that conclusion. Beyond that, however,
the decision also explores a truly remarkable set of possible solutions to
the underlying issue, and it raises very interesting (non-meta) points that
would have to be carefully considered before any revised proposal.


(Mike Kluev) #9

alternatively:

in case of private members with no implicit / explicit default values, we
may have a private memberwise initialiser with all private member
parameters as well. you can create your own public initializer and call
through the private memberwise initializer if needed

struct S {
  var a: Int
  var b: Int = 0

  private var c: Int
  private var d: Int?

// autogenerated (#change)
//
// private init(a: Int, b: Int = 0, c: Int, d: Int? = nil) { // private
// self.a = a
// self.b = b
// self.c = c
// self.d = d
// }

  init(... whatever ...) {
    self.init(a: ...., b: ....., c: ...., d: ....)
  }
}

Mike

···

On 23 June 2017 at 02:39, Mike Kluev <mike.kluev@gmail.com> wrote:

if a single private var has no implicit / explicit default value then
default member wise initialiser is not created (#change) and attempt to
opt-in to have one gives an error. let's call it proposal #5. this
particular one is not a backward compatible change, the old code will have
to be modified.