Tuesday, December 18, 2012

Adding 'condapply' to Scala, for natural JavaFX bindings

The Scala programming language is wonderful, and if anything, it suffers from a little bit too many tweaks and shortcuts (access syntactic sugar).

But here's one more suggestion.

The particular itch is that JavaFX bindings that in some languages can be expressed in a normal if/else:

  def z = bind if (b) then x else y;

 cannot be created so nicely in Scala (or in Java, for that matter) - since if and else are keywords.

  def z = when (b) then x otherwise y;

This is how the current ScalaFX library does it.

Since Scala is a very powerful language, I began to toy with the idea, how it could allow the "natural" binding syntax for ScalaFX. And - I think it can (well, not the current Scala, but it could be added).

Existing apply and unapply

Currently (Scala 2.9) Scala has methods called apply and unapply, which are called when i.e. a function call is invoked on a class. The compiler transforms the (...params...) of the call into a apply(...params...) call for the class. This is very, very common in Scala.

The unapply is used less seldom, in pattern matching (in case you want your class to support this amazing feature fully). Normally classes don't need to do that much (one can use "case classes" that do it automatically).

Anyways, this provides a precedent as to methods where the compiler transforms its syntax into bendy method calls.

Introducing condapply

I would like to call this cond, but that would probably be a bit too common, and it would lose the mental bridge to existing (and well understood) apply and unapply methods. So let's say it's condapply.

  def condapply[T]( t: => T, f: => T = null ): T

That's Scala prototype for a method that takes two "by name" parameters (that simply means, they will be lazily evaluated; only one of them). Both the parameters are expected to provide the same type of value (T), and the method itself will return this very type. If no else branch exists, f will be null.

What ScalaFX could do with this, is to have the Observable trait implement this method, and therefore make Observable's applicable within an if. What the implementation would do is bind that Observable to the two branches t and f (mathing then and otherwise in JavaFX API).

By the Scala prototype the branches would not need to be of the same type as the condition, but here they happen to be.

For cases where there is no else branch, the Scala compiler would create a condapply call with just one parameter. It would be up to the class to catch both cases (either using two separate prototypes, variable argument list, or default parameter for the latter parameter).

The benefits

The suggested addition to Scala would allow conditional bindings to be expressed in the usual if/else syntax, s.a.

  def z = if (b) x else y

It will not render any existing code invalid, since currently the if statement is limited to conditions of type Boolean

Whether it will make code more readable, and easily understandable, is a matter of opinion. Personally, I would probably use it, but also something like:

  def z = bindIf (b) x bindElse y

is quite okay (and can be done without changes to Scala).

Other uses

There may be other uses to the presented technique outside of ScalaFX.

No comments: