[Review] SE-0030 Property Behaviors

Proposal link:
https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?

I’m +1 on the feature. I have some comments for discussion though, I’m sorry I didn’t get these to you before the formal proposal went out:

First, on the most obvious bikeshed, the surface level syntax proposed:

  var [lazy] foo = 1738
  foo.[lazy].clear()

Given the recent trend to use # for compiler synthesized / macroish / magic syntax, it is worth considering whether:

  var lazy foo = 1738
  foo.lazy.clear()

might make sense, or even:

   lazy var foo = 1738

I think that something like this is a bit better aesthetically, since the delimiters are heavily array/collection/subscript-centric (which is a good thing). OTOH, this would be giving users access to the “#” namespace, which means that future conflicts would have to be resolved with backticks - e.g. if they wanted a “line” behavior, they’d have to use "var #`line` foo = 1738”. I guess the fact that we already have a solution to the imagined problem means that this isn’t a big concern in practice.

Doesn't seem like an obvious analogy to me, since there's potentially a runtime component to these. As you noted, it also introduces a naming collision problem until we have a proper general solution for what '#' means, and I don't think we want to just shovel stuff ad-hoc into that namespace without more of a plan. We already overload '' for other things like capture lists, which isn't necessarily an excuse to overload it further, but we aren't sacrificing some pure design principle either.

In any case, I think we should just pick a syntax, land the feature, and keep bikeshedding on it through the end of Swift 3 :-)

Agreed.

What are the semantics of behaviors applied to var/let decls with non-trivial patterns? For example:

  var [lazy] (foo, bar) = qux()

? While we could devise semantics for this in some cases, I think it is reasonable to only allow behaviors on single-identifier patterns, and reject the other cases as invalid. If you agree, please loop that into the proposal.

Yeah, it's impossible to do if the behavior binds the initial value expression. We could potentially support it for behaviors with DI-like initialization. I had mentioned this but accidentally cut it out while trying to simplify, thanks for the heads up.

I understand the need to specifying whether the getter/setter and other random methods are mutating or not, but doesn’t this eliminate the difference between a var/let behavior? Couldn’t we just say that a behavior with a nonmutating getter & setter are valid on a “let”, and that any other mutating members are unavailable on a let? This seems like a theoretical win, though I’m not sure whether it would actually be a win in practice for any conceivable behaviors you’ve been thinking about.

I think we should subset 'let' behaviors out for the same reason we don't yet allow computed 'let' properties—right now 'let' guarantees immutability, and we don't have a way in the language to enforce that for user code. It would definitely be nice to be able to declare 'let [delayed]' and 'var [delayed]', though.

I like the approach of having a behavior decl, but am not excited about the decl syntax itself:
behavior var [lazy] _: Value = initialValue {

I understand that this is intended to provide a template-like construct, but I have a few problems with this:

1) A number of the things here are actually totally invariant (e.g. the _, the colon) or only serve to provide a name (Value, initialValue) but cannot be expanded into general expressions, though they look like they are.

2) This is totally unprecedented in Swift. We have a very regular structure of “@attributes decl-modifiers introducer_keyword name” - we have no declarations that look like this. For example, while operator decls could follow this structure, they don’t.

3) If you agree that we don’t need to differentiate between var/let then “behavior var" is providing potentially misleading info.

IMO, the most regular decl structure would be:

@initial_value_required // or something like it.
property behavior lazy { // could be “subscript behavior” at some point in the future?
   var value: Self? = nil // Self is the property type? Seems weird if it is the enclosing type.
   ...
}

or perhaps:

@initial_value_required
property behavior lazy<PropertyType> { // specify the type of the property as a generic constraint?
   var value: PropertyType? = nil
   ...
}

though I agree that this would require us to take “behavior” as a keyword. Do we know whether or not this would be a problem in practice? If so, going with “behavior var” and “behavior let” is probably ok.

Of course, @initial_value_required is also pretty gross. The alternative is to follow the precedent of operator decls, and introduce random syntax in their body, such as:

property behavior lazy<PropertyType> {
   initializer initValue
   var value: PropertyType? = nil
   …
       value = initValue
   ...
}

Yeah, this is more like what I had proposed in previous drafts. If we go this route, I would rather use special contextual declarators like this than '@initial_value_required'-like attributes.

  // Behaviors can declare storage that backs the property.
  private var value: Value?

What is the full range of access control allowed here? Is there any reason to allow anything other than “public” (which would mean that the entity is exposed at whatever the properties access control level is)? If so, why allow specifying internal? For sake of exposition in the proposal, it seems simplest to say:

var value: Value? // private by default

or something.

  // Behaviors can declare initialization logic for the storage.
  // (Stored properties can also be initialized in-line.)
  init() {
    value = nil
  }

If a behavior has an init() with no arguments, then are the semantics that it is *always* run? What if there are both an init() and an init(value : Value)?

As proposed there is no 'init(value: Value)'; we can design that later. An 'init()' with no arguments would always run at initialization time, as if:

var [behavior] x: Int

instantiated storage like:

var `x.[behavior].storage`: BehaviorStorage<Int> = `[behavior].init()`

// Inline initializers are also supported, so `var value: Value? = nil`
  // would work equivalently.

This example is using a stored property of type Optional<Value> which has a default value of nil, does that count or is this a syntactic requirement? To me, it seems most natural to follow the existing rules we have:

1) If you write any init, then you get what you write.
2) If there are no init’s, and any stored property in a behavior is non-default initializable, then it is an error.
3) If there are no init’s, and all stored properties in a behavior are default initializable, then you get init() implicitly.

I thought that was self-evident. I can make it explicit.

Typographical comment:
    if let value = value {
      return value
    }

You have way too many “value”s floating around, for sake of clarity, how about:

    if let valuePresent = value {
      return valuePresent
    }

In "Resettable properties”, you have this:

  // Reset the property to its original initialized value.
  mutating func reset() {
    value = initialValue
  }

This raises the question of how “initialValue” works: Is it evaluated once when the property is bound and the resultant value is stored somewhere (evaluating any side effects exactly once) or is it an auto-closure-like concept? If it is autoclosure-like, what does it mean in terms of requiring “self." qualification & @noescape?

In the proposal, I say it behaves like a get-only property—it evaluates every time it's loaded.

In the context of your @NSCopying-replacement example, I would want something like this:

class Foo {
  var [copying] myproperty : NSString

  init(a : NSString) {
     myproperty = a
  }
}

