Above, Below, and Beyond Tech Talk

by Rahel Lüthy

June 22, 2016

Covariance in Java and Scala

While there are tons of articles explaining the concept of covariance in Java, this post attempts to highlight how and why Java and Scala differ when it comes to collection type safety.

Java Arrays

Let’s start with Java arrays. They are covariant, meaning that an array of type S[] is a subtype of an array of type T[] if S is a subtype of T:

Cat[] cats = { new Cat("Gizmo"), new Cat("Bella") };
Animal[] animals = cats;

Things are simple and life is good. The fact that arrays are covariant makes code re-use possible. Utility methods inside java.util.Arrays are perfect examples, e.g.:

public static int binarySearch(Object[] a, Object key) {
  // ...
binarySearch(new Integer[]{1, 2, 3}, 2); // VALID

As usual, everything falls apart because of mutability. Java arrays are mutable, making it possible to store the wrong type of object into an array without the compiler being able to tell:

Cat[] cats = { new Cat("Gizmo"), new Cat("Bella") };
Animal[] animals = cats;
animals[0] = new Dog("Max"); // FAILS AT RUNTIME

Ouch, heap pollution – an ArrayStoreException is thrown at runtime! People obsessed with types tend to call this unsound.

Java Generics

With Java 5 came generics, which allow “a type or method to operate on objects of various types while providing compile-time type safety.” (Oracle Java 1.5 Docs). Let me repeat that: Generics were introduced to improve type safety. It was thus not an option to accept the same unsound array behavior. Because Java collections are mutable, the only option was to make a List<T> invariant:

List<Cat> cats = Arrays.asList(new Cat("Gizmo"), new Cat("Bella"));
List<Animal> animals = cats; // DOES NOT COMPILE

Even though Cat is a subtype of Animal, List<Cat> is not a subtype of List<Animal>. This is not very intuitive, but it is necessary to prevent heap pollution at runtime.

Obviously, we still want to write re-usable utility code. Wildcards are making that possible. Methods inside java.util.Collections are good examples, e.g.:

public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {
  // ...

In Java, you thus get better compile-time safety when using generified collections than when using raw arrays.

Scala Arrays

In Scala, things are a lot different. Array[T] is Scala’s representation for Java’s T[]. Like in Java, a Scala Array is mutable. But in contrast to Java, Scala cares a lot more about compile-time type safety. The only way to prevent unsound heap pollution is to make Array[T] invariant:

val cats: Array[Cat] = Array(Cat("Gizmo"), Cat("Bella"))
val animals: Array[Animal] = cats // DOES NOT COMPILE

Scala arrays are thus safer to use than Java arrays.

Scala List

Things are even better for Scala List, which is immutable. It is thus perfectly safe to make List[+T] covariant (notice the little +). There’s just no way to ever sneak in an object, let alone sneaking in an object with the wrong type:

val cats: List[Cat] = List(Cat("Gizmo"), Cat("Bella"))
val animals: List[Animal] = cats // VALID

In other words: In Scala, List[S] is a subtype of List[T] if S is a subtype of T.

May 3, 2016

Programmatic JavaFX Styling

JavaFX components can be styled via CSS. This is great, but sometimes wiring up a separate CSS file to set a simple background color is a bit over the top. That’s where the setStyle method comes in handy:

Label label = new Label("Hello");
label.setStyle("-fx-background-color: deeppink");

CSS classes are used to style more advanced components. A ScatterChart’s plot background can e.g. be styled via the chart-plot-background class. This is how the styling would look like in a CSS file:

.chart-plot-background {
    -fx-background-color: transparent;

Unfortunately, we cannot simply cram the same CSS into the setStyle method:

NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
ScatterChart<Number, Number> chart = new ScatterChart<>(xAxis, yAxis);

        ".chart-plot-background {\n" +
        "    -fx-background-color: transparent;\n" +

Instead, the lookup method can be used to navigate the CSS hierarchy:

chart.lookup(".chart-plot-background").setStyle("-fx-background-color: transparent");

In case you were wondering why you would ever want to have a transparent chart background: The ScatterChart component does not support line connections. Using a StackPane to layer a ScatterChart and a LineChart can serve as a simple workaround. The chart in the top layer must then have a transparent background.

November 11, 2015

Play Audio via Elm

In preparation for a MaKey-MaKey-workshop, I needed a simple HTML page which plays a sound whenever any key is pressed. I am using all sorts of excuses to write Elm lately, so here’s how I ended up triggering the audio clip (full sources on GitHub, deployed version here).

First, we need a signal that indicates whether any key is currently down:

isAnyKeyDown : Signal Bool
isAnyKeyDown =
  Signal.map (\keys -> not (Set.isEmpty keys)) Keyboard.keysDown

For debugging, let’s visualize our events by displaying a small note image:

view : Bool -> Html
view isAnyKeyDown =
  if | isAnyKeyDown -> img [ src "note.png" ] []
     | otherwise -> div [] []

main : Signal Html
main =
  Signal.map view isAnyKeyDown

Next, we need to actually play the audio. Elm itself does not support this out-of-the-box, but triggering it via HTML5 and JavaScript is quite straight forward:

    <audio id="audio" src="piano.ogg" preload="auto"></audio>
var audio = document.getElementById('audio')
function playAudio() {
    // reset if already running
    audio.currentTime = 0;

    // go!

Finally, we need to wire our JS function to our Elm signal. This is where Elm ports come in:

port playAudio : Signal ()
port playAudio =
  Signal.map (\_ -> ()) (Signal.filter identity False isAnyKeyDown)

There are two things to note here: First, we’re filtering our isAnyKeyDown to no longer contain key release events. Second, we’re mapping our signal of boolean values to a signal of unit types () to match the signature of our parameter-less JS function.

All there is left, is to actually trigger the JS callback whenever Elm sends an event:

    var app = Elm.fullscreen(Elm.Main, {});

Older Posts » Archive