[swift-evolution] [Proposal Draft] Flexible memberwise initialization

Matthew Johnson matthew at anandabits.com
Tue Dec 22 07:54:39 CST 2015


> On Dec 22, 2015, at 12:08 AM, David Owens II <david at owensd.io> wrote:
> 
> I really like the idea and it’s something that’s extremely annoying to deal with today in Swift. My biggest concern is that the proposal, in its entirety, seems to really complicate the initialization rules.

Hi David.  I’m glad that you like the basic idea.  Thanks for taking the time to respond and offer feedback.

The proposal as written was only intended to be a place to start a conversation.  I expected it to be revised and am already in the process of doing that.  Chris had some great feedback that I am taking into account.  I hope both of you see the changes as improvements to the current draft.

I looked at the approach you are suggesting and discussed something similar to it in the alternatives section.  The problem with this approach is that it doesn’t address the fundamental problem I am trying to solve - namely that trivial code grows with MxN complexity (M memberwise initializable properties and N initializers).  

Your approach reduces the trivial code by about 50% but it does not address the scaling problem.  Scaling isn’t really significant in small struct examples but it would be quite non-trivial in something like a Swift UIKit.

You mentioned how your approach addresses private properties.  I did consider allowing parameters to be synthesized for lower access level members.  The property wouldn’t be visible, but a parameter could still be synthesized to initialize the member.  I decided against this approach because I believe it is probably relatively uncommon to want to initialize a member not visible to the caller with a value provided directly by the caller.  I will update the alternatives considered section to address this.

I would also like to point out:
1) Nothing in my proposal prevents you from taking full control of the signature for your initializer.  You just need to write the assignments manually.
2) What you are proposing and what I am proposing are not mutually exclusive.  It would be possible for them to exist side-by-side in the language.  

Matthew

> 
> Also, I don’t think it generates good API signatures. Take this example:
> 
> struct S {
> 	let s: String
> 	let i: Int
> 
> 	// user declares:
> 	memberwise init() {}
> 	// compiler synthesizes:
> 	init(s: String, i: Int) {
> 		self.s = s
> 		self.i = i
> 	}
> }
> 
> That is not a very descriptive API. It’s also not necessarily the case that your internal names are what you want exposed.
> 
> I would actually prefer the rule to simply be this: when an init() is modified by memberwise, the labelled parameters will be set. This lookup will try both the argument name and the parameter name, in that order, for reasons that become more clear with convenience inits described later.
> 
> So you would have this:
> 
> memberwise init(name s: String, value i: Int) {
>     // autogenerated: self.s = s; self.i = i
> }
> 
> This provides you all of the freedom that you may need:
> Order of the APIs is explicitly controlled
> API names of the members are not exposed if not desired, especially helpful for non-public members
> Default values are handled naturally
> 
> Optionally, you could keep the default simply with this:
> 
> memberwise init {}
> 
> This would use the property names as the argument labels.
> 
> Another example:
> 
> memberwise init(name s: String, value i: Int = 12) {
>     // autogenerated: self.s = s; self.i = i
> }
> 
> And the usage is much nicer:
> 
> let s = S(name: "Happy")
> 
> Here’s a subclassing example:
> 
> class Animal {
>     let type: String
>     let numberOfLegs: Int
>     
>     memberwise init(type: String, numberOfLegs: Int) {
>         /* generated */ self.type = type
>         /* generated */ self.numberOfLegs = numberOfLegs
>     }
> }
> 
> class Dog: Animal {
>     let name: String
>     
>     memberwise private init(type: String = "dog", numberOfLegs: Int = 4, n name: String) {
>         /* generated */ self.name = name
>         /* generated */ super.init(type: type, numberOfLegs: numberOfLegs);
>     }
>     
>     memberwise convenience init(name: String) {
>         /* generated */ self.init(type: "dog", numberOfLegs: 4, n: name)
>     }
> }
> 
> let d = Dog(name: "Fido")
> 
> The biggest thing about convenience inits is that the generation of the init needs to pull the defaults from the designated init. Also, for super.init(), it’s a straight label/name lookup for the designated init() only. Anything more fancy must be written by the user.
> 
> You also disallow private init, but that doesn’t need to happen with explicit members. That’s the beauty of giving the public API a real contract with names.
> 
> struct S {
>     let s: String
>     private let i: Int
>     
>     memberwise init(name s: String, value i: Int = 12) {
>         /* generated */ self.s = s
>         /* generated */ self.i = i
>     }
> }
> 
> Also, partial initialization could be possible, much like you proposed originally:
> 
> struct S {
>     let s: String
>     private let i: Int
>     
>     memberwise init(name s: String) {
>         /* generated */ self.s = s
>         self.i = s.utf8.count
>     }
> }
> 
> My concern is that this starts to be more complicated and magical. I’d actually cut this from the proposal.
> 
> The last change I would make is that if any property has an assignment in the declaration, it cannot be memberwise initialized.
> 
> struct S {
>     let s: String
>     let i: Int = 10
>     
>     memberwise init(name s: String) {
>         /* generated */ self.s = s
>     }
> }
> 
> In the above, i will always be 10.
> 
> -David
> 
>> On Dec 21, 2015, at 11:32 AM, Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> I have completed a draft of the proposal I have been working on for flexible memberwise initialization.  I am really looking forward to your input and will be refining the proposal based on our discussion.
>> 
>> I am including a current snapshot of the proposal in this message.  I will keep the proposal up to date on Github at this link:
>> 
>> https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151222/53db8f89/attachment.html>


More information about the swift-evolution mailing list