[Proposal]Add support for extension variables


(Yogev Sitton) #1

Hi,

I try to avoid using inheritance for just adding functionality - for that I use (and love) extensions.
This works great for functions - but I find the lack for stored properties support limiting.

Here’s my use case:
I want to map every textfield in my view controller to a JSON field - so it would be very helpful to add a string Key property to all of the UITextFields in my app.
For now this is how I solve this issue (inside the extension):

struct customProperties {
  static var key : String?
}
  
var key : String? {
  get {
    return objc_getAssociatedObject(self, &customProperties.key) as? String
  }
  set (value){
    objc_setAssociatedObject(self,&customProperties.key,value,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }
}

I would love to just add an actual variable to the extension without the need to use Obj-C associated objects.


(Haravikk) #2

There was a proposal for this a little while ago, what you’re looking for are mixins, though I’m not sure they would solve your problem either.

The reason this can’t be done as part of an extension is that there are only really two ways to implement it:

Expand the Type: Basically the new data has to be added to the type somehow. For example, a struct of four Ints is about 32 bytes, to add a stored property you’d need to increase that. This could be fine if it’s internal to a module, as it can be computed in advance, but if you’re extending an imported type (as in your case) then this isn’t ideal as your version of UITextFields would be a different from the size expected by other libraries that don’t know about your extension, which would mean that some kind of transformation is required between your version of UITextFields and any other variation of it that you need to work with; not impossible, but not the nicest thing to have to do and potentially not great for performance.

Lookup Added Properties: I believe this is exactly what you’re doing with your workaround; namely your added properties are being stored separately and associated to an instance so that they can be looked up/changed without touching the size of the original. However, this kind of lookup isn’t great for performance either.

So both options would have to trade performance for stored property style syntax, which IMO would not be a great thing to do as it would hide what’s going on behind the scenes to make it work. Maybe if we had some kind of Swiftier methods for doing either option explicitly that might be okay, since it could give simpler means that explicitly expose the performance impact, but APIs designed to handle type-erased wrappers shouldn’t need anything like this. Of course that’s no comfort when dealing with APIs that do, especially legacy ones, but you’ve found a workaround that seems to do what you need, and there may be other options (can’t think of any just now).

Anyway, that’s the gist of the problem as I understand it, hope some of that helps to fill in why it’s not a straightforward thing to address.

···

On 4 Apr 2016, at 11:00, Yogev Sitton via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I try to avoid using inheritance for just adding functionality - for that I use (and love) extensions.
This works great for functions - but I find the lack for stored properties support limiting.

Here’s my use case:
I want to map every textfield in my view controller to a JSON field - so it would be very helpful to add a string Key property to all of the UITextFields in my app.
For now this is how I solve this issue (inside the extension):

struct customProperties {
  static var key : String?
}
  
var key : String? {
  get {
    return objc_getAssociatedObject(self, &customProperties.key) as? String
  }
  set (value){
    objc_setAssociatedObject(self,&customProperties.key,value,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }
}

I would love to just add an actual variable to the extension without the need to use Obj-C associated objects.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #3

Such a feature would require limitations, yes. For a strawman, what would be the resulting system behavior if someone had a use case to add a string unit identifier to all numeric values?

At a minimum:
- every number in the system now would take more space
- you would be unable to redefine the comparison operator (all else being the same) to account for this new unit identifier
- if redefinition was possible, comparisons for this type are now more complex and more computationally expensive.

A more typical example - adding a flag to all Strings to indicate whether or not they have been pre-sanitized for use in XML/JSON/SQL/etc (such as in Ruby on Rails)

-DW

···

On Apr 4, 2016, at 3:49 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

There was a proposal for this a little while ago, what you’re looking for are mixins, though I’m not sure they would solve your problem either.

The reason this can’t be done as part of an extension is that there are only really two ways to implement it:

Expand the Type: Basically the new data has to be added to the type somehow. For example, a struct of four Ints is about 32 bytes, to add a stored property you’d need to increase that. This could be fine if it’s internal to a module, as it can be computed in advance, but if you’re extending an imported type (as in your case) then this isn’t ideal as your version of UITextFields would be a different from the size expected by other libraries that don’t know about your extension, which would mean that some kind of transformation is required between your version of UITextFields and any other variation of it that you need to work with; not impossible, but not the nicest thing to have to do and potentially not great for performance.

Lookup Added Properties: I believe this is exactly what you’re doing with your workaround; namely your added properties are being stored separately and associated to an instance so that they can be looked up/changed without touching the size of the original. However, this kind of lookup isn’t great for performance either.

So both options would have to trade performance for stored property style syntax, which IMO would not be a great thing to do as it would hide what’s going on behind the scenes to make it work. Maybe if we had some kind of Swiftier methods for doing either option explicitly that might be okay, since it could give simpler means that explicitly expose the performance impact, but APIs designed to handle type-erased wrappers shouldn’t need anything like this. Of course that’s no comfort when dealing with APIs that do, especially legacy ones, but you’ve found a workaround that seems to do what you need, and there may be other options (can’t think of any just now).

Anyway, that’s the gist of the problem as I understand it, hope some of that helps to fill in why it’s not a straightforward thing to address.

On 4 Apr 2016, at 11:00, Yogev Sitton via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

I try to avoid using inheritance for just adding functionality - for that I use (and love) extensions.
This works great for functions - but I find the lack for stored properties support limiting.

Here’s my use case:
I want to map every textfield in my view controller to a JSON field - so it would be very helpful to add a string Key property to all of the UITextFields in my app.
For now this is how I solve this issue (inside the extension):

struct customProperties {
  static var key : String?
}

var key : String? {
  get {
    return objc_getAssociatedObject(self, &customProperties.key) as? String
  }
  set (value){
    objc_setAssociatedObject(self,&customProperties.key,value,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }
}

I would love to just add an actual variable to the extension without the need to use Obj-C associated objects.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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