[swift-evolution] protocol based invocation forwarding

Matthew Johnson matthew at anandabits.com
Tue Dec 8 11:46:13 CST 2015


I would also like to see language support for concise forwarding.  It gives composition a conciseness that approaches inheritance which makes it much more pragmatic than it currently is.  This is a huge win IMO.

Forwarding protocol implementations would provide the most value, but forwarding individual methods (whether part of a protocol implementation or not) may also be useful.  Maybe a single mechanism could accomplish both?

When forwarding protocols, it would be ideal to also allow the forwarding class to explicitly implement specific methods of the protocol while the rest are forwarded automatically.

One detail that definitely needs to be addressed is how a protocol with members using Self would be handled, especially when Self is used as a return value.  I’m not sure if these methods could be forwarded automatically or not.  David, do you have any ideas as to how this would be handled?

Another detail that needs to be addressed is how protocols with associated types are forwarded.  I think this would be more straightforward but the details need to be written up to be sure.

Syntactically, I think it would make most sense to specify forwarding along side the member declaration rather than in a separate declaration and should support multiple protocols to be forwarded to the same member in the same declaration.  Something like this:

class Cluster: _Cluster, Foo, Bar {
    // Ideally the member could have any type conforming to the protocol.
    // This would require Swift to recognize that members typed with the protocol itself
    // also conform to the protocol.
    @forward(_Cluster) private var _instance: _Cluster

    // FooBar is a type that conforms to both Foo and Bar protocols
    @forward(Foo, Bar) private let fooBar: FooBar
    ...
}

Matthew

> On Dec 7, 2015, at 3:33 PM, David Owens II via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Often it is the case where one might want to implement a type that provides an interface but has inner components that actually handle the implementation. In those cases, we end up with a lot of boiler-plate code that simply turns around and invokes the on the instance.
> 
> Let’s take the example of class clusters:
> 
> private protocol _Cluster {
>     func description() -> String
> }
> 
> class Cluster: _Cluster {
>     
>     private var _instance: _Cluster
>     
>     init(name: String) {
>         _instance = _ClusterString(name: name)
>     }
>     
>     init(value: Int) {
>         _instance = _ClusterValue(value: value)
>     }
>     
>     // this is pure boiler-plate
>     func description() -> String {
>         return _instance.description()
>     }
> }
> 
> private class _ClusterString: _Cluster {
>     private var name: String
>     init(name: String) { self.name = name }
>     func description() -> String {
>         return "_ClusterString: \(name)"
>     }
> }
> 
> private class _ClusterValue: _Cluster {
>     private var value: Int
>     init(value: Int) { self.value = value }
>     func description() -> String {
>         return "_ClusterValue: \(value)"
>     }
> }
> 
> let s = Cluster(name: "a string")
> s.description()
> 
> let v = Cluster(value: 12)
> v.description()
> 
> 
> Now, it would be nice to not have to have to implement the boiler-plate (this example only has a single method, so the savings seem minimal).
> 
> class Cluster: _Cluster {
>     @forward(_Cluster, _instance)
> 
>     private var _instance: _Cluster
>     
>     init(name: String) {
>         _instance = _ClusterString(name: name)
>     }
>     
>     init(value: Int) {
>         _instance = _ClusterValue(value: value)
>     }
> }
> 
> The @forward(protocol, instance) attribute lets the compiler know that the _Cluster protocol should be forwarded to the _instance value. The compiler would then generate all of the implementation stubs. Refactoring is also made simple as API changes to _Cluster do not need to be manually reflected on the type.
> 
> Another way to solve this problem is with a sufficiently advanced macro system. But that is out-of-scope for v3. However, this seems like it could be a straight-forward enough implementation to support in the mean-time, with an easy path for removal/update if it were to be replaced by a macro system.
> 
> -David
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151208/60eb00a2/attachment.html>


More information about the swift-evolution mailing list