[Pitch] Reference storage for enum associated values


(Marc Prud'hommeaux) #1

The following code currently has a retain cycle and memory leak:

enum ParentChild {
    case SonOf(Parent)
    case DaughterOf(Parent)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(self))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
    deinit { print("deinit Parent") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }
    deinit { print("deinit Child") }
}

do {
    let parent = Parent()
    parent.son
    parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
    case SonOf(unowned Parent)
    case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
    public unowned let value: T
    public init(_ value: T) { self.value = value }
}

enum ParentChild {
    case SonOf(UnownedRef<Parent>)
    case DaughterOf(UnownedRef<Parent>)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
    deinit { print("deinit Foo") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }

    deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc


(Patrick Smith) #2

Why not separate the reference and its info? That way you can make the
reference weak.

enum ParentChildRelationship {

  case Son

  case Daughter

}

class Parent {

  lazy var son: Child = Child(sonOf: self)

  lazy var daughter: Child = Child(daughterOf: self)

  deinit { print("deinit Parent") }

}

class Child {

  weak var parent: Parent?

  var parentRelationship: ParentChildRelationship

  init(parent: Parent, relationship: ParentChildRelationship) {

    self.parent = parent

    self.parentRelationship = relationship

  }

  convenience init(sonOf parent: Parent) {

    self.init(parent: parent, relationship: .Son)

  }

  convenience init(daughterOf parent: Parent) {

    self.init(parent: parent, relationship: .Daughter)

  }

  deinit { print("deinit Child") }

}

do {

  let parent = Parent()

  parent.son

  parent.daughter

}

**Patrick Smith**

The following code currently has a retain cycle and memory leak:

enum ParentChild {

    case SonOf(Parent)

    case DaughterOf(Parent)

}

class Parent {

    lazy var son: Child = Child(parentChild: .SonOf(self))

    lazy var daughter: Child = Child(parentChild: .DaughterOf(self))

    deinit { print("deinit Parent") }

}

class Child {

    var parentChild: ParentChild

    init(parentChild: ParentChild) {

        self.parentChild = parentChild

    }

    deinit { print("deinit Child") }

}

do {

    let parent = Parent()

    parent.son

    parent.daughter

}

Child.parentChild cannot be declared unowned because ParentChild is a value

type. I propose adding the ability to declare the reference storage class for
an enum's associated value, like so:

enum ParentChild {

    case SonOf(unowned Parent)

    case DaughterOf(unowned Parent)

}

The only current alternative is to have some intermediate reference type

that itself holds the reference, akin to the old "Box" type that we used to
use to work around enum limitations. E.g., this is our current cumbersome
work-around:

/// An unowned reference to a value, which is useful for maintaining parent-

child relations through value types like enums

public struct UnownedRef&lt;T: AnyObject&gt; {

    public unowned let value: T

    public init(_ value: T) { self.value = value }

}

enum ParentChild {

    case SonOf(UnownedRef&lt;Parent&gt;)

    case DaughterOf(UnownedRef&lt;Parent&gt;)

}

class Parent {

    lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))

    lazy var daughter: Child = Child(parentChild:

.DaughterOf(UnownedRef(self)))

    deinit { print("deinit Foo") }

}

class Child {

    var parentChild: ParentChild

    init(parentChild: ParentChild) {

        self.parentChild = parentChild

    }

    deinit { print("deinit Child") }

}

The storage type of an enum would, of course, be limited to reference types,

and when the storage class is weak, it would require that the stored type be
Optional, just like when declaring a weak variable.

···

On May 4 2016, at 1:07 am, Marc Prud'hommeaux via swift-evolution &lt;swift- evolution@swift.org&gt; wrote:

What do people think?

-Marc


(Haravikk) #3

It’s an interesting idea, but with the workaround you’ve found I wonder if it might make more sense to just have Unowned<T> and Weak<T> in the standard library? I’ve already defined these myself to work with a few awkward cases and to allow entries in collections to be weakly referenced.

I mean it’d be nice to have keyword support too; I wonder if could be made more flexible, e.g- let us also write things like:

  let foo:[weak AnyObject] = …

Externally this would appear as if the array were of type AnyObject?, but internally the array would have an actual type of Weak<AnyObject> or whatever. I dunno, just thinking out loud, point being that maybe this isn’t enum specific, even though enum is an important use-case, i.e- a keyword or global function could make this easier to do throughout the language.

···

On 3 May 2016, at 16:07, Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org> wrote:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
    case SonOf(Parent)
    case DaughterOf(Parent)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(self))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
    deinit { print("deinit Parent") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }
    deinit { print("deinit Child") }
}

