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?
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 }
}
}
*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()
}
}
}
}
*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
}
}
}
}
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
}
}
···
On Fri, Jan 13, 2017 at 4:01 PM, Xiaodi Wu <xiaodi.wu@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 <jnewto32@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.wu@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