99

Let's say I have a protocol :

public protocol Printable {
    typealias T
    func Print(val:T)
}

And here is the implementation

class Printer<T> : Printable {

    func Print(val: T) {
        println(val)
    }
}

My expectation was that I must be able to use Printable variable to print values like this :

let p:Printable = Printer<Int>()
p.Print(67)

Compiler is complaining with this error :

"protocol 'Printable' can only be used as a generic constraint because it has Self or associated type requirements"

Am I doing something wrong ? Anyway to fix this ?

**EDIT :** Adding similar code that works in C#

public interface IPrintable<T> 
{
    void Print(T val);
}

public class Printer<T> : IPrintable<T>
{
   public void Print(T val)
   {
      Console.WriteLine(val);
   }
}


//.... inside Main
.....
IPrintable<int> p = new Printer<int>();
p.Print(67)

EDIT 2: Real world example of what I want. Note that this will not compile, but presents what I want to achieve.

protocol Printable 
{
   func Print()
}

protocol CollectionType<T where T:Printable> : SequenceType 
{
   .....
   /// here goes implementation
   ..... 
}

public class Collection<T where T:Printable> : CollectionType<T>
{
    ......
}

let col:CollectionType<Int> = SomeFunctiionThatReturnsIntCollection()
for item in col {
   item.Print()
}
Tamerlane
  • 2,031
  • 3
  • 21
  • 34
  • 1
    Here is a relevant thread on the Apple Developer Forums from 2014 where this question is addressed (to a degree) by a Swift developer at Apple: https://devforums.apple.com/thread/230611 (Note: An Apple Developer Account is required to view this page.) – titaniumdecoy Jan 04 '16 at 04:57

3 Answers3

95