do {
    let parent = Parent()
    parent.son
    parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
    case SonOf(unowned Parent)
    case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
    public unowned let value: T
    public init(_ value: T) { self.value = value }
}

enum ParentChild {
    case SonOf(UnownedRef<Parent>)
    case DaughterOf(UnownedRef<Parent>)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
    deinit { print("deinit Foo") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }

    deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Michael Peternell) #4

I wonder if there is a practical use-case for this.. Is there? Just curious...

-Michael

···

Am 03.05.2016 um 17:07 schrieb Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org>:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
    case SonOf(Parent)
    case DaughterOf(Parent)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(self))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
    deinit { print("deinit Parent") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }
    deinit { print("deinit Child") }
}

do {
    let parent = Parent()
    parent.son
    parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
    case SonOf(unowned Parent)
    case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
    public unowned let value: T
    public init(_ value: T) { self.value = value }
}

enum ParentChild {
    case SonOf(UnownedRef<Parent>)
    case DaughterOf(UnownedRef<Parent>)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
    deinit { print("deinit Foo") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }

    deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Joe Groff) #5

Another way to address this might be by allowing property behaviors, a feature proposed in https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md to allow for factoring out property implementations, could also apply to `case` declarations. "Weak" and "unowned" could be considered to be property behaviors once we have them, so you'd write something like:

class Foo {
  var parent: Foo?
    behavior weak
}

to define a property with "weak" behavior. We could potentially allow you to apply behaviors to 'case' associated values too:

enum ParentChild {
  case Parent(Parent) behavior unowned
  case Child(Parent) behavior unowned
}

-Joe

···

On May 3, 2016, at 8:07 AM, Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org> wrote:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
    case SonOf(Parent)
    case DaughterOf(Parent)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(self))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
    deinit { print("deinit Parent") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }
    deinit { print("deinit Child") }
}

do {
    let parent = Parent()
    parent.son
    parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
    case SonOf(unowned Parent)
    case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
    public unowned let value: T
    public init(_ value: T) { self.value = value }
}

enum ParentChild {
    case SonOf(UnownedRef<Parent>)
    case DaughterOf(UnownedRef<Parent>)
}

class Parent {
    lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
    lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
    deinit { print("deinit Foo") }
}

class Child {
    var parentChild: ParentChild
    init(parentChild: ParentChild) {
        self.parentChild = parentChild
    }

    deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Marc Prud'hommeaux) #6

I wonder if there is a practical use-case for this.. Is there? Just curious...

Without getting too deep into the weeds of our specific data modal, I'll summarize with "yes". If you need to mix classes with enums and don't have the ability to declare the storage class of the variables, then reference cycles can only be avoided by shimming in some other type that does permit the declaration of the storage type. I would be surprised if I was the only person to encounter this issue; anyone else?

It is analogous to the hoops you would need to jump through to define a recursive enum before the "indirect" keyword. As a matter of fact, it is so similar to that case that I wonder if it would make sense to have weak/unowned keywords be before the case name, as with indirect. E.g.:

enum ChildGuardianship {
    unowned case ChildOf(Parent)
    indirect case GrandChildOf(ChildGuardianship)
    weak case WardOf(State?)
}

The obvious limitation of this would be that all associated types would be pegged to the same storage class, preventing the ability of having one associated type be weak and another be unowned. This limitation of indirect was mentioned at https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151207/000312.html , but it doesn't seem to have been given much treatment since.

  -Marc

···

On May 4, 2016, at 12:25 PM, Michael Peternell <michael.peternell@gmx.at> wrote:

I wonder if there is a practical use-case for this.. Is there? Just curious...

-Michael

Am 03.05.2016 um 17:07 schrieb Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org>:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
   case SonOf(Parent)
   case DaughterOf(Parent)
}

class Parent {
   lazy var son: Child = Child(parentChild: .SonOf(self))
   lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
   deinit { print("deinit Parent") }
}

class Child {
   var parentChild: ParentChild
   init(parentChild: ParentChild) {
       self.parentChild = parentChild
   }
   deinit { print("deinit Child") }
}

do {
   let parent = Parent()
   parent.son
   parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
   case SonOf(unowned Parent)
   case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
   public unowned let value: T
   public init(_ value: T) { self.value = value }
}

enum ParentChild {
   case SonOf(UnownedRef<Parent>)
   case DaughterOf(UnownedRef<Parent>)
}

class Parent {
   lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
   lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
   deinit { print("deinit Foo") }
}

class Child {
   var parentChild: ParentChild
   init(parentChild: ParentChild) {
       self.parentChild = parentChild
   }

   deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Marc Prud'hommeaux) #7

Joe-

I hadn't seen that proposal; it would make sense to harmonize with enum cases. Instead of the trailing "behavior unowned" syntax, perhaps your proposed bracket syntax could be used:

enum ChildGuardianship {
  case [unowned] ChildOf(Parent)
  case [indirect] GrandChildOf(ChildGuardianship)
  case [weak] WardOf(State?)
}

Or maybe inline with the :

enum ChildGuardianship {
  case ChildOf([unowned] Parent)
  case GrandChildOf([indirect] ChildGuardianship)
  case WardOf([weak] State?)
  case SharedCustodyOf([unowned] parent: Parent, [indirect] guardian: ChildGuardianship, [weak] ward: State?)
}

If that is amenable, how would one go about proposing it as an amendment to #0030?

  -Marc

···

On May 6, 2016, at 11:47 AM, Joe Groff <jgroff@apple.com> wrote:

Another way to address this might be by allowing property behaviors, a feature proposed in https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md to allow for factoring out property implementations, could also apply to `case` declarations. "Weak" and "unowned" could be considered to be property behaviors once we have them, so you'd write something like:

class Foo {
var parent: Foo?
   behavior weak
}

to define a property with "weak" behavior. We could potentially allow you to apply behaviors to 'case' associated values too:

enum ParentChild {
case Parent(Parent) behavior unowned
case Child(Parent) behavior unowned
}

-Joe

On May 3, 2016, at 8:07 AM, Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org> wrote:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
   case SonOf(Parent)
   case DaughterOf(Parent)
}

class Parent {
   lazy var son: Child = Child(parentChild: .SonOf(self))
   lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
   deinit { print("deinit Parent") }
}

class Child {
   var parentChild: ParentChild
   init(parentChild: ParentChild) {
       self.parentChild = parentChild
   }
   deinit { print("deinit Child") }
}

do {
   let parent = Parent()
   parent.son
   parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
   case SonOf(unowned Parent)
   case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
   public unowned let value: T
   public init(_ value: T) { self.value = value }
}

enum ParentChild {
   case SonOf(UnownedRef<Parent>)
   case DaughterOf(UnownedRef<Parent>)
}

class Parent {
   lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
   lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
   deinit { print("deinit Foo") }
}

class Child {
   var parentChild: ParentChild
   init(parentChild: ParentChild) {
       self.parentChild = parentChild
   }

   deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Michael Peternell) #8

I wonder if there is a practical use-case for this.. Is there? Just curious...

Without getting too deep into the weeds of our specific data modal, I'll summarize with "yes". If you need to mix classes with enums and don't have the ability to declare the storage class of the variables, then reference cycles can only be avoided by shimming in some other type that does permit the declaration of the storage type. I would be surprised if I was the only person to encounter this issue; anyone else?

It is analogous to the hoops you would need to jump through to define a recursive enum before the "indirect" keyword. As a matter of fact, it is so similar to that case that I wonder if it would make sense to have weak/unowned keywords be before the case name, as with indirect. E.g.:

enum ChildGuardianship {
   unowned case ChildOf(Parent)
   indirect case GrandChildOf(ChildGuardianship)
   weak case WardOf(State?)
}

sorry, that doesn't really convince me. I don't consider a "ChildGuardianship" enum a practical use-case. The same for "child", "parent", "daughterOf", "sonOf", etc. Or were you programming a kindergarten application to help the kindergarten teachers to bill the parents correctly?

The obvious problem with a storage class in value types is that weak references are by definition mutable: they zero out as soon as the pointee starts deallocating. This means that the ChildGuardianship enum is no longer a real value type with value semantics but a value type with partial reference semantics.

I'm sure there is a practical use-case for this somewhere, but I doubt that it would be generally useful enough to be included into Swift at the language level.

-Michael

···

Am 06.05.2016 um 14:08 schrieb Marc Prud'hommeaux <marc@glimpse.io>:

The obvious limitation of this would be that all associated types would be pegged to the same storage class, preventing the ability of having one associated type be weak and another be unowned. This limitation of indirect was mentioned at https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151207/000312.html , but it doesn't seem to have been given much treatment since.

  -Marc

On May 4, 2016, at 12:25 PM, Michael Peternell <michael.peternell@gmx.at> wrote:

I wonder if there is a practical use-case for this.. Is there? Just curious...

-Michael

Am 03.05.2016 um 17:07 schrieb Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org>:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
  case SonOf(Parent)
  case DaughterOf(Parent)
}

class Parent {
  lazy var son: Child = Child(parentChild: .SonOf(self))
  lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
  deinit { print("deinit Parent") }
}

class Child {
  var parentChild: ParentChild
  init(parentChild: ParentChild) {
      self.parentChild = parentChild
  }
  deinit { print("deinit Child") }
}

