Best workaround for the lack of generic protocols

Let's say I have a protocol "ValueProvider", which is just something that provides a value:


protocol ValueProvider
{
     typealias ValueType
     func valueInContext(context: ValueProviderContext) -> ValueType
}


I have various structs that implement this protocol, such as:


struct Constant<T>: ValueProvider
{
     var value: T
     init(value: T)
     {
          self.value = value
     }
     func value(context: ValueProviderContext) -> T
     {
          return value
     }
}


and many other such (generic) structs.



I have another struct, Thing, that has properties which are value provides of particular type. Ideally, I would like to express it like this:


struct Thing
{
     var position: ValueProvider<CGPoint>
     var name: ValueProvider<String>
     ...
}



Unfortunately I cannot do this, because ValueProvider is a protocol (which cannot be generic in current version of Swift), and ValueProvider<String> is illegal as a property type.


It looks to me I basically have two options:


(1) Make a new generic ThingProperty<T> enum, use that as the type of the "position" and "name" properties. The enum will have a value provider as its associated value. I don't like this solution because it forces me to change my model only because the type system cannot express what I want, although the runtime shouldn't have any problem with what I want. It feels like changing my model just to make the typechecker happy, which is absurd.


(2) Instead of "ValueProvider<CGPoint>", I can just use "ValueProvider" as the type of the position (and name) properties. This works and allows me to keep the model simple, but I loose the benefits of static typing to a large degree - the system doesn't know that the "position" and "model" properties can only hold CGPoint and String value providers, respectively. As a consequence, when I call valueInContext method on those properties, type checker wouldn't know the correct type of the return value.


What's the "correct" approach to take here? Am I missing something?

Accepted Reply

Mike, you do point out a really problematic shortcoming of Swift's current type system. I've written about it in an earlier thread with the title "Workarounds for lack of parameterized protocols?".


The folks from Apple working on the standard library were facing this problem all the time as well. They invented a design pattern as a workaround. I'll explain it using your example but using Apple's naming conventions.


Let's assume you want to define a protocol like ValueProviderType which abstracts over a type. You do this in Swift via an associated type:


protocol ValueProviderType {
  typealias Value
  func valueAtTime(time: Time) -> Value
}


This protocol isn't very useful as a type. You can basically only use it as a type constraint, but not standalone. This limitation can be worked around (with a hack) by defining a struct AnyValueProvider which wraps any implementation of ValueProviderType. This struct hides the concrete implementation of the value provider while providing a usable standalone type. Here's a way to implement AnyValueProvider:


struct AnyValueProvider<T>: ValueProviderType {
  let _valueAtTime: (Time) -> T
  init<V: ValueProviderType where V.Value == T>(_ delegatee: V) {
    _valueAtTime = delegatee.valueAtTime
  }
  func valueAtTime(time: Time) -> T {
    return _valueAtTime(time)
  }
}


I have not found a good general way to implement such wrappers, but the approach above using closures should be good enough in most cases (this is discussed in the thread "Workarounds for lack of parameterized protocols?").


Now with AnyValueProvider, you finally have a useful type which you have to use whenever you want to refer to a variable, a parameter, etc. of a concrete value provider. Here's an adaptation of your code:


typealias Time = Int
typealias Color = String
struct Car {
  var color: AnyValueProvider<Color>? = nil
  var position: AnyValueProvider<Double>? = nil
  init () {}
  func draw(time: Time) { }
}


Your existing value provider implementations don't need to be changed. They can stay:


struct ConstantValue<T>: ValueProviderType {
  var value: T
  init(_ value: T) {
    self.value = value
  }
  func valueAtTime(time: Time) -> T {
    return value
  }
}
struct TimeDependentValue<T>: ValueProviderType {
  var min: T
  var max: T
  init(_ min: T, _ max: T) {
    self.min = min
    self.max = max
  }
  func valueAtTime(time: Time) -> T {
    return min  // just for the example
  }
}


Whenever you need to create an instance of AnyValueProvider, you can simply pass your real value provider implementation to its constructor:


var carA = Car()
carA.position = AnyValueProvider(ConstantValue<Double>(123))
carA.color = AnyValueProvider(ConstantValue<Color>("red"))
var carB = Car()
carB.position = AnyValueProvider(TimeDependentValue<Double>(0, 123))
carB.color = AnyValueProvider(TimeDependentValue<Color>("red", "green"))


Does this explain how this pattern works? If you want more inspiration on how Apple uses this pattern, you can check out the various Any* classes provided by Apple in the standard library.


