On Jun 25, 2011, at 10:42 PM, Brendan Eich wrote:

> 
> There are deeper waters here. See http://www.artima.com/intv/nonvirtualP.html 
> (via http://geekswithblogs.net/madhawa/archive/2006/09/17/91418.aspx) where 
> Anders Hejlsberg says:
> 
> "There are two schools of thought about virtual methods. The academic school 
> of thought says, "Everything should be virtual, because I might want to 
> override it someday." The pragmatic school of thought, which comes from 
> building real applications that run in the real world, says, "We've got to be 
> real careful about what we make virtual."
> When we make something virtual in a platform, we're making an awful lot of 
> promises about how it evolves in the future. For a non-virtual method, we 
> promise that when you call this method, x and y will happen. When we publish 
> a virtual method in an API, we not only promise that when you call this 
> method, x and y will happen. We also promise that when you override this 
> method, we will call it in this particular sequence with regard to these 
> other ones and the state will be in this and that invariant.

I have to say that I violently disagree with Anders' position on this and his 
characterization of "everything virtual" as representing an academic 
perspective.  The experience in the Smalltalk community (which is about as 
pragmatic as you can get...remember Smalltalk was once billed as the "Successor 
to COBOL") is that the ability to always over-ride an inherited method is 
extremely useful. Also note that Anders was trying to position C# as superior 
to Java by suggesting that Java (where everything is virtual) is too academic 
in its origins.

The root of this argument is really about open vs. closed system extensibility. 
 A closed system may be extensible, but only in certain predetermined manners.  
A totally open system can be extended in unanticipated manners. The Smalltalk 
experience is that attempts to predict or pre-limit how a class might be reused 
or extended are seldom correct. It is better to simply have everything open and 
leave it up to future extenders to decide what they need to do.

That said there are techniques that enhance the reliable extensibility of 
classes.  See:
www.wirfs-brock.com/PDFs/des_ext_classes.pdf
http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-OOPSLA92/ 

> 
> Every time you say virtual in an API, you are creating a call back hook. As 
> an OS or API framework designer, you've got to be real careful about that. 
> You don't want users overriding and hooking at any arbitrary point in an API, 
> because you cannot necessarily make those promises. And people may not fully 
> understand the promises they are making when they make something virtual."

Which is one of the reasons why complex object models are a bad way to design 
stable OS APIs, as was discovered by the designers of Windows Longhorn and many 
others.  That is not a use case we should be contemplating for ES.  See 
http://www.wirfs-brock.com/allen/posts/379 



> 
> The rest is well worth reading.
> 
> If subclass foo can call super.bar() and the superclass bar calls this.foo(), 
> what happens? Java without final before the method is like C++ with virtual 
> before the method. The subclass foo() will be called.

In a completely open class inheritance based system, every method is 
essentially a template method 
(http://www.oodesign.com/template-method-pattern.html ).  This creates 
potential hazards but it also creates many opportunities for unanticipated 
reuse and extensibility.  In the Smalltalk experience, the positive value of 
the latter typically far exceeded the negative value of the former.

> 
> Often, especially if you buy Anders' arguments, you want what Peter Michaux 
> has called a sideways call from the superclass bar, to lexical foo(). You can 
> do that and prevent overrides from subclasses. This is not always the right 
> thing, but it seems like the right default.

Exactly.  The way to avoid the "fragile superclass" problem is for methods that 
feel the need to protect themselves to only use lexically bound calls for 
procedural decomposition rather than method calls.  Every time a method makes a 
this call it is explicitly delegating to a potentially unknown implementation. 
If that is a concern, don't do it.


> 
> I asked Jeremy Ashkenaz about CoffeeScript's super()-only restriction, 
> modeled on Ruby. He wrote back:
> 
> "The rationale [is] that if you're overriding an implementation of a method, 
> you're responsible for preserving the proper API at that point. By reaching 
> behind the back of an overridden function, and calling its parent 
> implementation without going through it, you're saying that the override was 
> done wrong, or doesn't preserve the API you need.
> 
> This is basically the difference between Java super and Ruby super.
> 
> I'd be very curious to see an example of an inheritance pattern that relies 
> on being able to call super() against "other" methods."

The classic example is when you have groups of methods that need to be 
over-ridden in a consistent manner. 

For example, hash based data structures typically impose a constraint upon 
equality and hash methods such that items that compare equal much exhibit the 
same hash values.  Hence you might see a method such as:

Object.defineMethod(obj, "equal", functionl(another) {
      // in this abstraction hashCode is fast but equal is slow, so use 
hash/equal invariant to optimize equal
      if (super.hashCode != another.hashCode) return false;
      return super.equal(another);
});

> ...
> It seems to me that we missed something in jumping on the Java (or was it C#? 
> But C# has non-virtual, or Java final, methods by default, unlike Java) 
> bandwagon with the super.foo or super.bar() from foo design.

It's not clear to me what you think we missed. The super call to the same name 
is by far the most common case.  Probably >95%.  However, the super call to a 
different name does have utility.  In addition, because of the weak association 
between a method (property) name and actual function bodies the name following 
super arguably provides useful redundancy concerning the actual intent of the 
function.  Consider something like the following:


let f = function() {super.foo()+1};  //clear intent
let g= function() {super()+1};   //as a reader, how to I interpret the original 
authors intent?

let o = sup <| {};
Object.defineMethod(o,"foo",f);
Object.defineMethod(p,"bar", g);


Allen


_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to