Announcing KotlinFX


#1

I'm presenting KotlinFX — a “kotlinskoy” layer on top of JavaFX 8 in the same vein as ScalaFX and GroovyFX are. See https://github.com/eugenkiss/kotlinfx for more information. For the uninitiated, JavaFX 8 is Java's new and *much* improved spiritual Swing successor and is shipped by default with JDK 8. It seems that Oracle takes JavaFX very serious — and it shows as for instance it is even possible to use JavaFX on Android and iOS.

The main benefits that KotlinFX provides over pure Kotlin for JavaFX 8 are UI builders, extension properties and bindings. The builders allow you to define the UI layout in a tree-like manner just like Kotlin’s HTML builder example. The builders also have some common properties exposed as named parameters with default values so that even using the builders as mere constructors without nesting is beneficial. Extension properties make Java’s setters and getters transparent and KotlinFX’s bindings are a more succinct and natural way (i.e. using operators where possible) to define JavaFX bindings between JavaFX properties.

The hello world example from http://docs.oracle.com/javafx/2/get_started/hello_world.htm in KotlinFX looks as follows:

import javafx.application.Application import javafx.stage.Stage import kotlinfx.builders.* import kotlinfx.properties.*

fun main(args: Array<String>) {
  Application.launch(javaClass<HelloWorld>())
}

class HelloWorld : Application() {
  override fun start(stage: Stage?) {
  Stage(stage, title = “Hello World!”) {
           scene = Scene(width=300.0, height=250.0) {
           root = StackPane {
                   + Button(“Say ‘Hello World’”) {
                   setOnAction { println(“Hello World!”) }
                   }
           }
           }
  }.show()
  }
}