And if there's anyone who has a good idea how to implement those Any* wrappers, please let me know. I'm still very interested in this.

Replies

I ran into the same problem (and I am also looking for a better solution).


What I did as a work around is create separate protocols for each data type. So I have a "StringValueProvider" and a "IntValueProvider". This is annoying, and it only works because I have 4 data types that I need to work with. If you only have a few data types, it may work as a workaround for you as well.

Regarding this ... thing:

struct Thing
{
     var position: ValueProvider<CGPoint>
     var name: ValueProvider<String>
     ...
}


If I understand your requirements correctly, you want eg position to be something of any type T that conforms to the ValueProvider protocol and whose ValueProvider.Value is CGPoint? (there could of course be lots of such types) Will that type T be known at compile time?


If so, should position be able to change into a value of some other type than T at runtime, or will it always stay the same type for a particular instance of Thing? If, for example some Thing is initialized with position being of the type MidPointValueProvider, should it then remain a MidPointValueProvider or could it change into say a LineIntersectionPointValueProvider?


What I mean is, could you use something like this:

struct Thing<P, N where
    P: ValueProvider, P.ValueType == CGPoint,
    N: ValueProvider, N.ValueType == String
    >
{
    var position: P
    var name: N
}

?


I guess this could be a case where it is easy to get confused about what will work dynamically and/or statically, especially if coming from Objective C where people are used to letting the runtime do almost everything (dynamically), wheras in Swift we have a lot more ways to do things statically, which will of course not always be suitable for doing things dynamically and vice versa. And then you should probably search for and watch the talk entitled "Zero-Cost Abstractions" by Airspeed Velocity.

I need to have this dynamic, i.e. what concrete type of value provider is there can change at runtime.


Ideally, I would write ValueProvider<Double> as the type of the property and this will mean that at runtime, only value providers that provide Double values will be assigned to that property. But I do not know which particular value providers would that be, and they may change at runtime (provided they are all Double value providers).

So it needs to be dynamic, yet you want the generic (well, "associated type possessing") ValueProvider protocol to be at the heart of it?


Isn't this something that you should simply be doing dynamically, without using too much compile time / static features of the lnaguage (like eg generics)?


I can't see any point in "faking" compiler knowledge where there can't possibly be any. If the compiler can't possibly know, you'll of course just have to do things dynamically (something that people have always been doing in eg Objective C, even for problems that would have been better solved statically).


So isn't this where you should stop and think about the following words from the notes of the Airspeed Velocity talk I referred to above?


"The answer they get back is usually not very satisfying, because generics belong to the compile time. They’re designed to be used with static types, and mixing static generics with dynamic types at runtime is rather like mixing steak and ice cream. Steak is great. Ice cream is great. Steak with ice cream, well, it’s bit unpleasant."

This is a misunderstanding. Let me explain.


Lets say you have 2 structs, Constant and Variable:


protocol ValueProvider { ... }


struct Constant<T>: ValueProvider { ... }

struct Variable<T>: ValueProvider { ... }


Now, in my Thing struct, I want to have properties like this:


struct Thing

{

var rotation: ValueProvider<Double>

}


I don't know whether the rotation property will be Constant<Double> or Variable<Double>, but I want to be able to express that it will use Double, because I can guarantee that, even at compile time. However, Swift doesn't allow me to use ValueProvider<Double>, because it lacks generic protocols.