to work, where the behavior requires an initial value, but that value is provided by a flow-sensitive initialization point by DI . How does this happen?

Yeah. DI-like initialization is one of the future directions.

"This imposes an initializer requirement on the behavior. Any property using the behavior must be declared with an initial value"

Ah, this gets to the @NSCopying replacement example. This might be too limiting, but so long as there is a path forward to generalize this, it seems like a fine starting point.

[[Coming back to this after reading to the end]] Ok, I see this is in the future directions section. I’m fine with punting this to a further revision of the proposal, but I think that it is worthwhile to provide a syntactic affordance to differentiate properties that “must have an initializer expression” and “must be initialized by an 'init(v: Value)’ member on the behavior before otherwise used”. @NSCopying replacement seems like the later case. I’m fine with pushing off the general case to a later proposal, but we should carve out space for it to fit in.

public behavior var [changeObserved] _: Value = initialValue {
...
   
    if oldValue != newValue {

This makes me vaguely uncomfortable, given that “changeObserved” has undeclared type requirements that are only diagnosed when the behavior is instantiated.

That was an oversight. In reality you'd have to declare:

public behavior var [changeObserved] _: Value = initialValue
  where Value: Equatable {

In detailed design:
property-behavior-decl ::=
  attribute* decl-modifier*
  'behavior' 'var' '[' identifier ']' // behavior name
  (identifier | '_') // property name binding
If you go with this declaration syntax, I’d suggest requiring _ for the name, since the name isn’t otherwise used for anything. Ah, it looks like you say this later, maybe the grammar just needs to be updated?
A _ placeholder is required in the name position. (A future extension of behaviors may allow the property name to be bound as a string literal here.)

"Inside a behavior declaration, self is implicitly bound to the value that contains the property instantiated using this behavior. For a freestanding property at global or local scope, this will be the empty tuple (), and for a static or class property, this will be the metatype. Within the behavior declaration, the type of self is abstract and represented by the implicit generic type parameter Self."

This is somewhat strange to me. Would it be reasonable to say that self is bound iff the behavior has a classbound requirement on Self? Alternatively, perhaps you could somehow declare/name the declcontext type, and refer to it later by a name other than Self?

  mutating func update(x: Int) {
    [foo].x = x // Disambiguate reference to behavior storage
  }

It would be really nice to be able to keep “self” referring to the behavior, and require an explicit declaration for the enclosing declaration if a behavior requires one (e.g. in the case of atomic).

Sure, we could take that approach. I don't think it makes sense to constrain access to the enclosing value to classes, though.

"Nested Types in Behaviors”

This sounds great in the fullness of time, but it seems subsettable out of the first implementation/proposal. Lacking this, it can be easily worked around with a private peer type.

    // Parameter gets the name 'bas' from the accessor requirement
    // by default, as with built-in accessors today.

Not really important for the v1 proposal, but it seems like a natural direction to allow this to be controlled by “API names” in the accessor. The default would be that there are no API names, but if a behavior specified one, e.g.:

    willSet(newValue newValue : Value)

then “newValue” would be the default name in the body of the accessor. If no “API name” were specified, then no name would be default injected.

Typographical comment w.r.t. the "// Reinvent computed properties” example, you refer to the behavior as both “foobar” and “computed” later, I think the later reference is supposed to be "var [foobar] bar: Int”? Feel free to find more diversity in your metasyntactic names here :-)

"Accessor requirements cannot take visibility modifiers; they are always as visible as the behavior itself."

Maybe I’m misinterpreting this, but I see accessors differently. I see them as “never being accessible to the code that declares a property using the behavior”. If you want this, you can define a method that wraps them or something. To a client of a behaviors, accessors are “implementation only”.

Can property requirements in protocols have behaviors on them?

I think not.

-Joe

···

On Feb 10, 2016, at 10:13 PM, Chris Lattner <clattner@apple.com> wrote:
On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I just want to weigh in on this about expansions of the proposal. I think large expansions of the proposal are out-of-scope and should be handled in follow-on discussions, particularly in cases where the proposal is already fairly large. The big exception here is if the feature as proposed doesn’t stand well on its own: for example, it’s not actually useful without some particular expansion, so it shouldn’t be accepted. From reading the rest of your review, I don’t think you believe that expansion to functions is necessary for behaviors to be useful—but that behaviors could be better if they were extended to functions. In that case, I’d call it a follow-on discussion.

  - Doug

···

On Feb 11, 2016, at 2:12 AM, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

On Wed, Feb 10, 2016 at 2:00 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Hello Swift community,

The review of SE-0030 "Property Behaviors" begins now and runs through February, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-proposal.md&gt;
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md
Reply text

Other replies
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Would it be out-of-scope to propose extending this to functions? [snip]

What is your evaluation of the proposal?

I agree with a number of people that have already replied before me: I think this is a very exiting proposal, but as it is right now, its not 100% fleshed out. Chris and other already provided comments on a number of points that also came to my mind. Specifically, I am not very keen on the proposed v.[foo] syntax and also the declaration of behaviours looks off to me as well (even though its elegant in its own right). I’d prefer something like

property behavior foo<valueType: Value, instanceType: Self where …> {

}

e.g. where we take the already available generics syntax and extend it with arguments to signal that behaviours are generics over multiple specific dimensions (this can be also used for a future extension of generic types). Required initial value can be coded by a presence of a constructor that takes mandatory argument, for example, or by an attribute as Chris suggests.

One potential problem with a feature like this is that it is very fundamental —it affects many areas of the language, some of those in a non-trivial way. These things are hard to get from the first go. I’d suggest that one makes an experimental implementation first and lets a bunch of people test-drive it for a couple of weeks or even months. I am sure that there will be some subtle design decisions that one might regret later.

So yes, its a definitive +1 but of the kind “revise and resubmit”. Also, sole real-world testing should be done before feature like this is be accepted into the core language. I also believe that fundamental, non-trivial features like these are best designed in a team.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. it makes the language more consistent and flexible, while removing idiosyncrazy. I am also sure that it will open up new possibilities for the UI framework teams to come up with great APIs :)

Does this proposal fit well with the feel and direction of Swift?

Yes, definitely

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have used Python that has a very similar feature (property descriptors) quite extensively, and I have implemented many different patterns using this mechanism (such as property observing and binding). I have also implemented something similar for R. I believe that Joe’s proposal is much better thought out and fits the overall theme of a strongly typed language very well.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading. I was also following the discussion, but its a bit difficult to keep up — watching the swift-evolution list is more like a full-time job :)

— Taras

···

On 11 Feb 2016, at 07:13, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Proposal link:
https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?

I’m +1 on the feature. I have some comments for discussion though, I’m sorry I didn’t get these to you before the formal proposal went out:

First, on the most obvious bikeshed, the surface level syntax proposed:

  var [lazy] foo = 1738
  foo.[lazy].clear()

Given the recent trend to use # for compiler synthesized / macroish / magic syntax, it is worth considering whether:

  var lazy foo = 1738
  foo.lazy.clear()

might make sense, or even:

   lazy var foo = 1738

I think that something like this is a bit better aesthetically, since the delimiters are heavily array/collection/subscript-centric (which is a good thing). OTOH, this would be giving users access to the “#” namespace, which means that future conflicts would have to be resolved with backticks - e.g. if they wanted a “line” behavior, they’d have to use "var #`line` foo = 1738”. I guess the fact that we already have a solution to the imagined problem means that this isn’t a big concern in practice.

In any case, I think we should just pick a syntax, land the feature, and keep bikeshedding on it through the end of Swift 3 :-)

What are the semantics of behaviors applied to var/let decls with non-trivial patterns? For example:

  var [lazy] (foo, bar) = qux()

? While we could devise semantics for this in some cases, I think it is reasonable to only allow behaviors on single-identifier patterns, and reject the other cases as invalid. If you agree, please loop that into the proposal.

I understand the need to specifying whether the getter/setter and other random methods are mutating or not, but doesn’t this eliminate the difference between a var/let behavior? Couldn’t we just say that a behavior with a nonmutating getter & setter are valid on a “let”, and that any other mutating members are unavailable on a let? This seems like a theoretical win, though I’m not sure whether it would actually be a win in practice for any conceivable behaviors you’ve been thinking about.

I like the approach of having a behavior decl, but am not excited about the decl syntax itself:
behavior var [lazy] _: Value = initialValue {

I understand that this is intended to provide a template-like construct, but I have a few problems with this:

1) A number of the things here are actually totally invariant (e.g. the _, the colon) or only serve to provide a name (Value, initialValue) but cannot be expanded into general expressions, though they look like they are.

2) This is totally unprecedented in Swift. We have a very regular structure of “@attributes decl-modifiers introducer_keyword name” - we have no declarations that look like this. For example, while operator decls could follow this structure, they don’t.

3) If you agree that we don’t need to differentiate between var/let then “behavior var" is providing potentially misleading info.

IMO, the most regular decl structure would be:

@initial_value_required // or something like it.
property behavior lazy { // could be “subscript behavior” at some point in the future?
   var value: Self? = nil // Self is the property type? Seems weird if it is the enclosing type.
   ...
}

or perhaps:

@initial_value_required
property behavior lazy<PropertyType> { // specify the type of the property as a generic constraint?
   var value: PropertyType? = nil
   ...
}

though I agree that this would require us to take “behavior” as a keyword. Do we know whether or not this would be a problem in practice? If so, going with “behavior var” and “behavior let” is probably ok.

Of course, @initial_value_required is also pretty gross. The alternative is to follow the precedent of operator decls, and introduce random syntax in their body, such as:

property behavior lazy<PropertyType> {
   initializer initValue
   var value: PropertyType? = nil
   …
       value = initValue
   ...
}

  // Behaviors can declare storage that backs the property.
  private var value: Value?

What is the full range of access control allowed here? Is there any reason to allow anything other than “public” (which would mean that the entity is exposed at whatever the properties access control level is)? If so, why allow specifying internal? For sake of exposition in the proposal, it seems simplest to say:

var value: Value? // private by default

or something.

  // Behaviors can declare initialization logic for the storage.
  // (Stored properties can also be initialized in-line.)
  init() {
    value = nil
  }

If a behavior has an init() with no arguments, then are the semantics that it is *always* run? What if there are both an init() and an init(value : Value)?

// Inline initializers are also supported, so `var value: Value? = nil`
  // would work equivalently.

This example is using a stored property of type Optional<Value> which has a default value of nil, does that count or is this a syntactic requirement? To me, it seems most natural to follow the existing rules we have:

1) If you write any init, then you get what you write.
2) If there are no init’s, and any stored property in a behavior is non-default initializable, then it is an error.
3) If there are no init’s, and all stored properties in a behavior are default initializable, then you get init() implicitly.

Typographical comment:
    if let value = value {
      return value
    }

You have way too many “value”s floating around, for sake of clarity, how about:

    if let valuePresent = value {
      return valuePresent
    }

In "Resettable properties”, you have this:

  // Reset the property to its original initialized value.
  mutating func reset() {
    value = initialValue
  }

This raises the question of how “initialValue” works: Is it evaluated once when the property is bound and the resultant value is stored somewhere (evaluating any side effects exactly once) or is it an auto-closure-like concept? If it is autoclosure-like, what does it mean in terms of requiring “self." qualification & @noescape?

In the context of your @NSCopying-replacement example, I would want something like this:

class Foo {
  var [copying] myproperty : NSString

  init(a : NSString) {
     myproperty = a
  }
}

to work, where the behavior requires an initial value, but that value is provided by a flow-sensitive initialization point by DI . How does this happen?

"This imposes an initializer requirement on the behavior. Any property using the behavior must be declared with an initial value"

Ah, this gets to the @NSCopying replacement example. This might be too limiting, but so long as there is a path forward to generalize this, it seems like a fine starting point.

[[Coming back to this after reading to the end]] Ok, I see this is in the future directions section. I’m fine with punting this to a further revision of the proposal, but I think that it is worthwhile to provide a syntactic affordance to differentiate properties that “must have an initializer expression” and “must be initialized by an 'init(v: Value)’ member on the behavior before otherwise used”. @NSCopying replacement seems like the later case. I’m fine with pushing off the general case to a later proposal, but we should carve out space for it to fit in.

