Functional Programming

Turning Code into Data

René Schwietzke, Xceptance

What is Functional Programming?

Functional programming (fP) uses functions to achieve its goals. Compared to imperative or object-oriented programming where the use of control statements prevails.

What is fP again?

Seriously what is Functional Programming (fP)?

Challenges

  • Typical code has side effects
  • Typical data is mutable
  • Use of statements to control the flow
  • Less human: "Retrieve each element of an array and add this element to a total."

fP

  • Side-effect free
  • Same input data, same output data
  • No mutation of data ever
  • Apply functions instead of control statements
  • More human: "Sum up the array."

The Next Level

For fans of the pure theory.

Pure Functional Programming

  • Everything is final
  • No assignments
  • No states
  • No counters
  • No loops, recursion instead
  • No exceptions
  • Function encapsulate code

Functional Programming Style

  • Side-effect free as much as possible
  • Counters and loops are fine if invisible and not part of the control flow
  • Errors are ok, runtime exceptions only locally
  • Interfaces are exception-free and communicate via Optionals
  • Log files are ok

Summary

What do we really want?

  • Cleaner and easier to read code
  • Less boilerplate code
  • Predictability aka side-effect free
  • Repeatability
  • Testability
public void classic()
{
    // get me old Apples sorted by age
    List<SKU> apples = new ArrayList<>();
    
    for (SKU s : inventory)
    {
        if ("Apple".equals(s.getBrand()) && s.getAge() > 2)
        {
            apples.add(s);
        }
    }
    
    Collections.sort(apples, new Comparator<SKU>() {
            @Override
            public int compare(SKU s1, SKU s2) {
                return s1.getAge() > s2.getAge() ? 1 : -1;
            }
        };
    );
}
public void fP()
{
    // get me old Apples
    final List<SKU> apples = inventory
            .filter(
                s -> "Apple".equals(s.getBrand()) && s.getAge() > 2
            )
            .sort(
                (s, t) -> s.getAge() > t.getAge() ? 1 : -1
            );
}

Examples

What might be functional?

Functional Or Not?

Let's see if we can see what functional really is

public void functional(String s, List<String> l)
{
    final int length = s.length();
    final String s2 = s.substring(3);
    
    l.add(s);
    
    Collections.shuffle(l);
    
    final int i = Random.nextInt(l.size());
    final String s3 = l.get(i);
    
    final List<String> l2 = l.subList(0, l.size() / 2);
}
public void functional(String s, List<String> l)
{
    final int length = s.length(); // yes
    final String s2 = s.substring(3); // yes
    
    l.add(s); // no
    
    Collections.shuffle(l); // very no
    
    final int i = Random.nextInt(l.size()); // never
    final String s3 = l.get(i); // yes
    
    final List<String> l2 = l.subList(0, l.size() / 2); // yes and no
}

From Classic to Functional

Let's try something

Our Car Museum

Let's come up with some example to demonstrate a few nifty things.

  • We own a museum with cars and trucks aka vehicles
  • We have a list of our treasures
  • Each vehicles has criteria such as color, age, type, brand, and so on
  • We want to filter by brand "Maybach"
/**
 * The classic approach I
 */
public List<Vehicle> filterByBrand(final List<Vehicle> vehicles)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if ("Maybach".equals(v.getBrand())
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = filterByBrand(vehicles);
}    

Are we functional yet?

Is this functional?

/**
 * The classic approach I
 */
public List<Vehicle> filterByBrand(final List<Vehicle> vehicles)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if ("Maybach".equals(v.getBrand())
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = filterByBrand(vehicles);
}    
  • filterByBrand is functional without side-effects
  • someOtherCode is functional too
  • So what is the fuzz all about?
  • We are still a little hard-coded, aren't we?

Become flexible

Avoid hardcoding anything ever.

  • What if I want to filter for Maybach and Audi and...?
  • That is not yet flexible.
