Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Vavr - functional Java the easy way
Search
David Schmitz
September 07, 2018
120
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Vavr - functional Java the easy way
David Schmitz
September 07, 2018
More Decks by David Schmitz
See All by David Schmitz
Eventsourcing - You are doing it wrong
koenighotze
0
69
Read World Serverless
koenighotze
0
42
10 Tips for failing at microservices
koenighotze
0
100
Resilience Testing with Wiremock
koenighotze
0
210
Elixir for busy Javadevs
koenighotze
0
31
Javaslang
koenighotze
0
41
Featured
See All Featured
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Claude Code のすすめ
schroneko
67
230k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
340
Large-scale JavaScript Application Architecture
addyosmani
515
110k
A Soul's Torment
seathinner
6
3.4k
The Curious Case for Waylosing
cassininazir
1
450
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
470
Chasing Engaging Ingredients in Design
codingconduct
0
270
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Transcript
@koenighotze Revenge of the JavaSlang
@koenighotze David Schmitz - @koenighotze
@koenighotze Sticker construction kit
@koenighotze
@koenighotze
@koenighotze What’s in it for you?
@koenighotze Functional programming is hip How VAVR helped us Lot’s
of code - OMG!
@koenighotze Functors, applicatives, monads stay home
@koenighotze Code that is easier to reason about
@koenighotze Side-effects are evil
@koenighotze try { int i = 1/0; } catch (Throwable
t) { … } Exceptions are goto-statements :(
@koenighotze Referential Transparency
@koenighotze Math.random(); Math.max(1, 2); Math.random(); Math.max(1, 2); Pure functions are
a Good ThingTm :(
@koenighotze Thinking in values
@koenighotze Immutability Performance Safety
@koenighotze In a nutshell, think about “what to code“ not
“how to code”
@koenighotze Enter Java eight
@koenighotze (a) -> a + 2 list.stream().filter… Excitement…
@koenighotze until…
@koenighotze Try filtering all invalid users from a list of
users
@koenighotze users.stream() .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}) .collect(Collectors.toList());
@koenighotze import static io.vavr.API.*;
@koenighotze Immutable collections Some functional sugar Functional exception handling Pattern
matching Property based testing Circuit breaker
@koenighotze Before we get to the details… Let’s fix that
ugly code
@koenighotze users.stream() .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}) .collect(Collectors.toList());
@koenighotze List.ofAll(users) .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}) .collect(Collectors.toList());
@koenighotze List.ofAll(users) .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}) .collect(Collectors.toList());
@koenighotze List.ofAll(users) .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}); .collect(Collectors.toLis t());
@koenighotze List.ofAll(users) .filter(user -> { try { return user.validate(); }
catch (Exception ex) { return false; }}); .collect(Collectors.toLis t());
@koenighotze List.ofAll(users) .filter(user -> Try.of(user::validate) .getOrElse(false) ); } catch (IllegalStateException
ex) { return ); .collect(Collectors.toList();
@koenighotze List.ofAll(users) List.filter(user -> Try.of(user::validate) .getOrElse(false));
@koenighotze immutable Collections
@koenighotze Mutable Collections are Evil
@koenighotze Returning void == side-effect! interface Collection<E> { … void
clear(); } interface Collection<E> { … void clear(); }
@koenighotze Collections .unmodifiableList(list); .add(“”)
@koenighotze Collections .unmodifiableList(list) .add(“”);
@koenighotze Painting by Gustav Courbet java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableCollection .add(Collections.java:1055) at
java.util.stream.AbstractPipeline.<init> (AbstractPipeline.java:203) at java.util.stream.ReferencePipeline.<init> (ReferencePipeline.java:94)
@koenighotze https://cdn.infoq.com/statics_s2_20170314-0434/resource/news/2016/11/the-road-to-javaslang-3/en/resources/1infoq-version2.0-library.png
@koenighotze Functional data structures
@koenighotze Immutable Referentially transparent Persistent
@koenighotze Effectively immutable
heroes = List.of("Han", "Luke") Cons@661 “Han” T Cons@666 “Luke” ()
heroes
Cons@661 “Han” T Cons@666 “Luke” () heroes Cons@662 “Ben” T
more more = heroes.prepend("Ben")
droids = TreeSet.of( "C3PO", "R2D2", "K2SO" ) Node@676 Node@679 Node@681
“K2SO” () () “C3PO” () () “R2D2”
droids.add("Chopper");
Node@689 Node@691 () Node@694 () () “Chopper” Node@676 Node@679 Node@681
“K2SO” () () “C3PO” () () “R2D2”
Checking for updates?
old == new
@koenighotze Streams are glorified iterators
@koenighotze Stream<String> jdk = Stream.of("a", “B"); jdk.map(String::toUpperCase); jdk.map(String::toLowerCase);
@koenighotze Painting by Gustav Courbet java.lang.IllegalStateException: stream has already been
operated upon or closed at java.util.stream.AbstractPipeline.<init> (AbstractPipeline.java:203) at java.util.stream.ReferencePipeline.<init> (ReferencePipeline.java:94)
@koenighotze vavr streams
Stream<String> vavr = Stream.of("a", "B"); slang.map(String::toUpperCase); // “A”,“B” slang.map(String::toLowerCase); //
“a”,“b”
@koenighotze Stream<String> vavr = Stream.of("a", "B"); vavr.map(String::toUpperCase) .take(2); vavr.map(String::toLowerCase) .take(2);
@koenighotze Stream<String> vavr = Stream.of("a", "B"); vavr.map(String::toUpperCase) .take(2);// “A”,“B” vavr.map(String::toLowerCase)
.take(2);// “a”,“b”
@koenighotze Better performance Less unexpected behaviour
@koenighotze functional Sugar
@koenighotze find a user and, if an address is available,
fetch the user’s street
@koenighotze User user = repo.findOne("id"); if (user != null) {
Address address = user.getAddress(); if (null != address) { return address.getStreet(); } }
@koenighotze Cascading Pile of Shame
@koenighotze optional
@koenighotze Optional<User> opt = Optional.ofNullable(user); if (opt.isPresent()) { User user
= opt.get(); }
And now, young coder... you will die.
None
@koenighotze map or flatmap There is no isPresent()
@koenighotze optional or option
@koenighotze Optional
@koenighotze Option Some None
@koenighotze Option Some None Value Iterable
@koenighotze Option Some None Value Iterable
@koenighotze Option Some None Value Iterable Serializable
@koenighotze option is A Gift Box
@koenighotze None Some
@koenighotze Map opens the Gift Box
@koenighotze Map Some Some ( )->
@koenighotze nothing from nothing
@koenighotze ( )-> Map None None
@koenighotze Fixing the Pile of Shame
@koenighotze User user = repo.findOne("id"); if (user != null) {
Address address = user.getAddress(); if (null != address) { return address.getStreet(); } }
@koenighotze User user = repo.findOne("id"); if (user != null) {
Address address = user.getAddress(); if (null != address) { return address.getStreet(); } }
@koenighotze Option<User> user = repo.findOne("id"); if (user != null) {
Address address = user.getAddress(); if (null != address) { return address.getStreet(); } }
@koenighotze Option<User> user = repo.findOne("id"); if (user != null) {
Address address = user.getAddress(); if (null != address) { return address.getStreet(); } }
@koenighotze Option<User> user = repo.findOne("id"); user.flatMap(User::getAddress) Address address = user.getAddress();
if (null != address) { return address.getStreet(); } } Option<User> user = repo.findOne("id"); user.flatMap(User::getAddress) Address address = user.getAddress(); if (null != address) { return address.getStreet(); } } Option<Address> getAddress()
@koenighotze Option<User> user = repo.findOne("id"); user.flatMap(User::getAddress) Address address = user.getAddress();
if (null != address) { return address.getStreet(); } }
@koenighotze Option<User> user = repo.findOne("id"); user.flatMap(User::getAddress) Address address = user.getAddress();
.map(Address::getStreet) return address.getStreet(); } }
repo.findOne("id") .flatMap(User::getAddress) .map(Address::getStreet) .getOrElse("");
@koenighotze option.of(value) .map(nested code) == understandable story
@koenighotze functional exception Handling
@koenighotze validation like back in the days
@koenighotze public static String check(String iban){ if (validationMagic(iban)) { return
iban; } throw new IllegalArgumentException(“Peng”); }
@koenighotze Partial function
@koenighotze Awesome WTF Code
@koenighotze String iban; try { iban = check(“AL47…”); } catch
(IllegalArgumentException ex) { iban = ""; }
@koenighotze Lifting Exceptions to options
@koenighotze total function
@koenighotze String iban = lift(Iban::check) .apply("AL47...") .getOrElse("");
@koenighotze String iban = lift(Iban::check) .apply("AL47...") .getOrElse("");
@koenighotze Wrapping Exceptions with Try
@koenighotze Try.of(() -> stuffToDo())
@koenighotze Failure Exception Success Result Try.of(() -> stuffToDo())
@koenighotze Try.of(() -> check("AL..")) .getOrElse("")
@koenighotze Try.of(() -> check("AL..")) .getOrElse("")
@koenighotze Lifting and Try-ing reduce exception handling clutter and side-effects
@koenighotze Structural decomposition
@koenighotze Structural decomposition discouraged! http://cr.openjdk.java.net/ ~briangoetz/amber/pattern- match.html
@koenighotze (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=? ^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23- \x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?: [a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a- z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9] [0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a- z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21- \x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\]) Pattern
Matching Basics
@koenighotze Match(expression) .of(cases)
@koenighotze Case(pattern, function)
@koenighotze $() wildcard pattern $(“foo”) equals pattern isIn(“a”, “b”) conditional
pattern
@koenighotze Classic HTTP Handling
@koenighotze if (OK.equals(res.getStatusCode())) { return res.getBody(); } return emptyList();
@koenighotze Functional version
@koenighotze Note: some type details missing Match(res.getStatusCode()) .of( Case($(OK), res.getBody()),
Case($(), emptyList()) );
@koenighotze Match(res.getStatusCode()) .of( Case($(OK), res.getBody()), Case($(), emptyList()) ); Note: some
type details missing OK or anything else
@koenighotze Note: some type details missing Match(res.getStatusCode()) .of( Case($(OK), res.getBody()),
Case($(), emptyList()) );
@koenighotze Match(res.getStatusCode()) .of( Case($(OK), res.getBody()), Case($(), emptyList()) ); Note: some
type details missing
@koenighotze Match(res.getStatusCode()) .of( Case($(OK), res.getBody()), Case($(), emptyList()) ); Note: some
type details missing
@koenighotze Matching a Try
@koenighotze public Try<…> fetchFromUrl(…) { … }
@koenighotze Match(fetchFromUrl(“…”)) .of( Case(Success($()), identity()), Case(Failure($()), emptyList()) );
@koenighotze Match(fetchFromUrl(“…”)) .of( Case(Success($()), identity()), Case(Failure($()), emptyList()) );
@koenighotze Match(fetchFromUrl(“…”)) .of( Case($Success($()), identity()), Case($Failure($()), emptyList()) );
@koenighotze Match(fetchFromUrl(“…”)) .of( Case($Success($()), identity()), Case($Failure($()), emptyList()) );
@koenighotze Pattern matching replaces complex if- then-else sequences with clear
expressions
@koenighotze property tEST
@koenighotze property Based - The idea
@koenighotze Declarative, infinite test cases Have you tested for all
characters… …even for ? Have you tested usernames like… ౧ప唆⏑㐿䯰㨼ᮁ娤㮘?搃蘸阁㌃ᡧ㫈葷㖒ܪ匘ᤫ䳴㻅 ⦷♻痯ヲ㙃銐璚ೃⶕᄰ 䩰㒭㙵闆䩟嗀嗀侀
@koenighotze vavr property test
@koenighotze Property.def(“Something works”) .forAll(Generated Data) .suchThat(Checked Function) .check() .assertIsSatisfied();
@koenighotze Arbitrary<String> arbitraryUnicodeId() { Gen<Character> id = r -> (char)
r.nextInt(); return Arbitrary.string(id); }
@koenighotze Arbitrary<String> arbitraryUnicodeId() { Gen<Character> id = r -> (char)
r.nextInt(); return Arbitrary.string(id); }
@koenighotze Property .def("Should not go boom”) .forAll(arbitraryUnicodeString()) .suchThat(id -> isOkOrNotFound(id))
.check() .assertIsSatisfied();
@koenighotze 2017-03-21 10:45:59.913 INFO 45701 --- FrameworkServlet '': initialization started
2017-03-21 10:45:59.925 INFO 45701 --- FrameworkServlet '': initialization completed in 12 ms Storing User@217b0952[username=rwxbeoigyesbeqqz,email=W`
[email protected]
, Storing User@4e6280de[username=vptafghfwuwwrwall,
[email protected]
,,, Storing User@2fca282c[username=qmhkjdtvbtjzfciwcceqgzfznzkhhcokiyoipdefbr,
[email protected]
. Storing User@64d53f0d[publicId=e9d7a121-9f23-483a-828a-f9e3045fc297,username=unflrpvztxtmi... ... Storing User@1b10f60e[publicId=6f084c18-415c-42c4-b1a8-00c5c1fc9e67,username=xwhpdpjowirsmjym... Storing User@4b916cc2[publicId=a2b9db2c-0189-4fe8-843d-e709ef3886fa,username=yxdidpexnayyjpzo... Should not go boom: OK, passed 1000 tests in 3719 ms.
@koenighotze Should not go boom: Falsified after 23 passed tests
in 3005 ms. java.lang.AssertionError: Expected satisfied check result but was Falsified(…, sample = ( 䒖 ꜏, 燤䠽㏊密ᵓ〞খᄀ ꌎ ⬆鹮 ۄ鄽㏫魨 ࠞ⽆⢕ 㣁㸎Ԩ 䏨➰ìឧ寅罟 溌椡ﲡࣇ欙ㄓῴﯯ缲ꢶꇞ⌽ꪂ惗 㛨蘄䏄⩂粻ㇼ?ವ㝓㭃, fzqlbkljxhfrghllzcthvgqiglaabihkzgsqwgfcichamyonmayiewwsfwmw ntzvozqqydkqillhpyi,
[email protected]
V74.E.-----2T.z97..3f1ZM6))
@koenighotze functional resilience
@koenighotze Circuit breaker one-o-one
@koenighotze Consumer Backend
@koenighotze Fallback Resilience decorator Closed Open Half-Open Consumer Backend
@koenighotze Resilience decorator Closed Open Half-Open Consumer Fallback
@koenighotze Fallback Resilience decorator Closed Open Half-Open Consumer
@koenighotze Fallback Resilience decorator Closed Open Half-Open Consumer
@koenighotze Resilience For J
@koenighotze Backend Consumer Backend Service Resilience decorator Circuit Breaker Retry
Cache
@koenighotze Option<…> fetchBoard(String id) { result = restTemplate.getForEntity(…); return Match(result.getStatusCode())
.option(…); }
@koenighotze Option<…> fetchBoard(String id) { result = restTemplate.getForEntity(…); return Match(result.getStatusCode())
.option(…); }
@koenighotze CircuitBreaker .decorateCheckedFunction( circuitBreaker, db::fetchBoard);
@koenighotze Try.of(() -> decorated.apply(stationId)) .getOrElse(empty())
@koenighotze The last monad?
@koenighotze http://www.vavr.io/
@koenighotze Spring data integration
@koenighotze interface UserRepository extends MongoRepository<User, String> { Option<User> findByPublicId(String publicId);
Seq<User> findByLastname(String lastname); }
@koenighotze interface UserRepository extends MongoRepository<User, String> { Option<User> findByPublicId(String publicId);
Seq<User> findByLastname(String lastname); }
@koenighotze Resilience for J
@koenighotze CircuitBreaker .decorateCheckedFunction( circuitBreaker, db::fetchBoard);
@koenighotze Property based tests
@koenighotze Property .def("Should not go boom”) .forAll(arbitraryUnicodeString()) .suchThat(id -> isFoundOrNotFound(id))
.check() .assertIsSatisfied();
@koenighotze So, is this is the mother of all free
lunches?
@koenighotze What are the drawbacks?
@koenighotze
@koenighotze count(Collection-libs) > count(Logging-libs)
@koenighotze Option.of(foo) .map .filter .flatMap .map .flatMap .getOrElse(null) N O
T a drawback!
@koenighotze version one
@koenighotze Modularization Alignment with Java 9 Obsolete pattern match (maybe)
http://blog.vavr.io/fast-forward-to-vavr-1-0/
@koenighotze Wrapping up
@koenighotze Slick, stable, non-invasive, consistent API Object-functional advantages now Complex(?)
Property test(?) Pattern match(?)
@koenighotze RESIST THE TYRANNY OF THE COLLECTIONS FRAMEWORK JOIN THE
RESISTANCE!
@koenighotze thank you @Koenighotze