> On Dec 24, 2015, at 12:17 PM, Matthew Johnson <[email protected]> wrote:
> 
>> 
>> On Dec 24, 2015, at 11:11 AM, Joe Groff <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>>> 
>>> On Dec 24, 2015, at 5:41 AM, Matthew Johnson via swift-evolution 
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>> 
>>> 
>>> Sent from my iPad
>>> 
>>>> On Dec 24, 2015, at 5:46 AM, Thorsten Seitz <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>> 
>>>>> Am 22.12.2015 um 18:30 schrieb Matthew Johnson <[email protected] 
>>>>> <mailto:[email protected]>>:
>>>>> 
>>>>> My proposal is specifically suggesting that we treat “initial value” as a 
>>>>> default rather than an initialization that always happens.  IMO the 
>>>>> current behavior is limiting and problematic in a number of ways.
>>>>> 
>>>>> If we make the change I am suggesting double initialization / assignment 
>>>>> will not happen. 
>>>> 
>>>> Ah, ok! 
>>>> 
>>>> I'm a bit uneasy about overloading the initial-value-syntax with a new 
>>>> meaning, though.
>>>> 
>>>> -Thorsten
>>> 
>>> This was pulled from the latest draft of the proposal.  Please take a look 
>>> at the current draft and let me know if you like the new solution better.
>> 
>> I wonder whether we could avoid the problems of mixing up inline 
>> initialization and default initializer parameterization by taking a 
>> different approach. Sorry if this has been discussed and I missed it, but 
>> Scala and Kotlin both support a compact function-like class declaration 
>> syntax for simple "case classes". We could adopt something similar for our 
>> structs and classes, so that:
>> 
>> public struct Vec4(x: Double, y: Double, z: Double, w: Double = 1.0) { }
>> 
>> expanded to:
>> 
>> public struct Vec4 {
>>   public let x: Double
>>   public let y: Double
>>   public let z: Double
>>   public let w: Double // NB: No inline initializer
>> 
>>   // Default argument in struct decl becomes default argument in initializer
>>   public init(x: Double, y: Double, z: Double, w: Double = 1.0) {
>>     self.x = x
>>     self.y = y
>>     /* you get the idea */
>>   }
>> }
>> 
>> (and you could presumably stick `var` on parameters to make the 
>> corresponding properties `var`s instead of `let`s, if you wanted). That 
>> collects all the information you want to know about the members and their 
>> initialization together in one place, and the syntax naturally suggests 
>> function-like semantics for `=` expressions rather than 
>> inline-initializer-like semantics.
> 
> Hi Joe, thanks for jumping in to this thread with an idea.  
> 
> One thing that isn't obvious to me is how your idea would work when some 
> properties need a default and some need an initial value?  What about access 
> control for properties in the struct parameter list?  What about behaviors 
> (assuming your proposal is accepted).  

One possibility is that you could provide additional properties that aren't 
involved in the memberwise initialization, which could then be given initial 
values:

public struct Vec4(x: Double, y: Double, z: Double, w: Double = 1.0) {
  let xAxis = Vec4(x: 1, y: 0, z: 0)
}

My totally-unsubstantiated hunch is that, for most types where you want a 
simple memberwise initializer, you don't usually need all that much 
customization on the properties. Once behaviors and other interesting modifiers 
come into play, it's more and more likely a synthesized initializer isn't going 
to cut it.

> I’ll have to give this some thought, but my initial reaction is that I would 
> prefer a different path that sticks to current syntax and has clear 
> interaction with other language features.
> 
> Did you have a chance to see how I handled this in the latest draft by 
> introducing the `@default` attribute?
> 
> struct S {
>     @default("hello") let s: String
>     @default(42) let i: Int
> 
>     // user declares:
>     memberwise init(...) {}
>     // compiler synthesizes:
>     init(s: String = "hello", i: Int = 42) {
>         /* synthesized */ self.s = s
>         /* synthesized */ self.i = i
>     }
> }
> 
> 
> Something like this attribute might also be useful in other cases allowing 
> the default to be used by manual initializers:
> 
> struct S {
>   let  value: Int?
> }
> 
> public class X {
>   @default(42) let a     // same potential utility if this is a var
> 
>    // default is a keyword that is only valid in an expression
>    // on the rhs of a member initialization statement
>    // or possibly an initializer parameter with a name corresponding to a 
> stored property
> 
>    init(s: S) {
>       self.s = s.value ?? default    
>    }
>    init(int a = default) { … }
> }
> 
> As an alternative to specifying a value directly in the attribute, it could 
> require a `let` member name, although my initial reaction is that this is 
> more verbose and less good:
> 
> public class X {
>    let defaultForA = 42
>   @default(defaultForA) let a
> }

