I'm having trouble wrapping my head around the difference between accessors and methods defined by a property: AFAIU accessors are like abstract methods if required and just like methods if not, whereas methods are like final methods as they cannot be overridden in a property use site.
If I did understand this correctly then I'd prefer that instead of "accessor" we would just use "func" and introduce a new keyword "abstract". The latter can then be extended to classes in another proposal.
-Thorsten
···
Am 19.02.2016 um 03:56 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org>:
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:
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> wrote:
Hello Swift community,
The review of SE-0030 "Property Behaviors" begins now and runs through February, 2016. The proposal is available here:
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
I personally like the functionality outlined in this proposal. Also *tip of
my hat* to those that have put together this proposal and prototype
implementation.
The biggest issue seems to be the syntax for it and it is becoming hard to
follow the discussion thread given all the – often nicely – outlined
alternatives.
I guess it isn't clear what of the alternatives are seriously being
considered, etc. ... as well as the state of the proposal potentially
incorporating these alternates.
If property behaviors are likely to be user definable in addition to a
canned set of behaviors provided by the standard library then I feel we
should attempt to avoid yet another methodology of name-spacing /
extending. I feel we should attempt to leverage existing language
methodologies as possible (for example the suggestion around consider
behaviors as an extension to var and let, etc.).
I don't have anything really to propose along those lines (still trying to
get a lot worked out in my head) but I wanted to express my first gut
feeling on this.
-Shawn
···
On Mon, Feb 15, 2016 at 2:38 PM Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:
Hi everyone. In response to feedback, I've extended the review period for
SE-0030 for another week. I've also decided to subset out *member access*
from the initial proposal, as in accessing `x.[lazy].clear()` or other
behavior-provided methods on a property. There's a lot of contention about
the syntax, of course, but Michel Fortin and private reviewers have made
good points that tying these "out-of-band" non-type members to properties
is a feature that may not be best expressed as part of behaviors. Like many
of the other topics that were factored out, the design space is deep enough
that out-of-band member access deserves a design and review of its own.
Thank you all for the feedback so far!
-Joe
On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution < > 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:
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:
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
I think this is a good direction, however I'm concerned about possible
namespace conflicts here. Does user code break if someone defines an
attribute which becomes part of the standard library in a future Swift
version? Can multiple modules declare attributes with the same name? Maybe
they can be qualified as "@MyModule.lazy var x" — although this would also
conflict with syntax like "x.@lazy.foo" (is this a "lazy" attribute or a
"foo" attribute in a "lazy" module?).
Jacob
···
On Thu, 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:
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?
The only part of the existing proposal that my brainstorm last night
actually changed (as opposed to extended) was the need for behavior-added
methods on properties and a syntax for calling them (the foo.[lazy].clear()
case). This also seems to be one of the more contentious parts in the
discussion here, and appears to be non-critical in the use-cases listed in
the proposal. So +1 to the proposal itself, -1 to the ability to add
methods to properties and call them, with the latter perhaps separated out
into a future extension. +1 for keeping the underbar in the behavior
definition syntax, or even explicitly binding it to a string literal; it
seems quite likely this will be useful.
···
On Thu, Feb 11, 2016 at 11:05 AM, Douglas Gregor <dgregor@apple.com> wrote:
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> wrote:
Hello Swift community,
The review of SE-0030 "Property Behaviors" begins now and runs through
February, 2016. The proposal is available here:
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:
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]
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.
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@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@swift.org> wrote:
On Feb 10, 2016, at 2:00 PM, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:
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:
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:
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. :-)
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.
WFM.
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.
Ok, I’m fine with starting small and generalizing over time.
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.
Agreed. Where do you stand on taking behavior as a keyword? Do you prefer something like the example above, or something like what you wrote in your proposal?
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()`
Makes sense!
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.
Ok, that semantic makes sense to me. Given that, a behavior can emulate any of the other semantics it wants.
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.
Ok. That means that we can’t actually cut @NSCopying over to it yet, but I’m fine with tackling that later.
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 {
Ok, great.
I’m very curious where you stand on the syntax of the behavior decl, because this decl (as one concrete example) seems like it would be *much* nicer as:
public var behavior changeObserved<Value : Equatable> {
Can property requirements in protocols have behaviors on them?
I think not.
Ok, please mention that, thanks!
-Chris
···
On Feb 11, 2016, at 10:30 AM, Joe Groff <jgroff@apple.com> wrote:
Where [lazy] is currently used, could the syntax instead be #behavior(lazy)? That prevents a possible future naming clash, keeps the #
meaning compiler-magic, and doesn't use the , which is contentious.
···
On Thu, Feb 11, 2016 at 3:34 PM, Jim Hillhouse via swift-evolution < swift-evolution@swift.org> wrote:
> 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()
-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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution
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.
I don’t think it is more complex to distinguish between subscript and behavior access (behavior as a leading dot), than between attribute or « macro » and behavior access (if we choose to use @ or # for behavior).
That said, I’m not found either of the bracket proposal, but don’t have anything to propose to replace it though.
···
Le 13 févr. 2016 à 21:23, Jérôme Duquennoy via swift-evolution <swift-evolution@swift.org> a écrit :
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 <mailto: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.
• Angle brackets already appear in var declarations for generic types; that means there are two different meanings for angle brackets in the same construct.
• When we eventually allow you to apply multiple behaviors to a single property, that will also mean that *commas* are used in both of those angle bracket constructs, but with different meanings (one means "chain these", the other means "fill these two slots").
These are not fatal errors, but they're also completely unforced—either `` or `{}` would not have this problem. So why choose the option that has the problem?
• Angle brackets already appear in var declarations for generic types; that means there are two different meanings for angle brackets in the same construct.
Isn't the same true when you declare an array (or a closure)?
• When we eventually allow you to apply multiple behaviors to a single property, that will also mean that *commas* are used in both of those angle bracket constructs, but with different meanings (one means "chain these", the other means "fill these two slots").
True, but imho also no deal breaker; I would like to have labeled generic parameters anyway ;-)
Afaics, it isn't decided how multiple behaviors will be handled yet.
These are not fatal errors, but they're also completely unforced—either `` or `{}` would not have this problem. So why choose the option that has the problem?
Because those have a similar (and imho bigger) problems:
Grouping blocks of code or declaring arrays are things that are tightly coupled with "their" braces, and for me, "<>" causes the smallest disruption… arrays, capture lists and blocks have little in common with behaviors, but property behaviors could be expressed using generics*
Tino
* thinking of something like
protocol Behavior {
typealias ValueType
var value: ValueType { get set }
}
class DefaultBehavior<T> {
typealias ValueType = T
var value: ValueType
init(value: ValueType) {
self.value = value
}
}
class Sample {
var<DefaultBehavior> content: Int?
}
Here, "var" would create a container whose behavior is determined by a generic parameter; access of "content" would be mapped to that container.
I know behaviors are supposed to work slightly different, but that doesn't destroy the mental model of a parameterized container.
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.
I think you’re half right about behaviors; there’s certainly a core group that’s going to be the most common.
The other use for behaviors is as something like a weak macro, e.g. just for views/view-controllers/etc., where there are a lot of minor things you can streamline via behaviors:
…and so on (I brought some of these up in the earlier discussions, just listing them again as examples).
Composability here would be nice, although not entirely necessary (e.g. using custom accessors affords a lot of room for consolidation).
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)
I think you can draw a pretty nice distinction if you want; suppose that a decorator:
- *would* be able to access its container (like a behavior)
- *would not* have to deal with property storage (that’s for behaviors only)
- *perhaps* has maps sending accessor closures to closures, e.g.
- get-decorator: (() -> T) -> (() -> T)
- set-decorator: ((T) -> ()) -> ((T) -> ())
…which isn’t a real proposal (and e.g. doesn’t address a few major issues , like “how do we handle custom accessors (e.g. from behaviors)”), but illustrates one way one *could* make such a distinction.
···
On Feb 14, 2016, at 4:15 PM, Tino Heth <2th@gmx.de> wrote:
Do we need braces and commas at all? It seems to me that you can just do:
var lazy synchronized foo: Dictionary<Int, Set<Optional<String>>>
The name is always the one right before the colon, so this actually seems fine to me. If Xcode can colour them differently then it shouldn’t be hard to follow IMO. The order implies the order in which they are resolved where relevant, i.e- foo is mutable, then lazy, then synchronised.
···
On 14 Feb 2016, at 11:08, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
• Angle brackets already appear in var declarations for generic types; that means there are two different meanings for angle brackets in the same construct.
• When we eventually allow you to apply multiple behaviors to a single property, that will also mean that *commas* are used in both of those angle bracket constructs, but with different meanings (one means "chain these", the other means "fill these two slots").
These are not fatal errors, but they're also completely unforced—either `` or `{}` would not have this problem. So why choose the option that has the problem?
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”.
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`.
I see what you mean, but I’m curious:
Is there some fundamental implementation challenge here (e.g. `didSet` seeing an `oldValue` that might be initialized, unexpectedly-nil, or similar…)?
Or it this more of a “there’s too much room for confusion here”-style concern?
···
On Feb 15, 2016, at 4:45 PM, Joe Groff <jgroff@apple.com> wrote:
On Feb 14, 2016, at 12:02 PM, plx via swift-evolution <swift-evolution@swift.org <mailto: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. ;)
Maybe that is a bad prognosis for this feature. The more the discussion progresses, the complex it becomes and the worse the syntax becomes.
Myles
···
On Feb 15, 2016, at 8:04 PM, Wallacy via swift-evolution <swift-evolution@swift.org> wrote:
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.
On Feb 18, 2016, at 8:03 PM, Austin Zheng 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?
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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution
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.
I think we're doing fine on that, actually:
* & marks unusual parameter passing semantics.
* @ marks attributes, and should only be attached to declarations. (And perhaps also used when accessing data associated with an attribute, like `foo.bar@resettable.reset()`.)
* # is for two things: either expressions with a certain amount of compiler magic involved, or commands that affect large-scale code structure.
Personally, I think of # as the prefix on all Swift 4 macros and @ as the prefix on all Swift N behaviors. The full features aren't here yet, but we've brought some specific use cases back from the future.
Am 19.02.2016 um 06:01 schrieb Curt Clifton via swift-evolution <swift-evolution@swift.org>:
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`?
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:
The updated behavior declaration form is largely an improvement.
Yes, I prefer it as well.
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
This might be an editing problem with th examples. In the Proposed Solution the syntax is
public var behavior lazy<Value> = initialValue { ... }
which makes sense to me (though it is missing the type declaration (: Value) here.
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`. :-)
Couldn't we just add a name by introducing an accessor to be implemented by the user of a property?
Alternatively the syntax could be extended to
public var behavior(name: String) lazy<Value>...
but I don't think that is necessary.
-Thorsten
···
Am 19.02.2016 um 06:01 schrieb Curt Clifton via swift-evolution <swift-evolution@swift.org>:
On Feb 18, 2016, at 6:56 PM, 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?
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).
I think Haravikk makes a good point here. Currently, the `@` symbol seems like a holding ground for miscellaneous compiler directives and backwards compatibility issues (e.g., @objc, @IBOutlet). So from this view, putting behaviors, which are a forward looking, swift native idea, under the ‘@‘ symbol, seems debatable.
The syntax Haravikk proposes below makes sense to me. Does it cause big problems from a compiler implementer’s standpoint?
Matt
···
On Feb 19, 2016, at 06:14, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:
On 19 Feb 2016, at 02:56, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
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
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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution
Yeah, I'm not happy with it either. An alternative might be to bless a specifically-named `accessor` declaration, e.g.:
var behavior lazy<Value>: Value {
accessor initialValue() -> Value
}
-Joe
···
On Feb 18, 2016, at 9:01 PM, Curt Clifton <curt@curtclifton.net> wrote:
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`. :-)