Menu

Official website

If Scala Is Functional, Why Cannot Java?!


26 Mar 2018

min read

At work a lot of people use Java and the Object Oriented paradigm. The cool thing is that the very same people fill our functional programming Meetups. Most likely they would like to learn this programming paradigm that is little by little changing the Java-universe.

I am lucky enough to work on a Scala code base and be familiar with Java. So I am dedicating pieces of my schedule to prepare a workshop and course material to offer a bridge between OOP and FP in the Java ecosystem for my curious colleagues. I thought then that this could be of interest to others as well, so I decided to leak some of the material as blog posts.

The idea is to start from the basics: what makes Scala a renowned functional programming language and at what point is Java to become one? In this post we will see the power of Java 8 lambdas, and particularly how useful important is that lambdas can take as arguments and return other lambdas.

Functional programming revolves around the concept of composition. Composition is the act of having simple things work together to produce a more complex thing. For example, concatenating strings is a form of composition:

public class StringComposition {
  public static void main(String\[\] arg) {
    String simpleThing1 = "Complex";
    String simpleThing2 = "Thing";
    String complexThing = simpleThing1 + simpleThing2;
    System.out.println(complexThing);
  }
}

In my definition of composition I mentioned "things" for a reason: we would like to compose without caring about the type of things we are composing.

Now I will define a more general definition of composition in Scala:

def composition\[A, B, C\](f: A => B,
                         g: B => C): A => C = {
  (a: A) => g( f(a) )
}

This means that given two functions f and g</code I can produce a new function g . f by composing them together. If you are unfamiliar with Scala, you should note the following:

  1. the [A, B, C] is providing type variables (i.e., think of these as inline generics) to the function: so the compose function behaves as

def composition\[String, Char, Int\](f: String => Char,
                                   g: Char   => Int): String => Int = {
  (a: String) => g( f(a) )
}

but also as

def composition\[Double, Int, Char\](f: Double => Int,
                                   g: Int    => Char): Double => Char = {
  (a: String) => g( f(a) )
}

and (infinitely) many more.

2. f: A ⇒ B means that f if of type function and takes a argument of type A and returns a value of type B

3. {(a: A) ⇒ g(f(a))} defines a function inline that takes an argument A and returns g(f(a)).

Let’s see at an example of usage:

val fnProducingString = { u:Unit => "Complex"}
val fnConcatenatingThing = { s: String => val thing = "Thing"; s ++ thing }

val fnMakingComplexThing =
    composition(fnProducingString, fnConcatenatingThing)

val complexThing : String = fnMakingComplexThing.apply(())

println(complexThing)

So, how do we write this in Java? Let’s list some functional programming fundamentals that we have just touched in the Scala example:

First Class Functions

We can assign functions to variables:

val fnProducingString = {u: Unit => "Complex"}

Lexical Closures

We can save variables as context of a function:

val fnConcatenatingThing = {s: String => val thing = "Thing"; s ++ thing }

Higher Order Functions

We can have functions as arguments and/or return value of a function:

def composition\[A, B, C\](f: A => B, g: B => C): A => C = {(a: A) => g( f(a) )}

You may wonder why these are fundamentals of functional programming. We will cover the reasons more in detail in next posts, but in short:

  • First class functions let us treat functions as variables, which we can pass around and assign as input of other functions. This lets us make higher order functions.

  • Lexical closures lets us give a context to functions that cannot be modified from the outer scope: this makes functions independent from the state of the system they are running in. Luckily enough Java 8 comes with lambdas, which provide valuable syntax for exactly these things.

import java.util.function.\*;
public class FunctionalJava {
  public static <A, B, C> Function<A, C> composition(Function<A, B> f, Function<B, C> g){
    return (A a) -> g.apply(f.apply(a));
  }

  public static void main(String args\[\]) {
    Function <Void, String> fnProducingString = v -> "Complex";
    Function <String, String> fnConcatenatingThing = s -> {
      String thing = "Thing";
      return s + thing;
    };

    // fnConcatenatingThing.compose(fnProducingString);
    Function <Void, String> fnMakingComplexThing =
        FunctionalJava.composition(fnProducingString, fnConcatenatingThing);

    String complexThing = fnMakingComplexThing.apply(null);
    System.out.println(complexThing);
  }
}

The main difference in Java is that we have to define explicitly types for variables. Also we need to use apply explicitly when using functions as first class values (i.e., assigning lambdas to variables like fnProducingString). So to mirror our Scala analysis:

First Class Functions

We can assign functions to variables:

Function <Void, String> fnProducingString = v -> "Complex";

Note that we use Void to represent the Scala unit type. Java’s class Void has only one instance: the value null. Similarly Scala’s type Unit has only one instance: the value (). In this example we use Unit because I want to make clear that even values like "Complex" can be thought as a lambda in the functional programming paradigm.

Lexical Closures

We can save variables as context of a function:

Function <String, String> fnConcatenatingThing = s -> {
  String thing = "Thing";

  return s + thing;
};

Higher Order Functions

We can have functions as arguments and/or return value of a function:

public static <A, B, C> Function<A, C> composition(Function<A, B> f, Function<B, C> g){
  return (A a) -> g.apply(f.apply(a));
}

We used the static method to exploit the Java generics. Note that we defined composition as an high order function exercise; indeed both Java and Scala define composition on their Function types:

// Scala
val x = {s: String => s ++ "World"}.compose({u: Unit => "Hello"}).apply(())
println(x)
// Java
import java.util.function.\*;

public class FunctionalJava {
  public static void main(String args\[\]){
    Function <Void, String> h = (Void v) -> "Hello";
    Function <String, String> w = (String s) -> s + "World";

    String x = w.compose(h).apply(null);

    System.out.println(x);
  }
}
import java.util.function.\*;
import java.util.\*;
import java.util.stream.\*;
public class FunctionalJava {
  public static void main(String args\[\]){
    Function <String, Stream> readFromDB =
        (String key) -> Arrays.asList(1, 2, 3, 0, -1, 100).stream();

    Function <Stream, Integer> countValuableItems = (Stream l) -> {
      long count = l.filter(e -> e <= 1).count();
      return Math.toIntExact(count);
    };

    Function <String, Integer> howManyValuableItemsInDbForKey =
        countValuableItems.compose(readFromDB);

    String valuableInformation = "Valuable items: " +
        howManyValuableItemsInDbForKey.apply("someKey");

    System.out.println(valuableInformation);
  }
}

In this example I fake a database query that returns a Stream of very valuable integers. Then I define a function that picks only some of the database values (filter(e → e ⇐ 1)) and returns how many of these I found (.count()). Again you can see how easy is to create the more complex (and useful) function howManyValuableItemsInDbForKey through composition.

In future posts I plan to show how Java does with concerns like Immutability, Referential Transparency, Recursion, Expression-Oriented Programming, Currying, Lazy evaluation, Algebraic Data Types and Higher Kinder Types.

expand_less