Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Functional Programming in Java: Getting Started with Javaslang

DZone's Guide to

Functional Programming in Java: Getting Started with Javaslang

The author explains what all the hype around FP is about by outlining the features of Javaslang with multiple code examples

· Java Zone
Free Resource

Bitbucket is for the code that takes us to Mars, decodes the human genome, or drives your next car. What will your code do? Get started with Bitbucket today, it's free.

Java is an old language and there are many new kids on the block who are challenging it on its own terrain (the JVM). However, Java 8 arrived and brought a couple of interesting features. Those interesting features enabled the possibility of writing new amazing frameworks like the Spark web framework or Javaslang.

In this post we take a look at Javaslang which brings functional programming to Java.

Functional Programming: What is it Good For?

It seems that all the cool developers want to do some functional programming nowadays. They wanted to use object-oriented programming before. I personally think functional programming is great to tackle a certain set of problems, while other paradigms are better in other cases.

Functional programming is great when:

  • you can pair it with immutability: a pure function has no side-effect and it is easier to reason about. Pure functions means immutability, which drastically simplifies testing and debugging. However not all solutions are nicely solved with immutability. Sometimes you just have a huge piece of data shared between several users and you want to change it in place. Mutability is the way to go in that case.
  • you have code which depends on inputs, not on state: if something depends on state instead of input it sounds more like a method that a function to me. Functional code ideally should explicitly say which information is being used (so it should use just parameters). That also means more generic and reusable functions.
  • you have independent logic, which is not highly coupled: functional code is great when it is organized in small, generic and reusable functions.
  • you have streams of data that you want to transform: this is in my opinion the easiest place where you can see the values of functional programming. Streams received a lot of attention in Java 8.

Discuss the Library

As you can read on javaslang.com:

Java 8 introduced λ which dramatically increases the expressiveness of our programs, but “Clearly, the JDK APIs won’t help you to write concise functional logic (…)” – jOOQ™ blog

Javaslang™ is the missing part and the best solution to write comprehensive functional Java 8+ programs.

This is exactly how I see Javaslang: Java 8 gave us the enabling features to build more concise and composable code. But it did not do the last step. It opened a space and Javaslang arrived to fill it.

Javaslang brings many features to the table:

  • currying: currying is the partial application of functions
  • pattern matching: think of it as the dynamic dispatching for functional programming
  • failure handling: because exceptions are bad for function compositions
  • Either: this is another structure which is very common in functional programming. The typical example is a function which returns a value when things go well and an error message when things don't
  • tuples: tuples are nice lightweight alternatives to objects and perfect to return multiple values. Just do not be lazy and use classes when it makes sense to do so
  • memoization: this is caching for functions

For developers with experience in functional programming this will all sound familiar. For the rest of us let’s take a look at how we can use this in practice.

OK, But in Practice How Can We Use This Stuff?

Obviously showing an example for each of the feature of Javaslang is far beyond the scope of this post. Let’s just see how we could use some of them and in particular let’s focus on the bread and butter of functional programming: functions manipulation.

Given that I am obsessed with manipulation of Java code we are going to see how we can use Javaslang to examine the Abstract Syntax Tree (AST) of some Java code. The AST can be easily obtained using the beloved JavaParser.

If you are using gradle your build.gradle file could look like this:

apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile "com.javaslang:javaslang:2.0.0-beta"
    compile "com.github.javaparser:javaparser-core:2.3.0"
    testCompile "junit:junit:4.12"
}

We are going to implement very simple queries. Queries that can be answered just by looking at the AST without solving symbols. If you want to play with Java ASTs and solve symbols you may want to take a look at this project of mine: java-symbol-solver.

For example:

  • find classes with a method with a given name
  • find classes with a method with a given number of parameters
  • find classes with a given name
  • combining the previous queries

Let’s start with a function which given a CompilationUnit and a method name returns a List of TypeDeclarations defining a method with that name. For people who never used JavaParser: a CompilationUnit represents an entire Java file, possibly containing several TypeDeclarations. A TypeDeclaration can be a class, an interface, an enum or an annotation declaration.

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.Function2;
import javaslang.collection.List;

