Vy a Duck?
by Judith Dinowitz
There's been a lot of talk in the blogs lately, between Hal Helms, Sean Corfield and Joe Rinehart, about
duck typing, but like me, you might have been a bit confused as to what duck typing is and why ColdFusion programmers should care. As Hal Helms is doing a session on duck typing at
cf.Objective() and
CFUNITED, I figured that he could shed some light on the subject.
Hal had a lot of good information to impart, but before I give you his explanation of duck typing, I'd like to set the context of the discussion with a short lesson on the nature of Java.
Java and Static Typing
If I were a Java programmer, and I wanted to create an object, I would create my object class, name my methods, and specify a type for my object and for any arguments that my methods were expecting. Java is a statically typed language, which means it requires strict typing of all of its variables and objects.
Here's an example of a Java class.
public class Teacher{
public void addStudent(Student aStudent){
// code omitted
}
public Book assignReading(){
return this.book;
}
}
The class, Teacher, is a data type. The method, addStudent, specifies that it returns nothing ("void") and accepts an argument of type Student. The method, assignReading, accepts no arguments, but returns an object of type Book.
With the class created, we're ready to use it to create an object:
public Student judith = new Student();
Before the name of the variable, judith, we must provide the variable's type -- in this case a Student.
Hal explained that this explicit typing allows Java to ensure type safety. The Java compiler can eliminate an entire group of errors that could occur if you were to call a method and pass an argument that the method doesn't support. In terms of our example, if we were to call Teacher's "addStudent" method and pass it something other than a Student, the code would not compile.
"Static typing, which Java does, is an incredible thing," Hal said. "Because Java has a compiler checking these things, it won't allow you to call a method on an object and pass in an argument that that method doesn't support. It double-checks these things. But because we don't have a compiler in ColdFusion, we have none of the benefits of this rigid typing, but all of the minuses."
What are the minuses? Be patient, be patient... We'll get to that soon.
ColdFusion and Duck (or Dynamic) Typing
Unlike Java, ColdFusion does not support static typing, but it can mimic it. We do this by setting a type attribute in the CFArgument tag to validate that the data being passed in for that argument is for a specific type. This checks the data's type -- but only at run time, when the program is actually being executed.
"You can get the illusion of static typing by using the 'returntype' property of methods and the 'type' property of arguments," Hal explained, "but in doing this, you'll buy a number of really thorny problems and you'll get none of the benefits."
I'll be discussing those problems soon.
As Hal put it, the name, duck typing, was coined in the Ruby community "as a semi-humorous attempt to say that, regardless of what class an object belongs to, its essential character -- its type -- is determined by the messages that it can respond to. As in, 'if it walks like a duck, talks like a duck, and quacks like a duck -- it's a duck!'"
The Liskoff Substitution Principle
By now you're probably scratching your head. So what if we do use the "returntype" property of methods and the "type" property of arguments to validate our data types? What's the big deal? Isn't type safety a good thing in Java, and shouldn't we try to emulate that in ColdFusion?
You need one more piece of the puzzle in place before you can understand why Hal Helms is so adamantly against mimicking static typing in ColdFusion. That piece is the
Liskoff Substitution Principle.
This principle states that a method that accepts an object of a particular type can, instead, accept a subtype of that object. For example, if you're expecting your argument to be a type of "Animal," and your argument is not of that type but of a subtype (like a type of "Lion"), you can promote that "Lion" to be of type "Animal." Java will do this automatically to make sure that your types match up. In this case it works because "Lion" to "Animal" is a straight inheritance hierarchy. A lion is an animal, and you can expect that all methods of an Animal will be included in Lion.
But what if you have a type and subtype that's not part of an inheritance hierarchy? For example: Say you have a type called "Hippopotamus" which extends the type "Herbivore." You also have the type "Lion" which extends the type "Carnivore." Further, let's say Herbivore and Carnivore both extend the Animal class.
Let's say that we also have a Zookeeper class, one of whose jobs is to feed the animals. Now, our Zookeeper needs to distinguish between feeding innocuous animals (ducks, rabbits, etc.) and dangerous animals (lions, hippos, etc.) We might have a method called 'feedDangerousAnimal' -- but what type will it accept? It's not so simple as saying all carnivores are dangerous (they're not) and all herbivores are innocuous (they're not). A hippo is both a herbivore and a very dangerous animal.
We really need a 'DangerousAnimal' type that could be applied to individual Herbivore and Carnivore classes. If we had this, we could type a Lion as both a Carnivore and a DangerousAnimal and a Hippo as both a Herbivore and a DangerousAnimal. No other classes in the inheritance chain would be affected. In that case, the Liskoff Substitution Principle tells us that both our Lion and Hippo objects can be safely type-promoted to DangerousAnimal.
But, how do we accomplish this multiple typing of both Lion and Hippo?
Another Example: The Student and Teacher Example
Let's bring a more down-to-earth example (and one that's less deadly) to show how the idea of validating by type can break down and cause confusion and headaches in ColdFusion.
Imagine that you're setting up a website for a university. You set up two classes, Student and Teacher, each with their own properties. You create a Student object named Sally, and a Teacher object named Ann. Simple so far, right?
In our university, we have a Student Union that is holding an annual concert, which requires us to set up a StudentUnion class as well. We've created a method for the StudentUnion class called "attendConcert." To access this method, we must pass the StudentUnion CFC a Student object (Sally). Sally may now attend the concert.
Our school has also set up a conference for its teachers. So now we have a new class called TeacherConference, with a method called "registerForConference," and to access this method, we must pass in a Teacher object (Ann). Ann is now registered for the Teacher Conference.
So far our hierarchy is simple. Let's say we have a person who fits both the Student and Teacher categories: a Student-Teacher. This is a type that inherits from two different parent types. Now what do we do? We want our Student-Teacher to be able to attend the Teacher Conference. But a StudentTeacher class can extend only one other class. Shall we extend Student or Teacher? Which way do we go?
In Java, there's a simple solution: Interfaces.
Hal explained, "Interfaces look a lot like classes. They contain a specification of necessary methods, but no method implementations-and yet they're types, just like classes are. And while one class can extend only one class, it can implement multiple interfaces. So you could have a StudentTeacher class that extends the Student class and implements the Teacher interface. Now, the compiler can type promote a StudentTeacher to either Student or Teacher, depending on the context in which the object is being used. Similarly, you could have a DangerousAnimal interface that both the Lion and Hippopotamus class implement, to which both Lion and Hippopotamus can be type-promoted."
Interfaces made programming with objects much easier with Java's static typing.
There's only one problem. This strategy would not work with ColdFusion. ColdFusion doesn't support interfaces.
The Big Revelation: ColdFusion Does Not Need Interfaces; We have Duck Typing!
Let's get back to Hal's definition of duck typing, earlier in this article. (You remember, don't you?)
"Regardless of what class an object belongs to, its essential character -- its type -- is determined by the messages that it can respond to. As in, 'if it walks like a duck, talks like a duck, and quacks like a duck -- it's a duck!'"
Hopefully, with the context I've just given you, you'll understand what's so amazing about this revelation. Until now, people have been approaching OO programming in ColdFusion as if they were programming in Java. In Java, you've got static typing, which means that you must declare a type for all of your objects and your objects' arguments. You've got a compiler that works overtime to make sure that all of your arguments are of the right type, and that will promote a subtype to its parent type if necessary, just to satisfy those arguments. You've got interfaces that solve the problem of multiple inheritance (the Student-Teacher problem mentioned above).
But ColdFusion is not Java. It doesn't have interfaces -- a fact that has caused consternation in the ColdFusion OO community. But ColdFusion is not statically typed. It is dynamically typed. And that dynamic typing gives us advantages that Java doesn't have. When we pass an argument to a method, we are not concerned with the type of the argument, but with whether or not the argument contains a message that the object can respond to. Statically-typed languages need interfaces; dynamically-typed languages do not. While both type systems (static and dynamic) have benefits, treating a dynamically-typed language as if it were a statically-typed one offers the benefits of neither.
The Power of Duck Typing: Mixins
Hal has also been writing a lot about Mixins, a technique that allows you to take the methods of one object and inject them into another object.
"This technique is difficult in statically-typed languages, but quite simple for dynamically-typed ones," said Hal. "I'll be discussing mixins as an example of the kind of ideas we can experiment with once we're relieved of the burden of pseudo static typing." In other words, when we embrace ColdFusion's duck typing, a whole world of possibilities open up for OO programmers.
Hal will be focusing on mixins in great depth in his Duck Typing presentations at
cf.Obective() and
CFUNITED. Those who attend these presentations can expect to leave the room with a new outlook on how to use ColdFusion in an object oriented way. "I will be teaching you how to think in an object oriented way without thinking in types. In Java, Object Oriented programming is built around this idea of types. In ColdFusion, OO is built around the idea of messages."
In other words: Vy a duck? Because it quacks ...
Judith Dinowitz is Editor-in-Chief of Fusion Authority, the House of Fusion Technical Magazine. She is well-known as the CFEditor, having worked on many articles and books for other publications.