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

Matthew Johnson matthew at anandabits.com
Thu Jan 7 10:42:54 CST 2016


> On Jan 6, 2016, at 10:56 PM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
> 
> Yes I did mess up the translation, that will teach me to do it at night on an iPad (therefore no compiler). The correct translation is:
> 
> Types and let's needed for example:
> 
>     public class SuperType {
>         init(_: SPType) {}
>     }
>     public struct SPType {}
>     public struct PPType {}
>     public struct CPType {}
>     let sPInitial = SPType()
>     let pPInitial = PPType()
>     let cPInitial = CPType()
> 
> Example:
> 
>     public class Ex public init(
>         superParam: SPType = sPInitial, 
>         private label privateParam: PPType = pPInitial, 
>         calculatedParam: CPType = cPInitial
>     ): SuperType(superParam) {
>         var calculatedParam: CPType {
>             get { return CPType() }
>             set {}
>         }
>     }
> 
> Would be translated to:
> 
>     public class Ex: SuperType {
>         private let privateParam: PPType
>         public init(superParam: SPType = sPInitial, label privateParam: PPType = pPInitial, calculatedParam: CPType = cPInitial) {
>             // 1. Initialize generated stored properties and existing stored properties, but not calculated properties
>             self.privateParam = privateParam
>             // 2. Call super
>             super.init(superParam)
>             // 3. Initialize calculated properties
>             self.calculatedParam = calculatedParam
>         }
>         var calculatedParam: CPType {
>             get { return CPType() }
>             set {}
>         }
>     }
> 
> You say "There are also several aspects of this translation that are somewhat vague"; without more detail it is difficult to know what you mean, can you elaborate please. The proposal is meant to be equivalent to a textual translation so I was hoping that an example would be sufficient; happy to elaborate if you say what you don't get.

What you’re doing with the calculated parameter here is weird.  I don’t understand the point.  Is it supposed to be implicitly backed by storage and your getter is only a default?  Can you explain what this is supposed to do and how you would write something equivalent today?

How do you handle arguments to super that are not provided by the caller?  Is there a way to manually provide arguments for super?

You’re defining several kinds of parameters here and expecting the compiler to infer what to do:

1. parameters backed by a synthesized stored property
2. parameters forwarded to super
3. parameters are used to mutate computed properties during phase 2 of initialization.

How is the compiler supposed to know what to do with each parameter?  What if there are conflicts between the name of a stored property and the label of a super parameter?  Is that just not allowed?

It seems like it would be easily confusing to me.  Under my proposal all that is synthesized is parameters for a stored property.

> 
> You say "Several of these points are also true of my proposal and several others would be true if what I believe are the most important future enhancements are added." Again can you elaborate, difficult to respond without knowing what points and why. I am not trying to be awkward but I am unsure what you mean. As a general note there is a lot in the current proposal that says this could be added in the future, I was responding to the proposal as proposed not what might be added in the future. Also if there are some aspects of the proposal that really need to be added then they should be part of the proposal.

There are a couple of points in particular that I really wish could have been addressed under the initial proposal, however Chris felt strongly that we need to keep the proposal focused on core functionality.  It will be very useful as-is and can become even more useful with some enhancements.

I am sorry for not elaborating on your points further.  I am getting a little bit tired of the comparison to Scala syntax.  This topic came up late in the discussion and I feel like I have responded to it in quite some detail.  The proposal itself explains clearly why I did not take that approach:

	• This proposal supports partial memberwise initialization.  Initializers can receive non-memberwise parameters and can initialize private state manually while still exposing public properties for direct initialization by callers via memberwise initialization.
	• This proposal supports multiple memberwise initializers. It may be necessary to support more than one way to initialize private state while still desiring direct initialization of public properties via memberwise initialization.
	• This proposal supports more flexibility for organizing property declarations. The Scala / Kotlin syntax may be acceptable in really simple cases. Unfortunately it requires placing property declarations in a single list at the beginning of the type declaration. This is extremely limiting.
It is especially unfortunatey for types which contain generic parameters and inheritance clauses.  Property declarations would be sandwiched in between those two clauses cluttering up type-level information with member-level information

It is fair if you don’t agree with those reasons.  You don’t need to support this proposal and you’re welcome to submit one containing the syntax you would prefer.

Now to elaborate on your list:


> Because the syntax is so short it is part of this proposal to remove both the current default and memberwise initialisers thus simplifying the language overall (remove two features, add one) and at the same time gain power, which is a rare situation and therefore I would suggest optimal.


This is a completely orthogonal topic to your idea of Scala syntax.  Each of these would be required to be independent proposals.

> It is also more powerful than the proposed `memberwise init(..)` and/or existing automatic `inits` in the following ways:
> 
> 1. Allows `lets` to have a default value.

This is true but this approach to `let` defaults has its own problems.  Quoting from my proposal:

