What do "void *" (a la C), polymorphism (a la C++ classes), interfaces (a la Java), generics (a la C++ templates), and duck typing (a la Python) all have to do with one another? They're all ways in which you can write code that works with types that you didn't envision when writing the code.



A "void *" in C is a pointer to something of unspecified type. You can't do very much with it unless you know what type the something is. However, you can still pass it around. You can store it in a list or tree. You can take it and later pass it back to a callback function. All of these things are useful, and, in fact, this functionality still exists in Java (albeit, it's a lot safer in Java). However, instead of casting to "void *", you cast to "Object".



Polymorphism in languages like C++ and Java let you take an object and call methods on it without necessarily knowing exactly which subclass the object is a member of. Let's suppose there is a class named Fruit with a method named peel, and let's suppose there are two subclasses named Apple and Orange. If you have a list of apples and oranges, you can loop over that list and call peel on each fruit. Even better, if someone later creates a Lemon class, and slips a few lemons into that list, your code will still know how to peel them.



However, what if you don't want to subclass Fruit? What happens if you have an object that knows how to peel itself as well as a ton of other things? Do you need to subclass multiple classes? An interface in Java (or a typeclass in Haskell) lets you say that your object knows how to peel itself, without requiring any specific subclassing. Instead, it can implement some Peelable interface, and that's close enough. Hence, instead of peeling a list of fruit, your code can now peel a list of objects that each implement the Peelable interface. Those objects might not be related at all, and they're free to implement all sorts of interfaces aside from just the Peelable interface.



Generics, which are called templates in Java and C++, let you write code and leave blanks in it that can be filled in later. Generics are an interesting subject, and the question really comes down to what kind of stuff can you leave blank?



Generics in Java are actually pretty weak. It use to be that if you wanted a list, you had to have a list of Objects (remember the "void *" trick?). You didn't know exactly what was in the list. These days, with Java templates, you can tell the compiler that you're creating a list, and that the list can only contain Apples. The list is a template, and you're "filling in the blank" with the type Apple. However, templates in Java are limited. For instance, you can't create a template that says "Create a new [blank]". (If I'm wrong, please leave a comment!)



C++ templates are more powerful. You can do all the same things that you can do in Java, but you can also do things like create a template that says "Create a new [blank]". The differences have to do with how the compiler implements templates. When you tell the compiler that you want to "fill in the blank" with an Apple, i.e. "Create a new Apple", that's called instantiating the template. By the way, this is something that happens at compile time. Now, let's suppose you have a template for lists, and you want a "list of Apple" and a "list of Orange". One way the compiler can implement this is to take the code for list and fill in all the blanks with Apple, then take the code for list and fill in all the blanks with Orange. You'd end up with two slightly different versions of the same code in the compiled binary. I don't know how modern C++ compilers do it, and feel free to call me ignorant, but it really makes me suspicious when I see how big C++ binaries are compared to C binaries ;)



Generics in functional programming languages like Haskell are even more impressive. If a function takes a fruit and then peels it, Haskell can automatically figure out that the function will work with any object that can be peeled. The impressive thing is that it can in many cases automatically infer this interface at compile time! You don't even have to tell the compiler that you're trying to write generic code. (Note, I'm handwaiving a little about when you do and don't need to use type classes.)



Duck typing (also known as latent typing) achieves the same goal, but does so using runtime checks. Hence, if you write a function that takes an object and peels it, you don't need to subclass anything or write an interface. However, at runtime, the interpreter will figure out if the object actually knows how to peel itself. On the one hand, you don't get as many compile-time safety checks, but on the other hand, it's really easy to understand. You can accept whatever objects you want, and call any methods you want, and if it doesn't actually work at runtime, you'll get a nice exception that you can respond to in a controlled manner. There's an old joke that says that C++ is like juggling chainsaws in full body armor, whereas Python is like juggling rubber chickens. Even better, you can do tricks like have the same object respond to any method. For instance, you can call any method on a proxy object, and it will just proxy that method call to the object it is acting as a proxy for. The same proxy object can proxy any object with any interface.



Ok, that was a pretty basic overview of a bunch of related language features. As I said, they're all ways in which you can write code that works with types that you didn't envision when writing the code. Now, take a minute and think about that problem and the many different ways to solve it. If you wrote a new language, how might you solve it differently? Leave me a comment below!

Labels