@SingleImpl versus @SingleJsoImpl

2 views
Skip to first unread message

BobV

unread,
Jan 7, 2009, 10:30:44 AM1/7/09
to GWTcontrib, Scott Blum, Joel Webber
I've working on a branch that allows JavaScriptObjects to implement
interfaces with methods. Due to the lack of polymorphic dispatch of
JSOs, for a given method-bearing interface, there can be exactly one
JSO type implementing the interface. The question at hand is whether
or not to allow N-many regular Java types to also implement that
interface.

The choices are:

---- ONE ----

@SingleImpl
interface Person {
String getName();
}

class JsoPerson extends JavaScriptObject implements Person { ... }
// It is an error to define any other type that implements Person,
including anonymous types
// subclassing JsoPerson is OK


---- TWO ----

@SingleJsoImpl
interface Person {
String getName();
}

class JsoPerson extends JavaScriptObject implements Person { ... }
// It is an error to define any other JSO type that implements Person
// subclassing JsoPerson is OK

class RegularPerson implements Person { ... }
(new Person() { ... })
// But you can have as many non-JSO types implementing Person

Tradeoffs:
- Scenario one is more restrictive, but calling ((Person)
x).getName() can always be statically resolved to one method
- Land-mines with anonymous types

- Scenario two allows a JSO implementation to inter-operate with regular code
- Only land-mine would be in declaring a second JSO type
- ((Person) x).getName() would be more expensive to dispatch if
type-tightening doesn't occur; modulo optimizations, it would look
like "x.typeId$ ? x.getName() : getName$(x)"
- This dispatch penalty applies only to interfaces with a JSO
implementation and one or more regular implementations and
type-tightening occurs.
- Would be doing polymorphic dispatch anyway, this just adds a
ternary expression


I'm favoring scenario two because it provides the developer
significant additional flexibility. Consider the following where you
get a payload from the server in JSON format, but also want to edit
some of the resulting objects.

@SingleJsoImpl
interface Person {
String getName();
}

final class JsoPerson extends JavaScriptObject implements Person {
protected JsoPerson() {}
public native String getName() /*-{ return this.name; }-*/;
}

class MutablePerson implements Person {
private String name;
public MutablePerson(Person copyFrom) {this.person = copyFrom.getName();}
public String getName() {return name;}
public void setName(String name);
}

--
Bob Vawter
Google Web Toolkit Team

Isaac Truett

unread,
Jan 7, 2009, 10:40:02 AM1/7/09
to Google-Web-Tool...@googlegroups.com
+1 for option #2. An interface that can only ever be implemented once
seems very limited in usefulness.

Bruce Johnson

unread,
Jan 7, 2009, 10:48:15 AM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
I see an underlying philosophical question here related to how GWT helps developers produce the fastest code. 

