From: David Masover Date: 2008-05-29T18:14:50+09:00 Subject: Re: The duck's backside On Thursday 29 May 2008 00:59:42 Mark Wilden wrote: > On May 28, 2008, at 8:59 PM, Avdi Grimm wrote: > > > On Wed, May 28, 2008 at 6:23 PM, Mark Wilden wrote: > >> Well, the desired goal is in fact to recognize objects that are > >> numeric. One > >> of the purposes of classes in OOP is to categorize things. > > > > Not really, no. That reflects an attitude that has more to do with > > C++ or Java than with OOP. > > I must respectfully disagree. I learned OOP (in 1989) before C++ was > even hardly available on PCs, much less Java. The OOP language I > learned was an amalgam of Smalltalk and Lisp. That's interesting. It's my understanding that Ruby borrows its concept of "sending messages" from Smalltalk, which lets us do things like #send and #method_missing. > I can say > quite categorically, that in OOP, classes are ways to generalize. You > generalize based on characteristics and behaviors. A class is not > simply a random collection of methods. It would be rather useless if > class was unrelated to category. I'm going to have to respectfully disagree here. Having done prototype-based development in JavaScript, as I understand it, it's far easier to implement a class-based system on top of a prototype system than the other way around. In fact, I've seen it done, in various ways, with varying degrees of success and usability. Unless you are going to have branching based on type -- that is, simple method overloading (as in C++ and friends) -- I don't see as much point to a rigid type system. It boils down to: Why should I care whether I'm being passed a number or something which merely behaves like a number? Why should I care if I'm being passed a string or an array of characters? Or even, why should I care if I'm being passed a real local object, or a proxy to a remote object? Check this out: http://ruby-doc.org/core/classes/DRb/DRbObject.html Now, for the simple case of a Numeric, we're probably over-analyzing it. There's not a huge amount of potential for replacing it with something Numeric-like that does the same job, and we can easily see where relying on to_i might (in VERY unlikely circumstances) give us something we don't want. > But I'm finding these remarks about "attititude" kind of amusing. It's > like Ruby isn't a tool, it's a "mindset." We don't forgo something > like Numeric === v because it's _wrong_ (the OP in fact said it was > slightly more readable), but because Ruby has duck-typing, Well, there is some truth to that -- as an example, Ruby completely lacks anything resembling the C for loop. If that's what you're expecting, you have to build it out of a while loop: i=0 while i < data.length do_something_with data[i] i += 1 end There's nothing wrong with that. But I hope most of us forgo it in favor of: data.each { |datum| do_something_with datum } Which ends up being more readable, among other things. It's not absolute. There are cases where you want traditional looping constructs instead of Ruby iterators, and there are cases you want classes instead of duck typing. But these are the exception, not the rule. I won't stop you from using Ruby if you'd rather build for loops. Maybe you'll get better at it: data.length.times { |i| do_something_with data[i] } But if you do that, I don't understand why you're using Ruby. Again, nothing wrong with it -- do whatever makes you happy, and maybe you're right and I'm wrong. But I doubt I'll ever understand it. > However, the use case > might be that any object with that method is fine and dandy. In that > case, I totally agree that checking for response to it is reasonable, > if not 100% reliable. But if we're looking for Fixnums, Floats, and > other builtin classes, then checking again Numeric seems > straightforward. Can you outline a few real-world examples, where it's OK to get a descendant of Numeric, but not something which merely implements to_i or to_f? > > If you want you know whether an object is in some category in an OO > > way, you come up with a way to ask it. > > True. And often the most straightforward and fundamental way to do > that is to call its #class method. Not really. Firstly, #class doesn't provide any way to be in multiple categories. Ruby doesn't support multiple inheritance. Second, given no context, there's not really any way I could override class to provide multiple membership. I'm either an Array or I'm a String. I can't be both. (Actually, I could probably pull something incredibly sneaky by returning an object which pretends to be a class, and returns true when compared to other classes on some list -- but I think I'd have to override some methods in Class and/or Module to make it bulletproof.) Now, we could meet halfway. All Numerics define #integer?, which responds True for Integer and Bignum, and false for everything else. Object defines #nil?, which is true for NilClass and false for everything else. And, worst case, calling the object's own #kind_of? method gives me a place to override that type check -- I don't believe that the Class === obj syntax allows that. > Their power is indeed to > categorize and generalize objects, which can be specialized with > inheritance (another old school concept, I suppose). Inheritance can be approximated in Ruby with mixins. In JavaScript, it's actually possible to write mixin functionality from the ground up -- create a new class, and define it as a collection of methods from selected other classes, plus a new collection you define right here. I'm not sure I know how to implement ad-hoc prototypes and things like #define_method from pure inheritance semantics. I am sure I can create inheritance out of pure prototype semantics.