[Proposal Draft] property lists


(Matthew Johnson) #1

I have always considered the Flexible Memberwise Initialization proposal to be just a first step (as evidenced by the many future enhancements it discussed). Its review has inspired new ideas and helped to shape my vision of the best long-term solution. My final thoughts about the review can be found here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006176.html

Property lists is the third in a series of three proposals describing general features that can work together to form a complete solution.

The proposal drafts can be found at the following links:

* Parameter forwarding: https://github.com/anandabits/swift-evolution/blob/parameter-forwarding/proposals/NNNN-parameter-forwarding.md
* Partial initializers: https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md
* Property lists: https://github.com/anandabits/swift-evolution/blob/property-lists/proposals/NNNN-property-lists.md

Matthew
Property Lists

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-property-lists.md>
Author(s): Matthew Johnson <https://github.com/anandabits>
Status: Awaiting review
Review manager: TBD
Introduction

This proposal introduces the propertylist declaration. Property lists provide concise syntactic sugar for declaring memberwise partial initializers and memberwise computed tuple properties.

NOTE: I do not love the name “property lists” for the feature or the keyword but haven’t thought of anything better. Suggestions are welcome.

Swift-evolution thread: Proposal Draft: Property Lists <https://lists.swift.org/pipermail/swift-evolution>
Motivation

I believe the review of the Flexible Memberwise Initialization <https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md> proposal demonstrated a strong demand for concise yet flexible memberwise initialization. The discussion highlighted several areas where that proposal fell short:

Clarity regarding which parameters receive memberwise initialization.
Control over which parameters receive memberwise initialization.
Control over specific memberwise parameter ordering.
Control over parameter labels.
Control over default parameter values, especially for let properties.
It is a very narrow, special case feature.
This proposal builds on the Partial Initializer <https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md> proposal to solve these problems using a more general underlying mechanism. It enables truly flexible memberwise initialization.

Property lists also support other memberwise features, beginning with memberwise computed tuple properties.

Proposed solution

There are two ways to specify a property list. The basic mechanism is a property list declaration that looks as follows:

  propertylist configProperties: aPropName, customLabel anotherPropName = 42
All properties mentioned in the property list declaration must be visible at the site of the declaration.

A propertylist attribute also exists for property declarations to support cases where a list of property declarations should also be treated as a property list:

  @propertylist(aPropertyListIdentifier) let prop1, prop2: Int
A property list can contain any kind of property, including those with behaviors. However, when certain kinds of properties are included it will not be possible to synthesize a partial memberwise initializer and / or a setter for the computed tuple property.

Synthesized elements

A typealias will always be synthesized:

It will have a name matching the identifier of the property list, with the first character transformed to upper case.
It will be a tuple type containing labels and types matching those specified in the property list.
A computed tuple property will always be synthesized:

It will always include a getter.
Visibility of the getter will match the visibility of the least visible getter.
It will contain a setter as long as all of the properties are settable.
Visibility of the setter will match the visibility of the least visible setter.
A paritial initializer will only be generated if:

All properties are stored properties.
None are let properties with an initial value.
None have a behavior which is incompatible with phase 1 initialization.
Visibility of the partial initializer will match the least visible setter for structs. Partial initializers for classes are always private (as specified by the partial initializer proposal).
The property list is declared in the main body of the type, not an extension, unless the type is a struct.
If stored properties are allowed in extensions and / or protocols in the future, all properties included in the list must be declared within the same body as the property list for a partial initializer to be synthesized (either the main body of the type or the body of the same extension or same protocol).
The structure of the synthesized elements is as follows:

Ordering of partial initializer parameters and tuple members will match the order of the property list declaration.
The external parameter labels and tuple labels will match the label specified in the property list if it exists and the property name otherwise.
The default value for partial initializer parameters will be the default value specified in the property list if it exists and the initial value of the property otherwise (if that exists). If neither exist the parameter will not have a default value.
Visibility of a synthesized members is capped at internal unless public is explicitly specified. If public (or internal) is explicitly specified, all properties referenced must have getter and setter visibility of at least the specified access level or a compiler error will result.

Examples

Basic example

public struct S {
  let i: Int = 42
  public var s: String = "hello"
  public var d: Double
  
  // user declares:
  public propertylist custom: dLabel d = 42, s
  
  // compiler synthesizes:
  public typealias Custom = (dLabel: Double, s: String)
  public var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  public partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a let with a initial value

struct S {
  let i: Int = 42
  let s: String = "hello"
  var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
  }
  // NOTE: no setter because a `let` was included
  // and no partial initializer because the `let`
  // has an initial value.
}
Including a lazy property

struct S {
  let i: Int
  var s: String
  lazy var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a `lazy var` was included.
}
Including a var with a private setter

struct S {
  let i: Int
  var s: String
  private(set) var d: Double = 42
  
  // user declares:
  propertylist custom: dLabel d, s = "hello"
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  private(set) var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: The initial value for `d` is used as a default
  // parameter value because a different default parameter value
  // was not specified.
  private partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a computed property

struct S {
  let i: Int
  var s: String
  var d: Double {
    get { return getValueFromSomewhere() }
    set { storeValueSomewhere(newValue) }
  }
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a computed property was included.
}
Using the @propertylist attribute

struct S {
  @propertylist(custom) var s: String, d: Double = 42
  private let i: Int
  
  // compiler synthesizes:
  typealias Custom = (s: String, d: Double)
  var custom: Custom {
    get { return (s: s, d: d) }
    set { (s, d) = newValue }
  }
  partial init custom(s: String, d: Double = 42) {
    self.s = s
    self.d = d
  }
}
Using a property list declaration for memberwise intialization

struct S {
  var x, y, z: Int
  let s: String
  let d: Double
  
  propertylist randomGroup aString s = "hello", anInt x = 42
  
  // user declares
  init(...randomGroup) {
    y = 42
    z = 42
    d = 42
  }
  