In short, I want to express the compile-time knowledge I have statically (that the structs conforming tothe ValueProvider protocol here should deal with Doubles), but I want to leave the exact type of the value provider itself (Constant<Double> or Variable<Double>) unspecified till runtime (because I don't know that beforehand).

That is excactly the problem I currently have with swift. Coming from java I like to use many interfaces when designing my class architecture. In java those can be generic and can be stored in collections and declared as type of a variable.

Espacially as swift promotes "protocol oriented programming" I am very confused what the (practical or theoretical) problem with generic protocols would be, eg why they are not implemented.


Every time I try to port one of my java projects to swift I stumble uppon this issue and for me it really seams like a show stopper as it forces me to change my whole class design WITHOUT any comprehensible reason.

Well changing from the horrible Java-mindset would be first on my list 😉


Generic protocols sound strange. Protocols have no type so how could they be generic type?

What would you suggest as the alternative solution for the posed question? I (and many others) are looking for a canonical, elegant, Swift-like way of solving this problem. Your "Changing from the horrible Java-mindset" suggestion isn't particularly helpful, can you please be more specific?

Would it be possible for you to turn your example scenario into something even simpler but also more concrete and self contained? That way maybe people here will find it easier to suggest more concrete solutions (in the form of a short program).

Yeah my problem exactly. I don't get what the example should accomplish.


It is happaning with a lot of threads since the start of the new forum - People throw some strange non working code in the thread and expects people to understand what it is supposed to do. It's kinda frustrating, we had always such nice and productive talk in the old Swift subforum.

OK, I'll try to explain this better.


Imagine you have a Car struct. The car can draw itself at a particular time.


You have array of cars in your model. Some of those cars should be green. Some of them should be red.

Some of them should move as time passes. Some of them should stay in the same place as time passes.

Some of them should turn from green to red as time passes. Some of them should stay the same color.


struct Car {
     var color: ValueProvider<Color>
     var position: ValueProvider<Double>

     func draw(time: Time) { ... }
}

protocol ValueProvider<T> {
     func valueAtTime(time: Time) -> T
}

struct ConstantValue<T>: ValueProvider<T> {
     var value: T
     init(value: T) {
          self.value = value
     }
     func valueAtTime(time: Time) -> T {
          return value
     }
}

struct TimeDependentValue<T>: ValueProvider<T> {
     var min: T
     var max: T

     init(min: T, max: T) {
          self.min = min
          self.max = max
     }

     func valueAtTime(time: Time) -> T {
          return interpolatedValue(min, max, time)
     }
}

Then, you can create clocks like this:


var carA = Car()
carA.position = ConstantValue<Double>(123)
carA.color = ConstantValue<Color>(red)

var carB = Car()
carB.position = TimeDependentValue<Double>(0, 123)
carB.color = TimeDependentValue<Color>(red, green)


When I put these two cars in an array and iterate the array

periodically, drawing the cars, carA will always have the same color

and won't move. CarB, on the other hand, would change its color from red to green

and move from position 0 to position 123.


Crucially, I need to be able to do this at runtime:


carA.position = TimeDependentValue<Double>(23, 45)


Here, I changed carA's position from a constant value to an animated, time dependent value.


However, due to the lack of generic protocols, I cannot do this:


struct Car {
     var color: ValueProvider<Color>
}


(ValueProvider is a protocol).


That means that I simply have no way of expressing that the color property of the car should be any ValueProvider, but that ValueProvider also has to provide Colors.


I hope this makes more sense now.

Sadly not really. What I understand is that you want to fight the type system with some strange dynamic type magic 😕


Protocols are there to define and add behaviour - not type system magic.

I think the replies you're getting are a little bit harsh, but there are some larger issues to think about:


1. At least in your original example, you wanted protocols that differed only in the type on which they were based. In spite of recent enhancements, protocols are really about behavior, and it's at best questionable whether your various ValueProviders have different behavior when they just operate on different types. In a way, it's exactly the point of Swift protocols that the type represented by your ValueType typealias isn't part of the ValueProvider behavior.


2. There's an issue about the difference between generics (as in Swift), versus templates (as in C++). I think this has been discussed in the forums several times in the past. In a way, you're asking for a ValueProvider template, not a ValueProvider protocol.


3. As well as trying to define conformance, in your scenario you are also trying to implement something like inheritance for ValueProvider. It may be that the correct solution is to make your value provider implementations be classes, not structs, and to use actual inheritance.

Please read the code again. The code I wrote doesn't express anything that couldn't (in principle) be determined at compile time.


Heck, I can even express this in Swift itself if I just use classes instead of structs and use a generic superclass instead of the generic protocol!

But I want value semantics.


Please note that C++, C# and Java all support generic protocols / interfaces. This can also be expressed in Haskell type system. I, jonhull, laszlokorte and many other people are searching for a nice alternative to this in Swift.

Thanks Quincey :-)


(1) Ok, I can understand this angle of reasoning. Jonhull's suggestion of declaring separate ValueProvideDouble, ValueProviderInt, ValueProviderString protocols would then be the most Swift-like solution. Hopefully we can all agree that declaring a bunch of protocols like that looks like code smell and a potential candidate for language-level solution or deeper refactoring of the code.


(2) This might be the case, although I do not have experience with C++ templates. However, as I wrote above, C# and Java have equivalents of generic protocols/interfaces.


(3) As you correctly pointed out, this can be done in Swift using classes. Instead of the generic protocol, I can use a generic superclass (which Swift supports) and it will work just as I want it to.


Unfortunately, I also need value semantics, which is why I'm trying to do this with structs.


Also, there is a subtle issue - I don't really need the implementation inheritance. The superclass would have empty implementation, which is usually a pretty clear indication that you should use protocol instead of superclass.