Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why 'there cannot be more than one conformance, even with different conditional bounds'?

I hoped that Swift gives me the ability to create an extension for type with specified conditions in where block. I imagined that I can extend the same generic type with different extensions dependent on concrete generic type value (T). But not. Following example demonstrates my problem:

protocol P {    
    associatedtype Prop 
    var property: Prop { get }
}

enum E<T: P> {
   case single(T)
   case double(T)
}

extension E: P where T.Prop == Int {
   var property: Int {
      switch self {
      case .single(let o): return o.property
      case .double(let o): return o.property * 2
      }
   }
}

extension E: P where T.Prop == String {
   var property: String {
      switch self {
      case .single(let o): return o.property
      case .double(let o): return o.property + o.property
      }
   }
}

struct Int4: P {
   var property: Int {
       return 4
   }
}

struct StringHello: P {
    var property: String {
        return "Hello" 
    }
}

print(E.single(Int4()).property)
print(E.double(StringHello()).property)

Following error and note are the result of the compilation.

error: conflicting conformance of 'E' to protocol 'P'; there cannot be more than one conformance, even with different conditional bounds extension E: P where T.Prop == String {

note: 'E' declares conformance to protocol 'P' here extension E: P where T.Prop == Int {

Is it really impossible? Why? What can I do with my code to succeed?


Some details to demonstrate the problem in my real situation.
I have some generic enum, which is used with many different wrapped types.

enum Color<T> {
  case red(T), green(T)

  func map<T2>(_ transform: (T) -> T2) -> Color<T2> {
    switch self {
    case .red(let o): return .red(transform(o))
    case .green(let o): return .green(transform(o))
    }
  }
}

Very often, I need different extensions for Color to conform it to different protocols depending on the wrapped type. Sometimes these protocols have the same base (super) protocol and as a result, I have the current problem. Sometimes I cant extend Color to conform this base (super) protocol for all deriving protocols because I need different implementations.

like image 589
Valentine Zakharenko Avatar asked Jul 31 '19 03:07

Valentine Zakharenko


2 Answers

As was described earlier you cannot just do this kind of extension. However, you can use hack like this:

protocol SomeExtension {
    func doSomething()
}

extension SomeExtension {
    func doSomething() {
        print("Do nothing or error")
    }
}

extension SomeExtension where Self == [String] {
    func doSomething() {
        print("String")
    }
}

extension SomeExtension where Self == [Int] {
    func doSomething() {
        print("Int")
    }
}

extension Array: SomeExtension { }

let stringsArr = ["a", "b", "d"]
let numbersArr = [1, 2, 3]
stringsArr.doSomething()
numbersArr.doSomething()

In console you can see

String
Int
like image 178
vitaliy.pavlyuk.dev Avatar answered Nov 15 '22 22:11

vitaliy.pavlyuk.dev


Is it impossible? Yes and no. It's not currently possible in Swift, as it has been implemented. It is in principle possible to be implemented.

The name for this is "overlapping conformances", and it was explicitly and purposely rejected. You can find the rationale in the "Alternatives considered" section of SE-0143 Conditional conformances. The TL;DR is: because it's really complicated.

Without knowing more about what exactly you were trying to use this for, there's not much direction we can provide.

like image 35
Alexander Avatar answered Nov 15 '22 21:11

Alexander