TITLE: Devil's Advocate for Types AUTHOR: Eugene Wallingford DATE: September 24, 2007 4:47 PM DESC: ----- BODY: A couple of my recent entries (here and here) have questioned the value of data types, at least in relation to a corresponding set of unit tests. While running this weekend, I played devil's advocate with myself a bit, thinking, "But what would a person who prefers programming with manifest types say?" One thing that types provide that tests do not is a sense of universality. I can write a test for one case, or two, or ten, but at the end of the day I will have only a finite number of test cases. A type checker can make a more general statement, of a more limited scope. The type checker can say, "I don't know the values of all possible inputs, but I do know that all of the values are integers." That information can be quite useful. A compiler can use it to generate more efficient target code. A programmer can use it to generalize more confidently from a small set of tests to the much larger set of all possible tests. In the terms of unit testing, types give us a remarkable level of test coverage for a particular kind of test case. This is a really useful feature of types. I'd like to take advantage of it, even if I don't want my language to get in my way very much while I'm writing code. One way I can have both is to use type inference -- to let my compiler, or more generally my development environment, glean type information from my code and use that in ways that help me. There is another sense in which we object-oriented programmers use types without thinking about them: we create objects! When I write a class, I define a set of objects as an abstraction. Such an object is specified in terms of its behavioral interface, which is public, and its internal state, which is private. This creates a kind of encapsulation that is just like what a data type provides. In fact, we often do think of classes as abstract data types, but with the twist that we focus on an object's behavioral responsibility, rather than manipulating its state. That said, newcomers to my code benefit from manifest types because the types point them to the public interface expected of the objects that appear in the various contexts of my program. I think this gets to the heart of the issue. Type information is incredibly useful, and helps the reader of a program in ways that a set of tests does not. When I write a programs with a data-centric view of my abstractions, specifying types up front seems not only reasonable but almost a no-brainer. But when I move away from a data-centric view to a behavioral perspective, tests seem to offer a more flexible, comfortable way to indicate the requirements I place on my objects. This is largely perception, of course, as a Java-style interface allows me to walk a middle road. Why not just define an interface for everything and have the benefits of both worlds? When I am programming bottom-up, as I often do, I think the answer comes down to the fact that I don't know what the interfaces should look like until I am done, and fiddling with manifest types along the way slows me down at best and distracts me from what is important at worst. By the time I know what my types should look like, they are are of little use to me as a programmer; I'm on to the next discovery task. I didn't realize that my mind would turn to type inference when I started this line of questioning. (Thinking and writing can be like that!) But now I am wondering how we can use type inference to figure out and display type information for readers of code when it will be useful to them. -----