public behavior var [changeObserved] _: Value = initialValue {
...
   
    if oldValue != newValue {

This makes me vaguely uncomfortable, given that “changeObserved” has undeclared type requirements that are only diagnosed when the behavior is instantiated. I’d greatly prefer to see something like:

@initial_value_required
property behavior changeObserved<PropertyType : Equatable> { // constraint specified!

which would allow the behavior to be modularly type checked.

[[later…]] ah, I see that you’re doing something similar but different in the "Synchronized Property Access” example. I mostly care that we can express and modularly check this, the exact syntax isn’t as important.

In detailed design:
property-behavior-decl ::=
  attribute* decl-modifier*
  'behavior' 'var' '[' identifier ']' // behavior name
  (identifier | '_') // property name binding
If you go with this declaration syntax, I’d suggest requiring _ for the name, since the name isn’t otherwise used for anything. Ah, it looks like you say this later, maybe the grammar just needs to be updated?
A _ placeholder is required in the name position. (A future extension of behaviors may allow the property name to be bound as a string literal here.)

"Inside a behavior declaration, self is implicitly bound to the value that contains the property instantiated using this behavior. For a freestanding property at global or local scope, this will be the empty tuple (), and for a static or class property, this will be the metatype. Within the behavior declaration, the type of self is abstract and represented by the implicit generic type parameter Self."

This is somewhat strange to me. Would it be reasonable to say that self is bound iff the behavior has a classbound requirement on Self? Alternatively, perhaps you could somehow declare/name the declcontext type, and refer to it later by a name other than Self?

  mutating func update(x: Int) {
    [foo].x = x // Disambiguate reference to behavior storage
  }

It would be really nice to be able to keep “self” referring to the behavior, and require an explicit declaration for the enclosing declaration if a behavior requires one (e.g. in the case of atomic).

"Nested Types in Behaviors”

This sounds great in the fullness of time, but it seems subsettable out of the first implementation/proposal. Lacking this, it can be easily worked around with a private peer type.

    // Parameter gets the name 'bas' from the accessor requirement
    // by default, as with built-in accessors today.

Not really important for the v1 proposal, but it seems like a natural direction to allow this to be controlled by “API names” in the accessor. The default would be that there are no API names, but if a behavior specified one, e.g.:

    willSet(newValue newValue : Value)

then “newValue” would be the default name in the body of the accessor. If no “API name” were specified, then no name would be default injected.

Typographical comment w.r.t. the "// Reinvent computed properties” example, you refer to the behavior as both “foobar” and “computed” later, I think the later reference is supposed to be "var [foobar] bar: Int”? Feel free to find more diversity in your metasyntactic names here :-)

"Accessor requirements cannot take visibility modifiers; they are always as visible as the behavior itself."

Maybe I’m misinterpreting this, but I see accessors differently. I see them as “never being accessible to the code that declares a property using the behavior”. If you want this, you can define a method that wraps them or something. To a client of a behaviors, accessors are “implementation only”.

Can property requirements in protocols have behaviors on them?

Is the problem being addressed significant enough to warrant a change to Swift?

Yep.

Does this proposal fit well with the feel and direction of Swift?

Yep, moving generalizing special case hacks and moving them out of the compiler is a great direction.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have not.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I’m spent a lot of time following the discussion and chatting with Joe about this periodically, starting when Dmitri pointed out this possible direction back in Dec 2014. :-)

-Chris

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

-1 to the proposed use of the delimiters for property behaviors.

I think from a purely stylistic POV, further exceptions to the delimiters beyond current use for arrays, etc. amps-up ambiguity in Swift.

···

On Feb 11, 2016, at 12:13 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

  var [lazy] foo = 1738
  foo.[lazy].clear()

Hi everyone,

First, I'm sorry, I have very few spare time currently, so I will only make a quick contribution to that topic.

I love this discussion, it is really an interesting feature, that could be very useful in some parts of my existing code.
I would like to raise a concern about the syntax : as Chris said, the square brackets are already heavily used for collections and subscript.
Given that swift might be used as a scripting language too, it will probably be written using a "simple" text editor like sublimeText, textMate, vi or equivalent ones in many cases. Those editor does not use sourceKit or LLVM to apply syntax coloring. The analysis is much simpler, often based on regexp.
I think that re-using the square brackets here would make it hard for them to provide accurate coloring, thus lowering the ease of use and reducing the attractiveness of swift as a scripting language.

This feature is a +1 for me, but I would prefer an alternative syntax for it, like the @ prefix proposed in the review, or the # prefix proposed by Chris.

Jerome

···

Le 11 févr. 2016 à 07:13, Chris Lattner via swift-evolution <swift-evolution@swift.org> a écrit :

I think that something like this is a bit better aesthetically, since the delimiters are heavily array/collection/subscript-centric (which is a good thing). OTOH, this would be giving users access to the “#” namespace, which means that future conflicts would have to be resolved with backticks - e.g. if they wanted a “line” behavior, they’d have to use "var #`line` foo = 1738”. I guess the fact that we already have a solution to the imagined problem means that this isn’t a big concern in practice.

I guess there is an obvious response to a question left unanswered last year, but as seeing this

I think that something like this is a bit better aesthetically, since the delimiters are heavily array/collection/subscript-centric (which is a good thing).

I ask again:
What is wrong with arrow brackets?

var<lazy> foo

+1 for this review. I feel the same in regard to the clunky interface. It feels redundant and an extension instead of part of the property. One could argue that that is what was I ended, but...

There are many disparate problems this proposal is trying to address.

Some of the problems illustrated in the proposal would benefit from it, but not all. In some cases, such as the `observed` behavior, it makes things worse because your observer functions have to be tied to a behavior attached to the variable.

This is what I have been trying to say. One shouldn't have to invoke the behavior to get the behavior. It should be understood from declaration the a given property has a given set of behaviors.

Does this proposal fit well with the feel and direction of Swift?

Accessing functions inside behaviors such as `resettable` adds two layers of metadata/meta-operations to the property (one is the behavior name, the other is the `reset()` function). I would argue that including `resettable` before calling a `reset()` function is redundant.

Again, what Have been trying to say: you shouldn't need to say, "property, I'm going to use this behavior I gave you. Behavior please do ____". It's clunky and makes you feel like your now dealing with the property, but a third party. It's like HIPPA when you have a patient with dementia--"I'm going to discuss some things with your family unless you don't have any other questions. Family ... " (forgive me, I'm a physician, so this analogy makes sense to me).

It is also against the principles of separation between the interface and the implementation since you might want to define `reset()` in your own way for some variables, which would entail the creation of a separate behavior with a different name, which would change the way you make your `reset()` call. To me, the way you can call behavior functions seems rather heavyweight and inflexible.

This was pointed out to me that if the property and the behavior have a function with the same name, the problem of which to choose begins. I agree that this should be a non-issue. This leaves too much of the language implementation exposed to user of the language. To me the ideal would be to state that a property has some behavior and then everything "just works" from there with having to micromanage the behavior. We don't tell ARC what to do. Maybe that's a poor example, but it seems like it is on the same level.

Swift currently lets you add `willSet` and `didSet` observers without having to opt-in through some special syntax. Requiring that a behavior be added, such as `observed` before being able to use these or other custom kinds of observers seems like a step in the wrong direction to me.

I really didn't like this part of the proposal, but you take the good with the bad. I like that property over are available from the get go. You don't have to manually say, "property, I grant you the ability to observe".

···

On Feb 14, 2016, at 12:16 PM, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

--
Michel Fortin
https://michelf.ca

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

The main shortcoming here is the complete lack of composability. I understand why behavior composition got cut, but I’m not convinced it was the right decision.

I agree that a deliberate solution should include composition of behaviors, but I doubt that the practical value of this feature is big:
Most likely, there won't be many different behaviors, and I expect that each of those will be small (in terms of code size). Both assumptions may be wrong in some cases, but all examples that have been discussed so far could easily be combined by simply writing a new behavior.

This isn't elegant, but without a clear distinction between "decorating" behaviors and those that provide actual storage, many possible combinations would make no sense at all (that's no argument against composition, though)

