Proposal - Allow properties in Extensions


(Nutchaphon Rewik) #1

I only see the benefits on this.

  1. We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
  2. We can add new states to the existing type. Not just NSObject subclass with associated object.

protocol Incrementer{

   func increase()
}

extension Incrementer{

   var count = 1 //
allows stored properties

    func increase(){

       print(count)
        count = count + 1

   }

}

···

On Dec 8, 2015, at 10:51 AM, Kevin Kachikian via swift-evolution <swift-evolution at swift.org<https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:

I'd like to proposal to the Swift community and discuss the advantages and disadvantages of adding modifiable properties to extensions (in addition to the already existing computed properties, instance methods, subscripts, etc.):

extension SomeType {

      var aNewProperty: Int
      var anotherVariable: String
      var aThirdOne: MyStruct


(Brent Royal-Gordon) #2

I only see the benefits on this.
  • We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
  • We can add new states to the existing type. Not just NSObject subclass with associated object.

I think people generally want this; the question is how to implement it, and especially how to implement it *without* either limiting it to types in the same module, or dangling what amounts to a dictionary off every single object and struct. (Structs are more difficult than objects, because they don’t have stable identities.) Any ideas?

···

--
Brent Royal-Gordon
Architechies


(Dan Stenmark) #3

I’d +1 this proposal for extensions existing in the same module as the class declaration. However, creating new properties for classes defined in other modules would likely involve side-table lookups, and my understanding is that it has some performance implications.

In cases where you want to extend the storage of a class, consider if maybe a subclass is all you need.

Dan

···

On Dec 18, 2015, at 11:42 AM, Nutchaphon Rewik via swift-evolution <swift-evolution@swift.org> wrote:

I only see the benefits on this.
We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
We can add new states to the existing type. Not just NSObject subclass with associated object.

protocol Incrementer{
    func increase()
}

extension Incrementer{

    var count = 1 // allows stored properties
    
    func increase(){
        print(count)
        count = count + 1
    }

}

> On Dec 8, 2015, at 10:51 AM, Kevin Kachikian via swift-evolution <swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:
>
> I’d like to proposal to the Swift community and discuss the advantages and disadvantages of adding modifiable properties to extensions (in addition to the already existing computed properties, instance methods, subscripts, etc.):
>
> extension SomeType {
>
> var aNewProperty: Int
> var anotherVariable: String
> var aThirdOne: MyStruct
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #4

You could say that types whose conformance to the protocol lives in the same module as the type itself get the default property storage, but external extensions that introduce the conformance can't use the default stored property and have to supply their own implementation.

-Joe

···

On Dec 18, 2015, at 12:49 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I only see the benefits on this.
  • We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
  • We can add new states to the existing type. Not just NSObject subclass with associated object.

I think people generally want this; the question is how to implement it, and especially how to implement it *without* either limiting it to types in the same module, or dangling what amounts to a dictionary off every single object and struct. (Structs are more difficult than objects, because they don’t have stable identities.) Any ideas?


(Lily Ballard) #5

AFAIK there's no precedent today for extensions in the same module being able to do things that extensions in other modules can't do (beyond accessing `internal` members of course). I'm not completely opposed to the idea, but I would want to be very careful and make sure there's an extremely clear benefit to changing it so extensions in the same module can add extra storage.

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
    var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

That said, when looking at a definition imported from another module, you can't see which properties are computed and which are stored anyway, so it's maybe not a huge deal (although you can assume any property defined in an extension is computed). But it is still something to consider.

-Kevin Ballard

···

On Fri, Dec 18, 2015, at 03:15 PM, Joe Groff via swift-evolution wrote:

> On Dec 18, 2015, at 12:49 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
>
>> I only see the benefits on this.
>> • We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
>> • We can add new states to the existing type. Not just NSObject subclass with associated object.
>
> I think people generally want this; the question is how to implement it, and especially how to implement it *without* either limiting it to types in the same module, or dangling what amounts to a dictionary off every single object and struct. (Structs are more difficult than objects, because they don’t have stable identities.) Any ideas?

You could say that types whose conformance to the protocol lives in the same module as the type itself get the default property storage, but external extensions that introduce the conformance can't use the default stored property and have to supply their own implementation.


(Joe Groff) #6

True, this is a tradeoff. We've discussed moving in the direction of allowing same-module extensions to do anything that can be done in the original type declaration, to allow freedom of organization without arbitrary rules about what must go in the type, but sizing instances was one of our concerns. (You could always peek at the LLVM IR if you want to know for sure what's getting generated.)

-Joe

···

On Dec 18, 2015, at 3:24 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Fri, Dec 18, 2015, at 03:15 PM, Joe Groff via swift-evolution wrote:

On Dec 18, 2015, at 12:49 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I only see the benefits on this.
  • We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
  • We can add new states to the existing type. Not just NSObject subclass with associated object.

I think people generally want this; the question is how to implement it, and especially how to implement it *without* either limiting it to types in the same module, or dangling what amounts to a dictionary off every single object and struct. (Structs are more difficult than objects, because they don’t have stable identities.) Any ideas?

You could say that types whose conformance to the protocol lives in the same module as the type itself get the default property storage, but external extensions that introduce the conformance can't use the default stored property and have to supply their own implementation.

AFAIK there's no precedent today for extensions in the same module being able to do things that extensions in other modules can't do (beyond accessing `internal` members of course). I'm not completely opposed to the idea, but I would want to be very careful and make sure there's an extremely clear benefit to changing it so extensions in the same module can add extra storage.

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
   var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

That said, when looking at a definition imported from another module, you can't see which properties are computed and which are stored anyway, so it's maybe not a huge deal (although you can assume any property defined in an extension is computed). But it is still something to consider.


(Kevin Kachikian) #7

I’m really glad to see some discussion about this. I think it is an extremely worth while addition to Swift. Chris L. also thought it was a good idea, in general.

If I get a chance I’ll make a formal request to consider it. Anyone else interested in offering it up, officially?

Thanks,
Kevin Kachikian

···

On Dec 18, 2015, at 11:42 AM, Nutchaphon Rewik <nRewik@outlook.com> wrote:

I only see the benefits on this.
We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
We can add new states to the existing type. Not just NSObject subclass with associated object.

protocol Incrementer{
    func increase()
}

extension Incrementer{

    var count = 1 // allows stored properties
    
