1

Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?

instantiate from protocol.Type reference dynamically at runtime

public protocol ISpeakable {
    init()
    func speak()
}

class Cat : ISpeakable {
    required init() {}
    func speak() {
        println("Meow");
    }
}

class Dog : ISpeakable {
    required init() {}
    func speak() {
        println("Woof");
    }
}

//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
    func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
        let animal = Animal()
        animal.speak()
    }
}

// Users of the Test class are aware of the specific implementations at compile/runtime

//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)

//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]

for animal in animals {
    //t.instantiateAndCallSpeak(animal) //throws error
}

for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
    //t.instantiateAndCallSpeak(value) //throws error
}

Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.

Note (background): these types are pushed into array by users of the utility and for loop is executed on notification

var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]

for Animal in animals {
    if Animal is Cat.Type {
        if let AnimalClass = Animal as? Cat.Type {
            var instance = AnimalClass()
            instance.speak()
        }
    } else if Animal is Dog.Type {
        if let AnimalClass = Animal as? Dog.Type {
            var instance = AnimalClass()
            instance.speak()
        }
    }
}
Community
  • 1
  • 1
user2727195
  • 7,122
  • 17
  • 70
  • 118
  • It's a design pattern/philosophy, doesn't matter Java, .NET - C# etc., ActionScript etc. – user2727195 Dec 19 '14 at 18:17
  • I know that. But it is commonly mentioned in many Java books, hence the comment. – Unheilig Dec 19 '14 at 18:19
  • I see, I'm blocked on this, a final nail in the coffin and I'm done, building some design pattern utilities, I reached out to Chris Lattner on this as well, do you think it's a bug in the compiler? – user2727195 Dec 19 '14 at 18:22
  • What is the question? – matt Dec 19 '14 at 18:50
  • BTW the collection is a red herring. It's the "supertype" that's the problem. You can get the same error more simply just by saying `let Speaker : ISpeakable.Type = Cat.self; t.instantiateAndCallSpeak(Speaker)` – matt Dec 19 '14 at 18:58
  • @matt question is basically to accept references to user created classes, instantiate them and call a specific method on them. It's a Macro command design pattern. the classes are created by users of the API and are conforming to a protocol. – user2727195 Dec 20 '14 at 00:33
  • Right, but I think you've proved beautifully that Swift doesn't do that. So my question was, what else is there to know? Generics in Swift are not all one might hope for... :( – matt Dec 20 '14 at 01:26
  • yes seems like no way out "Yet" to implement macro command pattern in SWIFT, added an edit to my question, a limited workaround actually, I'm still not there yet but hopeful that I'll be... – user2727195 Dec 20 '14 at 10:49

1 Answers1

7

Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.

This can be a little clearer if we boil it down to a minimal test case

protocol Creatable { init() }

struct Object : Creatable { init() {} }

func instantiate<T: Creatable>(Thing: T.Type) -> T {
    return Thing()
}

// works. object is of type "Object"
let object = instantiate(Object.self)   // (1)

// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type)  // (2)

At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.

At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.

This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:

When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)

You'll need to do whatever you're trying to do another way in Swift.

As a fun bonus, try explicitly asking for the impossible:

let thing = instantiate(Creatable.self)

And... swift crashes.


From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):

// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing" 
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
    func speak()
}

// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
    func speak() {
        println("Meow");
    }
}

struct Dog : Speaking {
    func speak() {
        println("Woof");
    }
}

// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
    let name: String
    func speak() {
        println("My name is \(name)")
    }
}

// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
    let animal = builder()
    animal.speak()
}

// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. @autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }

// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]

for animal in animalBuilders {
    instantiateAndCallSpeak(animal)
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks Rob for the in-depth sharing of knowledge, so talking about another way in SWIFT, I'm still meditating on this. If I can tell what I need in one line, it'd be "have this utility store references to a set of classes in an array and call a specific method on them", only callers of the API knows those classes as they are user created, it's a Macro Command Design pattern I'm trying to implement in SWIFT, any lateral thinking Rob? – user2727195 Dec 20 '14 at 00:29
  • First and foremost: Do you absolutely need classes? Could you achieve everything you want with functions (closures)? Rather than "calling a specific method," just pass the function that would be that method. You may be too trapped in object-oriented thinking. Closures are often ideal for the Command pattern (which is why Cocoa has been moving towards them even in ObjC). In particular you should read Ole's post on this pattern with curried functions: http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/ – Rob Napier Dec 20 '14 at 01:10
  • yes and I'm realizing that there's no way out... I've added an edit section for my current approach but that involves type checking and casting and obviously it's limiting as the pattern api has to know all kinds of implementations, limits people using this utility to confine themselves to these types only. For the time being it will do the job. I've looked at curried functions, it's similar to what we have in javascript `functionName.call(this, params)` and `functionRef.apply(this, [params])`. Target action pattern... (continues) – user2727195 Dec 20 '14 at 10:17
  • Target action pattern seems very domain specific, not only that, the pattern still has to know all sorts of implementations, or the actor itself is instantiating related pieces, this pattern is ideal and usable within (ViewComponent -> Mediator pattern by GoF). – user2727195 Dec 20 '14 at 10:22
  • the objective of macro command that i've to follow is to cache references to a set of commands (non instantiated) and only instantiate and execute if notification is fired. It's more higher level, the curried functions are more inner level for the view component (View tier of mvc -> Mediator -> view component), right now I think I've to live with the workaround with specified set of types, reason i've to stick to classes as there will be async command implementations, but hopefully we can brainstorm some more ideas on my edited section above – user2727195 Dec 20 '14 at 10:29
  • Updated based on your comments. – Rob Napier Dec 20 '14 at 14:48
  • You're a rock star :), that's what I call lateral thinking... change gears, steer in another direction and get to your destination. – user2727195 Dec 20 '14 at 20:32
  • one last thing... do you see any problem with this line `var commands: [String: () -> Command] = [String: () -> Command]()` Error - Expected ',' separator – user2727195 Dec 20 '14 at 21:06
  • That should be "let commands: [()->Command] = [ ... closures that return Command ...]" – Rob Napier Dec 21 '14 at 03:26
  • 1
    actually it's implemented as a dictionary, turns out it's a compiler bug, `var commands: [String: () -> Command] = [:]` worked instead – user2727195 Dec 21 '14 at 08:02
  • Hi Rob, quick question, what's the naming convention in SWIFT world for parameters that receive a closure with a constructor call returning object. in original implementation param name was `commandClassRef` but here technically it's not a reference to a class, it's a closure that instantiates and returns an object. I tried `commandRef` instead, any thoughts around the name `commandClassRef`please – user2727195 Dec 22 '14 at 23:36
  • btw I forgot to clear, yes the commands (subcommands) itself are in an array, but the macro command is dictionary based, mapped to a notification name at application level – user2727195 Dec 22 '14 at 23:42
  • Ref would be confusing. That means "Core Foundation object" or at the very least "pointer." I haven't seen a standard naming for that type. In Swift I would use naming like: addCommand(createdWith:). Cocoa focuses on the clarity of the method name itself, not the parameter name in isolation. – Rob Napier Dec 23 '14 at 13:39
  • Hi Rob, have to reach out to you for some lateral thinking to achieve it through some other means (just like you solved this one question with closures) instead of hard coding type inspection thing, please advise, http://stackoverflow.com/questions/27753405/determining-protocol-types-at-runtime/ – user2727195 Jan 04 '15 at 09:44