Please note that KotlinFX is currently in the very early “proof-of-concept phase”. That means that I only implemented as much of the library as is needed to make the few examples in the same repository look good. I think the basic structure of the library is sound — I’m very open to suggestions nonetheless — so that implementing most of the rest of the library is primarily an “endurance test”. However, I don’t know if I’ll have the required endurance for the rest :(. At the very least there is now a proof of concept Kotlin integration for JavaFX 8.

I would be glad to hear suggestions for improvements. Kontributers are welcome!

Implementation Remarks

In the following I want to present some remarks and questions with respect to the implementation.

Trait Inheritence

I remember there being a discussion about the usefulness of class inheritance in traits. It turns out that I use this feature beneficially for the UI builders. Namely, I have the following trait K that extends Pane:

trait K : Pane {   fun Node.plus(): Node {   getChildren()!!.add(this)   return this   }   fun Node.plus(node: Node): Node {   getChildren()!!.add(node)   return node   } }

And then I use this trait for the concrete builders:

public fun HBox( spacing: Double = 0.0, padding: JInsets = Insets(0.0), f: HBoxK.() -> Unit): HBox {   val hbox = HBoxK()   hbox.spacing = spacing   hbox.padding = padding   hbox.f()   return hbox } class HBoxK : HBox(), K

Which results in a usage like:

HBox(spacing=10.0, padding=Insets(10.0)) {   + TextField() + Label("Celsius =") + TextField() + Label("Fahrenheit") }

By using the trait K this way I only have to define the + operator once in the trait instead of in each concrete Pane subclass like HBoxK.

JDK 8 Annotations

The fact that KAnnotator annotations for JDK 8 seemingly do not exist made interfacing with JavaFX 8 inconvenient due to the !!s.  When will Kotlin come with KAnnotator annotations for JDK 8?  I tried to use KAnnotator to produce annotations for my copy of the JDK but it chocked with an exception. Maybe I did something wrong or is KAnnotator not yet compatible with Java 8?

Extension Properties Limitation

JavaFX 8 has the concept of properties. For example, you can get and set the value of a TextField t with t.getText() and t.setText(string) but you can also get the text property with t.textProperty(). The cool thing about properties is that you can use them to declare declarative bindings (e.g. connect a text property bidirectionally with another text property with a conversion function) without the need to use callbacks or the observer pattern.

Anyway, I tried to imitate ScalaFX such that t.text is syntactic sugar for t.textProperty(). This way you can update the property with t.text = string and get the (primitive) value of the property with t.text.get. I tried to achieve the same effect with Kotlin’s extension properties. Ideally, I should be allowed to write the following:

public var TextField.text: StringProperty   get() = textProperty()   set(v: String) = textProperty().set(v) // or setText(v) which is the short form

But this is not possible currently. That is why I currently use Kotlin extension properties with the basic setters and getters as follows:

public var TextField.text: String   get() = getText()   set(v) = setText(v)

And at least have an abbreviation for JavaFX's properties:

public val TextField.textP: StringProperty   get() = textProperty()

So my question is if anything speaks against allowing the setter parameter of a Kotlin extension property to be a different type from the returned getter type.

Code Generation

Speaking of extension properties, it seems to me that I could save a lot of work by generating the extension property definitions. Do you have suggestions in terms of tools (and workflows) I could use for that?

Konklusion

All in all though Kotlin's been pretty kool for this task ;).


[Ann] KotlinFX 0.1
#2

Awesome. I'll play with this next weekend. I file a case already.


#3

I'm experimenting with this at the moment too. The missing annotations and generally weak Java 8 support are definitely the biggest pains. For instance, I have been enjoying using Java 8 lambdas, but the "convert from java" tool completely mangles them. I might wait until Java 8 support gets fully baked so I can try incrementally converting my codebase.

Tighter integration between Kotlin properties and JavaFX properties would be great. Not sure how to do it yet though.

I don’t see much value in the builders I’m afraid. FXML is already a kind of DSL for building Java object heirarchies, with the advantage of a visual designer. I don’t feel like going back to writing the code all by hand …

Oh yes. The other problem I see is that if you do want to use FXML (or already have some), the use of reflection to initialise controller members doesn’t play nice with Kotlin’s null safety. The problem is, the compiler doesn’t know that the references will be initialised automagically by some framework. So you have to mark all these members as nullable even if they will actually always be set. There is probably no workaround for this - I guess anything that uses java serialization would have the same problem. There might need to be a type system escape to say “you can’t see this field being initialised, but trust me, it is!”


#4

Hi Mike,

You can use Delegated Properties to declare things as non-null that are initialised before use in situations like this.

See “Delegated Properties” down the bottom of http://blog.omalley.id.au/2013/07/27/null-handling-in-kotlin.html

Cheers,
Andrew


#5

mikehearn wrote:

I don’t see much value in the builders I’m afraid. FXML is already a kind of DSL for building Java object heirarchies, with the advantage of a visual designer. I don’t feel like going back to writing the code all by hand …

Yes, if you are happy with Scene Builder (the visual designer) then there is not much value in builders for you. The rest of KotlinFX, i.e. extension properties and bindings, should still benefit you.

But if you are not happy with Scene Builder you now have an alternative to hard to read and write “linearized” layout code and FXML. The problem with “linearized” layout code is that its structure does not reflect the structure of the resulting layout hierarchy and you have to introduce a lot of valueless intermediate variables. Not for nothing did JavaFX in Java support builders which were only dropped for a very specific technical reason. But then you could also write FXML by hand which solves the problems of linearized layout code. The problem is that FXML is another language, namely XML. Therefore, you have a context switch between Kotlin code and XML code whenever you want to work on layout code. The view specification has to be in a separate file and you have to carefully connect the layout specification with the rest of the code (i.e. load the FXML file, annotate specific fields of a controller etc.). With Kotlin builders you have all the advantages of FXML without the disadvantages. You have no context switch, i.e. you stay in the language and with the familiar tooling, and you don’t have to abide by the requirement of having the layout specification in a separate file — although you surely could do this. Note that in principal Scene Builder could work with and produce Kotlin builder code instead of FXML. Then you could have your cake and eat it, too.

Here are some links that list potential problems of GUI builders: GUI Builders Considered Harmful and Why GUI Builders are Evil. The main point of the articles is that by using a GUI builder you lose abstraction capabilities. From personal experience I find GUI builders (and also Scene Builder although I admit it’s a good program compared to other GUI builders) finicky and it seems to me that I am as fast or even faster when writing layout code by hand with builders than with a GUI builder. Prototyping the GUI design is also faster with pen and paper imho than with either builders or GUI builders.


#6

It's great to see such projects emerging.

About generative workflow: similar projects we are playing with all use code generation, btu we didn’t shape out a common library for that yet.


#7

Thanks Andrew. That's a great and useful article.


#8

Thanks. I already found the following link: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib. I guess I could use some code/ideas from there for code generation.

Maybe it drowned in the wall of text I wrote for the announcement :), but I’m really curious about my question with respect to allowing the setter parameter of a Kotlin extension property to be a different type from the returned getter type. Would this be possible or does something speak against it?


#9

Making setters accept a different type is likely to complicate the mental model of properties rather severely. We'll postpone the decision on this feature for later


#10

Ok, I understand the potential for suprising behaviour as suddenly

text = text

will give a type error. If one sees a property as a field in disguise than this is indeed surprising. However, if one sees a property as syntactic sugar for getters and setters than such a behaviour should not really be surprising.

I see it like this: if this feature is allowed it won’t be suddenly misused as its usefulness is probably restricted to library code. But then it doesn’t hurt to relax the constraint for the use case of library code. I understand the reluctance to implement this feature but I created an issue anyway http://youtrack.jetbrains.com/issue/KT-4970

I’m working on another project where I believe that this feature would be beneficial as well. In any case, if I find other non-contrived use cases for the relaxation I’m going to list them in the issue above.


#11

A quick update on KotlinFX. This time I'm presenting Kalium – a reactive way to define functional dependencies in KotlinFX. To understand the motivation let's consider the following Flight Booker GUI example. In Swing all you had were callbacks which are an unsatisfying solution to encode functional dependencies between components due to their inversion of control and relative verbosity. See for yourself with the solution to the Flight Booker task in Java7/Swing. It turns out that data-binding is a good idea to solve these kinds of issues. Not for nothing did JavaFX introduce this feature. The solution can be found here. For illustration, here's an excerpt of the relevant part:

returnDate.disableProperty().bind(flightType.valueProperty().isEqualTo("one-way flight")); startDate.textProperty().addListener((v, o, n) ->   startDate.setStyle(isDateString(n) ? "" : "-fx-background-color: lightcoral")); returnDate.textProperty().addListener((v, o, n) ->   returnDate.setStyle(isDateString(n) ? "" : "-fx-background-color: lightcoral")); ChangeListener bookEnabledAction = (v, o, n) -> {   switch (flightType.getValue()) {   case "one-way flight": book.setDisable(!isDateString(startDate.getText()));   case "return flight":  book.setDisable(            !isDateString(startDate.getText()) ||            !isDateString(returnDate.getText()) ||            stringToDate(startDate.getText()).compareTo(stringToDate(returnDate.getText())) > 0);   } }; flightType.valueProperty().addListener(bookEnabledAction); startDate.textProperty().addListener(bookEnabledAction); returnDate.textProperty().addListener(bookEnabledAction); flightType.setValue("one-way flight");

The first line is great. This is what we want albeit there's a need for a non-native “sublanguage” for bindings. However, the standard bindings support does not bring us very far as the rest of the lines still use callbacks. We could in principle create new custom properties and bindings for this task but that would involve a lot of (boilerplate) code. The KotlinFX version before Kalium is essentially the same in terms of behavior related code.

Having been inspired by Scala.Rx – you should check it out it’s very good – and some very interesting papers, I set out to improve the way functional dependencies can be expressed in KotlinFX. So with Kalium this is what we achieve now:

returnDate.disable { flightType.value() == "one-way flight" } startDate.style  { if (startDate.text().isDate)  "" else "-fx-background-color: lightcoral" } returnDate.style { if (returnDate.text().isDate) "" else "-fx-background-color: lightcoral" } book.disable {   when (flightType.value()) {   "one-way flight" -> !startDate.text().isDate   else           -> !startDate.text().isDate || !returnDate.text().isDate                            || startDate.text().asDate.compareTo(returnDate.text().asDate) > 0   } }

Notice the non-inverted and native-looking way these functional dependencies are expressed. You don't have to use a sublanguage. You use all the familiar control structures and it still magically works. The only thing you have to be aware of is using () for properties you want to have a dependency on.

Another example is the Timer task where the relevant part goes from

val elapsed = SimpleDoubleProperty(0.0) progress.progressP bind (elapsed / slider.valueP) elapsed.addListener { (v,o,n) ->   numericProgress.text = formatElapsed(n!!.toInt()) } reset.setOnAction { elapsed.v = 0.0 }

to

``

val elapsed = V(0.0)
progress.progress { elapsed() / slider.value() }
numericProgress.text { formatElapsed(elapsed()) }
reset.setOnAction { elapsed u 0.0 }


It would be really really great if I could write elapsed() = 0.0 like in Scala.Rx instead of elapsed u 0.0 (which of course means elapsed.updateTo(0.0)).

