This sounds similar to automatic protocol forward, have you looked into prior discussions on that topic here?
Nevin On Thu, Jun 22, 2017 at 10:56 PM, Jay Abbott via swift-evolution < swift-evolution@swift.org> wrote: > Let's take a quick look at how we can achieve very simple compile-time > composition in Swift today. > > I want to define a `Doorman` behaviour, and I want to compose it from > other behaviours that are shared by some of my other staff types: > > ```swift > protocol Greeter { > func greet() > } > protocol Fareweller { > func farewell() > } > protocol Doorman: Greeter, Fareweller {} > ``` > > Great - that's my interface defined, now some implementations that I can > compose my staff from: > > ```swift > protocol FriendlyGreeter: Greeter {} > extension FriendlyGreeter { > func greet() { > print("Hello and welcome") > } > } > > protocol FriendlyFareweller: Fareweller {} > extension FriendlyFareweller { > func farewell() { > print("I bid thee farewell") > } > } > > protocol InsultingGreeter: Greeter {} > extension InsultingGreeter { > func greet() { > print("You make me sick") > } > } > > protocol InsultingFareweller: Fareweller {} > extension InsultingFareweller { > func farewell() { > print("Get lost") > } > } > ``` > > Now we have two kinds of `Greeter` and two kinds of `Fareweller` that can > be used to compose different `Doorman` types (and potentially other staff > types). Here's two examples: > > ```swift > struct FriendlyDoorman: Doorman, FriendlyGreeter, FriendlyFareweller {} > struct TrickingDoorman: Doorman, FriendlyGreeter, InsultingFareweller {} > ``` > > I can instantiate and make use of these to perform their defined > behaviours: > > ```swift > let friendly: Doorman = FriendlyDoorman() > let tricking: Doorman = TrickingDoorman() > friendly.greet() // Hello and welcome > friendly.farewell() // I bid thee farewell > tricking.greet() // Hello and welcome > tricking.farewell() // Get lost > ``` > > It works! But there are some things that could be nicer: > * I don't really want `***Greeter` or `***Fareweller` to be sub-protocols > at all, these are supposed to be implementations - the only reason they are > protocols is so I can extend them with a default implementation and then > use more than one of them to compose my actual `Doorman` types. This > clutters up the namespace with unnecessary protocols, that have the same > interface as their parent. > * Since the `***Doorman` types need to be instantiable, they are structs. > I couldn't compose a `LobbyMultiTasker` from a `FriendlyDoorman` and a > `GrumpyPorter` at compile-time, the same easy way I composed the > `***Doorman` types. The manual solution would be to add properties for > `doormanDelegate` and `porterDelegate` and assign appropriate instances > (run-time composition), then add the boiler-plate to pass on the > `LobbyMultiTasker` behaviour to these delegates. This is also how the > compiler could implement this feature. > * Actually providing the implementations is optional for the protocols > (the extensions can happily be missing), meaning any error messages will > appear in the types that fail to fully implement the protocols, instead of > here in my `***Greeter` implementation. > > So I'd like to discuss the possibility of a new category of types called > `component`, to allow compile-time composition; composition as part of the > language. The `***Greeter` and `***Fareweller` might look like this: > > ```swift > component FriendlyGreeter: Greeter { > func greet() { > print("Hello and welcome") > } > } > > component FriendlyFareweller: Fareweller { > func farewell() { > print("I bid thee farewell") > } > } > > component InsultingGreeter: Greeter { > func greet() { > print("You make me sick") > } > } > > component InsultingFareweller: Fareweller { > func farewell() { > print("Get lost") > } > } > ``` > > And the `TrickingDoorman` might look like this: > > ```swift > component TrickingDoorman: Doorman⎄(FriendlyGreeter, InsultingFareweller) { > // optionally override any Doorman-defined functions > } > > > ``` > > Here's how I think they would work: > > * Components must conform to at least one protocol. > * Components must provide an implementation for all the things from the > protocols - no part of it is 'abstract' - and they can't provide extra > things (although it might be useful to allow some kind of config-values, > but I'd say initially keep it simple). > * Components can be composed of other components and override some/all of > the borrowed behaviour. This should provide well defined > multiple-inheritence semantics (not sure of details), some syntax would be > required to allow the compiler to totally flatten the component, selecting > which "parent" implementations to use if needed to satisfy all the > protocols. Only the functions from the explicit protocols are pulled in, > not everything implemented in the "parent" components. > * Components can be instantiated and have value-semantics, like structs > (they are basically structs with extra features/checks). They can therefore > be used at compile-time but also at run-time for example `delegate = > MyComponent()` - so composed behaviour can be either static or dynamic, > also existing protocol-based `delegate` variables can have a component > instance assigned. > * Classes, structs, and enums can NOT override functions implemented in a > component, when they compose themselves from those components. To do this, > create a sub-component and override the method, then compose your type from > that instead. > > Other benefits: > * I think this would encourage more single-responsibility by default. > Because when designing a protocol, people tend to forget composition. > Enforcing must-implement-everything would remind/encourage API designers to > split protocols up, especially if they want to provide a default > implementation for some but not all of the functions. > * Tidier code, with much clearer intention (component vs extension in > particular). > * Easy re-use without having pass-through boilerplate code. > * Brings well-defined-ness to default implementations, which can sometimes > be unclear whether it's an actual default implementation or an empty > placeholder > > I haven't thought of everything here, obviously - and I'm tired, so > please poke holes and supply constructive corrections :) > > Any suggestions for the "composed-of" syntax for when a type wants to > utilise a component would be welcome. I used `Doorman⎄(FriendlyGreeter, > InsultingFareweller)` in the example above, where ⎄ is the composition > symbol that I just discovered, but this is just intended to be a > placeholder. > > > > _______________________________________________ > swift-evolution mailing list > 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