[Pitch] Reference storage for enum associated values


(James F) #1

I would like to see something like this; it would not only eliminate extra code, but could potentially be more efficient.

What syntax do you propose? If enum values were always labelled, I would expect something like SonOf(weak parent: Parent), but there would probably be resistance to allowing SonOf(weak Parent), since weak would look like a type modifier. Maybe SonOf(weak _: Parent) would work?

I just hope this isn't another feature that will get put on hold just because we can't decide on the syntax…

Just some rambling:

I remember that ‘partial function’ proposal, which would allow the case keyword inside closures to act as a switch on its input.

If a feature like that, but using ‘case’ in the sense of enumerations, were extended to classes and structs…

struct ParentFamily {
    weak var parent: Parent?

    case Son {
        var intProperty: Int
        init(_ int: Int) {self.intProperty = int}
    }
    case Daughter {
        var stringProperty: String = ""
    }
    case SignificantOtherOf {}
}

We wouldn't even need ‘enum’ anymore, since we'd have a similar kind of thing that could be both value-type and reference-type, with all-case stored properties, too. Almost like an abstract class (or struct) hierarchy, except the limited extensibility means the values can be safely, exhaustively switched over. Class and struct could become simple value-type and reference-type implementation modifiers.

------------ Begin Message ------------
Group: gmane.comp.lang.swift.evolution
MsgID: <CC184164-0A5F-48C5-9E8C-252DCB8DAFC0@glimpse.io>

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

------------- End Message -------------

From James F