How to use Java8's lambdas with PlayN 2.0 Signals and Slots

140 views
Skip to first unread message

Christoph Dietze

unread,
May 19, 2015, 4:11:23 PM5/19/15
to pl...@googlegroups.com
Hello,

I was in a cheerful mood when I switched the java version of my sekeleton PlayN 2 project up to 1.8 and was hoping to leverage the benefits of the new lambdas. I am aware to lose GWT since it doesn't support Java8 yet.
However, I cannot just use lambdas where a Slot is required. E.g. in my Game class:

 update.connect((Clock c) ->  System.out.println("Clock is: "+c.toString()));

Fails due to "cannot infer functional interface type". I guess the problem is that Slot has mutliple methods and the compiler does not figure out it shall overwrite the onEmit method.

Is there some way to use lambdas as Slots?
What's the plan to allow using lambdas together with Signal/Slots?
This is just such a natural fit and would make code much more concise.

Cheers,
Christoph

Ray Cromwell

unread,
May 19, 2015, 4:13:51 PM5/19/15
to pl...@googlegroups.com
GWT 2.8 nightly snapshot supports java8 language syntax FYI
--

---
You received this message because you are subscribed to the Google Groups "PlayN" group.
To unsubscribe from this group and stop receiving emails from it, send an email to playn+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michael Bayne

unread,
May 19, 2015, 4:35:07 PM5/19/15
to pl...@googlegroups.com
On Tue, May 19, 2015 at 1:11 PM, Christoph Dietze <christop...@gmail.com> wrote:
Is there some way to use lambdas as Slots?
What's the plan to allow using lambdas together with Signal/Slots?
This is just such a natural fit and would make code much more concise.

