Today's post takes a look at a few of the JavaFx language features that I find interesting. It's by no means an exhaustive rundown of the language; for that, take a look at the language reference (I expect this link to change at some point in the future — right now it points to a build system). And the most interesting feature, binding, will be the topic of its own post.
This is going to be a long post, so get a cup of coffee.
Scripts
A JavaFx source file is called a “script,” and looks a lot like a JavaScript source file: unstructured, with variable definitions, functions, and inline executable code all jumbled together. But, as I noted earlier, this “script” is actually compiled into a Java class.
This class definition has a lot of boilerplate in it to bridge the gap between features of JavaFx and JVM bytecode. All of this boilerplate is, of course, implementation dependent and subject to change. One piece is the static method javafx$run$()
, which serves the same function as Java's main()
; the javafx program looks for this method when it starts a JavaFx application.
Another implementation detail, and one that I think is a little disturbing, is that all of the script's variables and functions are represented as static class members. I suspect that this detail is driven by the idea that scripts a primarily used for initialization of the GUI, and most action takes place in classes.
Class Members and Initialization
Unlike Java, but like JavaScript, classes are happy to expose their internals: the language includes access modifiers, and the language reference indicates that the default is script-level access, but the generated code says that everything is public.
Rather than providing constructors, you initialize instances using an “object literal” notation that's very similar to JavaScript's object literals:
class MyClass { var a : Integer; var b : String; } var z1 = MyClass { a: 123, b: "456" };
There's no new
! And I should mention that JavaFx is very liberal with regards to delimiters in an object initializer: here I separated my initializers with a comma, which follows the JavaScript practice. However, I could have used a semi-colon, or simply omitted the delimiter altogether; the JavaFx compiler would recognize where each item ended. I still haven't figured out all of the rules, so simply follow Java practice: semi-colons at the ends of statements, and commas between items in a list.
Mixin Inheritance
The Java inheritance rules are imposed by the JVM: a given class may have only one superclass, but may implement any number of interfaces. JavaFx appears to bend the rules : you can create “mixin” classes, similar to Scala traits, and a JavaFx class can extend more than one mixin:
mixin class Foo { var x : Integer; } mixin class Bar { var y : String; } class Baz extends Foo, Bar { override function toString() : String { "[{x},{y}]"; } } var a = Baz { x : 123, y : "456" }; println(a);
Of course, JavaFx doesn't break the rules: the JavaFx compiler translates mixins into composition. The Baz
class actually holds instances of Foo
and Bar
, and has getter and setter methods that delegate to those instances. Incidentally, you won't find mixins in the current language reference; you need to go to the tutorials.
This example shows one additional feature of inheritance: you have to explicitly override member variables and methods. Here we implement a custom toString()
method. Since that method is already defined by java.lang.Object
, which is the ultimate ancestor of any JavaFx class, we need to mark it as explicitly overridden. As far as I can tell, this is simply a means of error-checking, similar to Java's @Override
annotation; JavaFx does not impose “final unless defined otherwise” semantics.
Variables, Definitions, and Type Inferencing
In the class examples above, you saw the canonical variable declarations:
var NAME : TYPE;
At first glance, this appears to be a case of the language designers wanting to look like ActionScript and not Java; it's semantically equivalent to a Java variable declaration, which puts the type first. However, JavaFx also supports type inferencing: you can declare and initialize a variable at the same time, and the compiler will figure out what type you're using:
var a = "foo"; var b = a;
For people tired of constantly repeating long variable declarations in Java, type inferencing is a godsend. For people used to JavaScript, which doesn't have strong typing, they may find type inference annoying:
var a = "foo"; a = 123; // this won't compile
To close out the discussion of variables, I'll note that JavaFx also provides the def
keyword, which is similar to Java's final
modifier:
def x = 123; x = 456; // won't compile
I'm dubious regarding the value of def
. With multi-threaded programming, it's very useful to declare that a “variable” is actually immutable. Scala, for example provides the val
keyword to do this. However, the JavaFx def
keyword does not imply immutability: it can reference a bound expression, which may change during runtime. Until the language matures, and develops truly different semantics for def
versus var
(and I'm not sure what those might be), I plan to stick with var
for all variables.
Blocks Have a Value
Most “curly brace” languages use those braces to make a group of statements syntactically equivalent to a single statement. For example, in Java:
for (int ii = 0 ; ii < 10 ; ii++) System.out.println("this is in the loop"); System.out.println("this isn't"); for (int ii = 0 ; ii < 10 ; ii++) { int zz = 12 * ii; System.out.println("this is in the loop"); System.out.println("as is this"); } System.out.println("this isn't");
Blocks also introduce a new scope for variables: in the example above, the variable zz
is only accessible from code within the block. In JavaFx, blocks have an additional feature: the last expression in the block becomes the value of that block:
var z = { var x = 10; var y = 20; x + y; };
Interesting, but what's the point? Well, consider that in JavaFx the the if
keyword introduces an expression, not a statement. So you get the following formulation of a ternary expression:
var x = 12; var y = if (x < 20) { var z = x / 2; z - 17; } else { 23; }
And not only is if
an expression, but for
is as well. Which is particularly useful when generating sequences (the JavaFx equivalent of an array):
var seq = for (x in [1..5]) { [x, x*x, x*x*x]; }
Explaining that last example would be a blog posting on its own. I suggest however, that you try it out if you've downloaded a development environment.
Closures
Closures are a hot topic in modern programming languages. As C programmers have known for decades, function pointers are an incredibly powerful tool (Lisp programmers have known this for longer, but such knowledge is indistinguishable from their general smugness). A big benefit of function pointers is that they let you abstract away a lot of boilerplate code, particularly in the area of resource management. For example, here's a typical piece of Java database code:
Connection cxt = DriverManager.getConnection("url"); PreparedStatement stmt = null; try { stmt = cxt.prepareStatement("sql"); ResultSet rslt = stmt.executeQuery(); // process result set } catch (SQLException ex) { // do something with the exception } finally { if (stmt != null) stmt.close(); if (cxt != null) cxt.close(); }
That boilerplate try
/catch
/finally
must be repeated for every single SQL statement that you execute. And as written, it has a bug: the call to stmt.close()
can throw, in which case the Connection
will never be closed. If you cache PreparedStatement
s, the code becomes even more complex. With first-class functions, you can implement the boilerplate in a DatabaseManager
class, and define a function that just processes the result set:
var dbManager = DatabaseManager { /* config */ }; dbManager.executeQuery( "sql", function(rslt:ResultSet) { // process results here });
In this example, we create a new function object as an argument to the executeQuery()
method. This method can then call the function, passing in the ResultSet
that it gets after executing the query. Once the method finishes, the function will be garbage-collected.
The example above is a bit abstract, so here's a simple compilable script:
var fn = function(x, y) { x + y; } var z = fn(12, 13); println(z);
Here we define a variable, fn
, that holds a function. We then execute the function, storing the result in variable z
. We could have passed the function call directly into println()
, and we could pass the function itself to some other function.
But wait, there's more! JavaFx implements true closures, where a function has access to the variables defined in its enclosing scope:
var a = 17; var b = 23; var c = function() { var t = a; a = b; b = t; }; println("before swap: a={a}, b={b}"); c(); println("after swap: a={a}, b={b}");
Personally, I don't particularly like closures. You could invoke c
from any point in the code, perhaps in a completely different script; this is a great way to make your program behave in an indeterminate and hard-to-debug manner. In cases where you actually want the ability to update script variables from somewhere else, binding is probably a better option; I'll write about that tomorrow.
To close out this post, I want to note that JavaFx does not support lazy evaluation of functions. To my mind (if no-one else's), that's a big part of what makes a “functional” programming language: you can pass a first-class function to another function that expects, say, an integer, and the passed function will be evaluated when needed to provide the value.
No comments:
Post a Comment