> 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