Exploring Swift’s Generics: Part 1

I think using magic for my generics examples captures the mystical aura that surrounds Swift’s generic capabilities. Some of the behaviors are very different than what I’m used to seeing in other languages.

Let’s build some context.

Define a base class for magic:

class Magic {
    func cast() -> String {
        return ""
    }
}

 
Define base classes for different categories of magic:

class BlackMagic: Magic {
    override func cast() -> String {
        return ""
    }
}

class WhiteMagic: Magic {
    override func cast() -> String {
        return ""
    }
}

 
Implement some black magic:

class Fire: BlackMagic {
    override func cast() -> String {
        return "Fire!"
    }
}

class Ice: BlackMagic {
    override func cast() -> String {
        return "Ice!"
    }
}

 
Implement some white magic:

class Cure: WhiteMagic {
    override func cast() -> String {
        return "Cure!"
    }
}

class Antidote: WhiteMagic {
    override func cast() -> String {
        return "Antidote!"
    }
}

 
And, finally, define a magic user:

class Mage<T: Magic> {
    
    func castAll(items: [T]) {
        for item in items {
            println(item.cast())
        }
    }
}

 

Now that we have plenty of boiler plate code, let’s look at something a little more interesting. I want to create a mage that can only cast black magic. My initial thought was to do something like this:

class BlackMage : Mage<BlackMagic> { }

 
Unfortunately, in Swift this causes the following error: “classes derived from generic classes must also be generic” Hrm…

You may be thinking that creating the BlackMage class isn’t necessary. After all, we can just specify the type when we create our variable:

var blackMage = Mage<BlackMagic>()

 
But, for the sake of argument, let’s assume that I want to have functions on my BlackMage class that aren’t on my WhiteMage class and vice versa.

So, it is required that classes derived from a generic class also be generic classes. Can I assume that the generic type for my derived class is related to the base class’s generic type? Nope.

class MeaningOfLife {
    func answer() -> Int { return 42 }
}

class BlackMage<T: MeaningOfLife> : Mage<BlackMagic> {
    func compute(meaningOfLife: T){
        meaningOfLife.answer()
    }
}

BlackMage<MeaningOfLife>().castAll([Fire(), Ice()])
BlackMage<MeaningOfLife>().compute(MeaningOfLife())

 
Why does a derived class have to be generic if it’s base class is generic (especially when the derived class’s generic type has nothing to do with the base classes generic type)? I don’t have a clue… If any knowledgeable readers of the blog would like to chime in, I’d be happy to learn something from them.

The takeaway is this: when specifying a generic type for a class that derives from another generic, the specified type has no impact on the base class–it only specifies the type for generics in the derived class.

You may be wondering why I didn’t use protocols for Magic, BlackMagic, and WhiteMagic. It seems that using protocols also has some interesting effects on generics, but I’m going to save that for another post.

  • S. Owen, Jr.

    I commend you heartily on your tutorial zeal. You are, indeed, talented. Code text could be a little larger w.r.t. other text. Regards, SMOjr.