A) One view is that we only allow constructs that generate fast code (e.g. your option #1); that is, we disallow flexibility that could harm efficiency if used.

B) The other view is that we do generally allow for more flexibility (e.g. your option #2) but advise developers that they'll get better performance if they are willing to not use the extra flexibility.

Probably most people would say (B) seems obviously better, and I basically  agree. However, in a large program that has a high performance bar, it is totally impractical to enforce "not using the extra flexibility" anywhere in the code base. Thus, a size and performance hit is likely to sneak in despite a desire to not let it.  And that's what concerns me.

I think we should establish a pattern for safeguards, though which a developer can state his/her intention and the compiler can warn if the intention isn't honored by the code base. 

We've discussed a family of quasi-unsafe flags, for example:

    -Xunsafe:disableCastChecking
    -Xunsafe:ignoreArrayStoreException
    ...

We could provide the same sort of thing for global intentions:

    -Xintention:monomorphicSingleImpl
    -Xintention:smallestPossibleScript
    ....

Back to your question, Bob: we could use your solution #2 and also provide the "monomorphicSingleImpl" intention flag. If the flag is set, then the compiler would warn you if your code actually produces polymorphic single impls, allowing you to weed them out.

Does anyone buy that reasoning? An important related question is whether these things are actually compiler flags or whether they are module settings. I think they are actually more appropriate as the latter.

-- Bruce

On Wed, Jan 7, 2009 at 10:30 AM, BobV <bo...@google.com> wrote:

John Tamplin

unread,
Jan 7, 2009, 11:17:18 AM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
On Wed, Jan 7, 2009 at 10:48 AM, Bruce Johnson <br...@google.com> wrote:
Does anyone buy that reasoning? An important related question is whether these things are actually compiler flags or whether they are module settings. I think they are actually more appropriate as the latter.

If they are module flags, what happens when you have different settings for different modules (as you point out, it will be impossible to keep them consistent in a large code base with shared code)?  If it is a compile-time error, then some code simply won't be shareable between projects -- maybe that is ok if that module's owner has decided it is important enough to set the flag in a library module?  If it is based on which module it is in, does the caller or callee win the battle, and will that behavior be surprising to a developer?

Regarding the original question, I don't have a strong preference for either solution over the other.  #2 does have additional utility, but it seems less consistent than simply saying that after type tightening there must be exactly one implementation.  Given the primary use case is shared code between client and server which will use a JSO object in the client (at least until generators are able to produce a JSO subclass), I am not sure the additional utility is worth the hidden code size risk and slightly inconsistency.

--
John A. Tamplin
Software Engineer (GWT), Google

Emily Crutcher

unread,
Jan 7, 2009, 11:46:17 AM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
In the library code we constantly use *Impl to denote a class  that users should not look at, but here we we have  *Impl annotations that is part of a public API. Could we modify the name slightly so we can keep the *Impl convention consistant throughout the gwt code base?
--
"There are only 10 types of people in the world: Those who understand binary, and those who don't"

Bruce Johnson

unread,
Jan 7, 2009, 12:26:07 PM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
On Wed, Jan 7, 2009 at 11:17 AM, John Tamplin <j...@google.com> wrote:
On Wed, Jan 7, 2009 at 10:48 AM, Bruce Johnson <br...@google.com> wrote:
Does anyone buy that reasoning? An important related question is whether these things are actually compiler flags or whether they are module settings. I think they are actually more appropriate as the latter.

If they are module flags, what happens when you have different settings for different modules (as you point out, it will be impossible to keep them consistent in a large code base with shared code)?

I think it would be last-one-wins, as with most other module settings.
 
  If it is a compile-time error, then some code simply won't be shareable between projects -- maybe that is ok if that module's owner has decided it is important enough to set the flag in a library module?  If it is based on which module it is in, does the caller or callee win the battle, and will that behavior be surprising to a developer?

I was suggesting a compile-time warning at worst. And I think it would typically be the childmost module that would actually specify settings in typical usage, rather than a library module itself.

Bruce Johnson

unread,
Jan 7, 2009, 12:26:58 PM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
@Emily: Does the fact that it's an annotation not make it completely clear? We don't use *Impl on interfaces anywhere (do we?)

Emily Crutcher

unread,
Jan 7, 2009, 1:15:42 PM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
FormPanelImplHost is an example of an impl interface.
 
I think once users see one *Impl type that has documented useful functionality for them, they are less likely to ignore the next *Impl  class/interface/annotation they see that looks like it might be useful for them, regardless of whether the *Impl type is an annotation, interface,  or class. 

Ray Cromwell

unread,
Jan 7, 2009, 1:20:14 PM1/7/09
to Google-Web-Tool...@googlegroups.com
Definately scenario two for me. I would argue that in most cases, the
non-JSO implementations will either be outside the translatable
packages, or would be pruned as dead code since they would never be
referenced from within client code, guaranteeing type-tightening. In
the rare cases where someone might want both the Java wrapper and JSO
implementations live in client code, the ternary expression doesn't
seem like a big deal, perhaps support emitting a WARNING or have a
SOYC report detailing this.


-Ray

Ray Ryan

unread,
Jan 7, 2009, 1:21:19 PM1/7/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
+1 for option 2. And I think Bob's Person/MutablePerson scenario is a hugely compelling justification for it.

I also wonder if something as obscure as the monomorphicSingleImpl flag will ever be found. If the performance issue really is a big enough concern to warrant its implementation (I can't judge it), would it make sense to provide a compiler warning when multiple implementations are found, which document the flag? Or perhaps allow/require them to be defeated by an @SuppressWarning?

rjrjr

Ray Ryan

unread,
Jan 7, 2009, 1:21:37 PM1/7/09
to Google-Web-Tool...@googlegroups.com
Jinx. Two Rays are for it, it must be right.

rjrjr

Lex Spoon

unread,
Jan 13, 2009, 10:19:27 AM1/13/09
to Google-Web-Tool...@googlegroups.com, Scott Blum, Joel Webber
On Wed, Jan 7, 2009 at 10:30 AM, BobV <bo...@google.com> wrote:
> - Scenario two allows a JSO implementation to inter-operate with regular code
> - Only land-mine would be in declaring a second JSO type
> - ((Person) x).getName() would be more expensive to dispatch if
> type-tightening doesn't occur; modulo optimizations, it would look
> like "x.typeId$ ? x.getName() : getName$(x)"
> - This dispatch penalty applies only to interfaces with a JSO
> implementation and one or more regular implementations and
> type-tightening occurs.
> - Would be doing polymorphic dispatch anyway, this just adds a
> ternary expression

There's another advantage to option 2: the Java implementations of the
interface will exist anyway, and this flexibility will mean it doesn't
cause a problem if those classes are added to the web-mode compile.
This can happen late during maintenance, and without option 2,
SingleImpl would force them into a larger refactor.

So, belatedly, +1 for option 2. The flexibility isn't just at initial
design time.

Lex

Reply all
Reply to author
Forward
0 new messages