do {
  let parent = Parent()
  parent.son
  parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
  case SonOf(unowned Parent)
  case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
  public unowned let value: T
  public init(_ value: T) { self.value = value }
}

enum ParentChild {
  case SonOf(UnownedRef<Parent>)
  case DaughterOf(UnownedRef<Parent>)
}

class Parent {
  lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
  lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
  deinit { print("deinit Foo") }
}

class Child {
  var parentChild: ParentChild
  init(parentChild: ParentChild) {
      self.parentChild = parentChild
  }

  deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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


(Marc Prud'hommeaux) #9

This means that the ChildGuardianship enum is no longer a real value type with value semantics but a value type with partial reference semantics.

That's already true of any enum that has an associated reference type.

···

On May 6, 2016, at 10:26 AM, Michael Peternell <michael.peternell@gmx.at> wrote:

Am 06.05.2016 um 14:08 schrieb Marc Prud'hommeaux <marc@glimpse.io>:

I wonder if there is a practical use-case for this.. Is there? Just curious...

Without getting too deep into the weeds of our specific data modal, I'll summarize with "yes". If you need to mix classes with enums and don't have the ability to declare the storage class of the variables, then reference cycles can only be avoided by shimming in some other type that does permit the declaration of the storage type. I would be surprised if I was the only person to encounter this issue; anyone else?

It is analogous to the hoops you would need to jump through to define a recursive enum before the "indirect" keyword. As a matter of fact, it is so similar to that case that I wonder if it would make sense to have weak/unowned keywords be before the case name, as with indirect. E.g.:

enum ChildGuardianship {
  unowned case ChildOf(Parent)
  indirect case GrandChildOf(ChildGuardianship)
  weak case WardOf(State?)
}

sorry, that doesn't really convince me. I don't consider a "ChildGuardianship" enum a practical use-case. The same for "child", "parent", "daughterOf", "sonOf", etc. Or were you programming a kindergarten application to help the kindergarten teachers to bill the parents correctly?

The obvious problem with a storage class in value types is that weak references are by definition mutable: they zero out as soon as the pointee starts deallocating. This means that the ChildGuardianship enum is no longer a real value type with value semantics but a value type with partial reference semantics.

I'm sure there is a practical use-case for this somewhere, but I doubt that it would be generally useful enough to be included into Swift at the language level.

-Michael

The obvious limitation of this would be that all associated types would be pegged to the same storage class, preventing the ability of having one associated type be weak and another be unowned. This limitation of indirect was mentioned at https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151207/000312.html , but it doesn't seem to have been given much treatment since.

  -Marc

On May 4, 2016, at 12:25 PM, Michael Peternell <michael.peternell@gmx.at> wrote:

I wonder if there is a practical use-case for this.. Is there? Just curious...

-Michael

Am 03.05.2016 um 17:07 schrieb Marc Prud'hommeaux via swift-evolution <swift-evolution@swift.org>:

The following code currently has a retain cycle and memory leak:

enum ParentChild {
case SonOf(Parent)
case DaughterOf(Parent)
}

class Parent {
lazy var son: Child = Child(parentChild: .SonOf(self))
lazy var daughter: Child = Child(parentChild: .DaughterOf(self))
deinit { print("deinit Parent") }
}

class Child {
var parentChild: ParentChild
init(parentChild: ParentChild) {
     self.parentChild = parentChild
}
deinit { print("deinit Child") }
}

do {
let parent = Parent()
parent.son
parent.daughter
}

Child.parentChild cannot be declared unowned because ParentChild is a value type. I propose adding the ability to declare the reference storage class for an enum's associated value, like so:

enum ParentChild {
case SonOf(unowned Parent)
case DaughterOf(unowned Parent)
}

The only current alternative is to have some intermediate reference type that itself holds the reference, akin to the old "Box" type that we used to use to work around enum limitations. E.g., this is our current cumbersome work-around:

/// An unowned reference to a value, which is useful for maintaining parent-child relations through value types like enums
public struct UnownedRef<T: AnyObject> {
public unowned let value: T
public init(_ value: T) { self.value = value }
}

enum ParentChild {
case SonOf(UnownedRef<Parent>)
case DaughterOf(UnownedRef<Parent>)
}

class Parent {
lazy var son: Child = Child(parentChild: .SonOf(UnownedRef(self)))
lazy var daughter: Child = Child(parentChild: .DaughterOf(UnownedRef(self)))
deinit { print("deinit Foo") }
}

class Child {
var parentChild: ParentChild
init(parentChild: ParentChild) {
     self.parentChild = parentChild
}

deinit { print("deinit Child") }
}

The storage type of an enum would, of course, be limited to reference types, and when the storage class is weak, it would require that the stored type be Optional, just like when declaring a weak variable.

What do people think?

  -Marc

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