x = x + 1
A Functional Library for Java 8+
Rahel Lüthy – @netzwerg999
May 2016
Immutable collections
Functional control structures
Fundamental nonsense at the heart of programming:
x = x + 1
But it’s an assignment, I hear you say – well…
Math
y = sin(x)
Java
List<String> doSomething(List<String> list)
y = sin(x)
1 input → 1 output
same input → same output
referential transparency
Known as pure or side-effect-free
Easy to understand
Easy to write
Easy to test
Function chaining: Programs as data pipelines
List<String> doSomething(List<String> list)
3 potential inputs
5 potential outputs
List<String> doSomething(List<String> list) {
if (this.state.isEmpty() || list.isEmpty()) {
throw new IllegalStateException("Ouch...");
}
list.add(this.state.get(0));
list.add(CONSTANT);
this.moreState.addAll(list);
OtherClass.mutableStaticField = list;
return new ArrayList<String>(list);
}
3 potential inputs
5 potential outputs
3 potential inputs
parameters
instance state
global state
5 potential outputs
return value
mutable instance state
mutable global state
mutable parameters
exceptions
Many possible interactions
Hard to write, read & test
Hard to re-use
Shared mutable state is evil
1 potential input
parameters
instance state – static methods, pass as param
global state - pass as param
1 potential output
return value
mutable instance state - immutability
mutable global state - immutability
mutable parameters - immutability
exceptions - pass as return value
No side effects
Fewer possible interactions
Sharing becomes safe
Deep-copying becomes unnecessary
==
implies equals
A simple mental model:
Your brain can focus on the scope of one method
You can be sure that nothing else is relevant
But:
We need immutable collections and functional control structures
Java only offers pseudo-immutability:
List<String> list = Arrays.asList("hello", "runtime-errors");
List<String> unmodifiableList = Collections.unmodifiableList(list);
unmodifiableList.add("boom"); // UnsupportedOperationException
Javaslang offers truly immutable collections:
List<String> l = List.of("immutability");
List<String> newCopy = l.prepend("hello");
Java:
List<String> words = Arrays.asList("hello", "world").stream().
filter(s -> s.startsWith("h")).collect(Collectors.toList());
Javaslang:
List<String> words = List.of("hello", "world").filter(s -> s.startsWith("h"));
Java needs dirty tricks for basic functionality:
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("Java", 8);
put("Javaslang", 2);
}
};
Javaslang:
Map<String, Integer> map = HashMap.ofEntries(
Tuple.of("Java", 8),
Tuple.of("Javaslang", 2)
);
Java 8 introduced Optional
but does not make use of it:
Integer value = map.get("whatever"); // maybe null
Javaslang:
Option<Integer> optionalValue = map.get("whatever");
Java streams are just fancy iterators, they can only be used once:
Stream<Integer> stream = Stream.of(1, 2, 3);
List<Integer> squares = stream.map(i -> i * i).collect(Collectors.toList());
Stream<Integer> timesTen = stream.map(i -> i * 10);
// IllegalStateException: stream has already been operated upon or closed
Javaslang streams are true collections:
Stream<Integer> numbers = Stream.of(1, 2, 3);
Stream<Integer> squares = numbers.map(i -> i * i); // 1, 4, 9
Stream<Integer> timesTen = numbers.map(i -> i * 10); // 10, 20, 30
Tuple2<String, Integer> pair = Tuple.of("java", 8);
String first = pair._1; // "java"
Integer second = pair._2; // 8
List<String> words = List.of("hello", "java");
List<Tuple2<String, Long>> wordsWithIndex = words.zipWithIndex();
List<Integer> numbers = List.of(42, 8);
List<Tuple2<String, Integer>> wordsWithNumbers = words.zip(numbers);
Option: Express absence vs. presence
Future: Express pending vs. completed
Try: Express failure vs. success
Lazy: Express uninitialized vs. initialized
Validation: Express invalid vs. valid
All of them are monadic containers which implement a common Value
interface