On Jun 26, 2011, at 5:09 AM, Allen Wirfs-Brock wrote:

> 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.

I know, and I'm not endorsing every syllable, but the interview is worth 
reading and I humbly suggest that it put some arrows into one or two practical 
pain-point targets.


> 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.

There's a pragmatic or real-world observation in that interview, which I didn't 
cite. The "incoming" (wrong word but close enough) contract of a method, its 
Hoare preconditions/postconditions, is almost always better understood than the 
"outgoing" contract: how its implementation depends on peer or other methods in 
or above (or below!) the abstraction in the class hierarchy. And virtual by 
default raises the risks of getting the latter wrong.

Peter Michaux raised this already in talking about "sideways" calls from a 
superclass method to its peer methods in that class, not to be overridden.

In C++, if a superclass explicitly declares a method virtual, perhaps even pure 
virtual, intending that subclasses implement it as a callback or "plugin API", 
then virtual is necessary or it's back to C function pointers. This is a clear 
counter-example to any "always use sideways calls among superclass methods that 
call one another" dogma. Such a plugin method must be delegated to the 
subclass, i.e., virtual in C++.

Of course, we don't need dogma. All we really need IMHO is the right default, 
and acknowledgement that practical concerns require both overrideable and 
sideways calls so the syntax for both should be about as usable.


> 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/ 

Thanks, will take a look.

Has it helped the masses get this right? Anders' point about Java class library 
updates always breaking the extensions in the wild rang true from what I 
remember.


>> 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 

Sure, people need to avoid complex object models as APIs in general. For 
implementations they may be manageable. But we don't have interfaces (proxies 
whether hand-coded classes or capitacal-P Proxies are too expensive).

Giving users a facet or shard of your abstraction without having to write a 
proxy may be the next shoe to drop after classes. Private name objects may help 
but the syntax isn't there (on purspose).


> 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.

Did Java user experience seem to go the other way from Smalltalk, in your view? 
If so, any ideas why?


>> 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.

But we don't have convenient syntax for sideways calls. Unless I'm missing 
something, they are darn inconvenient, requiring a closure and some refactoring 
into private helpers to share code.


>> 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);
> });

Why wouldn't this.hashCode suffice? There's no hashCode override, IIUC.


>> 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.

My point is not about utility, since people find all manner of practices useful.

My point is that super.bar from method foo always goes up the inheritance 
hierarchy, but superclass foo's calls to this.bar will go back down if the 
subclass overrides bar. Unless superclass foo uses a sideways call, which is 
too painful to be likely.

Should we consider making sideways bar calls about as convenient as this.bar 
calls?


>  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);

I'm frankly not sure about the value of this use-case, compared to methods in 
classes and methods in initialisers.

To retarget super with Object.defineMethod, you have to know a *lot* about both 
the function, and the target object and its prototype chain. This is a hard 
case. It will be rare. It could be useful. It could also be a source of woe.

On the plus side, I see the coherence of free super in any function not in a 
class or literal combined with Object.defineMethod. It has that low-level 
appeal that we often seek to improve the language without over-designing in 
advance of user testing. It fills the "super gap" in a maximally programmable 
way, with syntax for super that avoids most of the pitfalls. People may build 
on it in ways we don't foresee.

But it needs user testing too.

/be
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to