[swift-evolution] [Review] SE-0030 Property Behaviors

Ilya Belenkiy ilya.belenkiy at gmail.com
Thu Feb 11 06:26:15 CST 2016


I haven't looked at the proposal extensively, but I like the overall
direction. One thought about syntax. If it goes through, I hope that it
does not use the #. I like how it's similar to the closure capture list
now, and it doesn't seem to belong to a macro system. To me, it's in the
same category as the capture list.
On Thu, Feb 11, 2016 at 6:15 AM Taras Zakharko via swift-evolution <
swift-evolution at swift.org> wrote:

>
>    - 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 at swift.org> wrote:
>
> On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Proposal link:
>
>
> https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md
>
> <https://github.com/apple/swift-evolution#what-goes-into-a-review-1>What
> 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160211/b614ff44/attachment.html>


More information about the swift-evolution mailing list