  // compiler synthesizes
  partial init randomGroup(aString s: String = "hello", anInt x: Int = 42) {
    self.s = s
    self.x = x
  }
  init(aString s: String = "hello", anInt x: Int = 42) {
    randomGroup.init(aString: s, anInt: x)
    y = 42
    z = 42
    d = 42
  }
  
}
Using the property list attribute for memberwise intialization

```swift
struct S {
@propertylist(declaredTogether) var x, y, z: Int
let s: String
let d: Double

// user declares:
init(…declaredTogether) {
s = “hello”
d = 42
}

// compiler synthesizes:
partial init declaredTogether(x: Int, y: Int, z: Int) {
self.x = x
self.y = y
self.z = z
} init(x: Int, y: Int, z: Int) {
declaredTogether.init(x: Int, y: Int, z: Int)
s = “hello”
d = 42
}

}

Implicit property lists

It may be desirable to have several implicit property lists available, such as one that includes all properties of the type. Properties would appear in any implicit property lists in declaration order.

The specific details of what implicit property lists should be available and what they should be called is a good topic for bikeshedding.

Detailed design

TODO but should fall out pretty clearly from the proposed solution

Impact on existing code

This is a strictly additive change. It has no impact on existing code.

Alternatives considered

We could live without this syntactic sugar. There are several reasons the language is better with it:

It is much more convenient than manually writing memberwise partial intializers. It does not include any unnecessary information, thus making the details more clear.
It gives us the memberwise computed tuple properties for free. This would would not be possible when writing memberwise partial initializers manually.
It scales to any new memberwise features that might make sense for a type.
As always, the propertylist keyword is game for bikeshedding. The use of : to separate the identifier from the list of properties could also be game for bikeshedding. Also, as mentioned previously, the implicit property lists are game for bikeshedding.


(Janosch Hildebrand) #2

Hi Matthew,

Just a few quick comments:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

@propertylist:
I think this is mostly redundant and the proposal would be improved by its removal.

propertylist syntax:
I don't think the current syntax. Using `:` feels wrong as I instinctively expect a type to it's right.

How about: `propertylist fooProps = (aProp, bProp)`

Default values in propertylist:
Strongly against this. Only applicable to initializer use case and feels generally out of place.

I have also looked at the other two related proposals but I'm far from having a final opinion on any of the three.
I'm also posting my current very short and very rough impressions of the three related proposals here as I feel they are strongly related and I don't have enough to say about the other two to really justify separate posts:

Parameter forwarding:
This is the proposal I like the least. I'm not a fan of the syntax and it mostly saves some minor typing at the cost immediately knowing what the actual arguments are.

Partial initializers:
My dislike of parameter forwarding carries over to this but otherwise I find the basic idea quite nice.
My bigger issue here is that I currently don't see a huge use case for this that isn't already served by initializer delegation. Yes this seems to cover additional cases but I'm not sure it justifies it's existence. It makes a lot more sense when considered as part of the memberwise initialization trifecta with parameter forwarding but like I said that's the part I don't really like.

Property Lists:
Like with partial initializers I find this to be an interesting idea and this seems to have other potential use cases apart from memberwise initialization. Then again I'm unsure if this exact proposal is the way to go here. I'd really prefer to see more abstract discussion of this idea before committing to any specifics.

Particularly here but also in general for all three proposals I feel that we might be better served by letting the language form further and waiting for more general pieces to fall into place before charging ahead in these areas. None of these require stopgap solutions from my point of view and I at least feel like I could evaluate this much better once I have an idea of what Swift is going to look like at version 4, 5, ...
From reading the Swift team's comments it seems like future improvements like variadic generics might make a potential difference here and I imagine the macro system might as well.

I hope this doesn't sound too negative, you clearly invested a significant amount of work into these proposals and I value the effort. It helps me better understand the problems people see and what potential solutions might look like and stirs a lot of healthy discussion.
So don't let yourself be discouraged if I don't feel that changes in this area are required just yet :slight_smile:

- Janosch

···

On 11 Jan 2016, at 04:44, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

I have always considered the Flexible Memberwise Initialization proposal to be just a first step (as evidenced by the many future enhancements it discussed). Its review has inspired new ideas and helped to shape my vision of the best long-term solution. My final thoughts about the review can be found here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006176.html

Property lists is the third in a series of three proposals describing general features that can work together to form a complete solution.

The proposal drafts can be found at the following links:

* Parameter forwarding: https://github.com/anandabits/swift-evolution/blob/parameter-forwarding/proposals/NNNN-parameter-forwarding.md
* Partial initializers: https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md
* Property lists: https://github.com/anandabits/swift-evolution/blob/property-lists/proposals/NNNN-property-lists.md

Matthew
Property Lists

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-property-lists.md>
Author(s): Matthew Johnson <https://github.com/anandabits>
Status: Awaiting review
Review manager: TBD
Introduction

This proposal introduces the propertylist declaration. Property lists provide concise syntactic sugar for declaring memberwise partial initializers and memberwise computed tuple properties.

NOTE: I do not love the name “property lists” for the feature or the keyword but haven’t thought of anything better. Suggestions are welcome.

Swift-evolution thread: Proposal Draft: Property Lists <https://lists.swift.org/pipermail/swift-evolution>
Motivation

I believe the review of the Flexible Memberwise Initialization <https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md> proposal demonstrated a strong demand for concise yet flexible memberwise initialization. The discussion highlighted several areas where that proposal fell short:

Clarity regarding which parameters receive memberwise initialization.
Control over which parameters receive memberwise initialization.
Control over specific memberwise parameter ordering.
Control over parameter labels.
Control over default parameter values, especially for let properties.
It is a very narrow, special case feature.
This proposal builds on the Partial Initializer <https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md> proposal to solve these problems using a more general underlying mechanism. It enables truly flexible memberwise initialization.

Property lists also support other memberwise features, beginning with memberwise computed tuple properties.

Proposed solution

There are two ways to specify a property list. The basic mechanism is a property list declaration that looks as follows:

  propertylist configProperties: aPropName, customLabel anotherPropName = 42
All properties mentioned in the property list declaration must be visible at the site of the declaration.

A propertylist attribute also exists for property declarations to support cases where a list of property declarations should also be treated as a property list:

  @propertylist(aPropertyListIdentifier) let prop1, prop2: Int
A property list can contain any kind of property, including those with behaviors. However, when certain kinds of properties are included it will not be possible to synthesize a partial memberwise initializer and / or a setter for the computed tuple property.

Synthesized elements

A typealias will always be synthesized:

It will have a name matching the identifier of the property list, with the first character transformed to upper case.
It will be a tuple type containing labels and types matching those specified in the property list.
A computed tuple property will always be synthesized:

It will always include a getter.
Visibility of the getter will match the visibility of the least visible getter.
It will contain a setter as long as all of the properties are settable.
Visibility of the setter will match the visibility of the least visible setter.
A paritial initializer will only be generated if:

All properties are stored properties.
None are let properties with an initial value.
None have a behavior which is incompatible with phase 1 initialization.
Visibility of the partial initializer will match the least visible setter for structs. Partial initializers for classes are always private (as specified by the partial initializer proposal).
The property list is declared in the main body of the type, not an extension, unless the type is a struct.
If stored properties are allowed in extensions and / or protocols in the future, all properties included in the list must be declared within the same body as the property list for a partial initializer to be synthesized (either the main body of the type or the body of the same extension or same protocol).
The structure of the synthesized elements is as follows:

Ordering of partial initializer parameters and tuple members will match the order of the property list declaration.
The external parameter labels and tuple labels will match the label specified in the property list if it exists and the property name otherwise.
The default value for partial initializer parameters will be the default value specified in the property list if it exists and the initial value of the property otherwise (if that exists). If neither exist the parameter will not have a default value.
Visibility of a synthesized members is capped at internal unless public is explicitly specified. If public (or internal) is explicitly specified, all properties referenced must have getter and setter visibility of at least the specified access level or a compiler error will result.

Examples

Basic example

public struct S {
  let i: Int = 42
  public var s: String = "hello"
  public var d: Double
  
  // user declares:
  public propertylist custom: dLabel d = 42, s
  
  // compiler synthesizes:
  public typealias Custom = (dLabel: Double, s: String)
  public var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  public partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a let with a initial value

struct S {
  let i: Int = 42
  let s: String = "hello"
  var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
  }
  // NOTE: no setter because a `let` was included
  // and no partial initializer because the `let`
  // has an initial value.
}
Including a lazy property

struct S {
  let i: Int
  var s: String
  lazy var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a `lazy var` was included.
}
Including a var with a private setter

struct S {
  let i: Int
  var s: String
  private(set) var d: Double = 42
  
  // user declares:
  propertylist custom: dLabel d, s = "hello"
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  private(set) var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: The initial value for `d` is used as a default
  // parameter value because a different default parameter value
  // was not specified.
  private partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a computed property

struct S {
  let i: Int
  var s: String
  var d: Double {
    get { return getValueFromSomewhere() }
    set { storeValueSomewhere(newValue) }
  }
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a computed property was included.
}
Using the @propertylist attribute

struct S {
  @propertylist(custom) var s: String, d: Double = 42
  private let i: Int
  
  // compiler synthesizes:
  typealias Custom = (s: String, d: Double)
  var custom: Custom {
    get { return (s: s, d: d) }
    set { (s, d) = newValue }
  }
  partial init custom(s: String, d: Double = 42) {
    self.s = s
    self.d = d
  }
}
Using a property list declaration for memberwise intialization

struct S {
  var x, y, z: Int
  let s: String
  let d: Double
  
  propertylist randomGroup aString s = "hello", anInt x = 42
  
  // user declares
  init(...randomGroup) {
    y = 42
    z = 42
    d = 42
  }
  
  // compiler synthesizes
  partial init randomGroup(aString s: String = "hello", anInt x: Int = 42) {
    self.s = s
    self.x = x
  }
  init(aString s: String = "hello", anInt x: Int = 42) {
    randomGroup.init(aString: s, anInt: x)
    y = 42
    z = 42
    d = 42
  }
  
}
Using the property list attribute for memberwise intialization

```swift
struct S {
@propertylist(declaredTogether) var x, y, z: Int
let s: String
let d: Double

// user declares:
init(…declaredTogether) {
s = “hello”
d = 42
}

// compiler synthesizes:
partial init declaredTogether(x: Int, y: Int, z: Int) {
self.x = x
self.y = y
self.z = z
} init(x: Int, y: Int, z: Int) {
declaredTogether.init(x: Int, y: Int, z: Int)
s = “hello”
d = 42
}

}

Implicit property lists

It may be desirable to have several implicit property lists available, such as one that includes all properties of the type. Properties would appear in any implicit property lists in declaration order.

The specific details of what implicit property lists should be available and what they should be called is a good topic for bikeshedding.

Detailed design

TODO but should fall out pretty clearly from the proposed solution

Impact on existing code

This is a strictly additive change. It has no impact on existing code.

Alternatives considered

We could live without this syntactic sugar. There are several reasons the language is better with it:

It is much more convenient than manually writing memberwise partial intializers. It does not include any unnecessary information, thus making the details more clear.
It gives us the memberwise computed tuple properties for free. This would would not be possible when writing memberwise partial initializers manually.
It scales to any new memberwise features that might make sense for a type.
As always, the propertylist keyword is game for bikeshedding. The use of : to separate the identifier from the list of properties could also be game for bikeshedding. Also, as mentioned previously, the implicit property lists are game for bikeshedding.

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


(Matthew Johnson) #3

Hi Matthew,

Just a few quick comments:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

@propertylist:
I think this is mostly redundant and the proposal would be improved by its removal.

Removing it would require a redundant list of property names in some cases. Why do you think that is better?

propertylist syntax:
I don't think the current syntax. Using `:` feels wrong as I instinctively expect a type to it's right.

How about: `propertylist fooProps = (aProp, bProp)`

That syntax could work.

Default values in propertylist:
Strongly against this. Only applicable to initializer use case and feels generally out of place.

It is extremely important to the initializer use case. There is no way to provide a default for `let` properties in the initializer without allowing this. I would not have written this proposal at all if it weren’t for a desire to improvise the initializer use case.

I have also looked at the other two related proposals but I'm far from having a final opinion on any of the three.
I'm also posting my current very short and very rough impressions of the three related proposals here as I feel they are strongly related and I don't have enough to say about the other two to really justify separate posts:

Thanks. I appreciate your feedback!

Parameter forwarding:
This is the proposal I like the least. I'm not a fan of the syntax and it mostly saves some minor typing at the cost immediately knowing what the actual arguments are.

Have you used a language with a similar feature (such as tuple packing and unpacking in a dynamic language)? It is quite useful. IMO, removing the clutter of each argument, type, and default value makes it much more clear that simple forwarding is happening.

The complete parameter list would still appear in generated documentation, autocomplete, etc. An IDE could also provide an option to view the full parameter list in the function declaration itself.

Partial initializers:
My dislike of parameter forwarding carries over to this but otherwise I find the basic idea quite nice.
My bigger issue here is that I currently don't see a huge use case for this that isn't already served by initializer delegation. Yes this seems to cover additional cases but I'm not sure it justifies it's existence. It makes a lot more sense when considered as part of the memberwise initialization trifecta with parameter forwarding but like I said that's the part I don't really like.

Initializer delegation requires the initializer you call to be a complete initializer. It is not uncommon to have more than one designated initializer that need to share initialization logic. There are techniques to factor out some of this logic today, but it would be much easier with partial initializers.

Property Lists:
Like with partial initializers I find this to be an interesting idea and this seems to have other potential use cases apart from memberwise initialization. Then again I'm unsure if this exact proposal is the way to go here. I'd really prefer to see more abstract discussion of this idea before committing to any specifics.

Particularly here but also in general for all three proposals I feel that we might be better served by letting the language form further and waiting for more general pieces to fall into place before charging ahead in these areas. None of these require stopgap solutions from my point of view and I at least feel like I could evaluate this much better once I have an idea of what Swift is going to look like at version 4, 5, ...
From reading the Swift team's comments it seems like future improvements like variadic generics might make a potential difference here and I imagine the macro system might as well.

These proposals are partly a response to the comments Dave and Joe made about building on more general underlying features.

Partial initializers are a feature that would need to stand on its own, it would not be enabled by any other features. As noted in the proposal, it might actually help to prepare the initialization model to support initialization of extensions and protocols with stored properties.

I don’t believe variadic generics would cover the full set of forwarding capabilities I would like to see. Particularly forwarding of default values, but also forwarding a subset of parameters. I know you are not a fan of the forwarding idea so that may not matter much to you.

The property list idea could probably be implemented with a powerful-enough macro system. My opinion is that it would be nice to solve this problem in Swift 3 if possible. The feature could be replaced by a macro down the road if it becomes possible to write it that way.

I hope this doesn't sound too negative, you clearly invested a significant amount of work into these proposals and I value the effort. It helps me better understand the problems people see and what potential solutions might look like and stirs a lot of healthy discussion.
So don't let yourself be discouraged if I don't feel that changes in this area are required just yet :slight_smile:

That’s a fair opinion. The discussion has been healthy indeed! IMO it has at least validated that there is broad support for doing something to address these problems.

One of the big critiques of the Flexible Memberwise Initialization proposal was that it wasn’t powerful enough to support all the use cases people want to solve. I think there is a lot of demand to solve these problems and many people would like to see them solved in Swift 3.

Matthew

···

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com> wrote:

- Janosch

On 11 Jan 2016, at 04:44, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have always considered the Flexible Memberwise Initialization proposal to be just a first step (as evidenced by the many future enhancements it discussed). Its review has inspired new ideas and helped to shape my vision of the best long-term solution. My final thoughts about the review can be found here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006176.html

Property lists is the third in a series of three proposals describing general features that can work together to form a complete solution.

The proposal drafts can be found at the following links:

* Parameter forwarding: https://github.com/anandabits/swift-evolution/blob/parameter-forwarding/proposals/NNNN-parameter-forwarding.md
* Partial initializers: https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md
* Property lists: https://github.com/anandabits/swift-evolution/blob/property-lists/proposals/NNNN-property-lists.md

Matthew
Property Lists

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-property-lists.md>
Author(s): Matthew Johnson <https://github.com/anandabits>
Status: Awaiting review
Review manager: TBD
Introduction

This proposal introduces the propertylist declaration. Property lists provide concise syntactic sugar for declaring memberwise partial initializers and memberwise computed tuple properties.

NOTE: I do not love the name “property lists” for the feature or the keyword but haven’t thought of anything better. Suggestions are welcome.

Swift-evolution thread: Proposal Draft: Property Lists <https://lists.swift.org/pipermail/swift-evolution>
Motivation

I believe the review of the Flexible Memberwise Initialization <https://github.com/apple/swift-evolution/blob/master/proposals/0018-flexible-memberwise-initialization.md> proposal demonstrated a strong demand for concise yet flexible memberwise initialization. The discussion highlighted several areas where that proposal fell short:

Clarity regarding which parameters receive memberwise initialization.
Control over which parameters receive memberwise initialization.
Control over specific memberwise parameter ordering.
Control over parameter labels.
Control over default parameter values, especially for let properties.
It is a very narrow, special case feature.
This proposal builds on the Partial Initializer <https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md> proposal to solve these problems using a more general underlying mechanism. It enables truly flexible memberwise initialization.

Property lists also support other memberwise features, beginning with memberwise computed tuple properties.

Proposed solution

There are two ways to specify a property list. The basic mechanism is a property list declaration that looks as follows:

  propertylist configProperties: aPropName, customLabel anotherPropName = 42
All properties mentioned in the property list declaration must be visible at the site of the declaration.

A propertylist attribute also exists for property declarations to support cases where a list of property declarations should also be treated as a property list:

  @propertylist(aPropertyListIdentifier) let prop1, prop2: Int
A property list can contain any kind of property, including those with behaviors. However, when certain kinds of properties are included it will not be possible to synthesize a partial memberwise initializer and / or a setter for the computed tuple property.

Synthesized elements

A typealias will always be synthesized:

It will have a name matching the identifier of the property list, with the first character transformed to upper case.
It will be a tuple type containing labels and types matching those specified in the property list.
A computed tuple property will always be synthesized:

It will always include a getter.
Visibility of the getter will match the visibility of the least visible getter.
It will contain a setter as long as all of the properties are settable.
Visibility of the setter will match the visibility of the least visible setter.
A paritial initializer will only be generated if:

All properties are stored properties.
None are let properties with an initial value.
None have a behavior which is incompatible with phase 1 initialization.
Visibility of the partial initializer will match the least visible setter for structs. Partial initializers for classes are always private (as specified by the partial initializer proposal).
The property list is declared in the main body of the type, not an extension, unless the type is a struct.
If stored properties are allowed in extensions and / or protocols in the future, all properties included in the list must be declared within the same body as the property list for a partial initializer to be synthesized (either the main body of the type or the body of the same extension or same protocol).
The structure of the synthesized elements is as follows:

Ordering of partial initializer parameters and tuple members will match the order of the property list declaration.
The external parameter labels and tuple labels will match the label specified in the property list if it exists and the property name otherwise.
The default value for partial initializer parameters will be the default value specified in the property list if it exists and the initial value of the property otherwise (if that exists). If neither exist the parameter will not have a default value.
Visibility of a synthesized members is capped at internal unless public is explicitly specified. If public (or internal) is explicitly specified, all properties referenced must have getter and setter visibility of at least the specified access level or a compiler error will result.

Examples

Basic example

public struct S {
  let i: Int = 42
  public var s: String = "hello"
  public var d: Double
  