/**
 * The classic approach II with flexible brand
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, final String brand)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (brand.equals(v.getBrand())
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = filterByBrand(vehicles, "Maybach");
}    

Brute force multi-filter

Naive solution of multi-brand filtering

/**
 * The classic approach II with flexible brand
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, final Set<String> brands)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (brands.contains(v.getBrand())
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    
    final Set<String> brandsToFilter = new HashSet<>();
    brandsToFilter.add("Maybach");
    brandsToFilter.add("Audi"),
    
    final List<Vehicle> filtered = filterByBrand(vehicles, brandsToFilter);
}    
  • That is nice. We can use any number of brands now
  • But that is already so much code and inconvenient

The classic rework

Just to get rid off boilerplate

/**
 * The classic approach III less boilerplate code
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, final String... brands)
{
    final Set<String> brandsToFilter = new HashSet<>();
    for (String brand : brands)
    {
        brandsToFilter.add(brand);
    }
    
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (brandsToFilter.contains(v.getBrand())
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = 
                                filterByBrand(vehicles, "Audi", "Maybach");
} 
  • Well, our usage is nicer, but we just moved the boilerplate code
  • And now we want to filter suddenly by brand and age...
  • Classic pre-Java 8 code suggests a filter interface approach here
  • Gonna by nice but all custom
  • Ok... Guava and Apache Commons offer something ready to use... just more dependencies

Pre-Java 8 Style

public interface Filter<T>
{
    public boolean matches(T t);
}

/**
 * The classic approach IV with anonymous classes
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Filter<Vehicle> brandFilter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (brandFilter.matches(v))
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = 
            filterByBrand(vehicles, 
                new Filter<Vehicle>() 
                {
                    public boolean matches(Vehicle v)
                    {
                        return "Maybach".equals(v.getBrand()) && v.getAge() > 20;
                    }
                }
            );
}

The nice Java 7 solution

  • Well, that is nice... fully flexible and re-usable concept
  • We can take on any filter task
  • Generics to the rescue... for everything
  • Still a little bloated

Functions

Finally... I thought he won't stop

Functions

Time to save us

  • Effectively we just apply something to know if a vehicle qualifies or not
  • This something is a method... or more generic... a Function
  • First question to ask: What is input and output?
  • Simplest one: Vehicle as input and Boolean as output
  • True when the filter applies, false otherwise
  • As Java 8 function: Function<String, Boolean>
/**
 * Step One - Use Java 8 Functions
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Function<Vehicle, Boolean> filter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (filter.apply(v))
        {
            result.add(v);
        }
    }
    
    return result;
}
public void someOtherCode()
{
    final Function<Vehicle, Boolean> f =
                                v -> "Maybach".equals(v.getBrand());
    final List<Vehicle> filtered = filterByBrand(vehicles, f);
}
public void someOtherCode()
{
    final List<Vehicle> filtered = filterByBrand(vehicles, 
                                v -> "Maybach".equals(v.getBrand());
}

Wait! What?

What just happened?

public void someOtherCode()
{
    final List<Vehicle> filtered1 = 
                        filterByBrand(
                                    vehicles, 
                                    v -> "Maybach".equals(v.getBrand()));
    final List<Vehicle> filtered2 = 
                        filterByBrand(
                                    vehicles, 
                                    v -> "Maybach".equals(v.getBrand()) ||
                                         "Audi".equals(v.getBrand()));
    final List<Vehicle> filtered3 = 
                        filterByBrand(
                                    vehicles, 
                                    v -> "Maybach".equals(v.getBrand()) &&
                                          v.getAge() > 30));
}    
  • We passed code as data
  • We can pass anything that satisfies input Vehicle and return Boolean
  • v -> true works too
  • v doesn't need a type, the compile interferes it because the target method declares it, hence Java knows what is legal
  • Function<T, R> is a @FunctionalInterface offered by Java 8

Functional Interfaces

What the compiler needs to understand us

  • Any interface with one method to implement is a functional interface
  • Annotation @FunctionalInterface is not required, but meant to aid documentation and usage concepts
  • Empty input or output is permitted (we get to producers and consumers later)
  • Imagine your code as an adhoc implementation of the interface without the need for am anymous class
  • All compilers (javac and JIT) place your code where it belongs for correctness and efficiency
@FunctionalInterface
public interface Function<T, R>
{
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
     R apply(T t);
}
public interface Filter<T>
{
    /**
     * Reminder: This is what we invented for Java 7!
     */
    boolean matches(T t);
}
@FunctionalInterface
public interface Foobar<T, K, V, Y>
{
    /**
     * My very own interface
     */
     Y dance(T t, K k, V v);
}

