Javaslang
Vavr - Functional Java Done Right
Grzegorz Piwowarek
gpiwowarek@gmail.com
Me
Dev@TouK
Yoyo Player
Musician
pivovarit@
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
- 14,262