The '@default' idea was part of what prompted my response. It adds a bit of 
unfortunate complexity to the proposal, and I'm not sure how you would 
contextually resolve 'default' to the property whose default you want. I was 
hoping that maybe by exploring other approaches we could find a lower-energy 
state with all the important functionality. 

-Joe

> 
>> 
>> 
>>>> That said, I think the interaction of explicit initializers and memberwise 
>>>> initializers begs discussion.  It would be a much simpler model to only 
>>>> get memberwise parameters for properties without an explicit init.  Have 
>>>> you considered this model, what are the tradeoffs with allowing vars to 
>>>> overwrite them?  Allowing an explicit init to be overwritten by the 
>>>> memberwise initializer seems potentially really confusing, and since you 
>>>> allow explicit arguments on inits, this could always be handled manually 
>>>> if someone really really wanted it.  For example, they could write:
>>>> 
>>>> memberwise init(s : String) {
>>>>   self.s = s
>>>> }
>>>> 
>>>> If they wanted to get the sugar of memberwise inits (presumably for other 
>>>> properties without an explicit init) but still allow one to be overwritten.
>>> 
>>> Personally, I think there is a lot of value in allowing memberwise 
>>> initialization for properties that do contain an initial value.  Imagine a 
>>> hypothetical Swift version of Cocoa Touch.  UILabel might have initial 
>>> values for text, font, textColor, etc but still want to allow clients to 
>>> provide memberwise initialization arguments to override the default value.  
>>> I think there are many cases like this both in UI code and elsewhere.  
>> 
>> 
>> I think I confused the issue.  If we have to support properties that have a 
>> default value, then the model I’m advocating for is that this:
>> 
>> class C {
>>   let x : Int
>>   var y : Int = foo()
>> 
>>   memberwise init(...) {}
>> }
>> 
>> compile into:
>> 
>> init(x : Int, y : Int = foo()) {
>>   self.x = x
>>   self.y = y
>> }
>> 
>> Pertinent points of this are that lets without a default value would still 
>> turn into arguments, and that any side effects of the var initializer would 
>> be squished.  Another potential model is to compile it to:
>> 
>> init(x : Int, y : Int) {
>>   self.x = x
>>   self.y = foo()
>> }
>> 
>> which is guaranteed to run the side effect, but requires y to be specified.  
>> I do not think it is a good model to compile it to:
>> 
>> init(x : Int, y : Int? = nil) {
>>   self.x = x
>>   self.y = y ?? foo()
>> }
>> 
>> because that would allow passing in an Int? as the argument.  The final 
>> model (which I know you don’t like) is for memberwise initializers to *only* 
>> apply to properties without a default value.
>> 
>>>> This doesn’t seem like the right behavior to me.  The compiler shouldn’t 
>>>> be in the business of scanning the body of the init to decide what members 
>>>> are explicitly initialized.  I’d suggest that the model simply be that the 
>>>> contents of the {} on a memberwise init get injected right after the 
>>>> memberwise initializations that are done.  This mirrors how properties 
>>>> with default values work.
>>> 
>>> The model does inject the synthesized memberwise initialization just prior 
>>> to the body of the initializer.  
>>> 
>>> As written the proposal does scan the body of the init in order to 
>>> determine which properties receive memberwise initialization.  The idea 
>>> here is that it provides additional flexibility as it allows specific 
>>> initializers to “opt-out” of memberwise initialization synthesis for some 
>>> properties while receiving it for others.
>> 
>> 
>> This is a very problematic model for me, because it can lead to serious 
>> surprises in behavior, and in the case of lets, shares the problems above 
>> with not allowing one to define the long-hand form explicitly.

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to