From: Sean O'Dell Date: 2004-06-07T01:27:34+09:00 Subject: Re: How to ducktype a Hash? On Sunday 06 June 2004 05:03, David Garamond wrote: > Sean O'Dell wrote: > > Okay, I'm trying to sum up this thread as well as test whether my > understanding of this whole typing issue in Ruby is more or less correct. > > --- > > 1. Ruby _has_ types. It's called classes. In fact, all OO languages > which have a class concept (as opposed to some classless OO languages > like Self, I think) implement types through classes. All object belong > to a certain class. That class is the object's type. Types and classes are not the same thing in Ruby. In Java/C++, yes, but not in Ruby. A type is not just something an object is based on, it's something you can describe separately. Types are not just memory spaces that can respond to messages, they're entities which can be DESCRIBED. You can't describe any of Ruby's objects. They all have their own set of methods, and are constructed arbitrarily. Nowhere can you read "the rules of a hash interface" and then see that applied to the Hash object. All you have is the Hash object, and documentation of the methods it responds to. That doesn't mean Hash is a type. Hash is just a class. In C++, classes are typed because you describe them statically. In Ruby, classes are completely dynamic, and therefore you cannot call classes "types." Check the word type in a dictionary, and you'll see that there is a "characteristic" component to the meaning. Characteristics are something distinguished from composition. Characteristics are a DESCRIPTION of composition. You cannot describe any of Ruby's classes, they're all built arbitrarily on general notions, but there are no hard descriptions of their interfaces. It's impossible to compare one class to another to determine if they share the same characteristics. Ruby classes are COMPOSED, but never DESCRIBED. > 2. However, Ruby does _not_ do type checking for you. This means, method > declaration is not like this: > > 3. Ruby also does not tag a variable with type. This means, you don't > declare variable's type like this: > > 4. Static type checking (or static typing?) is #2 and #3, done at > compile time. This is what C++ and Java do. Ruby, of course, does not > have type checking, much less static type checking. I don't consider Ruby classes really types at all. Type has just become a label to mean the notion of what a class interface is, but none of the classes are solidly described by the language anywhere, so I have trouble seeing Ruby classes as types. To me, they're all just named, dynamic classes and it's anyone's guess what they do. Read the manual, but don't try to verify an object's purpose in Ruby code. > 5. "Duck typing" is basically no typing at all. Due to #2 and #3, we can > just pass around any kind of value (object) to any method, and the > method will just use that object as it wants. If the method needs to get > something out of the object (i.e. "make it quack") then the method will > just try to do 'obj.quack'. If the object fails to quack, then it's not > the right kind of object. This is what leads me to loads of frustration after awhile. Objects can respond to the same methods, but do completely different things. Duck typing means calling methods that may or may not do what you need. Often, by the time you get the result, the damage is done. Learning that an object method was unsuitable AFTER you call it is, to me, is obtuse and destructive. It would be better to know what an object will do BEFORE you call into its methods. > 7. Aside from classes as types, some OO languages (like Java and Perl 6) > also have interfaces/protocols as another sort of type checking. Ruby > does not have it, and I don't know whether it ever will. The choice is > currently to implement your own interface mechanism. Probably by > creating an Interface class and its subclasses (and thus creating the > interface hierarchy) and then tagging objects with these interfaces, > e.g. adding an 'implements' method to an object which will return a list > of interface classes that object implements. Interfaces as a separate mechanism of typing is pretty much just like Java/C++ classes, except it lets you break out of the ancestry craziness. Which is great, and something I think Ruby could not only do, but do without affecting its current way of doing things. > Sean seems to choose b), but reading the other posts, he also seems to > be able to control the generation of the incoming objects. Then why not > consider a) too? Instead of saying "hey, anything but a hash-like object > here is an error" then why not require every incoming object to be a > subclass of Hash and say, "raise ArgumentError unless obj.kind_of? > Hash". Ruby allows an object (like an empty hash) to be extended with > methods, so you can easily wrap object inside a hash and still be able > to do type checking with the simple kind_of?(Hash). One thing I love about Ruby is not having to always "derive" classes from other classes to make things work right. The dynamicism of Ruby objects is a real boon to my productivity. I don't want to get rid of that. I don't want to tie my objects to the Hash objects and create a hierarchy of classes. I just want to be able to say "a Hash is a hash, and my objects are a hash" and keep them separate, but be able to identify their similarity when I need to. Sean O'Dell