Darren,

Your problem reminds me of the "Expression Problem", which is something that IIRC Luke's Theory idea was trying to solve. Here is the link to a paper Luke referred me to on the subject:

http://scala.epfl.ch/docu/files/IC_TECH_REPORT_200433.pdf

Also, you can Google the phrase "Expression Problem" and find quite a bit on the subject.

Stevan

On Oct 19, 2005, at 6:11 PM, Darren Duncan wrote:

I'm in a long-standing situation with my module development where I want to design a set of associated classes such that invocations of submethods or class methods, such as new(), of one class by another class continue to work as expected when any or all of those classes is subclassed. I have a solution already that works, but I'm looking for the most optimal solution.

An example of when this situation can arise is if person X implements a simplified XML DOM implementation using 2 classes, Document and Node, that work together, where one of those classes (Document) can create objects of the other (Node), and person Y wants to subclass the XML DOM implementation, meaning that those same Node objects made by one of person Y's Document subclass should be objects of person Y's Node subclass.

To illustrate, say we had these 4 classes (the syntax may be wrong):

  # This is one associated class set:

  class A {
    submethod one () {
      return 'hello';
    }

    submethod two () {
      B.four();
    }
  }

  class B {
    submethod three () {
      A.one();
    }

    submethod four () {
      return 'here';
    }
  }

  # This is separate and optional associated class set:

  class C is A {
    submethod one () {
      return 'world';
    }
  }

  class D is B {
    submethod four () {
      return 'there';
    }
  }

What I want to be able to do is set things up so that user code can do something that is effectively like this:

  my $first = A.two(); # returns 'here'
  my $second = B.three(); # returns 'hello'
  my $first = C.two(); # returns 'there'
  my $second = D.three(); # returns 'world'

The situation is that classes C and D represent any arbitrary named 2 classes that are subclassed from A and B, and so the latter can't know the names of the former, and the latter have to work independently of C and D also.

This is one variant of a solution I have come up with:

  # This is one associated class set:

  role AB {
    submethod name_of_class_A () {
      return 'A';
    }

    submethod name_of_class_B () {
      return 'B';
    }
  }

  class A does AB {
    submethod one () {
      return 'hello';
    }

    submethod two () {
      .name_of_class_B().four();
    }
  }

  class B does AB {
    submethod three () {
      .name_of_class_A().one();
    }

    submethod four () {
      return 'here';
    }
  }

  # This is separate and optional associated class set:

  role CD {
    submethod name_of_class_A () {
      return 'C';
    }

    submethod name_of_class_B () {
      return 'D';
    }
  }

  class C is A does CD {
    submethod one () {
      return 'world';
    }
  }

  class D is B does CD {
    submethod four () {
      return 'there';
    }
  }

This is another variant of a solution I have come up with:

  # This is one associated class set:

  role AB {
    submethod invoke_one () {
      return A.one();
    }

    submethod invoke_four () {
      return B.four();
    }
  }

  class A does AB {
    submethod one () {
      return 'hello';
    }

    submethod two () {
      .invoke_four();
    }
  }

  class B does AB {
    submethod three () {
      .invoke_one();
    }

    submethod four () {
      return 'here';
    }
  }

  # This is separate and optional associated class set:

  role CD {
    submethod invoke_one () {
      return C.one();
    }

    submethod invoke_four () {
      return D.four();
    }
  }

  class C is A does CD {
    submethod one () {
      return 'world';
    }
  }

  class D is B does CD {
    submethod four () {
      return 'there';
    }
  }

In either case, the expectation here is that the submethods of role CD will override those of role BC regardless of which class' other methods invoke those, when the invocant class is C or D.

So I'm wondering what is the best way to develop my associated class sets such that it is easiest for third parties to be able to subclass-extend them. Should I use one of the two solutions above (both of which have been tried in real life, in Perl 5, the second more recently)? Or is there another solution that is better than both?

Also, in such a situation as the above, is it reasonable to support easy subclassing, or would it be better to avoid that complexity and instead expect users to create objects that wrap the others instead of subclassing them?

Assume also that it may be counter-productive for one class to expect user code to invoke the second class on its behalf, such as if when pair of classes is hidden behind a second pair of classes that mediate access to them.

What are some best practices here that can be used by anyone faced by a similar problem?

-- Darren Duncan



Reply via email to