[Draft proposal] Extending read-only properties with setters


(Jarod Long) #1

Swift extensions are fantastic for filling in gaps in an API, but they don't currently allow you to convert a read-only property into a read-write property by extending it with your own setter. I'd like to propose that an extension may contain a set-only computed property iff the extended type already has a getter for that property.

My specific motivation for this proposal stems from CGRect's translated API. If you frequently work with rects, it's very convenient to be able to access their x/y/width/height properties directly. It's easy to extend CGRect with x/y properties, but width and height are problematic because of the CGRectGetWidth and CGRectGetHeight functions that get imported as read-only width and height properties on CGRect.

If you extend CGRect with read-write width/height properties, they'll work within the module they're defined because the getters shadow the imported Objective-C versions, but using the getters in other modules becomes impossible due to ambiguity errors. If it was possible to only define the width/height setters, the ambiguity wouldn't exist.

This particular issue is pretty narrow, but it's moderately frustrating if you write a lot of layout code or do other work with rects, and I think this is just one of many situations where adding a setter to a property would be useful.

This proposal is somewhat related to the "support for pure setters" discussion, but since it doesn't involve the tradeoffs of introducing actual set-only properties, I feel that this warrants its own separate discussion.

Looking forward to your input!

Jarod


(Jordan Rose) #2

This violates the "what if two people did this?" rule; while you might be able to disambiguate between two modules that define the same method or property, it seems really hard to pick a particular module's setter.

I think it would be better to just request that the width and height fields get setters, since they're already being defined in Swift <https://github.com/apple/swift/blob/master/stdlib/public/SDK/CoreGraphics/CoreGraphics.swift#L172>. If you want to attack the general problem, I think it would be better to come up with ways to disambiguate overloads that differ only by module.

Best,
Jordan

···

On Feb 3, 2016, at 14:30, Jarod Long via swift-evolution <swift-evolution@swift.org> wrote:

Swift extensions are fantastic for filling in gaps in an API, but they don't currently allow you to convert a read-only property into a read-write property by extending it with your own setter. I'd like to propose that an extension may contain a set-only computed property iff the extended type already has a getter for that property.

My specific motivation for this proposal stems from CGRect's translated API. If you frequently work with rects, it's very convenient to be able to access their x/y/width/height properties directly. It's easy to extend CGRect with x/y properties, but width and height are problematic because of the CGRectGetWidth and CGRectGetHeight functions that get imported as read-only width and height properties on CGRect.

If you extend CGRect with read-write width/height properties, they'll work within the module they're defined because the getters shadow the imported Objective-C versions, but using the getters in other modules becomes impossible due to ambiguity errors. If it was possible to only define the width/height setters, the ambiguity wouldn't exist.

This particular issue is pretty narrow, but it's moderately frustrating if you write a lot of layout code or do other work with rects, and I think this is just one of many situations where adding a setter to a property would be useful.

This proposal is somewhat related to the "support for pure setters" discussion, but since it doesn't involve the tradeoffs of introducing actual set-only properties, I feel that this warrants its own separate discussion.

Looking forward to your input!

Jarod

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


(Jarod Long) #3

This violates the "what if two people did this?" rule; while you might be able to disambiguate between two modules that define the same method or property, it seems really hard to pick a particular module's setter.

I think I don't have a complete understanding of how disambiguation works, because it's not quite clear to me how disambiguating setters is harder than methods or properties. As far as I understand it, to disambiguate a symbol, you prefix it with the module name. I'm actually not sure how to disambiguate anything that isn't directly accessible in the current scope (where does the module name go?), so getters, setters, and methods seem equally difficult to disambiguate.

Apologies if I'm overlooking something here.

I think it would be better to just request that the width and height fields get setters, since they're already being defined in Swift <https://github.com/apple/swift/blob/master/stdlib/public/SDK/CoreGraphics/CoreGraphics.swift#L172>. If you want to attack the general problem, I think it would be better to come up with ways to disambiguate overloads that differ only by module.

Interesting -- thank you for the link! I was originally going to suggest a change in the Objective-C API translation thread, but I had assumed that the width and height properties came from a generic rule that automatically translated the CGRectGet___ functions and that making explicit translation rules for CGRect would be undesirable.

I would like to solve the general problem if there's a reasonable way to do so, since I do think there are applications outside of these specific properties. To use another CGRect example, you may want the midX/Y properties (among others) to be settable, but introducing built-in setters for all of these kinds of properties is likely not something we would want to do.

If it turns out that this isn't a feasible thing to do, then I think building these particular setters into Swift is the next best thing, but I'd like to explore the possibilities here for a bit before trying to go that route.

···

On Feb 3, 2016, at 15:23, Jordan Rose <jordan_rose@apple.com> wrote:

Best,
Jordan

On Feb 3, 2016, at 14:30, Jarod Long via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Swift extensions are fantastic for filling in gaps in an API, but they don't currently allow you to convert a read-only property into a read-write property by extending it with your own setter. I'd like to propose that an extension may contain a set-only computed property iff the extended type already has a getter for that property.

My specific motivation for this proposal stems from CGRect's translated API. If you frequently work with rects, it's very convenient to be able to access their x/y/width/height properties directly. It's easy to extend CGRect with x/y properties, but width and height are problematic because of the CGRectGetWidth and CGRectGetHeight functions that get imported as read-only width and height properties on CGRect.