As Thomas points out, you can declare your variable by not giving a type at all (or you could explicitly give it as type Printer<Int>. But here's an explanation of why you can't have a type of the Printable protocol.

You can't treat protocols with associated types like regular protocols and declare them as standalone variable types. To think about why, consider this scenario. Suppose you declared a protocol for storing some arbitrary type and then fetching it back:

// a general protocol that allows for storing and retrieving
// a specific type (as defined by a Stored typealias
protocol StoringType {
    typealias Stored

    init(_ value: Stored)
    func getStored() -> Stored
}

// An implementation that stores Ints
struct IntStorer: StoringType {
    typealias Stored = Int
    private let _stored: Int
    init(_ value: Int) { _stored = value }
    func getStored() -> Int { return _stored }
}

// An implementation that stores Strings
struct StringStorer: StoringType {
    typealias Stored = String
    private let _stored: String
    init(_ value: String) { _stored = value }
    func getStored() -> String { return _stored }
}

let intStorer = IntStorer(5)
intStorer.getStored() // returns 5

let stringStorer = StringStorer("five")
stringStorer.getStored() // returns "five"

OK, so far so good.

Now, the main reason you would have a type of a variable be a protocol a type implements, rather than the actual type, is so that you can assign different kinds of object that all conform to that protocol to the same variable, and get polymorphic behavior at runtime depending on what the object actually is.

But you can't do this if the protocol has an associated type. How would the following code work in practice?

// as you've seen this won't compile because
// StoringType has an associated type.

// randomly assign either a string or int storer to someStorer:
var someStorer: StoringType = 
      arc4random()%2 == 0 ? intStorer : stringStorer

let x = someStorer.getStored()

In the above code, what would the type of x be? An Int? Or a String? In Swift, all types must be fixed at compile time. A function cannot dynamically shift from returning one type to another based on factors determined at runtime.

Instead, you can only use StoredType as a generic constraint. Suppose you wanted to print out any kind of stored type. You could write a function like this:

func printStoredValue<S: StoringType>(storer: S) {
    let x = storer.getStored()
    println(x)
}

printStoredValue(intStorer)
printStoredValue(stringStorer)

This is OK, because at compile time, it's as if the compiler writes out two versions of printStoredValue: one for Ints, and one for Strings. Within those two versions, x is known to be of a specific type.

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • 25
    In other words there is no way to have generic protocol as a parameter and the reason is that Swift does not support .NET style runtime support of generics ? This is quite inconvenient. – Tamerlane Dec 31 '14 at 20:40
  • My .NET knowledge is a bit hazy... do you have an example of something similar in .NET that would work in this example? Also, it's a little hard to see what the protocol in your example is buying you. At runtime, what would you expect the behavior to be if you assigned printers of different types to your `p` variable and then passed invalid types into `print`? Runtime exception? – Airspeed Velocity Dec 31 '14 at 21:02
  • @AirspeedVelocity I have updated the question to include C# example. Well as for the value why I need it is that this will allow me to develop to an interface not to the implementation. If I need to pass printable to a function I can use interface in declaration and pass many difference implementations without touching my function. Also think about implementing collection library you will need this kind of code plus additional constraints on type T. – Tamerlane Dec 31 '14 at 23:15
  • 1
    OK the C# example is a bit different. There, you are kind of partially-specifying the protocol. In Swift, the equivalent would be to create a generic function, and to constrain the type of the function through a where clause (see my 2nd answer) – Airspeed Velocity Jan 01 '15 at 00:07
  • Couldn't "someStorer.getStored()" return SomeType or smth? – ULazdins Aug 31 '15 at 08:22
  • @ULazdins Even better than SomeType, it is possible to infer that someStorer is an Either – Patrick Goley Jan 07 '16 at 04:32
  • 5
    Theoretically, if it was possible to create generic protocols using angle brackets like in C#, would creation of variables of protocol type will be allowed? (StoringType, StoringType) – GeRyCh Dec 07 '16 at 09:18
  • 1
    In Java you could do the equivalent of `var someStorer: StoringType` or `var someStorer: StoringType` and solve the problem you outline. – JeremyP Sep 28 '17 at 14:23
  • "This is OK, because at compile time, it's as if the compiler writes out two versions of `printStoredValue:` one for `Int`, and one for `String`." Is there anyway I can verify this (within Xcode build log, `swiftc`, etc.) ? – Quang Vĩnh Hà Jan 16 '19 at 14:57
52

There is one more solution that hasn't been mentioned on this question, which is using a technique called type erasure. To achieve an abstract interface for a generic protocol, create a class or struct that wraps an object or struct that conforms to the protocol. The wrapper class, usually named 'Any{protocol name}', itself conforms to the protocol and implements its functions by forwarding all calls to the internal object. Try the example below in a playground:

import Foundation

public protocol Printer {
    typealias T
    func print(val:T)
}

struct AnyPrinter<U>: Printer {

    typealias T = U

    private let _print: U -> ()

    init<Base: Printer where Base.T == U>(base : Base) {
        _print = base.print
    }

    func print(val: T) {
        _print(val)
    }
}

struct NSLogger<U>: Printer {

    typealias T = U

    func print(val: T) {
        NSLog("\(val)")
    }
}

let nsLogger = NSLogger<Int>()

let printer = AnyPrinter(base: nsLogger)

printer.print(5) // prints 5

The type of printer is known to be AnyPrinter<Int> and can be used to abstract any possible implementation of the Printer protocol. While AnyPrinter is not technically abstract, it's implementation is just a fall through to a real implementing type, and can be used to decouple implementing types from the types using them.

One thing to note is that AnyPrinter does not have to explicitly retain the base instance. In fact, we can't since we can't declare AnyPrinter to have a Printer<T> property. Instead, we get a function pointer _print to base's print function. Calling base.print without invoking it returns a function where base is curried as the self variable, and is thusly retained for future invocations.

Another thing to keep in mind is that this solution is essentially another layer of dynamic dispatch which means a slight hit on performance. Also, the type erasing instance requires extra memory on top of the underlying instance. For these reasons, type erasure is not a cost free abstraction.

Obviously there is some work to set up type erasure, but it can be very useful if generic protocol abstraction is needed. This pattern is found in the swift standard library with types like AnySequence. Further reading: http://robnapier.net/erasure

BONUS:

If you decide you want to inject the same implementation of Printer everywhere, you can provide a convenience initializer for AnyPrinter which injects that type.

extension AnyPrinter {

    convenience init() {

        let nsLogger = NSLogger<T>()

        self.init(base: nsLogger)
    }
}

let printer = AnyPrinter<Int>()

printer.print(10) //prints 10 with NSLog

This can be an easy and DRY way to express dependency injections for protocols that you use across your app.

Patrick Goley
  • 5,397
  • 22
  • 42
  • Thanks for this. I like this pattern of type erasure (using function pointers) better than using an abstract class (which of course, doesn't exist and must be faked using `fatalError()`) that's described in other type erasure tutorials. – Chase Finch Feb 17 '20 at 17:26
4

Addressing your updated use case:

(btw Printable is already a standard Swift protocol so you’d probably want to pick a different name to avoid confusion)

To enforce specific restrictions on protocol implementors, you can constrain the protocol's typealias. So to create your protocol collection that requires the elements to be printable:

// because of how how collections are structured in the Swift std lib,
// you’d first need to create a PrintableGeneratorType, which would be
// a constrained version of GeneratorType
protocol PrintableGeneratorType: GeneratorType {
    // require elements to be printable:
    typealias Element: Printable
}

// then have the collection require a printable generator
protocol PrintableCollectionType: CollectionType {
    typealias Generator: PrintableGenerator
}

Now if you wanted to implement a collection that could only contain printable elements:

struct MyPrintableCollection<T: Printable>: PrintableCollectionType {
    typealias Generator = IndexingGenerator<T>
    // etc...
}

However, this is probably of little actual utility, since you can’t constrain existing Swift collection structs like that, only ones you implement.

Instead, you should create generic functions that constrain their input to collections containing printable elements.

func printCollection
    <C: CollectionType where C.Generator.Element: Printable>
    (source: C) {
        for x in source {
            x.print()
        }
}
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • Oh man this looks sick. What I needed is just to have protocol with generic support. I was hoping to have something like this : protocol Collection : SequenceType. And that's it. Thanks for the code samples I think it will take a while to digest it :) – Tamerlane Jan 01 '15 at 00:26