-1

I want to write extensions for more than one protocol and found this posting which was very interesting, but I wasn't able to follow Matthew Seaman completely.

In this case I want to write my own extension BinaryNumber which I want to add to BinaryInteger and BinaryFloatingPoint. But when I try to add my protocol, Xcode shows the error message Extension of protocol 'BinaryInteger' (or 'BinaryFloatingPoint') cannot have an inheritance clause.

Here is my code:

protocol BinaryNumbers {} // my protocol

extension BinaryNumbers {
    func foo() -> Void {
        print("It works!")
    }
}

// try to extend Swift protocols
extension BinaryInteger : BinaryNumbers {} // doesn't work
extension BinaryFloatingPoint : BinaryNumbers {} // doesn't work

//Updade

@sweeper suggested to extend Numeric, so I tried, but get an error.

extension Numeric {
    func foo() -> Bool {
        return self <= 127
        // Referencing operator function '<=' on 'BinaryInteger' requires
        // that 'Self' conform to 'BinaryInteger'
    }
}

When I extend BinaryInteger and BinaryFloatingPoint one by one, it works.

Christoph S.
  • 585
  • 3
  • 16

1 Answers1

2

In general, it is not possible to write a single extension on multiple protocols. This would require the compiler to figure out the set intersection of all the members in all those protocols (those are the members you have access to in the extensions), which the compiler cannot do.

To avoid code duplication, you would need to work out the members that you need - in your case init(integerLiteral:) and <=, put those in your own protocol, and make the concrete types you want the extension to apply to, conform to your own protocol:

// inheriting from Comparable and ExpressibleByIntegerLiteral gives you the members you need
protocol BinaryNumbers : Comparable & ExpressibleByIntegerLiteral {

}

extension BinaryNumbers {
    func atMost127() -> Bool {
        self <= 127
    }
}

extension Int: BinaryNumbers {}
extension Int8: BinaryNumbers {}
extension Int16: BinaryNumbers {}
extension Int32: BinaryNumbers {}
extension Int64: BinaryNumbers {}
// ... plus the unsigned versions, if you need them

extension Float16: BinaryNumbers {}
extension Float32: BinaryNumbers {}
extension Float64: BinaryNumbers {}
extension Float80: BinaryNumbers {}

Now you might ask, why don't we just make an extension of Comparable & ExpressibleByIntegerLiteral then, as they provide all the members we are using? Well, because that's not a nominal type, and you can't write extensions on non-nominal types :(

However, you could write it in this way:

// now you don't need your own "BinaryNumbers"!
extension Comparable where Self: ExpressibleByIntegerLiteral {
    func atMost127() -> Bool {
        self <= 127
    }
}

You are only able to do this because the two members you need all came from protocols. If for some reason you need a member that both BinaryFloatingPoint and BinaryInteger has, but isn't provided by any of the protocols they conform to, then you need to write your own protocol with those members, and manually conform everything to that protocol.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Why I should extend `Int`, `Int8`, ... one by one when I can extend `BinaryInteger` at once? Why I should extend `Float16`, `Float32`, ... once by one when I can extend `BinaryFloatingPoint`? Your solution to extend `Comparable` is great and I did it in that way – Christoph S. Mar 19 '21 at 04:33
  • 1
    @ChristophSchreiber Because you can't make a protocol inherit another protocol using an extension. That's just how it is. – Sweeper Mar 19 '21 at 04:36
  • But I can extend `BinaryInteger` for all integers and `BinaryFloatingPoint` for all floats. Nevertheless, your solution to do it in Comparable is the best – Christoph S. Mar 19 '21 at 18:46