Yet another example is the Temperature Converter task where the relevant part goes from

celsius.textP.bindBidirectional(fahrenheit.textP, object : StringConverter<String>() {   override fun fromString(c: String?): String? =   if (isNumeric(c!!)) cToF(c) else fahrenheit.text   override fun toString(f: String?): String? =   if (isNumeric(f!!)) fToC(f) else celsius.text })

to

``

celsius.text { if (isNumeric(fahrenheit.text())) fToC(fahrenheit.text()) else celsius.text }
fahrenheit.text { if (isNumeric(celsius.text())) cToF(celsius.text()) else fahrenheit.text }


We see that JavaFX has a special case binding construct for bidirectional dataflow whereas in Kalium it just works™.

The current implementation is naive and can surely be improved drastically – suggestions are welcome. It’s also incomplete, that is, I again only implemented as much as is needed to make the few examples look good (I really must look into how to have the code generated… if you know of tools that traverse Java 8 class hierarchies let me know). That said, I’m foremost interested in the usability aspects of such a reactive API and how to integrate it well with existing toolkits like for instance JavaFX via KotlinFX – suggestions are welcome as well. There are a lot more things to work on. E.g., making a reusable library out of Kalium with combinators and stuff in the same vein as Scala.Rx (hard), creating more examples to discover the limitations and further potential improvements of the current API, incorporating the many more ideas from the aforementioned papers, increasing efficiency and so on. So contributions are welcome ;).

I have to nag at the getter-setter-type-properties issue again :). It is very easy to forget to use () and then not having the reactive expression automatically update because e.g. flightType.value and flightType.value() return the same type. If flightType.value returned a JavaFX property instead (like it’s done in ScalaFX) in most cases the compiler would complain if you forgot the (). A clear usability advantage. I tried making flightType.value return the corresponding JavaFX property but then you lose convenience on other ends as setting a value must be done with a setter which leads to, for example, ugly builder code.


#12

Another good news for my many dear KotlinFX fans :p! The following extensions are now generated for the whole JavaFX class hierarchy:

  • Extension properties for getters and setters
    • e.g. val t = label.text and label.text = "KotlinFX best" instead of val t = label.getText() and label.text.setText("KotlinFX best")
  • Abbreviations for JavaFX properties
    • e.g. textp and textp.v instead of textProperty() and textp.getValue() or textp.value
  • Kalium extensions

I first tried to parse the JavaFX class hierarchy with Eclipse JDT Core but that did not work out although in principle it should be possible afaik. My second try with a reflection-based approach did work out though. A minor problem is that with this approach the (generic) types you get are sometimes wrong or mangled so you need to have some special case conditions in your code generation script. Fortunately, these cases are few and often a wrong pattern is consistently wrong so that you essentially only need one special condition to cover many wrong cases. In any case, for anybody who also wants to create bindings in Kotlin for a Java library the code generation scripts for KotlinFX might be a handy starting point.

Until probably next weekend I want to finish the builder and JavaFX bindings support in KotlinFX. This has to be done manually by necessity. I want to create some additional KotlinFX examples, too. Finally, the KotlinFX Wiki will be improved. Specifically, I want to write a section with the title “Why KotlinFX?”. When all this is finished I believe KotlinFX will already be practically useful so a KotlinFX 0.1 release will be deserved.


#13

Impressing progress.

BTW, another option for reading .class files is ASM (http://asm.ow2.org/). We tried ASM and reflection, and they both have ups and downs: with ASM you can read code without loading it, but have to jump over hoops to check whether a class is a subclass of another class.


#14

It's “pick your poison”, isn't it ;).

Another advantage of the reflection based approach is that there is almost no learning investment required. I literally just used a snippet from Stackoverflow and already could get started. With JDT Core (and I assume with ASM) I needed to work with a lot of documentation and tutorials to even get started at all, including finding all the dependencies to work with Java 8.

Now that I think about it the JDT Core approach should have worked, too, and probably with better results although there would have been edge cases as well, just like with the reflection and apparently ASM approach. I was probably too aggravated with all the scaffolding and implicit knowledge needed to make it work (hell, I even compiled the project because I first didn’t find a recent maven dependency and it took like an hour and an obscure maven command found in an inconspicuous Stackoverflow answer) but here’s the needed dependencies if anybody wants to go the JDT Core route for binding generation.