If the expansion of this syntax does not supply initial values to the synthesized properties and only uses the default value for parameters of the synthesized initializer this is true. The downside of doing this is that var properties no longer have an initial value which may be desirable if you write additional initializers for the type. I believe we should continue the discussion about default values for let properties. Ideally we can find an acceptable solution that will work with the current proposal, as well as any additional syntactic sugar we add in the future.

> 2. Allows other properties including computed properties to have a default value.

It seems really weird to me that computed properties are relevant to memberwise initialization.  I encourage you to demonstrate why this is necessary with concrete examples, including how you handle this manually today.

> 3. Allows any `super.init` to be called (not restricted to `super.init()`).

This proposal allows you to call any super initializer you want to.

> 4. Allows control of which properties participate in the `init` (they are listed in the brackets and are not in a super call and are not an existing property).

My proposal addresses the difference between the “automatic" and "opt-in" models of property eligibility.  I think the `opt-in` model is a valid preference and include a future enhancement showing how that model can be added to this proposal.

> 5. Allows private properties to be initialized.

They can be in a `private` initializer in my proposal.  

Under an `automatic` model for property eligibility it would be a mistake to automatically expose more-private properties to more-public initializers for several reasons.  Aside from default values for `let` properties, allowing a way to correct the rules of the "automatic” model when it doesn’t do what you need is the most important improvement to this proposal in my opinion.  I am leaning towards the idea that access control is the best way to do this, although adding the "opt-in" model would also solve it.

> 6. Allows properties including private properties to have a label instead of their actual name and hence not expose internals (also allows migration of implementation whilst retaining external interface).

I generally don’t think allowing distinct labels for memberwise parameters is a good idea.  However, I can understand why you might want to do this for `private` properties in a more-public initializer.  My proposal does cover this use case: just write the initialization of the private property manually and you have full control over the parameter.

> 7. Allows calls to the generated `init` that don’t specify all members, i.e. for `struct Ex init(i: Int = 0, s: String = “") {}` the following are allowed `Ex()`, `Ex(i: 1)`, `Ex(s: “A”)`, and `Ex(i: 2, s: “B”)`.

This is possible with my proposal.

> 8. Allows visibility of automatically generated `init` to be controlled.

This is possible with my proposal.

> 9. Supports property behaviours.

I don’t understand what this means.  I don’t see anything relating to property behaviors in your suggested syntax.

> 10. Does not require a new keyword.


It is actually a declaration modifier, not a keyword.  There is a significant difference.

`memberwise` is pretty unlikely to be used in any other context.  

> 
> Pretty much along the same lines, you say "This is not true.  There are additional differences." What additional differences?

My proposal discusses this in sufficient detail.  Please give the "Adopt "type parameter list" syntax like Kotlin and Scala” alternative considered section another read.

> 
> And again, you say "I disagree.  The current proposal clearly states reasons not addressed here." Can you expand? Obviously I thought I had covered everything otherwise I wouldn't have said that the points are covered. So can you point me in the direction of your concerns?

The relevant alternative considered section are not addressed at all.  If you want to continue the conversation, please give that a read, paste my points into a response, and comment on how your idea addresses them.

In short, the Scala syntax does not address important goals and design requirements of my proposal that are clearly stated.  If you believe it actually can address those goals and requirements please help us understand how it can do that.  You might be able to demonstrate a way to better fulfill those goals and requirements than I have put forward with this proposal.

On the other hand, you may agree that it does not address those goals and requirement, but disagree with them and prefer an alternative proposal with different goals and design requirements.  If that is the case you are welcome to oppose this proposal and work on such an alternative.

Matthew