Ready to use Interfaces

Java 8 delivers 40+ functional interfaces

  • Uses most of them itself
  • Yes, these are the famous lamdas, streams, and other things
  • package java.util.function
// Represents a supplier of results
Supplier<T> {T get();}

// Represents a predicate (boolean-valued function) of one argument
Predicate<T> {boolean test(T t);}

// Represents an operation that accepts a single input argument and 
// returns no result. Unlike most other functional interfaces, Consumer
// is expected to operate via side-effects.
Consumer<T> {void accept(T t);}
    
// Represents a function that accepts one argument and produces a result
Function<T, R> {R apply(T t);}

// Represents a function that accepts two arguments and produces a result. 
BiFunction<T, U, R>	{R apply(T t, U u);}

// and about 40 more...

Predicate

Java offers a ready version of Function<T, S> - Predicate<T>

/**
 * Step One - Use Java 8 Functions
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Function<Vehicle, Boolean> filter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (filter.apply(v))
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> filtered2 = 
                        filterByBrand(
                                    vehicles, 
                                    v -> "Maybach".equals(v.getBrand()));
} 
/**
 * Step One and A Half - Use Java 8 Predicate
 */
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Predicate<Vehicle> filter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (filter.test(v))
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> filtered2 = 
                        filterByBrand(
                                    vehicles, 
                                    v -> "Maybach".equals(v.getBrand()));
}

Predicate function summarized

Key thoughts at a glance

  • A Predicate expects one parameter T and returns a boolean
  • When using it, you define the type to use
  • The interface method is boolean test(T t)
  • So if you need the Predicate evaluated, call test
  • If you have only one statement and no {}, the result of the evaluation is the return value
  • Otherwise you have to return something
public void foo(final Predicate<String> p)
{
    final String s = "foobar";
    
    if (p.test(s))
    {
        // whatever
    }
}

public void something()
{
    foo(s -> s.startsWith("A"));
}
public void something()
{
    foo(s -> {
        boolean r = s.startsWith("AA");
        return r;
    });
}

Finally, how does it work?

Just to prove that it all existed before

You wrote this

public void foo(final Predicate<String> p)
{
    final String s = "foobar";
    
    if (p.test(s))
    {
        // whatever
    }
}

public void something()
{
    foo(s -> s.startsWith("A"));
}

The compiler "does" this

public void foo(final Predicate<String> p)
{
    final String s = "foobar";
    
    if (p.test(s))
    {
        // whatever
    }
}

public void something()
{
    foo(
        new Predicate<String>()
        {
            public boolean test(final String s)
            {
                return s.startsWith("A");
            }            
        }
    );
}

Kick it up a notch

More examples

public void something()
{
    // more code but once you use {} it requires a return value
    // if the Function requires one
    foo(s -> {
        final String t = s + s;
        return t.isEmpty()
    });
    
    
    // Predicate has several pre-implemented methods
    final Predicate<String> p = s -> true;
    foo(p.negate());


    final Predicate<String> p1 = s -> true;
    final Predicate<String> p2 = s -> s.isEmpty();
    foo(p1.and(p2));
    foo(p1.or(p2));

    
    // a plain method references... more next
    foo(String::isEmpty);
}

More questions

