From: Dave Thomas Date: 2000-10-23T13:18:45+09:00 Subject: [ruby-talk:5766] Re: Dynamic languages, SWOT ? "Hal E. Fulton" writes: > > In Ruby, unlike Java and C++, classes and types are different > > beasts. > > Dave... please expand on that last sentence to whatever extent > you're willing. and Charles Hixson writes: > I'm not certain that I really agree with you. I think (feel?) that > types and classes actually are the same kind of thing. OK - this is where I get to expose my old ways of thinking to general ridicule. They go something like this: A type is defined by a state domain and a set of operations that (normally) act on that domain. The state domain determines the valid set of values that objects of that type may have, and the operations talk about how objects of the type may be manipulated. Many languages, such as C and Fortran, have built-in types, such as integers and floating point numbers. Here the domain is fairly obvious, and the operations are our old friends from arithmetic. Some languages, such as Pascal, have the ability to create new types based on these existing types. In Pascal, these new types have the same operations as their base types, but their domain is restricted to some subset of their parent's domain. Object-oriented languages extend the paradigm. Now we can define a class, where we manage the state internally and export the operations that manipulate and use that state. Each of these classes defines a type. OO languages that support subclassing have the ability to create new types based on existing types. These new types have (at least) the same set of operations as their base types, but their domain is restricted. (Does this paragraph sound familiar? What a well-crafted note, eh?) Languages such as Java (and, less elegantly, C++), introduce a twist. Using multiple inheritance or using interfaces, a class may have several types simultaneously. Objects of Java's class RandomAccessFile, for example, can be used where ever one of the types RandomAccessFile, DataInput, or DataOutput is expected. Statically typed languages then add compile time support for a form of type checking. You can explicitly tell the compiler the type of object that you are expecting as (say) a method parameter, and the compiler can perform static analysis to see that you don't break the rule. In the case where this static analysis is not possible (for example when casting is involved), the compiler inserts a runtime check instead. So, up to this point, we have a range of languages which support types as built-in things, classes, and interfaces, often with some form of subtyping. In these languages a class will have one or more types associated with it. Ignoring the mess that Java makes of native types, a type will be associated with just one class or interface. _________ __________ | | 1 * | | | class |-------------| type | |_________| |__________| So, on to Ruby, Smalltalk, and the like. Here the picture is somewhat more subtle. At their simplest, Ruby classes implement types, and subclasses implement subtypes. Ruby mixins add capabilities similar to interfaces, and hence associate multiple types with a class (for example, [1,2,3] is both an Array and an Enumerable object). However, Ruby differs from Java and other static languages in that it does not check the types of objects at compile or runtime. It doesn't care about an object's class per se. Instead, it cares that an object implements the required methods. This leads to interesting possibilities. For example, things that would traditionally be implemented as subclasses in order to pass type-checking need not be in Ruby. Instead, they can be implemented as two independent classes that happen to implement a common set of methods (Think of the way that you can use ARGF as a File, or ENV as a Hash, even though they are actually just Objects.) So, Ruby immediately gives us a more complex class/type relationship: _________ __________ | | * * | | | class |-------------| type | |_________| |__________| Many classes can implement a type, and any one class can have many types. Then we muddy the waters even further. In Ruby, you can extend both classes and objects while your program is running. Say you want to allow threads to be compared based on their priority: class Thread def <=>(other) self.priority <=> other end include Comparable end Suddenly, every thread object in the system, both existing and new, has grown a new type: they are all now Comparable as well as being threads. Given this tremendous flexibility, I'm not sure how an enforced system of static tye checking could be shoe-horned in to Ruby: the paradigms are just plain different. In my opinion, this is not a bad thing. While programming Ruby, I have never really felt the need for static type analysis nor for dynamic type checking. In the face of mutable types and extendible classes, I'm not even sure what form type checking could take. Help me understand the other point of view. Regards Dave