At Tue Mar 1 19:00:21 CST 2016, Brian Pratt brian at pratt.io wrote:

I feel like the solution to the Arrow/Enemy problem that best fits with
Swift's current available tools is neither a protocol (which, as you
mentioned, doesn't get rid of the initialization/configuration of the
member data) or inheritance (which, as you mentioned, can only have one
base type) -- it's to extract a third type that handles

What you're describing sounds _exactly_ like Traits :-)

        http://scg.unibe.ch/research/traits

Traits could be a peer of extensions, filling a niche that isn't quite the same 
niche as a default implementation, but it could be consistently and safely 
consumed like an extension, with its own rules of consumption (flattening) - to 
implement any need or explicit protocol requirement that a Class, Value Type or 
Protocol has at compile-time.

Think of a Trait as providing a consumable set of functions.  Now imagine that 
we're combining the collective declarations of 1..N Traits all together, 
consumed by a Class or Value Type (or Protocol!) in a predicable and safe way 
(flattening: see the first Traits white paper).

i.e. we get the same result regardless of the order of consumption, how many 
times any given Trait(s) were consumed, Traits dependent on other Traits etc.  
Predictable results, given the same input we always get the same output.  This 
is flattening, but read the white papers for more detail.

The process of flattening itself could be a peer of the existing rules around 
static dispatch vs. dynamic dispatch around default-implementations/extensions 
vs. class overrides.

A Trait declaration could look something ~like:

        trait TraitX (ProtocolAdherenceY): DependentTrait1, DependentTrait2 {

                private var foo
                private let bah { .. }

                func fooify { .. }
                mutating func bahify { .. }
                private func hah { .. }

        }

with a Trait being a closure, where _only private_ data Properties can be 
declared, providing 1..N function implementations.  It could conform _towards_ 
a Protocol (partial to full conformance), and also be dependent upon other 
names Traits, which would be consumed in parallel as a first-class citizen with 
the Trait that depends on it.

Traits could be consumed by a class or value type, to provide function implementations 
which could be satisfying a Protocol implementation requirement, along with its own 
private functions and private data for its (private to Trait) local state etc.  The 
consumption syntax I'm still unsure of, but a clear declarative "flattens Trait1, 
Trait2, .. , TraitN" or similar would do.

The consuming Class or Value Type would remain fully responsible for its own 
Protocol conformance, and if any of the consumed Trait public implementations 
conflict or overlap with each other, then the conflicts must be resolved 
explicitly by the Class or Value Type itself, where it is consumed.

The resulting "flattened" set of Traits would be input towards the Type's own 
compiler requirements, with the author being required to explicitly resolve any and all 
conflicts at compile-time.  Cconsumption at run-time could be a later feature as Swift's 
core stabilises and specific run-time metaprogramming facilities are exposed.

Explicit conflict resolution, via a flattened 2D matrix of Trait:func 
identifying conflicts that need to be resolved by the consumer, also gives 
reliable results with no cognitive overhead as Brian's identified in resolving 
Mixin consumption.  Plus there is no diamond-problem, as there is no 
inheritance.  With Traits, it _must_ be resolved explicitly in code, with 
suitable compiler errors for malformed Types that have consumed 1..N Trait(s).

Stateful Traits suggest that as long as the data is private _to the Trait_ then 
we can safely ignore some of the complexity of state in Traits - it just isn't 
exposed as the Trait declaration itself is a closure.  Dependency on state in 
the consumer could proxy to class/instance/value-type data via Protocol.

Swift and Protocols seem like a perfect match for "capital-T" Traits.  Any 
thoughts on if this is suitable for a 3.0 or 4.0 Proposal?

I've recently built similar mechanisms exploring these concepts with basic 
metaprogramming and a common root class in a dynamic language, but as a core 
language feature of Swift I think it could very much complement the existing 
protocols and extension concepts, with 1..N re-usable implementations, and it 
also could help to resolves the uncertainty/rules around static vs. dynamic 
dispatch: static could remain the domain of extensions / default 
implementations; with dynamic dispatch available to Classes and Traits?

More Reading:

        http://scg.unibe.ch/research/traits

Cheers,

--
Niall Young
[email protected]


At Tue Mar 1 19:00:21 CST 2016, Brian Pratt brian at pratt.io wrote:

I think this sort of composition is preferable to inheritance in a lot of
ways, and Swift has some built-in tools that can augment it: a robust
protocol and extension system, a type constraint system that allows for
lots of flexibility at compile-time, etc.

Mixins (and in general, the sharing of code primarily via inheritance) tend
to create large objects with lots of responsibilities, and that tends to
bloat APIs as you need to either pick extremely specific names to avoid
collisions, or worse, keep the cognitive overhead of "shoot, what is this
method aliased to again?" in your head all the time. If something *is* both
an A and a B, it needs to act like (and speak the same language of) an A or
a B *all* of the time.

Beyond this, I think it's going to be extremely complex managing
compile-time type constraints with renames in place. Let's say I have a
class C that inherits from bases A and B, which implement protocol P and Q
respectively, and there's a naming collision. Functions that expect Ps or
Qs will have to know about the renaming of conflicts from the combination
of A+B? Unless I'm missing something, it feels like this complexity would
continue to spread out to all sorts of collaborators, when the current
system isolates it much more effectively.

I think protocols and protocol extensions (mixed with lots of composition)
is a better scenario than abstract classes or multiple inheritance, and
therefore, I'm still a -1 on mixins in Swift (strictly on principle; this
proposal actually argues the case very well).

- Brian

And agreed Thorsten!:

Unfortunately the current discussions about Mixins, abstract classes, POP
vs. OOP suffer from having forgotten achievements of the past which results
in creating differences where none should be.

It is unfortunate and IMO just for historical reasons that there is a
dichotomy between protocols and classes at all instead of having just
classes with multiple inheritance done right (and abstract methods).

- We should extend protocols to support real multiple inheritance with
renaming

-Thorsten
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to