  // user declares:
  public propertylist custom: dLabel d = 42, s
  
  // compiler synthesizes:
  public typealias Custom = (dLabel: Double, s: String)
  public var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  public partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a let with a initial value

struct S {
  let i: Int = 42
  let s: String = "hello"
  var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
  }
  // NOTE: no setter because a `let` was included
  // and no partial initializer because the `let`
  // has an initial value.
}
Including a lazy property

struct S {
  let i: Int
  var s: String
  lazy var d: Double
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a `lazy var` was included.
}
Including a var with a private setter

struct S {
  let i: Int
  var s: String
  private(set) var d: Double = 42
  
  // user declares:
  propertylist custom: dLabel d, s = "hello"
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  private(set) var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: The initial value for `d` is used as a default
  // parameter value because a different default parameter value
  // was not specified.
  private partial init custom(dlabel d: Double = 42, s: String = "hello") {
    self.d = d
    self.s = s
  }
}
Including a computed property

struct S {
  let i: Int
  var s: String
  var d: Double {
    get { return getValueFromSomewhere() }
    set { storeValueSomewhere(newValue) }
  }
  
  // user declares:
  propertylist custom: dLabel d, s
  
  // compiler synthesizes:
  typealias Custom = (dLabel: Double, s: String)
  var custom: Custom {
    get { return (dLabel: d, s: s) }
    set { (d, s) = newValue }
  }
  // NOTE: no partial initializer because a computed property was included.
}
Using the @propertylist attribute

struct S {
  @propertylist(custom) var s: String, d: Double = 42
  private let i: Int
  
  // compiler synthesizes:
  typealias Custom = (s: String, d: Double)
  var custom: Custom {
    get { return (s: s, d: d) }
    set { (s, d) = newValue }
  }
  partial init custom(s: String, d: Double = 42) {
    self.s = s
    self.d = d
  }
}
Using a property list declaration for memberwise intialization

struct S {
  var x, y, z: Int
  let s: String
  let d: Double
  
  propertylist randomGroup aString s = "hello", anInt x = 42
  
  // user declares
  init(...randomGroup) {
    y = 42
    z = 42
    d = 42
  }
  