If you extend CGRect with read-write width/height properties, they'll work within the module they're defined because the getters shadow the imported Objective-C versions, but using the getters in other modules becomes impossible due to ambiguity errors. If it was possible to only define the width/height setters, the ambiguity wouldn't exist.

This particular issue is pretty narrow, but it's moderately frustrating if you write a lot of layout code or do other work with rects, and I think this is just one of many situations where adding a setter to a property would be useful.

This proposal is somewhat related to the "support for pure setters" discussion, but since it doesn't involve the tradeoffs of introducing actual set-only properties, I feel that this warrants its own separate discussion.

Looking forward to your input!

Jarod

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


(Marc Knaup) #4

We ran into that annoying limitation too and worked around it by importing
the module's .swift file containing the extension directly into each module
relying on it. It's not pretty but it works.
I reported it at https://bugs.swift.org/browse/SR-4017 but now when
searching through the evolution archives I found this nice thread which
totally reflects our issue.

I'd still like to propose support for declaring only setters for a property!
It's valid to do in extensions to add a setter for a read-only property in
the type to be extended:

extension CGRect {

  public var height: CGFloat {
    mutating set { size.height = newValue }
  }

  public var width: CGFloat {
    mutating set { size.width = newValue }
  }
}

@Jordan Rose I understand your concern regarding introducing new
ambiguities. In this case however there's no difference from the ambiguous
getter problem we already have today or the ambiguous symbol errors you can
get from using *any* extension symbol which collides with another module's
extension. Resolving ambiguities is a distinct problem to be solved.

To increase the value of setter-only declarations I'd go even a step
further:
Setter-only declarations are also allowed in subclasses to override only
the setter of a property without having to override the getter just to call
super's implementation. The same applies to protocol conformances where the
getter would already be covered by a default implementation so the
conforming type just has to provide a setter.
Also this is then completely analogous to overriding either just getXYZ()
or setXYZ() functions as developers are used from most other languages,
including ObjC.

It may happen that developers who are new to Swift try to declare just a
setter for a *new* property in order to execute special logic there.
In that case the compiler would emit an error for not providing a getter
can offer three quick-fixes:

   - replace "(mutating) set" with "didSet"
   - replace "(mutating) set" with "willSet"
   - add "get {}" to provide a getter

···

On Thu, Feb 4, 2016 at 1:40 AM Jarod Long via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 3, 2016, at 15:23, Jordan Rose <jordan_rose@apple.com> wrote:

This violates the "what if two people did this?" rule; while you might be
able to disambiguate between two modules that define the same method or
property, it seems *really hard* to pick a particular module's *setter.*

I think I don't have a complete understanding of how disambiguation works,
because it's not quite clear to me how disambiguating setters is harder
than methods or properties. As far as I understand it, to disambiguate a
symbol, you prefix it with the module name. I'm actually not sure how to
disambiguate anything that isn't directly accessible in the current scope
(where does the module name go?), so getters, setters, and methods seem
equally difficult to disambiguate.

Apologies if I'm overlooking something here.

I think it would be better to just request that the width and height
fields get setters, since they're already being defined in Swift
<https://github.com/apple/swift/blob/master/stdlib/public/SDK/CoreGraphics/CoreGraphics.swift#L172>.
If you want to attack the general problem, I think it would be better to
come up with ways to disambiguate overloads that differ only by module.

Interesting -- thank you for the link! I was originally going to suggest a
change in the Objective-C API translation thread, but I had assumed that
the width and height properties came from a generic rule that automatically
translated the CGRectGet___ functions and that making explicit translation
rules for CGRect would be undesirable.

I would like to solve the general problem if there's a reasonable way to
do so, since I do think there are applications outside of these specific
properties. To use another CGRect example, you may want the midX/Y
properties (among others) to be settable, but introducing built-in setters
for all of these kinds of properties is likely not something we would want
to do.

If it turns out that this isn't a feasible thing to do, then I think
building these particular setters into Swift is the next best thing, but
I'd like to explore the possibilities here for a bit before trying to go
that route.

Best,
Jordan

On Feb 3, 2016, at 14:30, Jarod Long via swift-evolution < > swift-evolution@swift.org> wrote:

Swift extensions are fantastic for filling in gaps in an API, but they
don't currently allow you to convert a read-only property into a read-write
property by extending it with your own setter. I'd like to propose that an
extension may contain a set-only computed property iff the extended type
already has a getter for that property.

My specific motivation for this proposal stems from CGRect's translated
API. If you frequently work with rects, it's very convenient to be able to
access their x/y/width/height properties directly. It's easy to extend
CGRect with x/y properties, but width and height are problematic because of
the CGRectGetWidth and CGRectGetHeight functions that get imported as
read-only width and height properties on CGRect.

If you extend CGRect with read-write width/height properties, they'll work
within the module they're defined because the getters shadow the imported
Objective-C versions, but using the getters in other modules becomes
impossible due to ambiguity errors. If it was possible to only define the
width/height setters, the ambiguity wouldn't exist.

This particular issue is pretty narrow, but it's moderately frustrating if
you write a lot of layout code or do other work with rects, and I think
this is just one of many situations where adding a setter to a property
would be useful.

This proposal is somewhat related to the "support for pure setters"
discussion, but since it doesn't involve the tradeoffs of introducing
actual set-only properties, I feel that this warrants its own separate
discussion.

Looking forward to your input!

Jarod

_______________________________________________
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