On Sat, Jan 14, 2017 at 9:36 AM, Joseph Newton <jnewt...@gmail.com> wrote:
> Thank you for your responses Xiaodi and David, > > I believe you can do this using didSet without the `_bar`: >> class Foo { >> public var bar: String? { > > didSet { >> if (bar?.characters.count ?? 0) < 100 { >> bar = nil >> } >> } >> } >> } > > > Yes, although this is a perfectly valid way of implementing this, I'm not > a big fan of implementing this functionality in this way. Particularly, I > don't like the concept of validating data after setting it, and then > re-setting the property if the validation failed. I would much rather > implement it in a manner where it's conditionally set depending on > validation. > > It's an interesting idea, for sure. Because of the persistent storage >> required for `_bar`, I *think* one way to look at what you're proposing >> is essentially as a way to allow for a variable to be declared publicly >> as one type, be implemented as another (anonymous, in this case) type under >> the hood, and to specify a mechanism for automatically converting between >> the two types. > > > Essentially, yes. The programmer would have the option of using these > nested properties to be able to add custom storage or functionality to any > computed property that they please. > > (That said, there is a recurrent theme on this list where people ask for >> new support for encapsulating members from visibility in ever smaller >> scopes. I continue to be not in favor of the new `private`, but even >> allowing its existence, providing all the permutations of >> type/file/extension visibility is (afaict) and should continue to be (imo) >> a non-goal. The distinction between public and internal not only avoids >> pollution in your autocomplete list but also provides important safety >> wins, and I think those gains are increasingly limited the finer we dice >> these access levels.) > > > I'm not sure that I quite understand what you mean by this. Would you mind > elaborating? > See below. > I get that your proposal allows you to write one fewer declaration in >> the case of an ad-hoc behavior than SE-0030 would. That is, in the case of >> SE-0030, you'd have to declare both a behavior and a member that uses it; >> in your case, you could declare just the member and implement the behavior >> there. However, I think I'd want to see some concrete use cases that are >> better served by this proposal than by SE-0030, which is the more general >> solution as far as I can tell. In the two use cases you've mentioned, one >> is served by `didSet`, and the other (`synchronized`) is a reusable >> behavior for which SE-0030 offers the more elegant solution. > > > Here are some additional uses cases for this proposal: > > *Implementing Objective-C's "__null_resettable"* > > class Foo { > var bar: UIColor! { > var _bar: UIColor = .clear > > get { _return _bar } > set { _bar = newValue ?? .clear } > } > } > In this use case, your proposal differs from what's currently possible only in that `_bar` (presumably, after you write `private` in front of it, for consistency with current rules about default access levels) would be invisible even inside `Foo`. As I wrote to you above, avoiding "pollution" within ever smaller scopes [in this case, the scope inside `Foo`] is (AFAICT) and should be (IMO) a non-goal. To convince me that this use case is worth the engineering effort and complexity of a new syntax, you'd also have to convince me that having `_bar` visible within the entire scope of `Foo` has practical and not merely theoretical footgun potential or some other negative impact beyond the fact that autocomplete will show a member you'd rather not see. _Even if_ I were to buy that argument, I think there is a more general solution that covers your use case. A few months earlier on this list, several people discussed the possibility of same-module extensions allowing stored properties, to very positive reception. There are numerous use cases for that feature, but such a feature would also allow you to isolate `_bar` and `bar` in its own extension, a common Swift idiom. It would also allow you to group this implementation in the same extension with arbitrary functions that need to see `_bar` (such as your `init(_:)` example below) without exposing `_bar` to other initializer and methods that don't need to manipulate it. This would be both more flexible and more fine-grained than possible with your own proposal, and if I were to buy your argument that `_bar` being visible where it's not needed is suboptimal, this would be the optimal solution. *Implementing a stack interface* > > class Foo<T> { > var currentItem: T? { > var storage = [T]() > > get { return storage.last } > set { > if let newValue = newValue { > storage.append(newValue) > } > else { > storage.removeLast() > } > } > } > } > I'm not sure I understand why this requires your proposed feature, other than again that it hides `storage` from the rest of `Foo<T>`. > *Validation before changing (for performance)* > > class SessionManager { > var session: Session { > var _session: Session > > get { return _session } > set { > if newValue != _session { > // expensive teardown code for the old session > > _session = newValue > > // expensive setup code for the new session > } > } > } > } > This can be implemented using `willSet` and `didSet` (taking your example at face value, where it's not the actual swapping of newValue and _session that is expensive but rather the teardown and setup, assuming `==` and `!=` are implemented correctly, i.e. in such a way that `newValue == _session` implies that the two fully instantiated values really are interchangeable in every way that matters). Lastly, I've been doing some additional thinking and I think that it would > be beneficial to also setting values for these nested properties inside of > *init* methods. For example, I work with C APIs a great deal and create > Swift/ObjC wrappers for them a lot, I have a case where I provide an > implementation as follows: > > class Wrapper { > private var _unmanaged: UnsafePointer<UInt8> > var text: String { > return String(cString: _unmanaged) > } > > fileprivate init(_ unmanaged: UnsafePointer<UInt8>) { > _unamanged = unmanaged > } > } > > It's implemented in this manner because the C APIs may change the > characters pointed to by *_unmanaged* before the *text* property is > accessed. With this proposal, this code could be implemented as follows: > > class Wrapper { > var text: String { > private var unmanaged: UnsafePointer<UInt8> > > get { > return String(cString: unmanaged) > } > } > > fileprivate init(_ unmanaged: UnsafePointer<UInt8>) { > self.text.unmanaged = unmanaged > } > } > Unless I'm mistaken, this addendum to your idea can't be reconciled with access control rules. A `private` var is hidden from _all_ methods not in scope. One shouldn't (and probably can't, from an implementation perspective) start making rules saying that certain `private` vars are visible to initializers. Using your proposed feature, you'd have to make `unmanaged` in this case `fileprivate` and then implement the rest of `Wrapper` in an extension in a different file if you want to hide `unmanaged` from those methods but leave it visible to your initializer. By contrast, if you left `unmanaged` as a private var directly inside `Wrapper`, you could hide `unmanaged` from any methods implemented in an extension without having to move that extension to a different file, probably a cleaner result if this kind of fine-grained hiding from autocomplete is what you're after. On Fri, Jan 13, 2017 at 4:01 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: > >> (Forward to the list if your early replied was intended to be sent there.) >> >> >> On Fri, Jan 13, 2017 at 1:58 PM, Joseph Newton <jnewt...@gmail.com> >> wrote: >> >>> I believe that you're referring to SE-0030 Property Behaviors >>> <https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md>, >>> correct? >>> >>> I have indeed read over this proposal (I probably should have mentioned >>> that in my first message) but I don't believe that it quite covers what I'm >>> going for (or over-covers it). >>> >> >> The reason I mentioned it was because the use case sounded vaguely >> familiar. Reading over SE-0030, you'll see that one specific example is >> synchronized property access. It demonstrates how SE-0030 would enable a >> behavior called `synchronized` that would allow you to write, for any >> arbitrary member foo, `public [synchronized] var foo: String`. It would >> make it more than just easy to implement such a behavior for any member: >> you would not have to write anything but `[synchronized]`. >> >> As to your other use case, the case of validating a set value, David >> Sweeris has pointed out that `didSet` currently enables that functionality >> pretty well. >> >> >>> SE-0030 proposes making property behaviors requiring a specific >>> declaration and implementation of a particular behavior just to be able to >>> add stored properties to computed properties. Additionally, if you look at >>> the "Properties and Methods in Behaviors" section of the proposal, you'll >>> see that any stored property declared inside of a behavior is given the >>> same access level as the property that is using the behavior. Although this >>> could be convenient in some circumstances, my proposal calls for these >>> stored properties to be private to the scope of the property declaration so >>> as to avoid namespace pollution. >>> >> >> I think you misunderstand that text, or maybe I do. It says that the >> properties and methods are expanded into the containing scope, and that >> they must be of _types_ that are visible at the call site. However, the >> examples given for reimplementing lazy show very clearly that the >> properties and methods can be declared private: >> >> ``` >> public var behavior lazy<Value>: Value { >> private var value: Value? >> // ... >> } >> ``` >> >> SE-0030 was written before `fileprivate` and `private` were separated; if >> it were implemented today, it would make sense that `private` would cause >> `value` to be invisible outside the behavior. Even at the time, a `private` >> underlying `value` shouldn't be (unless I'm mistaken) visible outside the >> file. >> >> (That said, there is a recurrent theme on this list where people ask for >> new support for encapsulating members from visibility in ever smaller >> scopes. I continue to be not in favor of the new `private`, but even >> allowing its existence, providing all the permutations of >> type/file/extension visibility is (afaict) and should continue to be (imo) >> a non-goal. The distinction between public and internal not only avoids >> pollution in your autocomplete list but also provides important safety >> wins, and I think those gains are increasingly limited the finer we dice >> these access levels.) >> >> My proposal makes this feature (stored properties inside of computed >>> properties) significantly easier to implement. For singular or unique cases >>> of needing this functionality, in lieu of having to learn "Property >>> Behaviors," its syntax and how to effectively write them, one would only >>> need to add their properties to their computed properties' declaration >>> block. >>> >>> Thoughts? >>> >> >> I get that your proposal allows you to write one fewer declaration in the >> case of an ad-hoc behavior than SE-0030 would. That is, in the case of >> SE-0030, you'd have to declare both a behavior and a member that uses it; >> in your case, you could declare just the member and implement the behavior >> there. However, I think I'd want to see some concrete use cases that are >> better served by this proposal than by SE-0030, which is the more general >> solution as far as I can tell. In the two use cases you've mentioned, one >> is served by `didSet`, and the other (`synchronized`) is a reusable >> behavior for which SE-0030 offers the more elegant solution. >> >> >> >>> On Fri, Jan 13, 2017 at 12:34 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: >>> >>>> There was a proposal for custom behaviors using `@`, which if I recall >>>> would offer encapsulation along the lines of what you're proposing. It was >>>> an extensively designed system which was deferred for consideration, but >>>> which would be in scope again in phase 2. The proposal is in the >>>> swift-evolution repository--does that address your use case? >>>> On Fri, Jan 13, 2017 at 11:27 Joseph Newton via swift-evolution < >>>> swift-evolution@swift.org> wrote: >>>> >>>>> Hi, >>>>> >>>>> I've done a lot of Objective-C and have been been a fan of Swift since >>>>> it's come out, however, there's a small feature of Objective-C that I >>>>> would >>>>> really like to see implemented in Swift: >>>>> >>>>> In Objective-C you have a few options implementation-wise when >>>>> creating properties for your class. You could use the *@synthesize >>>>> *statement >>>>> to have the compiler create the backing ivar along with the appropriate >>>>> accessor methods or your could use the *@dynamic* statement to tell >>>>> the compiler that you're going to create your own accessor methods and >>>>> backing ivar (if needed). >>>>> >>>>> Additionally, one could use the *@synthesize* statement to have the >>>>> compiler create the backing ivar, and then they could create custom >>>>> accessor methods to supply custom functionality or validation. I use this >>>>> third case extensively in my Objecitve-C code but there's not a concise >>>>> way >>>>> of doing this in Swift. >>>>> >>>>> For example, I might have an Objective-C implementation that looks >>>>> like this: >>>>> >>>>> @interface Foo : NSObject >>>>> @property (nullable, copy) NSString *bar; >>>>> @end >>>>> >>>>> @implementation Foo >>>>> @synthesize bar = _bar; // Creates ivar '_bar' >>>>> >>>>> - (void)setBar:(NSString *)bar { >>>>> if (bar.length < 100) >>>>> _bar = nil; >>>>> else >>>>> _bar = [bar copy]; >>>>> } >>>>> >>>>> @end >>>>> >>>>> Currently, the only way to implement this in Swift - AFAIK - is as >>>>> follows: >>>>> >>>>> class Foo { >>>>> private var _bar: String? >>>>> public var bar: String? { >>>>> get { return _bar } >>>>> set { >>>>> if (newValue?.characters.count ?? 0) < 100 { >>>>> _bar = nil >>>>> } >>>>> else { >>>>> _bar = newValue.copy() as! String >>>>> } >>>>> } >>>>> } >>>>> } >>>>> >>>>> Although this works, it isn't exactly glamorous. The main drawback of >>>>> this implementation (unless intended) is that you now have any additional >>>>> '_bar' variable accessible within the scope of your class. >>>>> >>>>> My proposal is to allow stored properties in the declaration block of >>>>> computed properties. For this example, the '_bar' declaration would simply >>>>> be moved inside of the declaration block for 'bar': >>>>> >>>>> class Foo { >>>>> public var bar: String? { >>>>> var _bar: String? >>>>> >>>>> get { return _bar } >>>>> set { >>>>> if (newValue?.characters.count ?? 0) < 100 { >>>>> _bar = nil >>>>> } >>>>> else { >>>>> _bar = newValue.copy() as! String >>>>> } >>>>> } >>>>> } >>>>> } >>>>> >>>>> Only the getter and setter methods of 'bar' are allowed to access and >>>>> modify the stored '_bar' property. My proposal would also allow for >>>>> multiple stored properties of varying types within the scope of a single >>>>> computed property. This would also simply atomic synchronization for >>>>> single >>>>> variables: >>>>> >>>>> class Foo { >>>>> static var synchronizedBar: String? { >>>>> var queue = DispatchQueue(label: "Foo.synchronizedBar") >>>>> var bar: String? >>>>> >>>>> get { return queue.sync { return bar } } >>>>> set { queue.sync { bar = newValue } } >>>>> } >>>>> } >>>>> >>>>> Are there any suggestions or arguments, for or against, for this >>>>> proposal? >>>>> >>>>> -- Joe Newton >>>>> _______________________________________________ >>>>> swift-evolution mailing list >>>>> 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