First-class support for composition in Swift
| Originator: | bigzaphod | ||
| Number: | rdar://17251348 | Date Originated: | June 10, 2014 |
| Status: | Open | Resolved: | |
| Product: | Developer Tools / Swift | Product Version: | |
| Classification: | Reproducible: |
This might be a crazy idea, but often things can be better when implemented with composition rather than inheritance - however I can’t think of any language that directly supports that pattern in the way that languages tend to directly support the inheritance pattern.
I think it would be rather interesting if you could declare a “component” which lives at the same level as “class” and “struct.” A component would differ from a class in that an instance of a component cannot be instantiated on it’s own.
Components would be declared such that they specify the class allowed for their parent. Since this is not optional, I don’t think it’d be specified with the same syntax as generics, but instead more directly specified. An example of a “Body” component that works on class “Monster”:
component Monster Body {}
Perhaps if generics are allowed here, the syntax for a generic parent class could be something like this which would allow that component to exist for perhaps any parent type, but I don’t know if this is necessary:
component <T> Body {}
Components, when declared, would have an automatically generated property name which is added to the parent it’s attached to. This name could also be overridden in the component’s declaration. Note that this does kind of create a global-ish namespace for the names of components and I’m not sure if this is good or bad, but I sort of like the idea that there’s no ambiguity. Example:
component Monster Body {}
component(brain) Monster Behavior {}
aMonster.body // exists if Body component is attached to aMonster
aMonster.brain // exists if Behavior component is attached to aMonster
aMonster.behavior // does not exist since there is no component property named “behavior”
These properties would always be generated on the parent as optional so you can test for their presence or even do optional chaining:
aMonster.brain?.update()
Similar to how classes have standard functions named “init” and “deinit”, components would instead have standard functions named “attach” and “detach” which pretty much serve the same purpose.
A component would have a keyword and/or automatic properties for accessing the component’s current “parent” and “siblings.” The “siblings” feature would be a way to call a function on each of the parent’s other component instances *except* for the current component’s instance, so you can easily send messages to all other components attached to the same parent, for example, without having to special-case skipping “self” or something. Ideally using “siblings” would somehow allow you to call functions even if not every sibling component instance defined that function.
component Monster Body {
func attach() {
parent.sprite?.addChild(Sprite(named:”body.png”))
}
}
component(brain) Monster Behavior {
func update() {
siblings?.behaviorDidUpdate()
}
}
An instance that has attached components would have a feature for accessing “children” (perhaps some other name or keyword would be needed) that works the same as “siblings” in that you can use it to message all of the children at once somehow.
While it’d be nice to easily add and remove components at runtime, it might be better to restrict it such that you must declare all components you want to add to an instance at the time you make a new instance. This would somehow have to be done so you could have a data-driven approach to deciding which components are necessary when you instantiate an instance and not have to declare this stuff statically in code. This might be a strange syntax, but perhaps it could work by just following normal instantiation with an array:
let dumbMonster = Monster() [Body]
let monsterComponents = [Body, Behavior]
let smartMonster = Monster() monsterComponents
When a class instance that has attached components is being released, it would also release all attached component instances. This would mean that while it might be perfectly okay to reference an individual component, that reference would always have to be weak.
let aMonster = Monster() [Body, Behavior]
var theBrain = aMonster.brain // “theBrain” must be weak and optional since it could disappear at any time
I realize that some of how this is specified might be totally incompatible with how Swift handles typing and such, but I do think that it is worth considering adding composition to the language itself in a strong, opinionated way just as inheritance is already there. It’s likely that some or most of this could be done in Swift as-is through clever generics and a Component base class or something, but I still think there’d be value in actually baking this sort of thing in because then it could be used in a similar, well-known way everywhere.
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!