On Mon, 28 Oct 2002 23:49:39 +0900, William Djaja Tjokroaminata 
wrote:
> Jim Weirich <jweirich / one.net> wrote:
>> The problem is retraining class writers (myself included) to do
>> it the right way.
> I don't think retraining will solve the vast existence of codes
> that have been written in the "old" way, including the basic Ruby
> built-in classes themselves. That's why I am very interested
> whether from OO design point of view, the current design is
> "flawed". (I think OO has been around for a long time, so it is
> hard to believe that this kind of problem is not
> encountered/solved before.)

I think it is flawed. Personally.

>> This problem has little to do with static vs dynamic typing and
>> can easily occur in C++ programs.
> 
> Well, it may occur in C++ programs, but it is not the intention.
> Suppose
> 
> derived a;
> parent b;
> parent c;
> ....
> a = b + c;
> 
> Assuming that this code compiles and executes, here we are
> guaranteed that the class of a is "derived" (i.e., a will respond
> to methods that are defined for class derived). There may be some
> semantic/logic error depending on the overloadings of the '+' and
> '=' operators, but that is a separate issue.

This is where Ruby's typeless variables (not typeless objects)
causes problems. In the compiled language, the variable 'a' has a
distinct type. With respect to your example, I would *expect* that
'a' would take on the type of Parent in Ruby. What I don't expect,
however, is for:

    a = a + b

to take on the type of Parent. After all, we're really doing:

    a = a.+(b)

So the typeness of a should be carried, even though it's the
Parent's operator which is called. It's rather like the operator had
been declared without 'virtual' in C++.

Matters are made rather worse because there's no clear way of doing
typecasting (yes, there is 'coerce', but that's not really
typecasting) of objects. Additionally, because the variables
themselves are typeless, doing:

    a = b + a               # a = b.+(a)

Will result in a being a reference to a Parent, even though in C++,
the result would remain a Derived because types are associated with
variables. Jim's solution (self.class.new) works quite well for the
first case (a += b), but it works not at all for the second case (a
= b + a). It's correct, and it's consistent, but it still *feels*
wrong.

(There's a point where, at least IMO, if you're dealing with Parent
and Derived and only those two classes are involved, then the
results should be coerced into Derived. This, however, results in
problems if you have DerivedA and DerivedB, both children of Parent.
Which class should then be used as the result?)

It's not an easy problem. Jim's solution is a decent one, and it
might be that some syntactic sugar could be provided. If I do:

    class Foo
      def bar
        Foo.new
      end
    end

it could be interpreted as:

    class Foo
      def bar
        self.class.new
      end
    end

because we are in the context of "class Foo". I'm not sure if this
would be a good idea or not, but it would at least make Jim's
solution 'automatic.'

-austin
-- Austin Ziegler, austin / halostatue.ca on 2002.10.28 at 09.56.17