Javaslang

Vavr - Functional Java Done Right

 

Grzegorz Piwowarek

gpiwowarek@gmail.com

 

Me

Dev@TouK

Yoyo Player

Musician

pivovarit@

Twitter

GitHub

...everywhere else

Java 8

"Functional" Java8:

  • Lambda Expressions
  • Optional, Stream, CompletableFuture
  • ...

Java 8 issues:

  • Only 3 functional control structures
  • No memoization
  • No lifting
  • No pattern matching
  • No tuples
  • Optional not Serializable/Iterable
  • Lack of Stream/Optional in APIs
  • Checked exceptions in functions
  • "Type pollution"
  • list.stream().map(...).collect(toList())
  • ...
List<String> transform(List<String> list);
List<String> transform(List<String> list) {
    if (this.state.isEmpty() || list.isEmpty()) {
        throw new IllegalStateException("Ouch...");
    }

    list.add(this.state.get(0));
    list.add(SOME_CONSTANT);

    if (MOON.getLunarPhase() == FULL) list.remove(7); 

    this.moreState.addAll(list);
    OtherClass.mutableStaticField = list;

    return list;
}

Potential inputs:

  • declared params
  • instance state
  • global state

Potential outputs:

  • declared returned value
  • mutable instance state
  • mutable global state
  • mutable params
  • exceptions

Potential inputs:

  • declared params

Potential outputs:

  • declared returned value

Functional programming is not about lambdas.

 

"Functional programming is about writing pure functions,
about removing hidden inputs and outputs as far as we can,
so that as much of our code as possible
just describes a relationship between inputs and outputs."

http://blog.jenkster.com/2015/12/what-is-functional-programming.html

Immutability

Referential Transparency

Java Collections API:

  • add()
  • remove()
  • clear()
  • ...
Date date = new Date();

Set<Date> dates = new HashSet<>();
dates.add(date);
date.setTime(42);

dates.contains(date); //false

λ

Tuple

Value

λ

Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;
CheckedFunction2<Integer, Integer, Integer> sum = (a, b) -> a + b;
  • composition
  • lifting
  • currying
  • memoization

Lifting

Function2<Integer, Integer, Integer> divideBy = (a, b) -> a / b;
Function2<Integer, Integer, Option<Integer>> lift = lift(divideBy);

Currying

Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;
Function1<Int, Function1<Int, Int>> curried = sum.curried();
Function1<Integer, Integer> add2 = curried.apply(2);

Memoization

Function2<Integer, Integer, Integer> sum = (a, b) -> a + b;
Function2<Integer, Integer, Integer> memoizedSum = sum.memoized();
Function0<Double> mem = Function0.of(Math::random).memoized();
double randomValue1 = memoizedRand.apply();
double randomValue2 = memoizedRand.apply();

then(randomValue1).isEqualTo(randomValue2);

Tuple

Tuple2<String, Integer> java8 = Tuple.of("Java", 8); 
Tuple2<String, Integer> that = java8.map(
                s -> s + "slang",
                i -> i / 4);
String transform = java8.transform((s, i) -> s + i);
Iterable<?> objects = java8.toSeq()

Value

  • Option
  • Try
  • Lazy
  • Either
  • Future
  • Validation
  • Collections API
public interface Value<T> extends Iterable<T>

Option

String result = Option.of(somethingNullable)
  .map(Object::toString)
  .map(String::toLowerCase)
  .getOrElse(() -> "DEFAULT VALUE");
List<Optional<Integer>> list = ...
        
list.stream()
  .filter(Optional::isPresent)
  .map(Optional::get)
  .collect(toList());
List<Option<Integer>> list = ...

list.flatMap(o -> o);

Java 8

Vavr

Try

Try.of(() -> new URI("bebebe"))
  .map(URI::getScheme)
  .getOrElse(() -> "UNKNOWN");

Lazy

Lazy<String> lazy = Lazy.of(this::vLC)
  .map(Object::toString)
  .map(String::toUpperCase);
        
lazy.get();

Functional Data Structures

 

Functional Data Structures:

  • Persistent
  • Immutable
  • with Referential Transparent methods

"Functional Data Structures in Java" - Oleg Šelajev

Seq

public static Stream<Integer> fibonacci() {
   return Stream.of(1, 1)
            .appendSelf(self -> self.zip(self.tail())
            .map(t -> t._1 + t._2));
}

Pattern Matching

String on = Match(i).of(
    Case(x -> x % 2 == 0, "Even"),
    Case(x -> x % 2 == 1, "Odd"),
    Case($(), "?"));
Option<String> on = Match(i).option(
    Case(x -> x % 2 == 0, "Even"),
    Case(x -> x % 2 == 1, "Odd"));    
String on = Match(i).of(
    Case($(1), "One"),
    Case($(2), "Two"),
    Case($(),  "Something else"));
Match(localDate).of(  
    Case(LocalDate(2016, 2, 13), () -> "2016-02-13"),
    Case(LocalDate(2016, $(), $_), m -> "month " + m + " in 2016"),
    Case(LocalDate($(2016), $(), $_), (y, m) -> "my" + m + y),
    Case($_, () -> "(catch all)")
);
String actual = Match(opt).of(
    Case(Some($()), String::valueOf),
    Case(None(), "no value"));
Match(i).of(
    Case(is(1), "1"),
    Case(isIn(2, 3), "2 or 3"),
    Case(anyOf(is(4), noneOf(is(5), is(6))), "4 or not (5 or 6)"),
    Case(instanceOf(String.class), "String content"),
    Case($(), "?")
);

http://koziolekweb.pl/2016/06/18/pattern-matching-w-javie-z-javaslang-ii/

Match(param).of(
    Case(isIn("-h", "--help"),    o -> run(outerWorld::displayHelp)),
    Case(isIn("-v", "--version"), o -> run(outerWorld::displayVersion)),
    Case($(),                     o -> {}));
Try.of(this::doSomething)
    .recover(x -> Match(x).of(
        Case(instanceOf(Exception_1.class), ...),
        Case(instanceOf(Exception_2.class), ...),
        Case(instanceOf(Exception_n.class), ...)
    ))
    .getOrElse(other);

vavr.io

Thank you!

  • gpiwowarek@gmail.com
  • pivovarit@twitter (slides are here!)
  • pivovarit@github

Vavr - Functional Java Done Right

By Grzegorz Piwowarek

Vavr - Functional Java Done Right

  • 13,963