[swift-evolution] [Draft] Mixins

Niall Young niall at iinet.net.au
Wed Apr 27 22:50:49 CDT 2016

On Wed, 27 Apr 2016, Антон Жилин wrote:

> Yes, this is my proposal, and of course, I'm interested :)

Cool, given it's postponed until Swift 4+ I thought it best to keep this off-list but happy to keep going here if others don't find it distracting.

I've drafted a proposal focusing on the strict definition of Traits.  I'll give it another few iterations so I can flesh out where they could fit in the language relative to extensions etc.  Happy to share it here, or keep it off-list for now until we can dive into Swift's internals and come up with some ideas on how best to implement this.

> I haven't read into the mentioned papers, but symmetric sum, asymmetric sum, alias, exclusion
> and derived operations form a solid basis for conflict resolution.

Let's keep it very simple to begin with.  e.g. 3 Traits each implement one or more functions:

           function1    function2    function3    function4
TraitA       X             X           X
TraitB       X                                       X
TraitC                     X

for each column where there are >1 implementations, the conflict must be resolved explicitly by choosing which implementation will be used.  This is roughly how flattening works.  We "flatten" them into 1 cohesive set of methods, with no conflicts, e.g. consumption syntax could look ~like

 	class Foo : ProtocolA {
 		flattens TraitA, TraitB, TraitC { exclude TraitA.function1 TraitC.function2 }

or they could be re-used to provide protocol default implementations:

 	protocol ProtocolA {
 		flattens TraitA, TraitB, TraitC { exclude TraitB.function1 TraitA.function2 }

that also helps to resolve the dilemma of having to conform to a full protocol when you only use a couple of specific default implementations - you pick and choose from the underlying implementations, and/or conform to a more fine-grained sub-protocol.  I think Traits can help make extensions, protocols and default implementations more powerful, concise and re-usable.  They seem to complement each other well.

Note that the Trait doesn't target a named Class or Protocol like extensions do, the onus is reversed so that the consuming class/struct/enumeration/protocol elects to consume that implementation(s).  This means they are more fine-grained and re-usable than default implementations, and in reality I think default implementations could be provided by a protocol *or* a Trait, or both (say the Class conforms to the Protocol, but chooses to override a default implementation with an alternative from a Trait).

Renaming functions, aliasing, keeping all implementations available for the final function to call ... I'd leave all of this complexity until later.

State as well, that should remain a class/struct/enumeration responsibility.  Stateful Traits could be attempted later, Trait functions could benefit from some shared state amongst themselves, but that state should not be visible outside of that "instance" of Trait - the same Trait consumed elsewhere would have its own state, and no consumer could access this state.  This also means that there are no Type size issues as we're not introducing additional stored properties.

> I believe there are situations where non-privateness of fields would help, see my two examples.
> This can be overridden by adding a "proxy" computed property. But why is privateness needed?
> Do traits have initializers? How do they work in diamond pattern?
> Example: A -> (B, C) -> D
> B.init calls A.super.init(1)
> C.init calls A.super.init(2)
> D.init calls B.super.init() and C.super.init()
> Traits are flattened by default. Does it mean that A will be initialized twice? in what state
> will A be?

Traits cannot be instantiated, they are not inherited, and they do not have state.  So multiple inheritance and the diamond problem are not applicable here.  Swift's existing rules around single-inheritance remain as-is.

Computed properties I can see a valid argument for supporting, but not stored properties.  Initialisation is the responsibility of the class/struct/enumeration, not the Trait.  Traits are not a solution to this problem.

The white paper is a really good read, highly recommend it: http://scg.unibe.ch/archive/papers/Scha02bTraits.pdf

Niall Young
niall at iinet.net.au

> 2016-04-27 2:50 GMT+03:00 Niall Young <niall at iinet.net.au>:
>       On Fri, 22 Apr 2016, Антон Жилин wrote:
>             This feature has been previously discussed. Search for Mixins on
>             swift-evolution archives.
>             I believe it would help to read previous version of the proposal:
>             https://github.com/Anton3/swift-evolution/blob/mixins/proposals/NNNN-mixins.md
>       Cheers, already read through the thread - I'm more focused on implementing Traits,
>       which I think is very similar but slightly different to what's been described in the
>       thread to date.
>       Is this your proposal?  I'd love to give you some feedback, throw ideas back and
>       forth if you're interested?  If we can evolve the concept and implementation by
>       Swift 4 that gives me enough time to brush up on C++ and start delving into Swift
>       internals to see how best it could be done.
>       Cheers,
>       --
>       Niall Young
>       niall at iinet.net.au

More information about the swift-evolution mailing list