cincheo / jsweet

A Java to JavaScript transpiler.
http://www.jsweet.org
Other
1.46k stars 158 forks source link

method or function reference #65

Closed ssatguru closed 8 years ago

ssatguru commented 8 years ago

The way "method" references are transpiled today can lead to unexpected results if one is not careful.

Consider following code

public void foo(){
     canvas.addEventListener("pointerdown",  this::onPointerDown, false);
}

public void onPointerDown(MouseEvent e){
}

This works perfectly.

But now if I do

public void bar(){
     canvas.removeEventListener("pointerdown",  this::onPointerDown, false);
}

It compiles, transpiles and executes without any error, but the eventlistener is NOT removed during runtime.

The reason has to do with the way jsweet transpiles this::onPointerDown

jsweet transpiles this to (evt) => { return this.onPointerDown(evt) }

In otherwords it creates a new anonymous function.

So in addEventListener I am not passing a reference to the onPointerDown method but rather an anonymous method. Thus later on when I do removeEventListener I am, again, not passing a reference to the onPointerDown method, but a new anonymous method which the canvas, obviously, cannot find and is thus unable to remove.

To make this work I had to do the following

EventListener pointerdown ;
public void foo(){
     pointerdown =  this::onPointerDown;
     canvas.addEventListener("pointerdown",  pointerdown , false);
}

public void bar(){
     canvas.removeEventListener("pointerdown",  pointerdown , false);
}

I wonder if there is a better way to transpile "method" references? The current way has some "astonishment" factor associated with it :)

if interested here is some actual code to look at https://github.com/ssatguru/BabylonJS-EditControl/blob/master/src/main/java/org/ssatguru/babylonjs/component/EditControl.java#L71

renaudpawlak commented 8 years ago

Thanks satguru.... this is a tricky one. We are transpiling this way to avoid the "loosing this" effect (see the semantics section in the language specs)... It is generally a safer way for programmers, however, we did not anticipate this side effect (I was kind of expecting side effects but I was unsure under what form they would appear).

So, we have to find a better way to transpile, or we have to drop the idea to make this access safer in lambdas and let the programmers deal with the problem manually (similarly to TypeScript and JavaScript). I believe that ES6 and future versions will eventually make things better, so I am wondering if it should be JSweet's job to deal with these issues.

We need to think of it more before making a decision. Any additional thoughts are welcome.

ssatguru commented 8 years ago

Interesting. I wasn't aware of this JavaScript limitation. I now understand the reason for the current JSweet implementation. So a) given that we DO want the behavior to reflect Java's behavior (after all that is the reason for using JSweet in the first place :) ) and b) we have a way to handle this side effect it makes sense to leave the current implementation as is, until something better comes along.

renaudpawlak commented 8 years ago

Let me add a little bit to it. What we do want actually, is to follow the least astonishment principle for the Java programmers. Of course, this implies to be as close to the Java behavior as possible. This is why we transpiled the function references with a wrapping function in the first place. However, we don't want to implement the Java behavior at all costs, because otherwise, the generated code would become extremely complex. See my post on that subject: http://www.jsweet.org/comparing-the-gwt-transpiler-and-jsweet/.

So, in the end, what we need is to find a good balance. I don't think that there is a perfect solution because we are somewhere between two languages. In some ways, JSweet is becoming a language of its own with its own semantics. It is fine with me, but it needs to be very well documented and justified...

I will indeed probably leave the current implementation until some better idea is found... but then I should add a section in the language specifications to justify this choice.

renaudpawlak commented 8 years ago

In this post, you can see that JavaScript has exactly the same limitation when wanting to bind to this: http://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind. In a way, JSweet forces the use of bind in order to ensure safer semantics (although not implemented with bind the added anonymous function has exactly the same effect).

lgrignon commented 8 years ago

Super ce lien, ca appuie carrément l'approche..

Je ne vois par contre pas comment une solution pourrait arriver dans le futur :/ Même en TS ce n'est pas ultra satisfaisant.

On en reparlera :)

_L_ouis* Grignon*

2016-02-17 8:03 GMT+01:00 Renaud Pawlak notifications@github.com:

In this post, you can see that JavaScript has exactly the same limitation when wanting to bind to this: http://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind. In a way, JSweet forces the use of bind in order to ensure safer semantics.

— Reply to this email directly or view it on GitHub https://github.com/cincheo/jsweet/issues/65#issuecomment-185062757.

renaudpawlak commented 8 years ago

I am closing this issue because we can clearly live with this limitation. It would probably be possible to find a solution but it would be quite complicated to implement, so we would need to have a very important use case to support if we decided to implement it. I have linked this issue from the specs so that people are aware that this semantic difference between Java and JSweet exists.