...

    /**
     * Helper method
     */
    public static boolean hasMethodNamed(TypeDeclaration typeDeclaration, String methodName) {
        return List.ofAll(typeDeclaration.getMembers())
                .map(Match.whenType(MethodDeclaration.class)
                        .then((t)-> Option.of(t.getName())).otherwise(() -> Option.none()))
                .map((n)->n.isDefined() && n.get().equals(methodName))
                .reduce((a, b)->a || b);
    }

    public static List<TypeDeclaration> getTypesWithThisMethod(CompilationUnit cu, String methodName) {
        return List.ofAll(cu.getTypes()).filter((t) -> hasMethodNamed(t, methodName));
    }

getTypesWithThisMethod is very simple: we take all the types in the CompilationUnit (cu.getTypes()) and we filter them, selecting only the types which have a method with that name. The real work is done in hasMethodNamed.

In hasMethodNamed we start by creating a javaslang.collection.List from our java.util.List (List.ofAll(typeDeclaration.getMembers())Then we consider that we are only interested in the MethodDeclarations: we are not interested in field declarations or other stuff contained in the type declaration. So we map each method declaration to either Option.of(true) if the name of the method matches the desidered methodName, or we map it toOption.of(false). Everything that is not a MethodDeclaration is mapped to Option.none().

So for example, if we are looking for a method name “foo” in a class which has three fields, followed by methods named “bar”, “foo” and “baz” we will get a list of:

Option.none(), Option.none(), Option.none(), Option.of(false)Option.of(true)Option.of(false)

The next step is to map both Option.none() and Option.of(false) to false and Option.of(true) to true. Note that we could have done that immediately instead of having two maps operation concatenated. However I prefer to do things in steps. Once we get a list of true and false we need to derive one single value out of it, which should be true if the list contains at least one true, and false otherwise. Obtaining a single value from a list is called a reduce operation. There are different variants of this kind of operation: I will let you look into the details.

We could rewrite the latest method like this:

    public List<TypeDeclaration> getTypesWithThisMethod(CompilationUnit cu, String methodName) {
        Function2<TypeDeclaration, String, Boolean> originalFunction = 
                AstExplorer::hasMethodNamed;
        Function2<String, TypeDeclaration, Boolean> originalFunctionReversed = 
                originalFunction.reversed();
        Function1<String, Function1<TypeDeclaration, Boolean>> originalFunctionReversedAndCurried = 
                originalFunction.reversed().curried();
        Function1<TypeDeclaration, Boolean> originalFunctionReversedAndCurriedAndAppliedToMethodName =
                originalFunction.reversed().curried().apply(methodName);
        return List.ofAll(cu.getTypes()).filter(asPredicate(
                originalFunctionReversedAndCurriedAndAppliedToMethodName));
    }

Why? It seems (and is) much more complicated but it shows us how we can manipulate functions, and this is an intermediate step to obtain code which is more flexible and powerful. So let’s try to understand what we are doing.

First a quick note: the class Function1 indicates a function taking one parameter. The first generic parameter is the type of the parameter accepted by the function, while the second one is the type of the value returned by the function. Function2 takes two parameters. 

We:

  • reverse the order in which parameters can be passed to a function
  • we create a partially applied function: this is a function in which the first parameter is “fixed”

So we create our originalFunctionReversedAndCurriedAndAppliedToMethodName just by manipulating the original function hasMethodNamed. The original function took two parameters: a TypeDeclaration  and the name of the method. Our elaborated function takes just a TypeDeclaration. It still returns a boolean.

We then simply transform our function in a predicate with this tiny function which we could reuse over and over:

    private static <T> Predicate<T> asPredicate(Function1<T, Boolean> function) {
        return v -> function.apply(v);
    }

Now, this is how we can make it more generic:

/** 
 * Get all the types in a CompilationUnit which satisfies the given condition 
 */
public List<TypeDeclaration> getTypes(CompilationUnit cu, Function1<TypeDeclaration, Boolean> condition) {
    return List.ofAll(cu.getTypes()).filter(asPredicate(condition));
}

/**
 * It returns a function which tells has if a given TypeDeclaration has a method with a given name.
 */
public Function1<TypeDeclaration, Boolean> hasMethodWithName(String methodName) {
    Function2<TypeDeclaration, String, Boolean> originalFunction = AstExplorer::hasMethodNamed;
    return originalFunction.reversed().curried().apply(methodName);
}

/**
 * We could combine previous function to get this one and solve our original question.
 */
public List<TypeDeclaration> getTypesWithThisMethod(CompilationUnit cu, String methodName) {
    return getTypes(cu, hasMethodWithName(methodName));
}

Ok, now we could generalize also hasMethodWithName:

    /**
     * This function returns true if the TypeDeclaration has at 
     * least one method satisfying the given condition.
     */
    public static boolean hasAtLeastOneMethodThat(
            TypeDeclaration typeDeclaration, 
            Function1<MethodDeclaration, Boolean> condition) {
        return List.ofAll(typeDeclaration.getMembers())
                .map(Match.whenType(MethodDeclaration.class)
                        .then(m -> condition.apply(m)).otherwise(false))
                .reduce((a, b)->a || b);
    }

    /**
     * We refactor this function to reuse hasAtLeastOneMethodThat
     */
    public static boolean hasMethodWithName(TypeDeclaration typeDeclaration, String methodName) {
        return hasAtLeastOneMethodThat(typeDeclaration, m -> m.getName().equals(methodName));
    }

After some refactoring we get this code:

package me.tomassetti.javaast;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.Function2;
import javaslang.collection.List;
import javaslang.control.Match;

import java.util.function.Predicate;

public class AstExplorer {

    public static boolean hasAtLeastOneMethodThat(
            TypeDeclaration typeDeclaration, 
            Function1<MethodDeclaration, Boolean> condition) {
        return hasAtLeastOneMethodThat(condition).apply(typeDeclaration);
    }

    public static Function1<TypeDeclaration, Boolean> hasAtLeastOneMethodThat(
            Function1<MethodDeclaration, Boolean> condition) {
        return t -> List.ofAll(t.getMembers())
                .map(Match.whenType(MethodDeclaration.class)
                        .then(m -> condition.apply(m)).otherwise(false))
                .reduce((a, b)-> a || b);
    }

    public static boolean hasMethodNamed(TypeDeclaration typeDeclaration, String methodName) {
        return hasAtLeastOneMethodThat(typeDeclaration, m -> m.getName().equals(methodName));
    }

    private static <T> Predicate<T> asPredicate(Function1<T, Boolean> function) {
        return v -> function.apply(v);
    }

    public static List<TypeDeclaration> typesThat(
            CompilationUnit cu, Function1<TypeDeclaration, 
            Boolean> condition) {
        return List.ofAll(cu.getTypes()).filter(asPredicate(condition));
    }

    public static Function1<TypeDeclaration, Boolean> methodHasName(String methodName) {
        Function2<TypeDeclaration, String, Boolean> originalFunction = AstExplorer::hasMethodNamed;
        return originalFunction.reversed().curried().apply(methodName);
    }

    public static List<TypeDeclaration> typesWithThisMethod(CompilationUnit cu, String methodName) {
        return typesThat(cu, methodHasName(methodName));
    }

}

Now let’s see how it can be used:

package me.tomassetti.javaast;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import javaslang.Function1;
import javaslang.collection.List;
import org.junit.Test;

import java.io.InputStream;
import static me.tomassetti.javaast.AstExplorer.*;
import static org.junit.Assert.*;

public class AstExplorerTest {

    @Test
    public void typesNamedA() throws ParseException {
        InputStream is = AstExplorerTest.class.getResourceAsStream("/SomeJavaFile.java");
        CompilationUnit cu = JavaParser.parse(is);
        Function1<MethodDeclaration, Boolean> isNamedBar = m -> m.getName().equals("bar");
        List<TypeDeclaration> res = typesThat(cu, hasAtLeastOneMethodThat(isNamedBar));
        assertEquals(2, res.length());
        assertEquals("A", res.get(0).getName());
        assertEquals("B", res.get(1).getName());
    }

    @Test
    public void typesHavingAMethodNamedBar() throws ParseException {
        InputStream is = AstExplorerTest.class.getResourceAsStream("/SomeJavaFile.java");
        CompilationUnit cu = JavaParser.parse(is);
        Function1<MethodDeclaration, Boolean> isNamedBar = m -> m.getName().equals("bar");
        List<TypeDeclaration> res = typesThat(cu, hasAtLeastOneMethodThat(isNamedBar));
        assertEquals(2, res.length());
        assertEquals("A", res.get(0).getName());
        assertEquals("B", res.get(1).getName());
    }

    @Test
    public void typesHavingAMethodNamedBarWhichTakesZeroParams() throws ParseException {
        InputStream is = AstExplorerTest.class.getResourceAsStream("/SomeJavaFile.java");
        CompilationUnit cu = JavaParser.parse(is);
        Function1<MethodDeclaration, Boolean> hasZeroParam = m -> m.getParameters().size() == 0;
        Function1<MethodDeclaration, Boolean> isNamedBar = m -> m.getName().equals("bar");
        List<TypeDeclaration> res = typesThat(cu, hasAtLeastOneMethodThat(m -> 
                hasZeroParam.apply(m) && isNamedBar.apply(m)));
        assertEquals(1, res.length());
        assertEquals("A", res.get(0).getName());
    }

    @Test
    public void typesHavingAMethodNamedBarWhichTakesOneParam() throws ParseException {
        InputStream is = AstExplorerTest.class.getResourceAsStream("/SomeJavaFile.java");
        CompilationUnit cu = JavaParser.parse(is);
        Function1<MethodDeclaration, Boolean> hasOneParam = m -> m.getParameters().size() == 1;
        Function1<MethodDeclaration, Boolean> isNamedBar = m -> m.getName().equals("bar");
        List<TypeDeclaration> res = typesThat(cu, hasAtLeastOneMethodThat(m -> 
                hasOneParam.apply(m) && isNamedBar.apply(m)));
        assertEquals(1, res.length());
        assertEquals("B", res.get(0).getName());
    }

}

The source file we used in this tests is this one:

class A {
    void foo() { }
    void bar() { }
}

class B {
    void bar(int x) { }
    void baz() { }
}

This is of course a very limited introduction to the potential of Javaslang. What I think is important to get for someone new to functional programming is the tendency to write very small functions which can be composed and manipulated to obtain very flexible and powerful code. Functional programming can seem obscure when we start using it but if you look at the tests we wrote I think they are rather clear and descriptive.

Functional Programming: Is All The Hype Justified?

I think there is a lot of interest in functional programming but if that becomes hype it could lead to poor design decisions. Think about when OOP was the new rising star: the Java designers forcing programmers to put every piece of code in a class and now we have utility classes with a bunch of static methods. In other words, we took functions and asked them to pretend to be a class to gain our OOP medal. Does it make sense? I do not think so. Perhaps it helped to be a bit extreme to strongly encourage people to learn OOP principles. That is why if you want to learn functional programming you may want to use functional-only languages like Haskell: because they really, really push you into functional programming so that you can learn the principles and use them when it makes sense to do so.

Conclusions

I think functional programming is a powerful tool and it can lead to very expressive code. It is not the right tool for every kind of problem, of course. It is unfortunate that Java 8 comes without proper support for functional programming patterns in the standard library. However some of the enabling features have been introduced in the language and Javaslang is making it possible to write great functional code right now. I think more libraries will come later, and perhaps they will help keeping Java alive and healthy for a little longer.

Bitbucket is the Git solution for professional teams who code with a purpose, not just as a hobby. Get started today, it's free.

Topics:
java ,functional programming ,javaslang

Published at DZone with permission of Federico Tomassetti, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}