> 
> On Thursday, 7 January 2016, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
> 
>> On Jan 6, 2016, at 4:52 PM, Howard Lovatt <howard.lovatt at gmail.com <>> wrote:
>> 
>> Here is an expanded proposal for the syntax for a Scala style memberwise syntax and equivalent code, specification is via an example rather than formal syntax since this is easier to follow. Note it is like Scala’s syntax but it is ‘Swiftified” (in particular still retains `init` keyword).
>> 
>>     class Ex public init(
>>         superParam: sPType = sPInitial,
>>         private label privateParam: pPType = pPInitial,
>>         calculatedParam: cPType = cPInitial
>>     ): SuperType(superParam) {
>>         calculatedParam: cPType {
>>             get {…}
>>             set {…}
>>         }
>>     }
>> 
>> This gets translated to:
>> 
>>     class Ex: SuperType(superParam) { {
>>         private privateParam: pPType = pPInitial,
>>         public init(superParam: sPType = sPInitial, label privateParam: pPType = pPInitial, calculatedParam: cPType = cPInitial) {
>>             // 1. Call super
>>             super.init(superParam)
>>             // 2. Initialize generated parameters and existing parameters
>>             self.privateParame = privateParam
>>             self.calculatedParam = calculatedParam
>>         }
>>         calculatedParam: cPType {
>>             get {…}
>>             set {…}
>>         }
>>     }
> 
> This translation is not valid Swift for several reasons.  There are also several aspects of this translation that are somewhat vague and a detailed design is necessary to evaluate how you envision it working.
> 
>> 
>> Because the syntax is so short it is part of this proposal to remove both the current default and memberwise initialisers thus simplifying the language overall (remove two features, add one) and at the same time gain power, which is a rare situation and therefore I would suggest optimal.
>> 
>> It is also more powerful than the proposed `memberwise init(..)` and/or existing automatic `inits` in the following ways:
>> 
>> 1. Allows `lets` to have a default value.
>> 2. Allows other properties including computed properties to have a default value.
>> 3. Allows any `super.init` to be called (not restricted to `super.init()`).
>> 4. Allows control of which properties participate in the `init` (they are listed in the brackets and are not in a super call and are not an existing property).
>> 5. Allows private properties to be initialised.
>> 6. Allows properties including private properties to have a label instead of their actual name and hence not expose internals (also allows migration of implementation whilst retaining external interface).
>> 7. Allows calls to the generated `init` that don’t specify all members, i.e. for `struct Ex init(i: Int = 0, s: String = “") {}` the following are allowed `Ex()`, `Ex(i: 1)`, `Ex(s: “A”)`, and `Ex(i: 2, s: “B”)`.
>> 8. Allows visibility of automatically generated `init` to be controlled.
>> 9. Supports property behaviours.
>> 10. Does not require a new keyword.
> 
> Several of these points are also true of my proposal and several others would be true if what I believe are the most important future enhancements are added. 
> 
>> 
>> The downsides of the proposal relative to  `memberwise init(..)` and/or existing automatic `inits` are:
>> 
>> 1. That people would need to be careful when laying out their code otherwise the first line could become long (a bit of pretty printing solves this).
>> 2. Existing structs/classes that have automatically generated inits would need to be refactored, e.g. `CGRect` would become `struct CGRect init(var origin: CGPoint, var size: CGSize) {}` (a migration tool would help here).
>> 
>> Other than the downsides listed above the proposal does everything the current proposal and current implementation does and more (more also listed above) and is simpler to both explain and implement.
> 
> This is not true.  There are additional differences.  
> 
>> 
>> The above more than addresses the reasons given in the current proposal for not using the Scala syntax and demonstrates superiority in many areas. 
> 
> I disagree.  The current proposal clearly states reasons not addressed here.
> 
> I don’t wish to 
> 
>> However if it were the current proposal or nothing I would go with the current proposal since something is better than nothing.
>> 
>> On Wednesday, 6 January 2016, Matthew Johnson <matthew at anandabits.com <>> wrote:
>> 
>>> On Jan 5, 2016, at 12:12 PM, Tino Heth <2th at gmx.de <>> wrote:
>>> 
>>> 
>>>> The “type parameter list” syntax is sugar that could be implemented as a layer on top of the current proposal or could be implemented orthogonally.
>>> Hi Howard,
>>> I've heard this argument before, so I'll repeat my answer as well:
>>> Both offers don't make sense, it's either one way or the other (or something completely different).
>> 
>> I don’t think it’s clear whether both make sense or not.  They are not mutually exclusive and are aimed at solving related, but different problems.  If we adopt the current proposal, the Kotlin / Scala syntax *might* make sense for some use cases.  It would depend upon whether the current proposal is considered too verbose in enough cases or not.  This may or may not turn out to be the case.
>> 
>> The reason my proposal looks the way it does is because there are specific goals it intends to achieve and problems it is intended to solve.  These goals and problems are not adequately addressed by the Kotlin / Scala syntax.
>> 
>>> If diversity starts here, why not have "const" and "val" beside "let", or allow "fn" and "lambda"?
>>> 
>>> @Matthew: Please, if you support something, then say so - using clear words, not phrases like "could be implemented”.
>> 
>> This phrasing is plenty clear IMO.  I am stating a possibility.  I do not have a position on whether or not it is a good idea at the moment.
>> 
>> I have made some modifications to the proposal over the last few days.  These changes have been partly motivated by considering the conversation we have had.  You may wish to give it another look.  If so, please look here: https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/0018-flexible-memberwise-initialization.md <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/0018-flexible-memberwise-initialization.md>.  There is currently an open PR for the latest change so it is not in the main Swift evolution repo yet.
>> 
>> The most recent change includes a discussion of why it does not use the Scala / Kotlin syntax.  You may not like the choice but I hope you can at least understand the rationale (even if you disagree with it).
>> 
>> 
>> 
>> 
>> -- 
>>   -- Howard.
>> 
> 
> 
> 
> -- 
>   -- Howard.

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


More information about the swift-evolution mailing list