Adding support for multiple Inheritance?

1,146 views
Skip to first unread message

Aundro

unread,
Jan 7, 2011, 10:01:03 AM1/7/11
to Closure Compiler Discuss

Ladies, Gentlemen,

I have a fairly large JavaScript project, that I used to compress
using the YUICompressor.

In order to gain performance, both in terms of execution time & code
size, I've decided I'd have a proper look at the Closure Compiler.

So far, I could fairly easily "adapt" my code (& comments) to the
syntax that closure expects & understands, but I'm now facing some
sort of a brick wall: multiple inheritance.
Alas, I have a lot of classes that inherit from more than 1 class
(typically a class, and 1+ mixins).

Closure doesn't seem to support it (judging from what I could find on
the web), and spews errors of the type:
"WARNING - Parse error. type annotation incompatible with other
annotations".

In my JSDoc, I document my classes as follows:
-----
/**
* Build a child.
*
* @constructor
*
* @extends Parent
* @extends Mixin
*
* @class
*
*/
function Child () {
...
}
-----

(The two "@extends" directives allow JSDoc2 to properly declare more
than 1 'parent type' for the type "Child"; the generated JSDoc HTML
has proper links to both "Parent" and "Mixin" pages, and includes
links to inherited function from both parent types.)

In order to avoid the compiler warnings, I could, of course, remove
the second "@extends" tag. But if I do,
- my JSDoc will not be properly generated anymore
- that doesn't do any good wrt closure compilation; how could it know
where the functions/properties inherited from 'Mixin' come from?


Option #2 would be to do direct assignments of functions, from the
mixins prototypes; e.g.,
-----
// Define parent
function Parent () {

}
Parent.prototype = {...};

// Define a mixin
function Mixin () {

}
Mixin.prototype = {...}

// Define a child, that will be a subclass of 'Parent', and
// receive all the properties of 'Mixin'
function Child () {

}
extend (Child, Parent);
Child.prototype.foo = Mixin.prototype.foo;
Child.prototype.bar = Mixin.prototype.bar;
...
-----

That would (I suppose) enable closure to properly optimize access to
those properties. But If I do that
- that could take me a /while/.
- JSDoc2 would not automatically pick up the dependencies to "Mixin"
anymore (I could "@borrow" all the copied functions, though, I
guess...)

Judging from the Java code, the compiler can only know at most 1
parent class to a class.
Does it make sense to try and add multiple inheritance to the compiler
(I would then have a shot at it), or is it just not possible because
conceptually the compiler will never work w/ more than 1 parent type?

Thanks for any pointers!

Best regards,
Arnaud.

John Lenz

unread,
Jan 7, 2011, 12:12:56 PM1/7/11
to closure-comp...@googlegroups.com
Without speaking to the JSDoc annotations. The compiler already has some support for mix-ins: when using the ClosureCodingConvention (that is when the --third_party flag is not set) it recognizes "goog.mixin" of the form "goog.mixin(sub.prototype, super.prototype)" and "Function.mixin" in the form "sub.mix(super.prototype)". 

Nick Santos

unread,
Jan 7, 2011, 1:44:27 PM1/7/11
to closure-comp...@googlegroups.com
Closure Compiler's type system is based on JS prototype inheritance
rules. So, yes, it's inherently impossible to have more than one
superclass, because in JS a function only has one prototype. But it's
not clear to me that you really want multiple inheritance, or whether
you just want a way to copy properties to different classes.

do you want to be able to annotate functions as "@param {Mixin}"?

Aundro

unread,
Jan 8, 2011, 11:58:24 AM1/8/11
to Closure Compiler Discuss
Nick, John,

thanks for your feedback.
My goal is to reach the 2 following targets:
1) get the Closure compiler to understand that a type, i.e., "Child"
can access the properties of both "Parent" and "Mixin"
2) get JSDoc2 to generate complete documentation. For that, I'm
currently using more than 1 "@extends" tag.


The reason I'm trying to achieve #1
--------------------------------------------------
Is because I /guess/ it is necessary that the compiler properly
understands that a subtype has the properties of 2 types (although we
all agree that it has only 1 parent class, since as you mentioned it's
impossible to do otherwise w/ prototype inheritance).

Let's consider the following scenario, where the mixin and child types
have the following properties:
- Mixin.prototype.prepareOptions = function () {...}
- Child.prototype.submitInfo = function () {
var opts = this.prepareOptions();
[...]
};

