<div dir="ltr">This sounds similar to automatic protocol forward, have you looked into prior discussions on that topic here?<div><br></div><div>Nevin</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Jun 22, 2017 at 10:56 PM, Jay Abbott via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div></div>Let's take a quick look at how we can achieve very simple compile-time composition in Swift today.<br><br></div><div>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:<br><br></div>```swift<br>protocol Greeter {<br> func greet()<br>}<br>protocol Fareweller {<br> func farewell()<br>}<br>protocol Doorman: Greeter, Fareweller {}<br>```<br><br></div><div>Great - that's my interface defined, now some implementations that I can compose my staff from:<br><br></div><div>```swift<br></div><div>protocol FriendlyGreeter: Greeter {}<br>extension FriendlyGreeter {<br> func greet() {<br> print("Hello and welcome")<br> }<br>}<br><br>protocol FriendlyFareweller: Fareweller {}<br>extension FriendlyFareweller {<br> func farewell() {<br> print("I bid thee farewell")<br> }<br>}<br><br>protocol InsultingGreeter: Greeter {}<br>extension InsultingGreeter {<br> func greet() {<br> print("You make me sick")<br> }<br>}<br><br>protocol InsultingFareweller: Fareweller {}<br>extension InsultingFareweller {<br> func farewell() {<br> print("Get lost")<br> }<br>}<br>```<br><br>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:<br><br></div><div>```swift<br></div><div>struct FriendlyDoorman: Doorman, FriendlyGreeter, FriendlyFareweller {}<br>struct TrickingDoorman: Doorman, FriendlyGreeter, InsultingFareweller {}<br>```<br><br>I can instantiate and make use of these to perform their defined behaviours:<br><br></div><div>```swift<br></div><div>let friendly: Doorman = FriendlyDoorman()<br>let tricking: Doorman = TrickingDoorman()<br>friendly.greet() // Hello and welcome<br>friendly.farewell() // I bid thee farewell<br>tricking.greet() // Hello and welcome<br>tricking.farewell() // Get lost<br>```<br><br>It works! But there are some things that could be nicer:<br>* 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.<br></div><div>* 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.<br></div><div>* 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.<br></div><div><br></div><div>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:<br><br><div>```swift<br></div><div>component FriendlyGreeter: Greeter {<br> func greet() {<br> print("Hello and welcome")<br> }<br>}<br><br>component FriendlyFareweller: Fareweller {<br> func farewell() {<br> print("I bid thee farewell")<br> }<br>}<br><br>component InsultingGreeter: Greeter {<br> func greet() {<br> print("You make me sick")<br> }<br>}<br><br>component InsultingFareweller: Fareweller {<br> func farewell() {<br> print("Get lost")<br> }<br>}<br>```<br></div><div><br>And the `TrickingDoorman` might look like this:<br><br></div><div>```swift<br></div><div>component TrickingDoorman: Doorman⎄(FriendlyGreeter, InsultingFareweller) {<br></div><div> // optionally override any Doorman-defined functions<br></div><div>}<br><br><br></div><div>```<br><br></div><div>Here's how I think they would work:<br><br></div><div>* Components must conform to at least one protocol.<br></div><div>* 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).<br></div><div>* 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.<br></div><div>* 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.<br>* 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.<br><br></div><div>Other benefits:<br></div><div>* 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.<br></div><div>* Tidier code, with much clearer intention (component vs extension in particular).<br></div><div>* Easy re-use without having pass-through boilerplate code.<br></div><div>* Brings well-defined-ness to default implementations, which can sometimes be unclear whether it's an actual default implementation or an empty placeholder<br></div><div><br></div> I haven't thought of everything here, obviously - and I'm tired, so please poke holes and supply constructive corrections :)<br><div><br>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.<br></div><div><br><br></div></div></div></div>
<br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div>