I haven't looked into that yet, but clearly the intent is to support using lambdas for Slots. I'm probably going to have to have a 1.8-only version to accomplish that, which is kind of unfortunate (because I expect I'll have to use default interface methods to preserve the existing mechanisms), but I'll try to find a way to avoid that annoyance.

In any case, I should get that show on the road. I'll move it up my TODO list.


Christoph Dietze

unread,
May 19, 2015, 4:43:26 PM5/19/15
to pl...@googlegroups.com
Thanks for moving it up!
And for all the work you already put into these nice libraries!

--

---
You received this message because you are subscribed to a topic in the Google Groups "PlayN" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/playn/OGNFSyzAfKk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to playn+un...@googlegroups.com.

Michael Bayne

unread,
May 19, 2015, 5:19:14 PM5/19/15
to pl...@googlegroups.com
On Tue, May 19, 2015 at 1:35 PM, Michael Bayne <m...@samskivert.com> wrote:
In any case, I should get that show on the road. I'll move it up my TODO list.

Well, that didn't take long. But there is one annoying choice that I have to make. To wit:

Lambdas can only implement functional interfaces, not single abstract method classes. React was using abstract classes both for the tiny performance improvement that comes with using classes rather than interfaces, and because it allowed Slot to do double duty as a ValueView.Listener (which is notified of two things: old and new value) and a SignalView "Listener" (which is notified of only one: the value emitted by the signal).

I can do everything I want with the following scheme:

interface ValueView<T> {
  interface Listener<T> {
    void onChange (T oldValue, T newValue);
  }

  void connect (Listener<T> listener);
  void connectNotify (Listener<T> listener);

  // these are necessary to let Java 8 know that it can synthesize a SignalView.Listener
  // (which is a subtype of ValueView.Listener and could otherwise be passed to the
  // above connect methods) when it sees a lambda like "connect(event -> { body })"
  void connect (SignalView.Listener<T> listener);
  void connectNotify (SignalView.Listener<T> listener);
}

interface SignalView<T> {
  interface Listener<T> extends ValueView.Listener<T> {
    void onEmit (T event);
    default void onChange (T oldValue, T newValue) { onEmit(newValue); }
  }
  void connect (Listener<T> listener);
}

abstract class Slot<T> extends SignalView.Listener<T> {
  // other slot methods like compose, andThen, etc.
}

But the one wrinkle is that "default" method in SignalView.Listener which makes the whole scheme Java 8 only. Blah. I really don't want to have two separate versions of React, one for 1.7 and one for 1.8. I'm also not ready to abandon Java 1.7 (that's a tempting solution, but I should at least wait for GWT's 1.8 support to be officially released :).

So I think what I'll do is put most of this machinery into place, but not make SignalView.Listener a ValueView.Listener yet, and instead have ValueView's connect methods wrap the supplied SignalView.Listener in a shim. The performance sensitive part of me recoils, but the reality is that this probably won't be a big performance deal, and when I finally do stop supporting Java 1.7, I can just add the default method to SignalView.Listener and change the implementation of the connect methods and everything will magically start using the more efficient approach.


Michael Bayne

unread,
May 19, 2015, 5:38:04 PM5/19/15
to pl...@googlegroups.com
On Tue, May 19, 2015 at 2:19 PM, Michael Bayne <m...@samskivert.com> wrote:
So I think what I'll do

Michael Bayne

unread,
May 19, 2015, 5:40:22 PM5/19/15
to pl...@googlegroups.com


Oh, I realize I need to do more work on RFuture and those bits, but at least Signal and Value will now work as expected.


Christoph Dietze

unread,
May 20, 2015, 3:02:08 PM5/20/15
to pl...@googlegroups.com
Very nice! It's working :-)

I patched playn to use react 1.6-SNAPSHOT and had to rebuild both playn and tripleplay to make it work. Before the old bytecode was still referencing the now removed methods causing errors like:

Exception in thread "main" java.lang.NoSuchMethodError: react.Signal.connect(Lreact/Slot;)Lreact/Connection;
 at tripleplay
.ui.Interface.<init>(Interface.java:45)

Thanks a bunch!

I'll see if I can get it to work with GWT 2.8-SNAPSHOT

Christoph Dietze

unread,
May 20, 2015, 4:58:03 PM5/20/15
to pl...@googlegroups.com
Well, my adventure in GWT 2.8 land using Java 8 wasn't so smooth.
I needed to patch playn code because one deprecated GWT api was removed going from 2.7 to 2.8. The tests pass and the change seems pretty straight forward so here is a diff for what it's worth:
diff --git a/html/src/playn/rebind/AutoClientBundleGenerator.java b/html/src/playn/rebind/AutoClientBundleGenerator.java
index b7deb36
..418a7a8 100644
--- a/html/src/playn/rebind/AutoClientBundleGenerator.java
+++ b/html/src/playn/rebind/AutoClientBundleGenerator.java
@@ -257,17 +257,15 @@ public class AutoClientBundleGenerator extends Generator {
   
*
   
*/
   private HashSet<Resource> getResources(GeneratorContext context, JClassType userType, FileFilter filter) {
-    Map<String, Resource> map = context.getResourcesOracle().getResourceMap();
     final String pack = userType.getPackage().getName().replace('.', '/');
   
     HashSet<Resource> resourceList = new HashSet<Resource>();
-    for (Entry<String, Resource> entry : map.entrySet()) {
-      String path = entry.getKey();
+    for (String path : context.getResourcesOracle().getPathNames()) {
       if (!path.startsWith(pack))
         continue;
       String ext = getExtension(path);
       if (EXTENSION_MAP.containsKey(ext))
-        resourceList.add(entry.getValue());
+        resourceList.add(context.getResourcesOracle().getResource(path));
     }
 
     return resourceList;


After that I could build playn against GWT 2.8.0-SNAPSHOT.
Building against GWT 2.7.0 also succeeds, so the change can be applied now. I can make a pull request for that if that's beneficial?!

However when compiling my game I get GWT compile errors like:
[ERROR] Line 153: The method stream() is undefined for the type List<Point>

So, the new JRE stuff like streams seems not to be available in GWT 2.8 yet.



Michael Bayne

unread,
May 20, 2015, 6:52:20 PM5/20/15
to pl...@googlegroups.com
On Wed, May 20, 2015 at 1:58 PM, Christoph Dietze <christop...@gmail.com> wrote:
Building against GWT 2.7.0 also succeeds, so the change can be applied now. I can make a pull request for that if that's beneficial?!

That looks fine to me, so I just went ahead and applied the diff and committed it.

However when compiling my game I get GWT compile errors like:
[ERROR] Line 153: The method stream() is undefined for the type List<Point>

So, the new JRE stuff like streams seems not to be available in GWT 2.8 yet.

Well, at least you can use lambdas in places if not all the other fancy Java 8 stuff. I don't even know if they're going to port the Stream stuff at all, though it sure would be nice if only for use on Java collections, which otherwise lack nice functional APIs. Certainly all the Spliterator/parallelism stuff isn't going to add much value in GWT land.


Ray Cromwell

unread,
May 20, 2015, 6:56:15 PM5/20/15
to pl...@googlegroups.com
Some subset of streams that make sense I believe are being ported, for example java.lang.functional is being worked on (https://gwt-review.googlesource.com/#/c/10243/)

We are obviously relying on the community to help us, and of course, never copy code from the Oracle JDK if you're going to reimplement. :)



--

---
You received this message because you are subscribed to the Google Groups "PlayN" group.
To unsubscribe from this group and stop receiving emails from it, send an email to playn+un...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages