I had a quick look at Elm and it is quite impressive (and expressive).
In Elm you construct your scene as
Signal Element
which in JavaFX would be roughly equivalent to
ObservableValue<Node>
Naive translation to JavaFX would mean that you swap the whole scene on each update, which is prohibitively costly. In Elm, Element
is an immutable abstract representation of a scene node, constructed off the screen (off the DOM). It is cheap enough to recreate the whole scene graph on every update (because of immutability, it is safe to reuse unchanged branches). The root Element
is rendered to DOM, but, as I understand it, only the first time the full DOM is constructed. On subsequent updates, Elm runtime runs an algorithm to compare the new root element to the old one and only modifies those parts of the DOM that need updating, which is fast enough. The same technique is used by React.js. So both Elm and React.js provide high-level functional abstraction, but use mutability under the covers for performance reasons.
With some additional syntactic noise, you can translate most Elm constructs to JavaFX (*). For example,
lift2 display Window.dimensions gameState
is equivalent to
import static org.fxmisc.EasyBind.*;
combine(Window.dimensions, gameState, display)
The missing piece is the library of immutable abstract widget representations and their efficient rendering into JavaFX scene graph.
What you could do is:
- create such a widget library for JavaFX;
- or even compile Elm to JavaFX (instead of HTML+JavaScript).
I would love to see either of those done.
(*) I think Elm records are very powerful and would require a lot of boilerplate in Java (or Scala).
Elm:
type Point = { x:Float, y:Float }
p = { 0.0, 0.0 }
q = { p | x <- 5.0 }
Java:
class Point {
public final float x;
public final float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public Point updateX(float x) {
return new Point(x, y);
}
public Point updateY(float y) {
return new Point(x, y);
}
}
Point p = new Point(0.0, 0.0);
Point q = p.updateX(5.0);
Scala:
case class Point(x: Float, y: Float)
val p = Point(0.0, 0.0)
val q = p.copy(x = 5.0f)