Tino

I like this alternative proposal and would like to see it discussed more deeply, as it seems to be very lightweight and natural.

-Thorsten

···

Am 14.02.2016 um 18:16 schrieb Michel Fortin via swift-evolution <swift-evolution@swift.org>:

What is your evaluation of the proposal?

I think property behaviors are a nice idea. But I feel the current proposal is trying to handle too many disparate use cases using an approach that doesn't work that well for all of them.

I already expressed my view of this in pre-review, and I'm a bit disappointed to see no mention of my suggestions in the "Alternatives considered" section. Basically I think this proposal should be broken in three or four separate smaller features that can each stand on their own:

1. the ability to add `func`, `var`, and `let` inside any property declaration to handle metadata and meta-operations. For instance:

   var count: Int {
       func reset() { count = 0 }
   }

   count::reset() // or some other syntax

   var root: NSObject {
       var lastModified: NSDate? = nil
       didSet { lastModified = NSDate() }
   }

   print(root::lastAccess)

   var lang: String {
       let xmlName = "xml:lang"
   }

   element.addAttribute(name: lang::xmlName, value: lang)

2. the ability to encapsulate those `func` and `var` inside a behaviour for reuse:

   behavior resettable<T>: T {
       func reset() { currentValue = initValue }
   }
   var [resettable] count: Int

   count::reset() // or some other syntax

   behavior changeMonitored<T>: T {
       var lastModified: NSDate? = nil
       didSet { lastModified = NSDate() }
   }
   var [changeMonitored] root: NSObject

   print(root::lastModified)

3. the ability for the behavior (extending point 2) to change the base storage type of the property:

   behavior synchronized<T>: Synchronized<T> {
       get { return currentValue.value }
       set { currentValue.value = newValue }
   }

   var [synchronized] count: Int

   count = 1

4. a way to define property observers such as `willSet` globally, with no need to attach a particular behavior to a variable:

   observer willSet<T>(newValue: T) {
       set {
           willSet(newValue)
           currentValue = newValue
       }
   }

   var count: Int {
       willSet {
           print("\(count) will become \(newValue)")
       }
   }

I don't have a fully formed proposal, but there's a bit more details in a relatively recent discussion here (I changed the syntax a bit since then):

[swift-evolution] [Proposal] Property behaviors
[swift-evolution] [Proposal] Property behaviors
[swift-evolution] [Proposal] Property behaviors

Is the problem being addressed significant enough to warrant a change to Swift?

There are many disparate problems this proposal is trying to address.

Some of the problems illustrated in the proposal would benefit from it, but not all. In some cases, such as the `observed` behavior, it makes things worse because your observer functions have to be tied to a behavior attached to the variable.

Does this proposal fit well with the feel and direction of Swift?

Accessing functions inside behaviors such as `resettable` adds two layers of metadata/meta-operations to the property (one is the behavior name, the other is the `reset()` function). I would argue that including `resettable` before calling a `reset()` function is redundant. It is also against the principles of separation between the interface and the implementation since you might want to define `reset()` in your own way for some variables, which would entail the creation of a separate behavior with a different name, which would change the way you make your `reset()` call. To me, the way you can call behavior functions seems rather heavyweight and inflexible.

Swift currently lets you add `willSet` and `didSet` observers without having to opt-in through some special syntax. Requiring that a behavior be added, such as `observed` before being able to use these or other custom kinds of observers seems like a step in the wrong direction to me.

That's the two main points that I think don't work well in the current proposal when compared to the rest of Swift.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I haven't.

Though the syntax `property.[behavior].func()` reminds be a bit of how you select an axis when accessing nodes in XPath.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I followed and participated to the discussions pre-review. I have less time to follow it now, so I might have missed recent developments.

--
Michel Fortin
https://michelf.ca

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

General comment: you should look at proposals as steps along the way. Things that got subset out for the basic proposal may still be added back after the dust settles on the first pass.

-Chris

···

On Feb 14, 2016, at 12:02 PM, plx via swift-evolution <swift-evolution@swift.org> wrote:

What is your evaluation of the proposal?

It’s “ok”. It is obviously scaled-back from earlier attempts, enough to be useful but not enough to be exciting.

The main shortcoming here is the complete lack of composability. I understand why behavior composition got cut, but I’m not convinced it was the right decision.

Behaviors that make synchronization promises are an obvious problem with composition, but there are also subtle issues with other compositions. Consider the composition of `lazy` with `didSet`—if you apply `didSet` inside `lazy`, you'll observe the lazy initialization as a "set" of the lazy storage, whereas you won't if you apply `lazy` inside `didSet`.

-Joe

···

On Feb 14, 2016, at 12:02 PM, plx via swift-evolution <swift-evolution@swift.org> wrote:

As far as I have followed it, it does seem that it’s only `synchronized`-style behaviors that are fundamentally-problematic under composition; that is, using the terminology in the proposal, linear behavior composition would work fine, and to my eyes most behaviors are “linear”.

What is your evaluation of the proposal?

I think property behaviors are a nice idea. But I feel the current proposal is trying to handle too many disparate use cases using an approach that doesn't work that well for all of them.

I already expressed my view of this in pre-review, and I'm a bit disappointed to see no mention of my suggestions in the "Alternatives considered" section. Basically I think this proposal should be broken in three or four separate smaller features that can each stand on their own:

1. the ability to add `func`, `var`, and `let` inside any property declaration to handle metadata and meta-operations. For instance:

  var count: Int {
    func reset() { count = 0 }
  }

  count::reset() // or some other syntax

  var root: NSObject {
    var lastModified: NSDate? = nil
    didSet { lastModified = NSDate() }
  }

  print(root::lastAccess)

  var lang: String {
    let xmlName = "xml:lang"
  }

  element.addAttribute(name: lang::xmlName, value: lang)

2. the ability to encapsulate those `func` and `var` inside a behaviour for reuse:

  behavior resettable<T>: T {
    func reset() { currentValue = initValue }
  }
  var [resettable] count: Int

  count::reset() // or some other syntax

  behavior changeMonitored<T>: T {
    var lastModified: NSDate? = nil
    didSet { lastModified = NSDate() }
  }
  var [changeMonitored] root: NSObject

  print(root::lastModified)

I think the whole space of out-of-band meta-operations deserves its own separate discussion. In addition to your concerns, exposing metaoperations as interface also introduces bigger concerns about resilience, protocol conformance, etc. that deserve deeper consideration.

3. the ability for the behavior (extending point 2) to change the base storage type of the property:

  behavior synchronized<T>: Synchronized<T> {
    get { return currentValue.value }
    set { currentValue.value = newValue }
  }

  var [synchronized] count: Int

  count = 1

4. a way to define property observers such as `willSet` globally, with no need to attach a particular behavior to a variable:

  observer willSet<T>(newValue: T) {
    set {
      willSet(newValue)
      currentValue = newValue
    }
  }

  var count: Int {
    willSet {
      print("\(count) will become \(newValue)")
    }
  }

I don't have a fully formed proposal, but there's a bit more details in a relatively recent discussion here (I changed the syntax a bit since then):

[swift-evolution] [Proposal] Property behaviors
[swift-evolution] [Proposal] Property behaviors
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007295.htm

As I noted in your pre-review comments, trying to separate storage control from observers doesn't seem like a net simplification to me. If you try to extend either feature on its own, you end up encroaching in the other's space. A behavior that manipulates storage may need per-declaration hooks, which accessor provides. An accessor may need to introduce bookkeeping storage.

Perhaps we could support inferring behaviors from accessor names, so that you can write `var foo: Int { didSet { ... } }` instead of `var [observed] foo: Int { didSet { ... } }`, but that seems like syntactic sugar over the basic functionality.

-Joe

···

On Feb 14, 2016, at 9:16 AM, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

In the last mail a mean, this:
a*lazy.*clear()
b*observable.*addObserver()

I forgot the name of the behavior. ;)
Syntax can be discussed in another thread anyway.

···

__

Just a question, there any plan to discuss some candidates as "default"
behaviour on Swift 3 time-frame? Like 'lazy', 'observable', etc?

And some this "future work" like "Composing behaviors"?

I ask this because, knowing that we can use some of the functionality in
time to propose improvements even before finalizing the version will help
us suggest adjustments and reinforces the idea of a very simple initial
proposal.

Em seg, 15 de fev de 2016 às 21:26, Drew Crawford via swift-evolution < swift-evolution@swift.org> escreveu:

I've been meaning to review this one.

   - What is your evaluation of the proposal?

This is an excellent proposal.

   - Is the problem being addressed significant enough to warrant a
   change to Swift?

Yes. Actually, the "examples" within the proposal itself really only
scratches the surface of the places I want to use this feature.

Swift's reflection system is pretty poor, supporting only read-only
access. If I was writing a library to parse the options to `swiftc` for
example, an application might want to declare a structure like

struct Options: String {
let applicationExtension : Bool
        let assertConfig: Bool
        let embedBitcodeMarker: Bool
        let emitBitcode: Bool
       //etc
}

...and use this application-provided structure as part of the API itself.
With clever use of Mirror, a library can reflect this structure,
re-interpret the names under command-line-case-rules, and generate a parser
that can read "-application-extension" "-assert-config"
"embed-bitcode-marker" and so on.

However, while a library can *understand* this structure, it cannot *set
the values* back on the structure defined by the application. Thus the
application is doomed to work with a parse result object that has poor type
information (such as a stringly-typed result). This basically negates the
benefit of declaring a structure like this to begin with.

After this proposal, I can define a property behavior that relocates the
variable storage to a stringly-typed underlying mechanism. As a result, I
can construct a full-duplex bridge between "stringly-typed" access patterns
and "statically-typed" access patterns, allowing me to alternate between
one and the other as necessary. I can also get "writeable" reflection
behavior.

I think the benefits of such a scheme (particularly as an opt-in
mechanism, so as not to impact performance in general) is pretty endless.
The API that I describe seems "obviously" the optimal API for practically
any parse problem that terminates in a strongly typed structure (which is
the case for most JSON, YAML, XML, CSV, CLI, and RPC-type application-layer
problems), and under this proposal it becomes achievable.

One obvious extension I would like to see is "function behaviors" (a.k.a.
decorators), but that is obviously a story for another proposal. Still, I
would be really interested in studying in that direction in future
proposals.

As others have mentioned, composeability is also interesting, but may not
be entirely necessary for an initial pass at the feature. This seems like
something that should be evaluated in the context of "so now we have two
different libraries that each want their behavior on a property," which is
a circumstance far removed from the present.

   - Does this proposal fit well with the feel and direction of Swift?

As explained above, the proposal can bridge an important problem; namely
allowing dynamic behavior in a static type system. I think that this would
allow clever library authors to design more Swift-like APIs.

• If you have used other languages or libraries with a similar feature,
how do you feel that this proposal compares to those?

I am familiar with function decorators in Python, which are a
generalization of this scheme (they're composeable and they also work for
functions). I miss decorators terribly in Swift. These are not quite as
good, but they're a step.

• How much effort did you put into your review? A glance, a quick reading,
or an in-depth study?

I've tried to get to the meat of the proposal.

I'm deliberately avoiding the syntax bikeshed here–I'm much more
interested in the behavior this proposal unlocks than the color of the
paint. Paint the damn thing any color you like, but the underlying idea
here is a really important step towards more dynamic kinds of runtime
behaviors.

Drew

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

I'm surprised no one has said this yet, but nice work Joe for getting a
partial implementation we can start to play with! :)

I'm sure it will help to better understand the impact of the proposal and
how it relates to our code in a more concrete/practical way.

- *What is your evaluation of the proposal?*+1
- *Is the problem being addressed significant enough to warrant a change to
Swift?*Yes, I'm excited by the potential here, and once the idea has had
some real-world use I would like to see this pattern applied to more things.
- *Does this proposal fit well with the feel and direction of Swift?*Yes.
- *If you have used other languages or libraries with a similar feature,
how do you feel that this proposal compares to those?*C# and Java have some
annotation systems, they are loosely related. I've used them and found they
add a lot of power to those languages, I've not tried to implement them
though so I cannot comment on that.
- *How much effort did you put into your review? A glance, a quick reading,
or an in-depth study?*Only a brief look unfortunately, I've been skimming
the new posts, but other than reading the proposal I haven't had time to
look more thoroughly.

···

On Thu, Feb 18, 2016 at 4:18 AM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

If anyone wants to start playing with the feature, I now have some of the
core functionality working in a branch:

Property behaviors prototype by jckarter · Pull Request #1297 · apple/swift · GitHub

I didn't want to waste time parsing a behavior declaration syntax while
we're still painting the bikeshed, so behaviors are currently exposed as
protocols with extension methods following a convention:

protocol delayedImmutable {
  // Type of the property.
  associatedtype Value
  // Storage required by the property.
  var storage: Value? { get set }
}
extension delayedImmutable {
  // Implementation of the property.
  var value: Value {
    // The property can only be read after it's been initialized.
    get {
      guard let theValue = storage else {
        fatalError("delayedImmutable property read before initialization")
      }
      return theValue
    }

    // The property can only be written once to initialize it.
    set {
      guard storage == nil else {
        fatalError("delayedImmutable property rewritten after
initialization")
      }
      storage = newValue
    }
  }

  // Initialization logic for the property storage.
  static func initStorage() -> Value? {
    return nil
  }
}

Custom accessors and initializer expression bindings aren't handled yet,
but there's enough there now to implement `delayed` initialization. Here's
an example test case:

https://github.com/jckarter/swift/commit/9da36f8e1e45564a61da4cfc9ed5327bf57862df

-Joe

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

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors. What do you all think of this direction?

Seems like a good idea.

It would be great if we could eventually nail down 'recommended semantics' for symbols like @, #, &, etc, and then normalize the language syntax to match. We have a couple of informal conventions emerging, but I find that (e.g.) trying to figure out why Language Feature X has a leading '@' rather than being a keyword is still rather difficult.

Based on review feedback, I've revised the declaration syntax proposal for property behaviors to be more in line with our other declaration forms, reverting to the earlier pre-review "var behavior" proposal. I've updated the proposal in swift-evolution:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md

The updated behavior declaration form is largely an improvement.

With the placeholder for binding the name of the attributed property gone from the grammar, I wonder if you’ve given any thought to how that binding might be done. I recognize that the binding is left for future work, but would hate to see my pet feature painted into a corner.

I find the naked `initialValue` “declaration" quite odd. It’s not exactly a declaration even. What are the implications for parsing? If we’re already willing to special case`initialValue` in the parser in this context, perhaps we could introduce a binding list instead, like:

public var behavior lazy<Value>: Value {
  // Behaviors can bind the property's initializer expression with a
  // binding declaration.
  bind initialValue
  …

Besides eliminating the odd naked “declaration”, this has the added advantage that it could be extended to `bind initialValue, propertyName`. :-)

Also, the initialValue in the “Proposed Solution” section doesn’t match the naked syntax used throughout the Examples section.

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors. What do you all think of this direction?

I think this is an excellent direction. Would we also use @ syntax for accessing property-supplied members, like `a@runcible.x`?

Cheers,

Curt

···

On Feb 18, 2016, at 6:56 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

-----------------------------------------------------------------------------
Curt Clifton, PhD
Software Engineer
The Omni Group
www.curtclifton.net

Based on review feedback, I've revised the declaration syntax proposal for property behaviors to be more in line with our other declaration forms, reverting to the earlier pre-review "var behavior" proposal. I've updated the proposal in swift-evolution:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors. What do you all think of this direction?

I like all of this overall. Discussion in a few parts below.

···

***

I'm fine with the new declaration syntax. I thought the declaration-follows-use syntax was unusually well suited to this feature since it more or less acts as a template for code surrounding a property, but there's nothing wrong with this new alternative.

I think the one part I don't really like is the first three words. It's a little odd having so many lowercase keywords in a row without any punctuation; I'd prefer a syntax with more texture. More worryingly, though, it appears that Apple frameworks have at least four public classes with properties called `behavior`, including `NSPopover`; these could be mistaken for behavior declarations. I see a few potential solutions here:

1. Disambiguate on the absence of punctuation; `var behavior` without a `:` or `=` after it can't be a property, so it must be a variable behavior. This seems mistake-prone, though:
  var behavior NSPopoverBehavior
  
2. Require backticks if you're declaring a property. This is the simplest solution, but kind of ugly.
  var `behavior`: NSPopoverBehavior

3. Reverse the order of the keywords, yielding `behavior var`. This avoids ambiguity, but it doesn't read as well:
  behavior var lazy<Value>: Value

4. Require the usage punctuation, whatever it is, at the declaration site. (I'll assume `@`.) `:` or `=` is a property named "behavior"; `@` is a property behavior; anything else is an error. This also gives the declaration some of that missing texture I was complaining about.
  var behavior @lazy<Value>: Value

(Technically, the `behavior` keyword is redundant in option 4, but I think it's valuable as something you can search for when you see `var @lazy` and have no idea what that means.)

***

One thing I don't like about this new syntax is the standalone `initialValue` keyword (nor the similar `name` keyword in Future Directions, which has a type attached for additional weirdness). I'm wondering if instead, we can have the user declare an accessor named `initialValue`:

  var behavior lazy<Value>: Value {
    accessor initialValue() -> Value
    
    private var value: Value?
    
    mutating get {
      value ??= initialValue() // Borrowing syntax from SE-0024: Optional Value Setter
      return value!
    }
  }

This turns the computed property into a function, but I don't think that's necessarily a bad thing. But if you want it to remain a computed property, I do have another option for you.

Suppose we allow accessors to be either methods *or* readonly computed properties. In the past, we've discussed parameterizing behaviors; this would be a way to do it. For example:

  var behavior json<Value: JSONRepresentable where Self: JSONObject>: Value {
    // Accessor methods now carry an additional `func` keyword:
    accessor func toJSON(value: Value) -> JSONValue {
      return value.json
    }
    accessor func fromJSON(json: JSONValue) -> Value {
      return Value(json: json as! Value.JSONRepresentation)
    }
    
    // And we can now say:
    
    accessor var key: String
    
    // `key` is a readonly computed property which is bound to the expression provided
    // when the behavior is applied to the property.
    //
    // Just as you can provide an accessor method with a default implementation, so you
    // can provide an accessor property with a default value. For instance, if `#name` were
    // the syntax to access the property name, the above could be:
    //
    // accessor var key = #name
    
    get {
      return fromJSON(self.json[key])
    }
    set {
      self.json[key] = toJSON(newValue)
    }
  }
  
  struct Person: JSONObject {
    var json: [String: JSONValue]
    
    // I have two different ideas for the syntax at the application site.
    
    // Option 1:
    @json var identifier: String { key = "id" }
    
    // Option 2:
    @json(key="id") var identifier: String

    // Note: The `=` in Option 2 would become a `:` if Erica Sadun's draft proposal on using
    // colons in attributes is accepted.
  }

(If we take option 2, there are all sorts of things we can consider doing to bring behavior accessor vars in line with the things we currently see in attribute parameters. For instance, we could allow boolean accessor vars to be set by the presence or absence of their name in the list, without a `true` value being included. We might also allow you to give an accessor var an external name of _ to indicate that no label is needed. If we make this sophisticated enough, we might be able to turn many attributes into empty behaviors that the compiler knows are magic. For instance, if we're willing to turn @objc(foo) into @objc("foo"):

  // `Value: @objc` is imaginary syntax for "any Objective-C-compatible type".
  var behavior objc<Value: @objc>: Value {
    accessor var _ objCName: StaticString = #name
    
    var value: Value
    get { return value }
    set { value = newValue }
    
    // The compiler knows about this behavior and looks in its `objCName`
    // when it's writing the Objective-C metadata.
  }

But I'm digressing.)

With the basic accessor var feature in place—no fancy parameter syntax extensions needed—`initialValue` could be declared as an accessor var:

  var behavior lazy<Value>: Value {
    accessor var initialValue: Value
    
    private var value: Value?
    
    mutating get {
      value ??= initialValue()
      return value!
    }
  }
  
  @lazy var foo = Bar()
  // Exactly equivalent to:
  @lazy var foo: Bar {
    initialValue = Bar()
  }
  // Or:
  @lazy(initialValue=Bar()) var foo: Bar

If we take this course, then when the time comes to do names, we could of course use another magic `accessor var`:

  var behavior json<Value: JSONRepresentable where Self: JSONObject>: Value {
    accessor var name: String
    accessor var key: String = name
    …

That seems like it might be a feature worth having.

***

On the `@runcible` syntax, I believe I recommended using it pretty early on, so obviously I approve. :^) The proposal currently still shows the `[runcible]` syntax; I take it that just hasn't been updated?

(I previously had a big section here talking about how to adapt behavior composition to this new syntax, but it grew into a horrendous monster inside an already long email, so I've cut it out to avoid cluttering the review. I'll send it to the list as a new thread.)

***

Incidentally, while I was writing up the JSON example, I thought of another item for the Future Directions list: behavior extensions. It'd be nice if I could do something like this:

  var behavior json<Value where Self: JSONObject>: Value {
    accessor var key: String
    
    accessor func toJSON(value: Value) -> JSONValue
    accessor func fromJSON(json: JSONValue) -> Value

    get {
      return fromJSON(self.json[key])
    }
    set {
      self.jsonObject[key] = toJSON(newValue)
    }
  }

  extension var behavior json where Value: JSONRepresentable {
    accessor func toJSON(value: Value) -> JSONValue {
      return value.json
    }
    accessor func fromJSON(json: JSONValue) -> Value {
      return Value(json: json as! Value.JSONRepresentation)
    }
  }

Behavior extensions would probably only be able to add accessor defaults and *maybe* methods and computed properties. I don't think they could add a setter, stored properties, initializers, or new accessors.

--
Brent Royal-Gordon
Architechies

Hi Joe,

What you have presented below looks good to me. However, I don't see changes to your proposal on GitHub. I'm curious what happens to accessors.

Cheers,
-Patrick

···

On Feb 18, 2016, at 9:56 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Based on review feedback, I've revised the declaration syntax proposal for property behaviors to be more in line with our other declaration forms, reverting to the earlier pre-review "var behavior" proposal. I've updated the proposal in swift-evolution:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors. What do you all think of this direction?

-Joe

On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0030 "Property Behaviors" begins now and runs through February, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-proposal.md&gt;
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md
Reply text

Other replies
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

Doug Gregor

Review Manager

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

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

Sorry, that should have read, "hopefully many kinds of user-defined *attributes*".

-Joe

···

On Feb 18, 2016, at 6:56 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

Based on review feedback, I've revised the declaration syntax proposal for property behaviors to be more in line with our other declaration forms, reverting to the earlier pre-review "var behavior" proposal. I've updated the proposal in swift-evolution:

https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors

Are they though? We’ve been discussing this a lot in the thread on enforcing super method requirements; while my preference in that case is for an attribute, I feel it is appropriate there because they’re more like directives and informative, and will generate warnings (and possibly errors too) in the same way as @warn_unused_result does. A lot of other attributes are either informational or are produce some kind of restriction that the compiler warns us about (or errors on).

However, property behaviours are more like defining protocols for properties, which is clearly very different from an attribute, and IMO not really compiler magic either. I think a keyword makes most sense, such as behaviour(lazy), though personally I don’t see why we can’t just include the names directly, i.e:

  var lazy x = 111

Isn’t ambiguous, because the right most token (x) is used as the name, var is an explicitly defined keyword, therefore everything else (lazy) can be looked up as a behaviour, in much the same way as types would be. I know there’s some risk of naming collision, but I don’t think it’s all that high, and if property behaviours are functional enough there should be no reason to add any new keywords for use on properties in future as they will be added as behaviours in the standard library. So long as they’re a way to disambiguate your property behaviours from any in the standard library (which will be needed anyway) then I think it’s fine, even when you’re dealing with stuff like:

  static final internal var lazy synchronized x = 111

If we require the order to be:

  [static, final, private|internal|public] [var|let] [behaviours…] [name]

Then I don’t think there should be any real problems. This may even already be how it is, I’m not sure as this is how I structure them anyway.

···

On 19 Feb 2016, at 02:56, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

In discussion with the core team, we've also strongly come in favor of applying behaviors to properties using attribute syntax, e.g.:

@lazy var x = 111
@delayed var x: Int

They're definitely attribute-like, and we think it makes sense for behaviors to be the first of hopefully many kinds of user-defined behaviors. What do you all think of this direction?