protocol based invocation forwarding


(David Owens II) #1

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


(Joe Groff) #2

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

···

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #3

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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #4

Would it be acceptable to make forwarding of member with Self return types optional for the forwarder (i.e. If the initializer / factory function is not provided the member is not forwarded and must be implemented manually)?

···

Sent from my iPad

On Dec 8, 2015, at 11:53 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@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@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


(Joe Groff) #5

Would it be acceptable to make forwarding of member with Self return types optional for the forwarder (i.e. If the initializer / factory function is not provided the member is not forwarded and must be implemented manually)?

That's definitely a reasonable answer.

In my mind, an ideal solution would make it easy to implement 'newtypes' that wrap a type while exposing selected parts of the original type's interface. For example, if you wanted to make strongly-typed units valued as Doubles that still support arithmetic:

protocol Addable { func + (_: Self, _: Self) -> Self }

struct Weight: Addable {
  var value: Double implements Addable
}
struct Distance: Addable {
  var value: Double implements Addable
}

it'd be nice if the unwrapping and wrapping defaulted to something sensible.

-Joe

···

On Dec 8, 2015, at 10:09 AM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On Dec 8, 2015, at 11:53 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #6

Yes, it should be pretty straightforward to handle the wrapping and unwrapping for the single member / newtype case. Thanks bringing that up. I will keep that in mind if I get around to writing this proposal before someone else does.

Is a forwarding mechanism like this something that might be considered in the Swift 3 timeframe?

···

Sent from my iPad

On Dec 8, 2015, at 2:52 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 8, 2015, at 10:09 AM, Matthew Johnson <matthew@anandabits.com> wrote:

Would it be acceptable to make forwarding of member with Self return types optional for the forwarder (i.e. If the initializer / factory function is not provided the member is not forwarded and must be implemented manually)?

That's definitely a reasonable answer.

In my mind, an ideal solution would make it easy to implement 'newtypes' that wrap a type while exposing selected parts of the original type's interface. For example, if you wanted to make strongly-typed units valued as Doubles that still support arithmetic:

protocol Addable { func + (_: Self, _: Self) -> Self }

struct Weight: Addable {
  var value: Double implements Addable
}
struct Distance: Addable {
  var value: Double implements Addable
}

it'd be nice if the unwrapping and wrapping defaulted to something sensible.

-Joe

Sent from my iPad

On Dec 8, 2015, at 11:53 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@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@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


(Alejandro Martinez) #7

Hi,
Quick mail to check if I understood correctly :wink:
Since swift introduction I've been trying to explore creating more domain specific types in order to avoid passing numbers and strings around (that can mean anything) and leverage the type checker to write safer code. (For context http://alejandromp.com/blog/2015/12/5/solving-the-strings-problem-in-swift/ )
You can quickly realize that it increases the safety a lot but at cost of s bunch of boilerplate. At the end you just want that your type behaves as the other (string, int) but that the type checker treats it as different types.
Seems like this proposal and making easier creating 'newtype' will be the solution for this right?
Thanks

···

Sent from my iPad

On 08 Dec 2015, at 22:41, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Yes, it should be pretty straightforward to handle the wrapping and unwrapping for the single member / newtype case. Thanks bringing that up. I will keep that in mind if I get around to writing this proposal before someone else does.

Is a forwarding mechanism like this something that might be considered in the Swift 3 timeframe?

Sent from my iPad

On Dec 8, 2015, at 2:52 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 8, 2015, at 10:09 AM, Matthew Johnson <matthew@anandabits.com> wrote:

Would it be acceptable to make forwarding of member with Self return types optional for the forwarder (i.e. If the initializer / factory function is not provided the member is not forwarded and must be implemented manually)?

That's definitely a reasonable answer.

In my mind, an ideal solution would make it easy to implement 'newtypes' that wrap a type while exposing selected parts of the original type's interface. For example, if you wanted to make strongly-typed units valued as Doubles that still support arithmetic:

protocol Addable { func + (_: Self, _: Self) -> Self }

struct Weight: Addable {
  var value: Double implements Addable
}
struct Distance: Addable {
  var value: Double implements Addable
}

it'd be nice if the unwrapping and wrapping defaulted to something sensible.

-Joe

Sent from my iPad

On Dec 8, 2015, at 11:53 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@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@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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Owens II) #8

Yes, this would help with that.

···

Sent from my iPad

On Dec 8, 2015, at 11:39 PM, Alejandro Martinez via swift-evolution <swift-evolution@swift.org> wrote:

Hi,
Quick mail to check if I understood correctly :wink:
Since swift introduction I've been trying to explore creating more domain specific types in order to avoid passing numbers and strings around (that can mean anything) and leverage the type checker to write safer code. (For context http://alejandromp.com/blog/2015/12/5/solving-the-strings-problem-in-swift/ )
You can quickly realize that it increases the safety a lot but at cost of s bunch of boilerplate. At the end you just want that your type behaves as the other (string, int) but that the type checker treats it as different types.
Seems like this proposal and making easier creating 'newtype' will be the solution for this right?
Thanks

Sent from my iPad

On 08 Dec 2015, at 22:41, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Yes, it should be pretty straightforward to handle the wrapping and unwrapping for the single member / newtype case. Thanks bringing that up. I will keep that in mind if I get around to writing this proposal before someone else does.

Is a forwarding mechanism like this something that might be considered in the Swift 3 timeframe?

Sent from my iPad

On Dec 8, 2015, at 2:52 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 8, 2015, at 10:09 AM, Matthew Johnson <matthew@anandabits.com> wrote:

Would it be acceptable to make forwarding of member with Self return types optional for the forwarder (i.e. If the initializer / factory function is not provided the member is not forwarded and must be implemented manually)?

That's definitely a reasonable answer.

In my mind, an ideal solution would make it easy to implement 'newtypes' that wrap a type while exposing selected parts of the original type's interface. For example, if you wanted to make strongly-typed units valued as Doubles that still support arithmetic:

protocol Addable { func + (_: Self, _: Self) -> Self }

struct Weight: Addable {
  var value: Double implements Addable
}
struct Distance: Addable {
  var value: Double implements Addable
}

it'd be nice if the unwrapping and wrapping defaulted to something sensible.

-Joe

Sent from my iPad

On Dec 8, 2015, at 11:53 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

It'd definitely be awesome to support forwarding protocol conformances. One thing a forwarding design needs to consider is how to handle `Self` requirements in the forwarded protocol. If the protocol requirements consume `Self` types, you need a conversion operation to go from the forwarder to the forwardee type, such as the getter for the forwardee property.
If there are any requirements that return `Self` you'd need to additionally provide an initializer or factory function capable of building a new instance of the forwarder type from the forwardee.

-Joe

On Dec 7, 2015, at 1:33 PM, David Owens II via swift-evolution <swift-evolution@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@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

_______________________________________________
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