Skip to main content
JavazJavaz
Functional designFunctional design
in Java 8in Java 8
IntroIntro
All Java code can be found at Javaz
https://github.com/escalate42/Javaz
The emergence of native support for lambdas in Java 8
has opened the possibility to adapt the rich experience
of statically typed functional programming languages to
the Java world. So the Javaz appears.
finally!This presentation can be found at
https://slides.com/vadimdubs/javaz​
Common functionalCommon functional
programming patternsprogramming patterns
Functor
ApplicativeFunctor
Monad
First appeared in Haskell programing langugage
TypeclassesTypeclasses
First appeared in Haskell programing langugage
Is a sort of interface that defines some behavior
You can think of them kind of as Java interfaces
-- Typeclass definition with name Eq, type variable a
-- and function == which has two params of type a and
-- returns Bool
class Eq a where
eq :: a -> a -> Bool
-- Data Type definition (like Java enum in this case)
data TrafficLight = Red | Yellow | Green
-- Instance definition for TrafficLight data type and
-- Eq typeclass
instance Eq TrafficLight where
eq Red Red = True
eq Yellow Yellow = True
eq Green Green = True
eq _ _ = False
public interface Eq<A> {
public boolean eq(A other);
}
public final class TrafficLight implements Eq<TrafficLight> {
public static TrafficLight RED = new TrafficLight();
public static TrafficLight YELLOW = new TrafficLight();
public static TrafficLight GREEN = new TrafficLight();
private TrafficLight() {}
@Override
public boolean eq(TrafficLight other) {
return other == this;
}
}
FunctionFunction
@FunctionalInterface
interface Function<A, B> {
B apply(A a);
}
Function<A, B> function1 = Functions::function;
Function<A, B> function2 = a -> new B(a);
function :: a -> b
@FunctionalInterface
interface Function<A, B> {
B apply(A a);
}
Function<A, B> f;
Function<B, C> g;
static <A, B, C> Function<A, C> compose(
Function<A, B> ab, Function<B, C> bc
) {
return a -> bc.apply(ab.apply(a));
}
Function<A, C> h = compose(f, g);
f :: a -> b
g :: b -> c
. :: (a -> b) -> (b -> c) -> a -> c
f1 . f2 = x -> f2 (f1 x)
h :: a -> c
h = f . g
FunctorFunctor
for things that can be mapped over.
class Functor F where
fmap :: (a -> b) -> F a -> F b
data Maybe a = Just a | Nothing
FunctorFunctor
for things that can be mapped over.
class Functor F where
fmap :: (a -> b) -> F a -> F b
Applicative FunctorApplicative Functor
class (Functor F) => Applicative F where
pure :: a -> F a
(<*>) :: F (a -> b) -> F a -> F b
Applicative FunctorApplicative Functor
class (Functor F) => Applicative F where
pure :: a -> F a
(<*>) :: F (a -> b) -> F a -> F b
MonadMonad
class Monad M where
return :: a -> M a
(>>=) :: M a -> (a -> M b) -> M b
MonadMonad
class Monad M where
return :: a -> M a
(>>=) :: M a -> (a -> M b) -> M b
f :: a -> M a
g :: a -> M b
-- Bind function
(>>=) :: M a -> (a -> M b) -> M b
a -> (f a) >>= a -> (g a)
-- Same in term of types
-- (>>=) is the same as
-- composition of functions
(a -> M a) >>= (a -> M b)
-- Composition of functions
(a -> a) . (a -> b)
CommonCommon
implementationsimplementations
Collection - container for a group of
values
Option/Maybe​ - for optional values
Either - for results that either success or
failure
Future - for async computations
OptionOption
For optional values, typesafe way to avoid null and null-checks
Option<T> always is in one of two states:
Some<T> - simple container for value of type T
None<T> - represents absence of any value of type T
Javaz implementation
User auth(String l, String p);
Role getRole(User u);
Permissions getPermissions(Role r);
List<Partner> getPartners(Permissions p);
User user = auth("user", "password");
Role role = null;
Permissions permissions = null;
List<Partner> partners = new ArrayList<>();
if (user != null) {
role = getRole(user);
}
if (role != null) {
permissions = getPermissions(role);
}
if (permissions != null) {
partners.addAll(getPartners(permissions));
}
Function2<String, String, Option<User>> auth;
Function<User, Option<Role>> getRole;
Function<Role, Option<Permissions>> getPermissions;
Function<Permissions, List<Partner>> getPartners;
List<Partner> partners =
// trying to authenticate user
auth.apply("login", "password")
// trying to get Role for this
// user from service via http
.flatMap(getRole) // the same as >>=
// trying to load permissions
// from database
.flatMap(getPermissions)
// trying to load partners from
// another data source
.map(getPartners) // the same as fmap
.getOrElse(new ArrayList());
OptionalOptional
import static Optional.of;
import static Optional.empty;
final Optional<Integer> first = of(3);
final Optional<Integer> second = of(4);
final Optional<Integer> empty = empty();
// Optional is a functor and monad
first.map(i -> i * i) // Some(9)
empty.map(i -> i * i) // None
first.flatMap(f -> second.map(s -> f + s));
// Some(7)
first.flatMap(f -> empty.map(s -> f + s));
// None
Implementation from standard Java 8 library
EitherEither
For results that either success or failure, typesafe way to avoid
usege of exceptions.
Has no analogs in standard Java library.
Either<L, R> always is in one of two states:
Right<L, R> - container for value of type R
Left<L, R> - container for values of type L that represents some
failure
Javaz implementation
F2<String, String, Either<ErrorInfo, User>> auth;
F<User, Either<ErrorInfo, Role>> getRole;
F<Role, Either<ErrorInfo, Permissions>> getPermissions;
F<Permissions, List<Partner>> getPartners;
Either<ErrorInfo, List<Partner>> eitherPartners =
// trying to authenticate user
auth.apply("login", "password")
// trying to get Role for this
// user from service via http
.fmap(getRole)
// trying to load permissions
// from database
.fmap(getPermissions)
// trying to load partners from
// another data source
.map(getPartners);
eitherPartners.mapLeft(logger::error);
List<String> partnerNames = eitherPartners.foldRight(
new ArrayList(), partner -> partner.getName
)
StreamStream
final Stream<Integer> stream =
Arrays.asList(1, 2, 3, 4, 5).stream();
// Streams are functors
stream.map(i -> i + 1);
// [2, 3, 4, 5, 6]
stream.forEach(System.out::print);
// out > 12345
Implementation from standard Java 8 library
StreamStream
final Stream<Integer> stream
= Arrays.asList(1, 2, 3, 4, 5).stream();
// Streams are monads
stream.flatMap(
i -> Arrays.asList(i + 1, i + 2).stream()
);
// [2, 3, 3, 4, 4, 5, 5, 6, 6, 7]
Implementation from standard Java 8 library
StreamStream
Function<User, Stream<Permission>> permissionsByUser;
Function<Permission, Stream<Partner>> partnersByPermissions;
Stream<User> users = Arrays.asList(user1, user2).stream();
Set<Partners> availablePartners = users
.flatMap(permissionByUser)
// get all permissions of user 1 and user2
.distinct()
// left only unique items
.flatMap(partnersByPermissions)
// get all partners available through permissions
.collect(Collectors.toSet);
Implementation from standard Java 8 library
FutureFuture
Javaz implementation
Function2<String, String, Future<User>> auth;
Function<User, Future<Stream<User>>> getFriends;
Function<User, Stream<Group>> getGroups;
Future<User> user = auth.apply("login", "password");
Future<Stream<Group>> myGroups = user.map(getGroups)
Future<Stream<User>> friendsGroups = user
.flatMap(getFriends)
.map(Stream::flatMap(getGroups).distinct());
Future<Set<Group>> uniqueFriendsGroups = yieldFor(
myGroups, myFirends, (myGroups, firendsGroups) ->
friendsGroups.collect(toSet)
.removeAll(myGroups.collect(toSet))
)
uniqueFriendsGroups.get(100, TimeUnit.MILLISECONDS)
CompletableFutureCompletableFuture
Implementation from standard Java 8 library
final CompletableFuture<Integer> future =
CompletableFuture.supplyAsync(() -> 3 * 2);
// CompletableFuture is functor
future.thenApplyAsync(i -> i * i); // CompletableFuture(36)
future.handleAsync(
(val, exc) -> val != null ? val.toString() : ""
); // CompletableFuture("6")
future.thenAcceptAsync(System.out::println); // out > 6
// CompletableFuture is monad
future.thenComposeAsync(
i -> CompletableFuture.supplyAsync(() -> i * 2)
); // CompletableFuture(12)
Why should I care?Why should I care?
Simple complexity
Composability
Type safety
Unification
Какие практические достоинства и
недостатки вы видете в
использовании функциональных
абстракций?
Чем оператор связывания отличается от
оператора композиции функций?
Проанализируйте накладные расходы
от реализации управления потоком
выполнения на основе
функциональных паттернов по
сравнению с условными выражениями
для любого известного вам языка.
Thumbtack Expertise Days # 5 - Javaz