public void foo(Predicate<String> s)
{
    // stuff here
}
    
public void something()
{
    /* 1 */ foo(s -> true);
    /* 2 */ foo(t -> t.substring(3, 4));
    /* 3 */ foo(water -> water.length() > 0);
    /* 4 */ foo(s -> { return true; });
    
    // 5
    String k = "Hello";
    foo(s -> k.length() > 0);
    
    // 6
    boolean result = false;
    foo(s -> { result = s.isEmpty; return result; });
    
    // 7
    boolean temp = true;
    foo(s -> s.isEmpty() && temp);
    
    // 8
    boolean temp2 = true;
    foo(s -> s.isEmpty() && temp2);
    temp2 = false;
}

Parameters

When we need more or less parameters

public interface MyFunction<A, B, C, D>
{
    int evaluate(A a, B b, C c, D d);
}
    
public void foo(MyFunction<String, String, String, Integer>)
{
    final int i = f.evaluate("AB", "B", "Foo", 42);
}

public void something()
{
    foo((a, b, c, o) -> {
        final String t = a + c.replaceAll(a, b);
        return t.length() + o;
    });   
}
public void foo(Supplier<String> s)
{
    // a supplier creates data, just a return value 
    final String a = p.get();
}

public void bar(Consumer<Integer> c)
{
    // a consumer "eats" data
    p.accept(1);
}

public void something()
{
    foo(() -> "A"); // no data as input
    bar(c -> System.out.print(c)); // no return value!
    bar(System.out::print);
}

Method References

When you are tired of typing

  • Imagine s -> s.isEmpty() for our Predicate<String>
  • We just call a method, nothing else... so why all the typing?
  • Let's reference the method directly to save a little
  • String::isEmpty
  • Golden rule: As long as your lamda just calls a method, you can use a method reference instead.
  • You can even call a constructor String::new if this would be just your method call
public void foo1(Predicate<String> p) {...}
public void foo2(BiFunction<String, String, Integer> p) {...}
public void foo3(BiFunction<String, String, String> p) {...}

public void something()
{
    foo1(s -> s.isEmpty());
    foo1(String::isEmpty);
    
    foo1(s -> Boolean.getBoolean(s));
    foo1(Boolean::getBoolean); // static method!
    
    foo1(s -> s.equalsIgnoreCase("A"));
    // foo1(String::equalsIgnoreCase("A")); // wont't work!!

    foo2((s, t) -> s.indexOf(t));   
    foo2(String::indexOf); 
    
    foo2((s, t) -> s.compareTo(t));
    foo2(String::compareTo);
    foo2((s, t) -> t.compareTo(s)); // that is not the same!!!
    
    foo3((s, t) -> s.replaceAll(t, t));
    // foo3(String::replaceAll); // won't work
    
    foo3((s, t) -> String.format(s, t));
    foo3(String::format); // static again!
}

Old vs. New

The Ultimate Showdown

The battle of Java 7 vs. Java 8

public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Filter<Vehicle> brandFilter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (brandFilter.matches(v))
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = 
            filterByBrand(vehicles, 
                new Filter<Vehicle>() 
                {
                    public boolean matches(Vehicle v)
                    {
                        return "Maybach".equals(v.getBrand()) && v.getAge() > 20;
                    }
                }
            );
}

public interface Filter<T>
{
    public boolean matches(T t);
}
public List<Vehicle> filterByBrand(
                    final List<Vehicle> vehicles, 
                    final Predicate<Vehicle> filter)
{
    final List<Vehicle> result = new ArrayList<>();
    
    for (Vehicle v : vehicles)
    {
        if (filter.test(v))
        {
            result.add(v);
        }
    }
    
    return result;
}

public void someOtherCode()
{
    final List<Vehicle> vehicles = getAllVehicles();
    final List<Vehicle> filtered = 
                filterByBrand(vehicles, 
                    v -> "Maybach".equals(v.getBrand()) && v.getAge() > 20
                );
}