    func increase(){
        print(count)
        count = count + 1
    }

}

> On Dec 8, 2015, at 10:51 AM, Kevin Kachikian via swift-evolution <swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:
>
> I’d like to proposal to the Swift community and discuss the advantages and disadvantages of adding modifiable properties to extensions (in addition to the already existing computed properties, instance methods, subscripts, etc.):
>
> extension SomeType {
>
> var aNewProperty: Int
> var anotherVariable: String
> var aThirdOne: MyStruct


(David Owens II) #8

Have you considered using “partial” or “extendable" for these types of definitions (similar to C# partials) to reduce this confusion?

extendable struct Foo {
   var x: Int
}

When the keyword is there, all bets are off that the layout of the struct only contains a single member. Then extensions, defined within the same module, could add to it.

-David

···

On Dec 18, 2015, at 3:34 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 18, 2015, at 3:24 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

AFAIK there's no precedent today for extensions in the same module being able to do things that extensions in other modules can't do (beyond accessing `internal` members of course). I'm not completely opposed to the idea, but I would want to be very careful and make sure there's an extremely clear benefit to changing it so extensions in the same module can add extra storage.

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
   var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

That said, when looking at a definition imported from another module, you can't see which properties are computed and which are stored anyway, so it's maybe not a huge deal (although you can assume any property defined in an extension is computed). But it is still something to consider.

True, this is a tradeoff. We've discussed moving in the direction of allowing same-module extensions to do anything that can be done in the original type declaration, to allow freedom of organization without arbitrary rules about what must go in the type, but sizing instances was one of our concerns. (You could always peek at the LLVM IR if you want to know for sure what's getting generated.)


(John McCall) #9

I only see the benefits on this.
  • We don't have to store all states in one file. States can be stored separately. So, we can write code in more composition style.
  • We can add new states to the existing type. Not just NSObject subclass with associated object.

I think people generally want this; the question is how to implement it, and especially how to implement it *without* either limiting it to types in the same module, or dangling what amounts to a dictionary off every single object and struct. (Structs are more difficult than objects, because they don’t have stable identities.) Any ideas?

You could say that types whose conformance to the protocol lives in the same module as the type itself get the default property storage, but external extensions that introduce the conformance can't use the default stored property and have to supply their own implementation.

AFAIK there's no precedent today for extensions in the same module being able to do things that extensions in other modules can't do (beyond accessing `internal` members of course). I'm not completely opposed to the idea, but I would want to be very careful and make sure there's an extremely clear benefit to changing it so extensions in the same module can add extra storage.

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
   var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.

John.

···

On Dec 18, 2015, at 3:24 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
On Fri, Dec 18, 2015, at 03:15 PM, Joe Groff via swift-evolution wrote:

On Dec 18, 2015, at 12:49 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

That said, when looking at a definition imported from another module, you can't see which properties are computed and which are stored anyway, so it's maybe not a huge deal (although you can assume any property defined in an extension is computed). But it is still something to consider.

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


(Austin Zheng) #10

+1 for "[giving] same-module extensions to do anything that can be done in
the original type declaration", +1 for something akin to C#'s 'partial'.
I've often wanted to organize my code for a single type within several
files, grouping methods and properties by purpose, but have been stymied by
both the way access control works and by the restriction on stored
properties.

Best,
Austin

···

On Fri, Dec 18, 2015 at 3:58 PM, David Owens II via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 18, 2015, at 3:34 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

On Dec 18, 2015, at 3:24 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

AFAIK there's no precedent today for extensions in the same module being
able to do things that extensions in other modules can't do (beyond
accessing `internal` members of course). I'm not completely opposed to the
idea, but I would want to be very careful and make sure there's an
extremely clear benefit to changing it so extensions in the same module can
add extra storage.

One potentially large downside is you can no longer look at a type
declaration and find out how large it is. For example, I could define

struct Foo {
   var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a
separate file that bloats that out to some ridiculously massive struct, and
that wouldn't be obvious from looking at it.

That said, when looking at a definition imported from another module, you
can't see which properties are computed and which are stored anyway, so
it's maybe not a huge deal (although you can assume any property defined in
an extension is computed). But it is still something to consider.

