Swift method for Protocol-constrained Type in function arg

So, porting some of my ObjC code to Swift... I have a function like this:


- (void)queryElement:(Element<Queryable> *)element


Note the class type 'Element' specialized by the protocol 'Queryable'. In my application's domain, not all 'Elements' are 'Queryable' and not all 'Queryable' things are 'Elements', but this function's implementation needs access to aspects of both 'Element' and 'Queryable'.


I've tried a few different ways of getting this modeled in Swift, and the closest I've come is:


func queryElement<T: Element where T: Queryable>(element: T)


Now this compiles fine on the declaration side, but on the calling side, I get:


query.queryElement(element)  //  <-- Generic parameter 'T' could not be inferred


I've tried variations on explicitly typing the element:


if let element = element as? Queryable {
if element is Queryable {
query.queryElement(element as Queryable)


All of which give the same error. Can anyone tell me what the magic invocation is for this situation? Is my problem on the calling side, or the declaration side?


Any help appreciated.

Replies

The following works in a playground (Xcode 7.3):


protocol Queryable { }

class Element { }
class ElementQ: Element, Queryable {}

let e: Element = ElementQ ()

func queryElement<T: Element where T: Queryable>(element: T) { }

if let e = e as? ElementQ {
  queryElement (e)
}


Unless you have a subclass that conforms to Queryable, I don't see how you can create an instance of Element that conforms to Queryable.


Note that if the instance is created in Obj-C, the lightweight generic specifier <Queryable> is ignored by Swift when it converts the Obj-C header. Swift honors Obj-C lightweight generics only for collection classes (Array, Dictionary, Set).

You're right, I forgot to mention that 'Element' is a base class, and several (but not all) of its sub-classes conform to 'Queryable'. So there's no way to structure this without testing explicitly for ever single subclass that might conform to 'Queryable'? That's disappointing.

If you can change the Obj-C side you could add a new ElementQueryable subclass (with no implementation) and change all your existing subclasses that implement your protocol to extend that one instead. That would be minimal code changes and less fragile than listing all the subclasses on the Swift side. You would only have to test the one class.


Does seem like it kind of defeats the purpose of the protocol though.

You could make an ElementQ subclass that all the Queryable sub-sub-classes inherit from.


It seems to me that Swift is heading away from subclassing in favor of protocol composition. If you can go down that road, you could — for example — define an ElementType protocol that contains all the behavior of an Element, apart from Queryable. Then you would specify your function parameter as 'protocol<ElementType,Queryable>' (or whatever that composition syntax is).


It feels unnatural for anyone used to class inheritance, but it's probably what you'd need if the intermediate (ElementQ) class doesn't solve the problem.

Unfortunately, there's no way to do a single base class for the "Queryable Element". That's why I used the protocol in the first place. There are specific Element subtypes that are Queryable, and they don't share common parent classes.


I'll give the ElementType protocol a try. As much as I like Swift, sometimes the type system can be really frustrating. Why should I have to create a protocol that simply describes some of the properties of an Element only because the language won't let me specify a type constraint of "an Elementtype that is aQueryable"

Yes, the impossibility of using a generic protocol as a type is very frustrating.

It forces me to write switches on conforming types all over the place.

We can only hope this gets better in Swift 3 although I am not aware of any concrete plans at swift.org (please correct me if I'm wrong).

>> There are specific Element subtypes that are Queryable, and they don't share common parent classes.


They do — Element. So there is an actual hierarchy. If you want an OO-inheritance-based solution, then you need each descendant class to inherit from Element or ElementQ, according to whether the descendant should conform to Queryable or not. This has always been the weakness of single-inheritance languages.


>> Why should I have to create a protocol that simply describes some of the properties of an Element only because …


a. But you were ready to do exactly that in creating a protocol called Queryable.


b. But you were ready to do exactly that in creating a base class called Element.


Both of those involve extracting parts of a more comprehensive class API into separately defined part.


The difference is that these are strategies that you're familiar with, and the full-on composition strategy still seems strange. What happens if you think in terms of eliminating class Element and keeping only protocol ElementType, having an extension with some default implementations for shared behavior?


I'm not intending to devalue the OO-inheritance approach. It is, after all, what I'm comfortable with too. But the fact is that Swift has a somewhat different philosophy, and I think the best long-term outcome is to get more familiar with it. I'm trying, anyway. 😉

OK... so let me try again. I have an OO class graph such that:


base class is Element

Parent extends Element

Parametric extends Parent

Element, Parent, and Parametric each serve as base classes for various specialized elements.

Some of these specializations are Queryable.

The nearest common base class of Queryable Elements is Element

Not all subclasses of Element are Queryable.


"a. But you were ready to do exactly that in creating a protocol called Queryable."


No, I want to describe specific functionality that is common to Queryable types, some of which happen to be Element subclasses (and some of which derrive from other base types).


"b. But you were ready to do exactly that in creating a base class called Element."


No, Element implements a fairly complex base model type that contains a fair amount of business logic specific to Element-ness that is shared by all Elements, and that already exists, and generally has nothing to do with Queryable-ness.


So, if I have to describe this Element-ness in a protocol, I wind up copying and pasting a whole bunch of code/declarations that already exist into a protocol. And since protocols A) can't have storage, and B) lose the ability to be treated as Types under various conditions, I can't move it into the protocol, because Element is a model, and stores a fair bit of data & state, and needs to be a fully-fledged Type for a variety of applications within the business logic.


This is my biggest issue currently with Swift, is that to do things "correctly" still requires a great deal of duplication of code just to make the compiler happy.


The fact is that I can easily model this in Swift by applying the Queryable protocol to specific classes in the Element type graph, but it seems that I can't qualify a function argument with the combination of Type and Protocol conformance. There's no reason why I shouldn't be able to tell the compiler exactly what I'm passing into the function and avoid the inference in this case just like one can do with optionals or with type casting, except that the language inexplicably doesn't allow qualification by Type<Protocol>. Since you can qualify by Type, or Protocol, or <ProtocolA, ProtocolB>, I don't see why its anything other than a language gap that I can't do Type<Protocol>.


As I said... frustrating.


Anyway, I have a work-around for now, but its quite ugly, and definitely smells ;(