Sponsored Link •
Python creator Guido van Rossum talks with Bill Venners about the nature of contracts in a runtime typed programming language such as Python.
Guido van Rossum is the author of Python, an interpreted, interactive object-oriented programming language. In the late 1980s, Van Rossum began work on Python at the National Research Institute for Mathematics and Computer Science in the Netherlands, or Centrum voor Wiskunde en Informatica (CWI) as it is known in Dutch. Since then, Python has become very popular among developers, who are attracted to its clean syntax and reputation for productivity.
In this interview, which is being published in six weekly installments, Van Rossum gives insights into Python's design goals, the source of Python programmer productivity, the implications of weak typing, and more:
In this installment, Van Rossum discusses the nature of contracts in a runtime typed programming language such as Python.
Bill Venners: One promise of object-oriented programming is that a strong separation of interface and implementation facilitates change. Because client code links just to the interface, I can change method implementations and private data. I can make those kinds of changes without breaking existing client code, because that code is not coupled to the method implementations or private data. Client code is just coupled to the method signatures that make up the interface. Separating interface and implementation is really just a way of enabling change by minimizing coupling.
But this promise of easier change requires that the various people who define the
interface, implement the interface, and write the client code all agree on what the
interface methods mean. The interface implies a contract. If an
method is supposed to add two numbers, I can change my implementation to add more
efficiently, but the method must continue to perform addition. For example, if I change
the implementation such that one number is subtracted from the other, I will most likely
break client code at runtime even though the client code will still link at compile time.
In a weakly typed system like Python, variables don't have types. Say I write a
method and it takes two parameters,
y. If I invoke a
x, the method invocation will succeed at runtime if a method
of that signature is declared in the object referenced by
x. But I don't
necessarily know what that method will do.
In a strongly typed language, each variable has a type that's known at compile time. If I attempt to invoke a method on a variable whose type doesn't declare that method, the compiler of a strongly typed language tells me of the error. That's one difference between a strongly typed and weakly typed language. In a strongly typed language, I discover such errors at compile time. In a weakly typed language, I will hopefully find out at runtime. But there's another difference. In a strongly typed language, a variable's type implies the object it references will have a particular interface. That interface doesn't just tell me at compile time what method signatures exist, it also tells me what those methods mean. It tells me what the methods promise to do.
For example, both an
Artiste object and a
object might have a method with the signature
void draw(). When you
draw on the
Artiste, it sketches a picture. When
draw on the
GunSlinger, it draws its pistol and
shoots. In a strongly typed language, I can figure out if the object will sketch or shoot by
looking at the variable's type. I know the type, and therefore
meaning, at compile time. In a runtime typed language like Python, I can find out
draw's meaning by looking at the type at runtime, but that's rarely done in
practice. I never check types at runtime when I write in Python. I just invoke
draw assuming it means something, but with no guarantee.
Why does that work in practice? Why do Python programs work, if no one is certain what the method will do when it's invoked?
Guido van Rossum: That sounds like an irrational fear to me. In my experience, designing the interface is often the hardest part. The flexibility to change the implementation without changing the interface works well only for certain things. If you must sort by zip code, for example, you can input a simple sort implementation. If later you find your initial sort implementation is not fast enough, you can do more work on it. That's the classic way you use interfaces.
But in many situations you find that when people design interfaces, in the next program revision or after user feedback of a library, the interfaces are actually designed improperly. Perhaps certain information was kept private. Perhaps certain data never leaves a method that is useful to the consumer. Maybe the data is redundant. You can calculate it from the data returned from the method. But because it's calculated as a side effect of an algorithm, it's a shame to throw that away, because it forces the data's consumer to recalculate that information based on the data she receives.