True, this is a tradeoff. We've discussed moving in the direction of
allowing same-module extensions to do anything that can be done in the
original type declaration, to allow freedom of organization without
arbitrary rules about what must go in the type, but sizing instances was
one of our concerns. (You could always peek at the LLVM IR if you want to
know for sure what's getting generated.)

Have you considered using “partial” or “extendable" for these types of
definitions (similar to C# partials) to reduce this confusion?

extendable struct Foo {
   var x: Int
}

When the keyword is there, all bets are off that the layout of the struct
only contains a single member. Then extensions, defined within the same
module, could add to it.

-David

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


(Lily Ballard) #11

Oh that's an interesting idea. My immediate reaction to "I don't want unpredictable sizes" was upon reflection something that only applies to structs. Classes are reference types already, so adding storage to them doesn't really have much consequence (structs get copied around so their size is important). Not only that, but we can already use associated objects with classes, so adding proper stored properties to them doesn't actually change the semantics of extensions, it just means avoiding the overhead of associated objects (and of value wrappers for associated objects) when the extension is part of the same module.

-Kevin Ballard

···

On Fri, Dec 18, 2015, at 04:11 PM, John McCall wrote:

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.


(John McCall) #12

Right. And I think we’d also want to support some way to explicitly request the out-of-line representation even within the module.

John.

···

On Dec 18, 2015, at 4:19 PM, Kevin Ballard <kevin@sb.org> wrote:
On Fri, Dec 18, 2015, at 04:11 PM, John McCall wrote:

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.

Oh that's an interesting idea. My immediate reaction to "I don't want unpredictable sizes" was upon reflection something that only applies to structs. Classes are reference types already, so adding storage to them doesn't really have much consequence (structs get copied around so their size is important). Not only that, but we can already use associated objects with classes, so adding proper stored properties to them doesn't actually change the semantics of extensions, it just means avoiding the overhead of associated objects (and of value wrappers for associated objects) when the extension is part of the same module.


(Lily Ballard) #13

Property behaviors could do out-of-line storage for classes (assuming property behaviors retain the ability to get at the owning class, which is something I mentioned as being problematic in my review). You could technically do out-of-line storage for structs by having an inline storage of object type and then using the lifetime of that object to manage the out-of-line storage, but that doesn't seem particularly great and it also breaks value semantics.

-Kevin Ballard

···

On Fri, Dec 18, 2015, at 04:38 PM, John McCall wrote:

> On Dec 18, 2015, at 4:19 PM, Kevin Ballard <kevin@sb.org> wrote:
> On Fri, Dec 18, 2015, at 04:11 PM, John McCall wrote:
>> I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.
>
> Oh that's an interesting idea. My immediate reaction to "I don't want unpredictable sizes" was upon reflection something that only applies to structs. Classes are reference types already, so adding storage to them doesn't really have much consequence (structs get copied around so their size is important). Not only that, but we can already use associated objects with classes, so adding proper stored properties to them doesn't actually change the semantics of extensions, it just means avoiding the overhead of associated objects (and of value wrappers for associated objects) when the extension is part of the same module.

Right. And I think we’d also want to support some way to explicitly request the out-of-line representation even within the module.


(Joe Groff) #14

A behavior should also be able to implement a copy-on-write policy on mutation in order to preserve value semantics in struct containers.

-Joe

···

On Dec 18, 2015, at 5:43 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Fri, Dec 18, 2015, at 04:38 PM, John McCall wrote:

On Dec 18, 2015, at 4:19 PM, Kevin Ballard <kevin@sb.org> wrote:
On Fri, Dec 18, 2015, at 04:11 PM, John McCall wrote:

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.

Oh that's an interesting idea. My immediate reaction to "I don't want unpredictable sizes" was upon reflection something that only applies to structs. Classes are reference types already, so adding storage to them doesn't really have much consequence (structs get copied around so their size is important). Not only that, but we can already use associated objects with classes, so adding proper stored properties to them doesn't actually change the semantics of extensions, it just means avoiding the overhead of associated objects (and of value wrappers for associated objects) when the extension is part of the same module.

Right. And I think we’d also want to support some way to explicitly request the out-of-line representation even within the module.

Property behaviors could do out-of-line storage for classes (assuming property behaviors retain the ability to get at the owning class, which is something I mentioned as being problematic in my review). You could technically do out-of-line storage for structs by having an inline storage of object type and then using the lifetime of that object to manage the out-of-line storage, but that doesn't seem particularly great and it also breaks value semantics.


(Chris Lattner) #15

I agree.

-Chris

···

On Dec 18, 2015, at 4:11 PM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
  var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.


(Lily Ballard) #16

I suppose that's true, if a class is used for out-of-line storage then
you can use the same uniqueness testing that the existing COW
containers use. I was originally thinking about using it like
associated objects, but it makes a lot more sense to use something like
ManagedBuffer instead.

-Kevin Ballard

···

On Fri, Dec 18, 2015, at 05:45 PM, Joe Groff wrote:

On Dec 18, 2015, at 5:43 PM, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> wrote:

On Fri, Dec 18, 2015, at 04:38 PM, John McCall wrote:

On Dec 18, 2015, at 4:19 PM, Kevin Ballard <kevin@sb.org> wrote: On
Fri, Dec 18, 2015, at 04:11 PM, John McCall wrote:

I think any storage-in-extensions proposal ought to be a special
feature of classes; I would not support the ability to add stored
properties to structs in extensions, even from within the module.

Oh that's an interesting idea. My immediate reaction to "I don't
want unpredictable sizes" was upon reflection something that only
applies to structs. Classes are reference types already, so adding
storage to them doesn't really have much consequence (structs get
copied around so their size is important). Not only that, but we
can already use associated objects with classes, so adding proper
stored properties to them doesn't actually change the semantics of
extensions, it just means avoiding the overhead of associated
objects (and of value wrappers for associated objects) when the
extension is part of the same module.

Right. And I think we’d also want to support some way to explicitly
request the out-of-line representation even within the module.

Property behaviors could do out-of-line storage for classes (assuming
property behaviors retain the ability to get at the owning class,
which is something I mentioned as being problematic in my review).
You could technically do out-of-line storage for structs by having an
inline storage of object type and then using the lifetime of that
object to manage the out-of-line storage, but that doesn't seem
particularly great and it also breaks value semantics.

A behavior should also be able to implement a copy-on-write policy on
mutation in order to preserve value semantics in struct containers.


(Tino) #17

+1/-1:

When the extension is in the same module, it's nice to be able to group all aspects of an extension in one place instead of being forced to declare properties in the definition.
But I guess the most important use case is to extend types that aren't in the same module, and I wouldn't like to see that hacking the runtime becomes a common thing to do.

Tino


(Jordan Rose) #18

I don't see why any reasons that apply to classes wouldn't apply to structs: code organization, making the property private, etc. But maybe it should be called out explicitly with "@partial" or similar for structs.

Jordan

···

On Dec 18, 2015, at 20:11 , Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 18, 2015, at 4:11 PM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

One potentially large downside is you can no longer look at a type declaration and find out how large it is. For example, I could define

struct Foo {
var x: Int
}

and it looks like a tiny struct, but I could then add an extension in a separate file that bloats that out to some ridiculously massive struct, and that wouldn't be obvious from looking at it.

I think any storage-in-extensions proposal ought to be a special feature of classes; I would not support the ability to add stored properties to structs in extensions, even from within the module.

I agree.


(Kevin Kachikian) #19

I don’t think hacking the runtime is much of an issue, more that it might already be now.

However part of the thinking behind the idea of extending extensions to encompass property declarations, above and beyond grouping related code together, is to maintain orthogonality: Structs and classes can declare properties and so should extensions to such types be able to as well.

Kevin

···

On Dec 23, 2015, at 3:42 AM, Tino Heth <2th@gmx.de> wrote:

+1/-1:

When the extension is in the same module, it's nice to be able to group all aspects of an extension in one place instead of being forced to declare properties in the definition.
But I guess the most important use case is to extend types that aren't in the same module, and I wouldn't like to see that hacking the runtime becomes a common thing to do.

Tino


(Matthew Johnson) #20

I don’t think hacking the runtime is much of an issue, more that it might already be now.

However part of the thinking behind the idea of extending extensions to encompass property declarations, above and beyond grouping related code together, is to maintain orthogonality: Structs and classes can declare properties and so should extensions to such types be able to as well.

The problem I think we need to consider carefully is that introducing stored properties is not necessarily orthogonal. It can have significant impact on all initializers.

···

On Dec 23, 2015, at 10:02 AM, Kevin Kachikian via swift-evolution <swift-evolution@swift.org> wrote:

Kevin

On Dec 23, 2015, at 3:42 AM, Tino Heth <2th@gmx.de> wrote:

+1/-1:

When the extension is in the same module, it's nice to be able to group all aspects of an extension in one place instead of being forced to declare properties in the definition.
But I guess the most important use case is to extend types that aren't in the same module, and I wouldn't like to see that hacking the runtime becomes a common thing to do.

Tino

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