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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
David Schmitz
September 07, 2018
110
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
66
Read World Serverless
koenighotze
0
39
10 Tips for failing at microservices
koenighotze
0
96
Resilience Testing with Wiremock
koenighotze
0
200
Elixir for busy Javadevs
koenighotze
0
27
Javaslang
koenighotze
0
37
Featured
See All Featured
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Git: the NoSQL Database
bkeepers
PRO
432
67k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
The SEO Collaboration Effect
kristinabergwall1
1
490
A better future with KSS
kneath
240
18k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
260
Context Engineering - Making Every Token Count
addyosmani
9
990
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Designing for Timeless Needs
cassininazir
1
260
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
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