If I understand correctly how the closure compiler works, it /might/
alias "Mixin.prototype.prepareOptions = function () {...}" to
something shorter and more direct; e.g., "function qD () {...}",
right?

But then, if closure does _not_ realize that "Child" has the
properties not only of the "Parent" type, but *also* of the "Mixin"
type, I guess it will not be able to call "qD();" directly in
"submitInfo()", and still try and call an unexisting
"prepareOptions()" function.
Is this correct, or am I missing something?


The reason I'm trying to achieve #2
--------------------------------------------------
Is rather straightforward: I need relevant and complete
documentation :)



So, in summary, what I want is, as Nick says: copy [mixins] properties
to different classes.
As I said previously, I could do this manually, but I have a pretty
large codebase, and it would take me a looong time to change my code
to do direct assignments of the form:
"Child.prototype.prepareOptions = Mixin.prototype.prepareOptions".

I'm just trying to find a way to avoid having to do that
manually. ..but there appears to be none, alas.

In any case, thanks for the response!

Best regards,
Arnaud Diederen

Nick Santos

unread,
Jan 8, 2011, 12:58:41 PM1/8/11
to closure-comp...@googlegroups.com
On Sat, Jan 8, 2011 at 11:58 AM, Aundro <aun...@gmail.com> wrote:
> But then, if closure does _not_ realize that "Child" has the
> properties not only of the "Parent" type, but *also* of the "Mixin"
> type, I guess it will not be able to call "qD();" directly in
> "submitInfo()", and still try and call an unexisting
> "prepareOptions()" function.
> Is this correct, or am I missing something?

Nope, you're missing something :)

Closure Compiler does not use type information to decide how to
rename properties in ADVANCED mode. (There are ways that you can tell
it to use type information, but they are only available from the Java
API).

So in your example, it would rename prepareOptions to something short
(like qD), but it rename every prepareOptions property in your program
to that.

>
>
> The reason I'm trying to achieve #2
> --------------------------------------------------
> Is rather straightforward: I need relevant and complete
> documentation :)
>
>
>
> So, in summary, what I want is, as Nick says: copy [mixins] properties
> to different classes.
> As I said previously, I could do this manually, but I have a pretty
> large codebase, and it would take me a looong time to change my code
> to do direct assignments of the form:
> "Child.prototype.prepareOptions = Mixin.prototype.prepareOptions".
>
> I'm just trying to find a way to avoid having to do that
> manually. ..but there appears to be none, alas.

There's currently no way to do this.

I don't think it would be implement what you want. Someone just has to
sit down and figure out what the syntax and semantics would be. There
are subtle differences in semantics that make this tricky.

multiple @extends doesn't quite fit because of the single-prototype issue

I've talked to people that are really excited about Traits for JS
http://soft.vub.ac.be/~tvcutsem/traitsjs/index.html
but I haven't really been following the discussion, or gotten a chance
to use them.

The compiler currently support @lends
http://code.google.com/p/jsdoc-toolkit/wiki/TagLends
But it only works for object literals mixed-in to one type, and not
for something mixed-in to multiple types.

And then of course you would want something that works with jsdoc-toolkit.

thoughts?

Nick

Aundro

unread,
Jan 10, 2011, 4:16:40 AM1/10/11
to Closure Compiler Discuss

Hi Nick,

On Jan 8, 6:58 pm, Nick Santos <nicksan...@google.com> wrote:
> On Sat, Jan 8, 2011 at 11:58 AM, Aundro <aun...@gmail.com> wrote:
> > But then, if closure does _not_ realize that "Child" has the
> > properties not only of the "Parent" type, but *also* of the "Mixin"
> > type, I guess it will not be able to call "qD();" directly in
> > "submitInfo()", and still try and call an unexisting
> > "prepareOptions()" function.
> > Is this correct, or am I missing something?
>
> Nope, you're missing something :)
>
> Closure Compiler does not use type information to  decide how to
> rename properties in ADVANCED mode.

Oh, ok.


> (There are ways that you can tell
> it to use type information, but they are only available from the Java
> API).

