Implicit / transitive protocol conformance for conforming properties?

I have been enjoying the benefits of extended protocol support in Swift 2.x. Upon it's introduction, I was hoping that you could use protocols as facades to isolate separate domains in an application. For example, in the MVC pattern, it would be nice to present a data-backed object to a view as a view model, augmented with any required derivative properties


Alas, this does not seem to be supported. I wanted to present the problem and get input on the viability of such functionality.


Consider the following simplified example. It is possible to deduce that FooModel does semantically satisfy the FooViewModel protocol since it's member bar:BarModel, implements BarViewModel, but it appears that the compiler is not making that leap. Is this possible, planned, and / or somehow dangerous?


struct BarModel {}

struct FooModel { var bar:BarModel }


protocol BarViewModel {}

extension BarModel : BarViewModel {}


protocol FooViewModel { var bar:BarViewModel { get } }

extension FooModel : FooViewModel {} // complains that bar:BarModel is not BarViewModel


Thanks

Repeat after me: Swift is NOT Java - This is a good thing.

As opposed to the comment "Swift is not Java", I find Swift and Java quite similar and both excellent languages. In this case if you code your example in Java:


public class MVCInterfaces {

  interface BarViewModel {}

  static class BarModel implements BarViewModel {}

  interface FooViewModel {
    default BarViewModel getBar() {
      return new BarViewModel() {};
    }
  }

  static class FooModel implements FooViewModel {
    @Override public BarModel getBar() {
      return new BarModel();
    }
  }


  public static void main(final String... notUsed) {
    final FooViewModel fooModel = new FooModel();
    System.out.println(fooModel.getBar());
  }

}


Then it works as you would expect and the `getBar()` call returns a `BarModel`.


Interestingly if you do a literal translation into classes:


class BarViewModel {}

class BarModel: BarViewModel {}

class FooViewModel {
    func getBar() -> BarViewModel {
        return BarViewModel()
    }
}

class FooModel: FooViewModel {
    override func getBar() -> BarModel {
        return BarModel()
    }
}

let fooModel = FooModel()
fooModel.getBar() // Returns a BarModel


Then this also works 🙂


However as soon as you introduce a protocol then it fails:


protocol BarViewModelP {}

struct BarViewModelImplementationP: BarViewModelP {}

class BarModelP: BarViewModelP {}

class FooViewModelP {
    func getBar() -> BarViewModelP {
        return BarViewModelImplementationP()
    }
}

class FooModelP: FooViewModelP {
    func getBar() -> BarModelP {
        return BarModelP()
    }
}

let fooModelP = FooModelP()
fooModelP.getBar() // ERROR: Ambiguous use of `getBar()`


Therefore I would suggest that you have uncovered a compiler bug 😟

This isn't a compiler bug. The behavior is expected. Note that the two getBar() methods have a different signature because their return types are different. This is further supported by the fact that you don't and can't have an "override" for this method in your subclass for this method. They are in fact different methods. You can resolve the desired method by assigning it to a variable with the desired type of that variable specified such as, for example:

let bar : BarModelP = fooModelP.getBar()

I think the language doesn't allow this because there is a potential danger. Consider the behavior if you were to allow a set on the bar property, too. In that case, one could potentially assign an arbitrary BarViewModel instead of the required BarModel. I think there are other potential dangers (slippery slope) in automatically casting types. Depending on the details of your problem, the right solution may be to include two protocols (typed and common).


struct BarModel {}
struct FooModel { var rawBar:BarModel }
protocol BarViewModel {}
extension BarModel : BarViewModel {}

protocol FooViewModeled { var bar: BarViewModel { get } }

protocol FooViewModel : FooViewModeled {
  typealias ModelType : BarViewModel
  var rawBar : ModelType { get }
}

// extension to implement common interface by default
extension FooViewModel {
  var bar : BarViewModel {
       return self.rawBar
  }
}

extension FooModel : FooViewModel {}

let foo = FooModel(rawBar: BarModel())
let rawBar = foo.rawBar // bar as BarModel
let bar = foo.bar // bar as BarViewModel

Here is another option that uses shadows the bar property and may be closer to what you are seeking:


struct BarModel {}
struct FooModel { var bar:BarModel }
protocol BarViewModel {}
extension BarModel : BarViewModel {}
protocol FooViewModeled { var bar: BarViewModel { get } }
protocol FooViewModel : FooViewModeled {
  typealias ModelType : BarViewModel
  var bar : ModelType { get }
}

// default implementation of FooViewModeled
extension FooViewModel {
  var bar : BarViewModel {
       let rawBar : ModelType = self.bar
       return rawBar
  }
}
extension FooModel : FooViewModel {}

let foo = FooModel(bar: BarModel())
let rawBar = foo.bar // bar as BarModel

let fooModel = foo as FooViewModeled
let bar = fooModel.bar // bar as BarViewModel

It is type save to return a derrived type, co-varient return tpe overloads are common in many type-safe languages. In this case all BarModels are BarViewModels, therefore you can always (1) put a BarModel where a BarViewModel is expected. You can see this in the class version that allows, and works as expected, getBar() -> BarModel to override getBar() -> BarViewModel.


However even though it is type safe to have co-varient return types the language could just simply ban this, early versions of Java did for example. Therefore the reason I think it is a bug is because co-varient return types are allowed in classes and you should be able to change a class into a protocol (provided that there is no functionality used in the class that cannot be expressed as a protocol). In this specific case you can declare a method in both a class and a protocol and therefore I would expect the same declarion in a different context, class or protocol, to have the same meaning.


(1) The statement "you can always put a BarModel where a BarViewModel is expected" isn't strictly true, for arguments to function types, closures, the required type relationship is contra-varient as oppossed to co-varient. But in the context of this discussion the satement is true.

Sorry, I don't really get what you are driving at with your example.


But you are correct in that for a method agument an override cannot be co-varient. In the specific case, the problem is that not all `BarModels` are `BarViewModels` and therefore `setBar(BarModel)` cannot override `setBar(BarViewModel)` (this would be an overload not an override).

Implicit / transitive protocol conformance for conforming properties?
 
 
Q