  // compiler synthesizes
  partial init randomGroup(aString s: String = "hello", anInt x: Int = 42) {
    self.s = s
    self.x = x
  }
  init(aString s: String = "hello", anInt x: Int = 42) {
    randomGroup.init(aString: s, anInt: x)
    y = 42
    z = 42
    d = 42
  }
  
}
Using the property list attribute for memberwise intialization

```swift
struct S {
@propertylist(declaredTogether) var x, y, z: Int
let s: String
let d: Double

// user declares:
init(…declaredTogether) {
s = “hello”
d = 42
}

// compiler synthesizes:
partial init declaredTogether(x: Int, y: Int, z: Int) {
self.x = x
self.y = y
self.z = z
} init(x: Int, y: Int, z: Int) {
declaredTogether.init(x: Int, y: Int, z: Int)
s = “hello”
d = 42
}

}

Implicit property lists

It may be desirable to have several implicit property lists available, such as one that includes all properties of the type. Properties would appear in any implicit property lists in declaration order.

The specific details of what implicit property lists should be available and what they should be called is a good topic for bikeshedding.

Detailed design

TODO but should fall out pretty clearly from the proposed solution

Impact on existing code

This is a strictly additive change. It has no impact on existing code.

Alternatives considered

We could live without this syntactic sugar. There are several reasons the language is better with it:

It is much more convenient than manually writing memberwise partial intializers. It does not include any unnecessary information, thus making the details more clear.
It gives us the memberwise computed tuple properties for free. This would would not be possible when writing memberwise partial initializers manually.
It scales to any new memberwise features that might make sense for a type.
As always, the propertylist keyword is game for bikeshedding. The use of : to separate the identifier from the list of properties could also be game for bikeshedding. Also, as mentioned previously, the implicit property lists are game for bikeshedding.

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


(Greg Parker) #4

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

···

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

--
Greg Parker gparker@apple.com Runtime Wrangler


(Matthew Johnson) #5

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

···

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com> wrote:

--
Greg Parker gparker@apple.com Runtime Wrangler


(Janosch Hildebrand) #6

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

I will. There are also a lot of possibilities along the `propertygroup`, `propertyset` axis that share most of the downsides of `propertylist` but at least they wouldn't be too similar to https://en.wikipedia.org/wiki/Property_list. Although I'm not sure if that naming overlap should be considered a problem.

@propertylist:
I think this is mostly redundant and the proposal would be improved by its removal.

Removing it would require a redundant list of property names in some cases. Why do you think that is better?

1) One less attribute to document, understand, maintain, ...
2) It makes the proposal simpler and clearer focusing more on the basic idea. If it were accepted and it turns out there is a big desire for something like that it could always be added – the reverse is much harder.
3) Having to explicitly spell out the propertylist aids clarity of intent and consequences
3) It has a very small use case. I don't think there are a lot of property declarations that would profit from this so I don't think it requires special consideration. At least personally I rarely declare multiple properties on a single line and not all of these might form a logical group.

[snip]
Default values in propertylist:
Strongly against this. Only applicable to initializer use case and feels generally out of place.

It is extremely important to the initializer use case. There is no way to provide a default for `let` properties in the initializer without allowing this. I would not have written this proposal at all if it weren’t for a desire to improvise the initializer use case.

I understand and I'm certainly biased by having mixed feelings about the initializer story in the first place...

My issues with the default values are roughly as follows:

1) My biggest issue is that it is really weird with regards to the other use case(s) of this idea, that is getters and setters for multiple properties.
I'm not really sure yet how useful that would be but I find the idea interesting. But default values really don't fit well with it. They aren't really applicable to that use but it feels really weird to have these default values in the declaration and then not have them influence the synthesized behaviour.

2) If a property has a generally applicable default value -> on the property declaration.
If a property has an applicable default value for a given initializer -> on the initializer argument.

I understand that you're introducing a sort of middle ground that is applicable to multiple (related) initializers but I feel it muddles the waters with regards to the other cases and from my perspective has limited use. I'd feel better with something that were still part of the initializer (syntax completely arbitrary): `init(...someGroup default s = "")` but again for me the complexity outstrips the usefulness so I'd be against that too :wink:

I tend towards requiring one to spell out (at least parts of) the initializer when one wants custom behaviour (or hope for a suitable flexible macro system) so we're probably bound to be at odds in these regards :wink:

[snip]
Parameter forwarding:
This is the proposal I like the least. I'm not a fan of the syntax and it mostly saves some minor typing at the cost immediately knowing what the actual arguments are.

Have you used a language with a similar feature (such as tuple packing and unpacking in a dynamic language)? It is quite useful. IMO, removing the clutter of each argument, type, and default value makes it much more clear that simple forwarding is happening.

I'm aware of the concept but I have to admit that I don't have much experience with these features or the respective languages.
Although I think plain Swift tuples go more into that direction (albeit less powerful (for now?!)).

I agree that it is visually simpler but I think the visual similarity of the forwarding call (with all arguments) to the function definition is often enough to quickly deduce what is happening while still allowing one to check at a glance what the arguments, types and default values are...

The complete parameter list would still appear in generated documentation, autocomplete, etc. An IDE could also provide an option to view the full parameter list in the function declaration itself.

I agree that would happen and would be helpful but I can't help but feel that I'd be better served by a much smarter autocompletion than what we have today that would take care of the redundant typing without actually changing the resulting source code.

My larger issues are with:

a) the syntax
... which I dislike. Currently it's more at an emotional than intellectual level so I haven't fully grasped the why and how.
Also I actually quite liked the `init(...)` syntax in your earlier proposal so it's not the `...` per se...
At least I'd like to see more discussion and proposals towards what something like that would look like and weigh the options.

b) not having a clear and settled picture of the concepts and ramifications under discussion.

This actually applies to all three proposals and the memberwise initializers proposal before as well. I can see that there is a problem being solved and a reasonable proposal for a solution. However I'm neither sure that the problem is big enough to require a or the proposed solution and I'm unsure if the proposed solution is the right one and I feel that I can't yet fully grasp the long term implications.

In part this is because the problems at hand don't irk me personally enough to want immediate remedy and in a larger part because I simply haven't much if any experience with languages and concepts that (attempt to) solve these issues. I'm really missing a frame of reference as to what the solution-space looks like in these areas and what the respective advantages and disadvantages are – especially since disadvantages of language constructs are often not immediately obvious.

That's also why I really appreciate these proposals and the related discussions. Currently I feel like I'm still swimming too much in the dark to form a well-reasoned opinion and I'd like to see more approaches, more discussion and get a better feeling of future Swift changes that might impact these areas before settling down. I really don't mind waiting a few years before attempting to solve these issues (if they still exist). I also don't feel the need for a fast (stopgap) solution since these are not fundamental issues to me.

I'm not sure if that is of much help to you but I hope it explains my current wait-and-see attitude a bit better. YMMV of course :slight_smile:

Partial initializers:
My dislike of parameter forwarding carries over to this but otherwise I find the basic idea quite nice.
My bigger issue here is that I currently don't see a huge use case for this that isn't already served by initializer delegation. Yes this seems to cover additional cases but I'm not sure it justifies it's existence. It makes a lot more sense when considered as part of the memberwise initialization trifecta with parameter forwarding but like I said that's the part I don't really like.

Initializer delegation requires the initializer you call to be a complete initializer. It is not uncommon to have more than one designated initializer that need to share initialization logic. There are techniques to factor out some of this logic today, but it would be much easier with partial initializers.

Indeed! Which is why I like this part of the proposal and agree with what some others have written in response in the respective thread. It is also something that can and perhaps should be decided now. I'm just unsure if there are enough use-cases to justify it. I can think of cases where it would have been useful but not enough to convince me.
There is also the downside that this can lead to less code duplication at the cost of fragmented and harder-to-follwo logic if abused.

Currently speaking I have no strong opinion either way and the discussion will probably convince me one way or another but as it stands I would certainly not object to this being added to the language.

Property Lists:
Like with partial initializers I find this to be an interesting idea and this seems to have other potential use cases apart from memberwise initialization. Then again I'm unsure if this exact proposal is the way to go here. I'd really prefer to see more abstract discussion of this idea before committing to any specifics.

Particularly here but also in general for all three proposals I feel that we might be better served by letting the language form further and waiting for more general pieces to fall into place before charging ahead in these areas. None of these require stopgap solutions from my point of view and I at least feel like I could evaluate this much better once I have an idea of what Swift is going to look like at version 4, 5, ...
From reading the Swift team's comments it seems like future improvements like variadic generics might make a potential difference here and I imagine the macro system might as well.

These proposals are partly a response to the comments Dave and Joe made about building on more general underlying features.

Partial initializers are a feature that would need to stand on its own, it would not be enabled by any other features. As noted in the proposal, it might actually help to prepare the initialization model to support initialization of extensions and protocols with stored properties.

I would certainly be in favor of extracting partial initialization into a separate proposal without forwarding and have forwarding optionally build on that later. As it stands partial initialization seems more like step 2 of 3.

I don’t believe variadic generics would cover the full set of forwarding capabilities I would like to see. Particularly forwarding of default values, but also forwarding a subset of parameters. I know you are not a fan of the forwarding idea so that may not matter much to you.

The property list idea could probably be implemented with a powerful-enough macro system. My opinion is that it would be nice to solve this problem in Swift 3 if possible. The feature could be replaced by a macro down the road if it becomes possible to write it that way.

Like I wrote above I fall on the other side of that argument and would rather wait than implement something now. For example I'd first like to see what the variadic generics and macro system look like. However if the Swift team and community decide to implement a solution now and maybe revisit the topic later I won't really mind that on any of these topics.

I hope this doesn't sound too negative, you clearly invested a significant amount of work into these proposals and I value the effort. It helps me better understand the problems people see and what potential solutions might look like and stirs a lot of healthy discussion.
So don't let yourself be discouraged if I don't feel that changes in this area are required just yet :slight_smile:

That’s a fair opinion. The discussion has been healthy indeed! IMO it has at least validated that there is broad support for doing something to address these problems.

Absolutely. Although that alone is not enough to convince me per se. Not a fair comparison really, but there was once broad support for adding GC to objc too :wink:

One of the big critiques of the Flexible Memberwise Initialization proposal was that it wasn’t powerful enough to support all the use cases people want to solve. I think there is a lot of demand to solve these problems and many people would like to see them solved in Swift 3.

Indeed! And there's probably no solution that fully satisfies everyone. It's also easy to desire increased power or a simpler system but you're the one left to actually present a cohesive solution and argument and defend the resultant complexity or lack of power so I respect your effort and certainly don't envy your position :wink:

- Janosch

···

On 11 Jan 2016, at 18:17, Matthew Johnson <matthew@anandabits.com> wrote:


(Thorsten Seitz) #7

I am afraid they are not general enough for my taste and leaning too much towards initialization.

Parameter forwarding:
This does not make the language more expressive, i.e. there is no abstraction of the parameter list. I cannot do anything with it except forwarding it. That seems not very useful, it just allows saving some keystrokes which is not really necessary.

Furthermore the syntax is really wrong in my opinion:

// user writes:
func bar(...fooParams) {
    foo(i: 32, ...fooParams)
}

The problem is that I do not see anymore what the API of bar() is! I have to look at method foo() (after scanning the body of bar() to find it) and look there. That’s really bad.

Something like the following (just using some random syntax) would be much better, as it (1) keeps the API of bar() visible and (2) abstracts over the arguments giving us a handle to manipulate it.

func bar(i i: Int, s: String, f: Float = 42, d: Double = 43, b: Bool = false) {
  foo(#arguments.with(i: 32))
  // we can do something with #arguments!
  for (Argument arg in #arguments) {
    print("\(arg.name): \(arg.type) = \(arg.value)“)
  }
}

But then I’m not sure what concrete problems this would solve. Writing out the parameter list is not so bad. I’d wager that in cases of long argument lists a redesign would be better, i.e. by encapsulating groups of parameters into structs or classes (which will probably attract behavior as well in contrast to the property list tuples).
I’d really like to see some concrete examples of use cases where this would be useful and better than refactoring the design.

Partial initializers:
These might have the most merit but are a special case of initialization, so there is nothing gained in general expressivity.

Property Lists:
This is an attempt to solve the problem of the „members“ attribute having to be narrowed for different use cases, like narrowing with regards to var parameters only or with regards to accessibility. Declaring the different views which are required for a specific class or struct explicitly as property list is an interesting solution for that and might possibly be the right thing for a statically safe language.
What I definitely don’t like is having a partial initializer automatically being created as well. I’m not too fond of automatically created code as that is (a) not visible, (b) therefore not part of the documentation (and even if the documentation gets enriched by explicit entries for automagic functions, which would be nice [the memberwise initializer should be auto-expanded in the documentation, for example], it is still not visible in the source file), and last not least it is (c) not searchable.
Currently I’m thinking the potential problems to be solved with this are not worth the magic (= making the language more difficult and reducing the readability of code).

-Thorsten

···

Am 11.01.2016 um 18:17 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

These proposals are partly a response to the comments Dave and Joe made about building on more general underlying features.


(Patrick Gili) #8

Ruby has the notion of attribute accessors, which is very similar to this proposal. If you align the proposal to look more like attribute accessors in Ruby, then it might play well with the forwarding, like the Ruby Forwardable module.

-Patrick

···

On Jan 11, 2016, at 6:02 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

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


(Matthew Johnson) #9

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

I will. There are also a lot of possibilities along the `propertygroup`, `propertyset` axis that share most of the downsides of `propertylist` but at least they wouldn't be too similar to https://en.wikipedia.org/wiki/Property_list. Although I'm not sure if that naming overlap should be considered a problem.

Yeah, I thought of those. I went with list because it implies order which is important to this feature while group and set do not imply order. Not sure why the overlap with plist files didn’t occur to me. I guess because I’m used to the plist abbreviation. :slight_smile:

@propertylist:
I think this is mostly redundant and the proposal would be improved by its removal.

Removing it would require a redundant list of property names in some cases. Why do you think that is better?

1) One less attribute to document, understand, maintain, ...
2) It makes the proposal simpler and clearer focusing more on the basic idea. If it were accepted and it turns out there is a big desire for something like that it could always be added – the reverse is much harder.
3) Having to explicitly spell out the propertylist aids clarity of intent and consequences
3) It has a very small use case. I don't think there are a lot of property declarations that would profit from this so I don't think it requires special consideration. At least personally I rarely declare multiple properties on a single line and not all of these might form a logical group.

Fair points. I agree that people usually don’t declare things this way.

Part of the reason I included this was as a gesture to the people who like the Scala syntax which does declare a bunch of properties in one list. If it was a major source of contention I would take it out.

[snip]
Default values in propertylist:
Strongly against this. Only applicable to initializer use case and feels generally out of place.

It is extremely important to the initializer use case. There is no way to provide a default for `let` properties in the initializer without allowing this. I would not have written this proposal at all if it weren’t for a desire to improvise the initializer use case.

I understand and I'm certainly biased by having mixed feelings about the initializer story in the first place...

My issues with the default values are roughly as follows:

1) My biggest issue is that it is really weird with regards to the other use case(s) of this idea, that is getters and setters for multiple properties.
I'm not really sure yet how useful that would be but I find the idea interesting. But default values really don't fit well with it. They aren't really applicable to that use but it feels really weird to have these default values in the declaration and then not have them influence the synthesized behavior.

I agree it is a bit weird here, but IMO the primary use case is the partial initializer.

The computed tuple properties seemed to get a lot of interest during the memberwise init review. I generalized the idea from just sugar for a partial init because I realized we could get the those for free (and maybe other memberwise features down the road as well).

But maybe that was a mistake. Especially since we will eventually be able to do this kind of stuff with macros.

2) If a property has a generally applicable default value -> on the property declaration.
If a property has an applicable default value for a given initializer -> on the initializer argument.

I understand that you're introducing a sort of middle ground that is applicable to multiple (related) initializers but I feel it muddles the waters with regards to the other cases and from my perspective has limited use. I'd feel better with something that were still part of the initializer (syntax completely arbitrary): `init(...someGroup default s = "")` but again for me the complexity outstrips the usefulness so I'd be against that too :wink:

Sure. Maybe it would make more sense to focus on making memberwise partial init declarations really concise.

I tend towards requiring one to spell out (at least parts of) the initializer when one wants custom behaviour (or hope for a suitable flexible macro system) so we're probably bound to be at odds in these regards :wink:

I agree in terms of custom behavior. But simple property assignments are just boilerplate that would ideally be stated as concisely as possible.

The problem IMO isn’t the repetition as much as the fact that it clutters up your code making the nontrivial stuff stand out less.

[snip]
Parameter forwarding:
This is the proposal I like the least. I'm not a fan of the syntax and it mostly saves some minor typing at the cost immediately knowing what the actual arguments are.

Have you used a language with a similar feature (such as tuple packing and unpacking in a dynamic language)? It is quite useful. IMO, removing the clutter of each argument, type, and default value makes it much more clear that simple forwarding is happening.

I'm aware of the concept but I have to admit that I don't have much experience with these features or the respective languages.
Although I think plain Swift tuples go more into that direction (albeit less powerful (for now?!)).

I agree that it is visually simpler but I think the visual similarity of the forwarding call (with all arguments) to the function definition is often enough to quickly deduce what is happening while still allowing one to check at a glance what the arguments, types and default values are...

The complete parameter list would still appear in generated documentation, autocomplete, etc. An IDE could also provide an option to view the full parameter list in the function declaration itself.

I agree that would happen and would be helpful but I can't help but feel that I'd be better served by a much smarter autocompletion than what we have today that would take care of the redundant typing without actually changing the resulting source code.

My larger issues are with:

a) the syntax
... which I dislike. Currently it's more at an emotional than intellectual level so I haven't fully grasped the why and how.
Also I actually quite liked the `init(...)` syntax in your earlier proposal so it's not the `...` per se...
At least I'd like to see more discussion and proposals towards what something like that would look like and weigh the options.

Syntax is something I don’t care much about as long as it is clear and makes sense, etc. I would change it if a better idea comes up.

b) not having a clear and settled picture of the concepts and ramifications under discussion.

This actually applies to all three proposals and the memberwise initializers proposal before as well. I can see that there is a problem being solved and a reasonable proposal for a solution. However I'm neither sure that the problem is big enough to require a or the proposed solution and I'm unsure if the proposed solution is the right one and I feel that I can't yet fully grasp the long term implications.

In part this is because the problems at hand don't irk me personally enough to want immediate remedy and in a larger part because I simply haven't much if any experience with languages and concepts that (attempt to) solve these issues. I'm really missing a frame of reference as to what the solution-space looks like in these areas and what the respective advantages and disadvantages are – especially since disadvantages of language constructs are often not immediately obvious.

That's also why I really appreciate these proposals and the related discussions. Currently I feel like I'm still swimming too much in the dark to form a well-reasoned opinion and I'd like to see more approaches, more discussion and get a better feeling of future Swift changes that might impact these areas before settling down. I really don't mind waiting a few years before attempting to solve these issues (if they still exist). I also don't feel the need for a fast (stopgap) solution since these are not fundamental issues to me.

I'm not sure if that is of much help to you but I hope it explains my current wait-and-see attitude a bit better. YMMV of course :slight_smile:

Partial initializers:
My dislike of parameter forwarding carries over to this but otherwise I find the basic idea quite nice.
My bigger issue here is that I currently don't see a huge use case for this that isn't already served by initializer delegation. Yes this seems to cover additional cases but I'm not sure it justifies it's existence. It makes a lot more sense when considered as part of the memberwise initialization trifecta with parameter forwarding but like I said that's the part I don't really like.

Initializer delegation requires the initializer you call to be a complete initializer. It is not uncommon to have more than one designated initializer that need to share initialization logic. There are techniques to factor out some of this logic today, but it would be much easier with partial initializers.

Indeed! Which is why I like this part of the proposal and agree with what some others have written in response in the respective thread. It is also something that can and perhaps should be decided now. I'm just unsure if there are enough use-cases to justify it. I can think of cases where it would have been useful but not enough to convince me.

Glad you like this part.

There is also the downside that this can lead to less code duplication at the cost of fragmented and harder-to-follwo logic if abused.

Yes, but that is always the case. :slight_smile:

Currently speaking I have no strong opinion either way and the discussion will probably convince me one way or another but as it stands I would certainly not object to this being added to the language.

Property Lists:
Like with partial initializers I find this to be an interesting idea and this seems to have other potential use cases apart from memberwise initialization. Then again I'm unsure if this exact proposal is the way to go here. I'd really prefer to see more abstract discussion of this idea before committing to any specifics.

Particularly here but also in general for all three proposals I feel that we might be better served by letting the language form further and waiting for more general pieces to fall into place before charging ahead in these areas. None of these require stopgap solutions from my point of view and I at least feel like I could evaluate this much better once I have an idea of what Swift is going to look like at version 4, 5, ...
From reading the Swift team's comments it seems like future improvements like variadic generics might make a potential difference here and I imagine the macro system might as well.

These proposals are partly a response to the comments Dave and Joe made about building on more general underlying features.

Partial initializers are a feature that would need to stand on its own, it would not be enabled by any other features. As noted in the proposal, it might actually help to prepare the initialization model to support initialization of extensions and protocols with stored properties.

I would certainly be in favor of extracting partial initialization into a separate proposal without forwarding and have forwarding optionally build on that later. As it stands partial initialization seems more like step 2 of 3.

I don’t believe variadic generics would cover the full set of forwarding capabilities I would like to see. Particularly forwarding of default values, but also forwarding a subset of parameters. I know you are not a fan of the forwarding idea so that may not matter much to you.

The property list idea could probably be implemented with a powerful-enough macro system. My opinion is that it would be nice to solve this problem in Swift 3 if possible. The feature could be replaced by a macro down the road if it becomes possible to write it that way.

Like I wrote above I fall on the other side of that argument and would rather wait than implement something now. For example I'd first like to see what the variadic generics and macro system look like. However if the Swift team and community decide to implement a solution now and maybe revisit the topic later I won't really mind that on any of these topics.

I hope this doesn't sound too negative, you clearly invested a significant amount of work into these proposals and I value the effort. It helps me better understand the problems people see and what potential solutions might look like and stirs a lot of healthy discussion.
So don't let yourself be discouraged if I don't feel that changes in this area are required just yet :slight_smile:

That’s a fair opinion. The discussion has been healthy indeed! IMO it has at least validated that there is broad support for doing something to address these problems.

Absolutely. Although that alone is not enough to convince me per se. Not a fair comparison really, but there was once broad support for adding GC to objc too :wink:

Lol. :slight_smile:

One of the big critiques of the Flexible Memberwise Initialization proposal was that it wasn’t powerful enough to support all the use cases people want to solve. I think there is a lot of demand to solve these problems and many people would like to see them solved in Swift 3.

Indeed! And there's probably no solution that fully satisfies everyone. It's also easy to desire increased power or a simpler system but you're the one left to actually present a cohesive solution and argument and defend the resultant complexity or lack of power so I respect your effort and certainly don't envy your position :wink:

Yeah, it’s clear that no one thing will make everyone happy. I’m hoping to find solutions that make most people mostly happy. :slight_smile:

Matthew

···

On Jan 11, 2016, at 4:50 PM, Janosch Hildebrand <jnosh@jnosh.com> wrote:

On 11 Jan 2016, at 18:17, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:


(Matthew Johnson) #10

Ruby has the notion of attribute accessors, which is very similar to this proposal. If you align the proposal to look more like attribute accessors in Ruby, then it might play well with the forwarding, like the Ruby Forwardable module.

It’s been a while since I’ve written Ruby but I have written quite a bit of it in the past. Did something change? As far as I know attribute accessors are basically like properties.

As far as forwarding goes, I have worked on a totally separate proposal for forwarding and have a second draft of that close to complete. I don’t see how the two are related.

Matthew

···

On Jan 13, 2016, at 9:15 AM, Patrick Gili <gili.patrick.r@gili-labs.com> wrote:

-Patrick

On Jan 11, 2016, at 6:02 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

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


(Patrick Gili) #11

Not much has changed with regard to attribute accessors.

Here is a link to a great tutorial:

http://www.rubyist.net/~slagell/ruby/accessors.html

Here is a link to the Ruby documentation the Module module, which contains documentation for attire, attr_accessor, attr_reader, and attr_writer:

http://ruby-doc.org/core-1.9.3/Module.html

I think the Ruby notion of an attribute is the direction you're trying to take this. You can take your concept and build on it.

My apologies regarding my comment about forwarding. I momentarily confused the concepts of protocol forwarding and parameter forwarding.

Cheers,
-Patrick

···

On Jan 13, 2016, at 11:21 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 13, 2016, at 9:15 AM, Patrick Gili <gili.patrick.r@gili-labs.com <mailto:gili.patrick.r@gili-labs.com>> wrote:

Ruby has the notion of attribute accessors, which is very similar to this proposal. If you align the proposal to look more like attribute accessors in Ruby, then it might play well with the forwarding, like the Ruby Forwardable module.

It’s been a while since I’ve written Ruby but I have written quite a bit of it in the past. Did something change? As far as I know attribute accessors are basically like properties.

As far as forwarding goes, I have worked on a totally separate proposal for forwarding and have a second draft of that close to complete. I don’t see how the two are related.

Matthew

-Patrick

On Jan 11, 2016, at 6:02 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

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


(Matthew Johnson) #12

Not much has changed with regard to attribute accessors.

Here is a link to a great tutorial:

http://www.rubyist.net/~slagell/ruby/accessors.html

Here is a link to the Ruby documentation the Module module, which contains documentation for attire, attr_accessor, attr_reader, and attr_writer:

http://ruby-doc.org/core-1.9.3/Module.html

I think the Ruby notion of an attribute is the direction you're trying to take this. You can take your concept and build on it.

This hasn’t changed at all since I used Ruby. It is really not like this proposal at all. It is synthesizing the property (attribute) accessors themselves. Swift does this automatically when you declare a stored property, or allows you to do so manually by writing computed accessors.

The idea in this proposal was that we could synthesize at least two useful memberwise features for a type from a list of properties - a memberwise partial initializer and a computed tuple property.

That said, I won’t be pursuing this proposal further given Chris’s comments about syntactic sugar proposals. It seems doubtful that something like this would be considered in the Swift 3 timeframe and Swift 4 might bring macros which would hopefully make it possible to implement something like this ourselves if desired.

Matthew

My apologies regarding my comment about forwarding. I momentarily confused the concepts of protocol forwarding and parameter forwarding.

No problem, although I don’t see how either are directly related to this topic.

Matthew

···

On Jan 13, 2016, at 11:46 AM, Patrick Gili <gili.patrick.r@gili-labs.com> wrote:

Cheers,
-Patrick

On Jan 13, 2016, at 11:21 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 13, 2016, at 9:15 AM, Patrick Gili <gili.patrick.r@gili-labs.com <mailto:gili.patrick.r@gili-labs.com>> wrote:

Ruby has the notion of attribute accessors, which is very similar to this proposal. If you align the proposal to look more like attribute accessors in Ruby, then it might play well with the forwarding, like the Ruby Forwardable module.

It’s been a while since I’ve written Ruby but I have written quite a bit of it in the past. Did something change? As far as I know attribute accessors are basically like properties.

As far as forwarding goes, I have worked on a totally separate proposal for forwarding and have a second draft of that close to complete. I don’t see how the two are related.

Matthew

-Patrick

On Jan 11, 2016, at 6:02 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

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


(Patrick Gili) #13

Just for the record, I like these proposals that add syntactic sugar the compiler uses to synthesize code in an effort to save time and effort for developers. However, developers need a means of inspecting synthesizing code.

Cheers,
-Patrick

···

On Jan 13, 2016, at 12:55 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 13, 2016, at 11:46 AM, Patrick Gili <gili.patrick.r@gili-labs.com <mailto:gili.patrick.r@gili-labs.com>> wrote:

Not much has changed with regard to attribute accessors.

Here is a link to a great tutorial:

http://www.rubyist.net/~slagell/ruby/accessors.html

Here is a link to the Ruby documentation the Module module, which contains documentation for attire, attr_accessor, attr_reader, and attr_writer:

http://ruby-doc.org/core-1.9.3/Module.html

I think the Ruby notion of an attribute is the direction you're trying to take this. You can take your concept and build on it.

This hasn’t changed at all since I used Ruby. It is really not like this proposal at all. It is synthesizing the property (attribute) accessors themselves. Swift does this automatically when you declare a stored property, or allows you to do so manually by writing computed accessors.

The idea in this proposal was that we could synthesize at least two useful memberwise features for a type from a list of properties - a memberwise partial initializer and a computed tuple property.

That said, I won’t be pursuing this proposal further given Chris’s comments about syntactic sugar proposals. It seems doubtful that something like this would be considered in the Swift 3 timeframe and Swift 4 might bring macros which would hopefully make it possible to implement something like this ourselves if desired.

Matthew

My apologies regarding my comment about forwarding. I momentarily confused the concepts of protocol forwarding and parameter forwarding.

No problem, although I don’t see how either are directly related to this topic.

Matthew

Cheers,
-Patrick

On Jan 13, 2016, at 11:21 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 13, 2016, at 9:15 AM, Patrick Gili <gili.patrick.r@gili-labs.com <mailto:gili.patrick.r@gili-labs.com>> wrote:

Ruby has the notion of attribute accessors, which is very similar to this proposal. If you align the proposal to look more like attribute accessors in Ruby, then it might play well with the forwarding, like the Ruby Forwardable module.

It’s been a while since I’ve written Ruby but I have written quite a bit of it in the past. Did something change? As far as I know attribute accessors are basically like properties.

As far as forwarding goes, I have worked on a totally separate proposal for forwarding and have a second draft of that close to complete. I don’t see how the two are related.

Matthew

-Patrick

On Jan 11, 2016, at 6:02 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 11, 2016, at 4:49 PM, Greg Parker <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jan 11, 2016, at 9:17 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 11, 2016, at 10:21 AM, Janosch Hildebrand <jnosh@jnosh.com <mailto:jnosh@jnosh.com>> wrote:

propertylist:
Not a big fan of the name either, `propertyalias` came to mind but that sounds like it's only for a single property.

If you think of something better please let me know!

"property list" already means something in Cocoa. You are unlikely to get approval under that name.

Good point! Any better ideas? I don't like it, just didn't think of a good one yet.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

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