Actually, I'm using closure via the API.
(Although for the moment, I'm simply setting the CompilerOptions from
the ADVANCED_OPTIMIZATIONS enum, it's likely to change in a near
future: I'll try and squeeze the best out of closure ;) )


>
> So in your example, it would rename prepareOptions to something short
> (like qD), but it rename every prepareOptions property in your program
> to that.
>

Got it.

> [...]
> > I'm just trying to find a way to avoid having to do that
> > manually. ..but there appears to be none, alas.
>
> There's currently no way to do this.
>
> I don't think it would be implement what you want. Someone just has to
> sit down and figure out what the syntax and semantics would be. There
> are subtle differences in semantics that make this tricky.
>
> multiple @extends doesn't quite fit because of the single-prototype issue

Agreed (to be honest, I was even surprised JSDoc2 supports it).

>
> I've talked to people that are really excited about Traits for JShttp://soft.vub.ac.be/~tvcutsem/traitsjs/index.html
> but I haven't really been following the discussion, or gotten a chance
> to use them.
>

Thanks for the pointer; I didn't know about traits.


> The compiler currently support @lends
> http://code.google.com/p/jsdoc-toolkit/wiki/TagLends
> But it only works for object literals mixed-in to one type, and not
> for something mixed-in to multiple types.
>
> And then of course you would want something that works with jsdoc-toolkit.

Correct.

>
> thoughts?

Not really. It looks like I'm pretty stuck: my options are (list of
pros and cons are not exhaustive):
1) use goog.mixin/sub.mix
Pros:
- recognized by closure (and will enable it to do type recognition to
rename properties)
Cons:
- dependency on closure library (I'm trying to keep dependencies to a
minimum)
- not recognized by JSDoc2
- requires a little bit of work

2) leave multiple @extends
Pros:
- recognized by JSDoc2
Cons:
- not recognized by closure

3) define "borrowed" properties explicitely (C.prototype.foo =
M.prototype.foo), and add @borrows tags
Pros:
- recognized by both JSDoc2 and closure
Cons:
- will take quite some time to do that, for all the types that
require it

Right now I think that, although I'd gladly spend my time doing
something else, I'd be better off implementing option #3.

After all, it's fair enough. Using multiple @extends was a bad idea in
the first place.
It just so happened that it "just worked" in JSDoc2, but it was
semantically twisted - I'm now paying the price for picking a bad
option.

Thanks for your help!
..and for closure!

Best,
Arnaud Diederen




>
> Nick

Aundro

unread,
Jan 10, 2011, 10:01:07 AM1/10/11
to Closure Compiler Discuss

Nick,

So far I have done the following changes in my code:
- removed multiple @extends
- replaced those w/ a custom @mixin tag
- instruct the compiler (I'm using the Java API) to ignore non-
standard JsDoc)

I'm about to change all my changes to direct assignments.
E.g.,

--
function Parent () {}
function Mixin () {}
function Child () {}

extend (Child, Parent);
mixinExtent (Child, Mixin)
--

into
--
function Parent () {}
function Mixin () {}
function Child () {}

extend (Child, Parent);

Child.prototype.foo = Mixin.prototype.foo;
Child.prototype.bar = Mixin.prototype.bar;
...
--

That would keep me busy for some time.. ;)


However, since you specified that Closure recognizes the "goog.mixin"
syntax, I dug a little into the code and found the CodingConvention
thing.
If I understand correctly, I could implement my own coding convention,
that would recognize my own convention?
That would be a real time-saver.

What do you think?

Thanks,
A.

Nick Santos

unread,
Jan 10, 2011, 10:45:16 AM1/10/11
to closure-comp...@googlegroups.com
Yeah, you'll want to override CodingConvention's
getClassesDefinedByCall and applySubclassRelationship.

getClassesDefinedByCall allows you to define your own function that does mixins.

applySubclassRelationship will let you add properties based on the
mixin relationship.

Aundro

unread,
Jan 11, 2011, 3:46:57 AM1/11/11
to Closure Compiler Discuss

Nick,

On Jan 10, 4:45 pm, Nick Santos <nicksan...@google.com> wrote:
> Yeah, you'll want to override CodingConvention's
> getClassesDefinedByCall and applySubclassRelationship.
>
> getClassesDefinedByCall allows you to define your own function that does mixins.
>
> applySubclassRelationship will let you add properties based on the
> mixin relationship.
>

Thanks for the support! It looks like closure now understands my
mixins!

Best,
Arnaud.

LioLick

unread,
Feb 3, 2012, 2:07:16 AM2/3/12
to closure-comp...@googlegroups.com
Hi, Nick,
could you please help?
I want to be able to use Child type wherever Mixin type is required,
including calling Mixin ctor from Child ctor.
Currently I get message like this:
actual parameter 1 of Mixin.call does not match formal parameter
  found   : Child
  required: (Mixin|null|undefined)
Is it possible to teach Closure Compiler to treat Mixin as a base class of Child in such cases?

Thank you!
Reply all
Reply to author
Forward
0 new messages