TRANSACTION IS A MONADTRANSACTION IS A MONAD
JAREK RATAJSKIJAREK RATAJSKI
Software Developer, Wizard, AnarchitectSoftware Developer, Wizard, AnarchitectSoftware Developer, Wizard, Anarchitect
at Engenius GmbHat Engenius GmbHat Engenius GmbH
A STORYA STORY
public static void main(String[] args) {
//take mushrooms from the baskets,
// but not more than 20 from each
List<Integer> mushrooms = new ArrayList<>(
Arrays.asList(14, 52, 31, 62, 71, 22, 34));
int sum = 0;
int i = 0;
while (i < 6) {
if (mushrooms.get(i++) > 20) {
sum = sum + 20;
} else {
sum = sum + mushrooms.get(i);
}
}
System.out.println(sum);
}
And there are bugsAnd there are bugsAnd there are bugs
public static void main(String[] args) {
//take mushrooms from the baskets,
// but not more than 20 from each
List<Integer> mushrooms = new ArrayList<>(
Arrays.asList(14, 52, 31, 62, 71, 22, 34));
int sum = 0;
int i = 0;
while (i < 6) {
if (mushrooms.get(i++) > 20) {
sum = sum + 20;
} else {
sum = sum + mushrooms.get(i);
}
}
System.out.println(sum);
}
bugs in this code have their own bugsbugs in this code have their own bugsbugs in this code have their own bugs
HOW NOT TOHOW NOT TO HAVE BUGS?HAVE BUGS?
You have to be carefulYou have to be carefulYou have to be careful
It does not workIt does not workIt does not work
One cannot be careful 8One cannot be careful 8One cannot be careful 8 hours a dayhours a dayhours a day
ve days a weekve days a weekve days a week
Week after weekWeek after weekWeek after week
Errors will happenErrors will happenErrors will happen
ARE WE DOOMED?ARE WE DOOMED?
PREVENTING BUGSPREVENTING BUGS
PREVENTING BUGSPREVENTING BUGS
write tests
PREVENTING BUGSPREVENTING BUGS
write tests
use the power of type system
PREVENTING BUGSPREVENTING BUGS
write tests
use the power of type system
use safer language features
PREVENTING BUGSPREVENTING BUGS
write tests
use the power of type system
use safer language features
use safer languages
PREVENTING BUGSPREVENTING BUGS
write tests
use the power of type system
use safer language features
use safer languages
...
TESTSTESTS
they do work
additional code
refactoring paradox
Safer languages / safer featuresSafer languages / safer featuresSafer languages / safer features
LAMBDASLAMBDAS
public static void main(String[] args) {
//take mushrooms from the baskets,
// but not more than 20 from each
final List<Integer> mushrooms = new ArrayList<>(
Arrays.asList(14, 52, 31, 62, 71, 22, 34));
final int sum = mushrooms.stream()
.map(m -> Math.min(m, 20))
.reduce(0, (a, b) -> a + b);
System.out.println(sum);
}
BETTERBETTER
BETTERBETTER
less imperative
BETTERBETTER
less imperative
more declarative
BETTERBETTER
less imperative
more declarative
no mutation
BETTERBETTER
less imperative
more declarative
no mutation
separation of concerns
THIS IS ONLY THE BEGINNINGTHIS IS ONLY THE BEGINNING
MUTANTSMUTANTS
public static void main(String[] args) {
//take mushrooms from the baskets,
// but not more than 20 from each
final List<Integer> mushrooms = new ArrayList<>(
Arrays.asList(14, 52, 31, 62, 71, 22, 34)
);
int avg = calcAverage(mushrooms);
final int sum = mushrooms.stream()
.map(m -> Math.min(m, 20))
.reduce(0, (a, b) -> a + b);
System.out.println(sum);
}
FIGHTING MUTANTS WITHFIGHTING MUTANTS WITH
IMMUTABLE LISTSIMMUTABLE LISTS
List<String> a = [...] ;
a.add("sth"); //no change on a (futile operation)
List<String> a = [...] ;
List<String> b = a.add("sth");
VAVRVAVR
final io.vavr.List<Integer> mushrooms =
List.of ( 14, 52, 31, 62, 71, 22, 34);
int avg = calcAverage(mushrooms); //no problem
final int sum = mushreooms
.map(m -> Math.max(m, 20))
.fold(0, (a,b)-> a+b);
System.out.println(sum);
IMMUTABLE (FUNCTIONAL) LISTIMMUTABLE (FUNCTIONAL) LIST
MUTABLEMUTABLE JAVAJAVA
public class Person {
private String firstName;
private String lastName;
private BigDecimal salary;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BigDecimal getSalary() {
return salary;
}
Immutability in javaImmutability in javaImmutability in java
public class PersonImmutable {
public final String firstName;
public final String lastName;
public final BigDecimal salary;
public PersonImmutable(
String firstName,
String lastName,
BigDecimal salary) {
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
}
PersonImmutable withSalary(BigDecimal newSalary) {
return new PersonImmutable(
this.firstName,
this.lastName,
newSalary);
}
}
Immutability inImmutability inImmutability in kotlinkotlinkotlin
data class Person(
val firstName: String,
val lastName : String,
val salary: BigDecimal)
val x:Person = ...
val y = x.copy( salary = BigDecimal("15000"))
Java recordsJava recordsJava records
record Person(
String firstName,
String lastName,
BigDecimal salary )
var x = ...
//val y = x.copy( salary = BigDecimal("15000"))
Two great things You can do to avoid bugsTwo great things You can do to avoid bugsTwo great things You can do to avoid bugs
stop using java.util. collections
prefer immutable objects
use Kotlin
NEXT STEPNEXT STEP
ASPECTSASPECTS
transactions
security
diagnostics
resource handling
FANTASTICFANTASTIC
FRAMEWORKSFRAMEWORKS
Fantastic frameworksFantastic frameworksFantastic frameworks
What do they promise:What do they promise:What do they promise:
concentrate on business logic
let the magic work
inject whatever wherever
Fantastic frameworksFantastic frameworksFantastic frameworks
What they really do:What they really do:What they really do:
leaking abstractions
mess in code
hard to debug problems
null pointer exceptions
slow tests
@Retryable
void myMethod () {
}
@Transactional
void myMethod () {
}
@Transactional
@Retryable
void myMethod () {
}
Is retry inside transaction or transaction insideIs retry inside transaction or transaction insideIs retry inside transaction or transaction inside
retry?retry?retry?
@Transactional
@Retryable
void myMethod () {
}
Libraries > FrameworksLibraries > FrameworksLibraries > Frameworks
What typical coder thinks when you say you doWhat typical coder thinks when you say you doWhat typical coder thinks when you say you do
not use framework to do transactionnot use framework to do transactionnot use framework to do transaction
code fromcode fromcode from
public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
https://docs.oracle.com/javase/tutorial/jdbc/bashttps://docs.oracle.com/javase/tutorial/jdbc/bashttps://docs.oracle.com/javase/tutorial/jdbc/bas
HIGHER ORDERHIGHER ORDER
FUNCTIONFUNCTION
JOOQ ExampleJOOQ ExampleJOOQ Example
create.transaction(configuration -> {
AuthorRecord author =
DSL.using(configuration)
.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.values("George", "Orwell")
.returning()
.fetchOne();
DSL.using(configuration)
.insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE)
.values(author.getId(), "1984")
.values(author.getId(), "Animal Farm")
.execute();
// Implicit commit executed here
});
Hibernate can do that as wellHibernate can do that as wellHibernate can do that as well
TransactionUtil.doInHibernate( this::sessionFactory, session -
...
session.persist( item );
...
} );
SECURITYSECURITY
private <T> void renderSecure(Context ctx,
Function<Session, CompletionStage<T>> async
) {
final Option<String> bearer = Option.of(ctx.getRequest().getHeaders().get("Authorizatio
final Option<String> sessionId = bearer.map(b -> b.replace("Bearer ", ""));
final Option<Session> session = sessionId.flatMap(sessionsRepo::getSession);
ctx.render(JsonMapping.toJsonPromise(session.map(
sess -> (CompletionStage<Object>) async.apply(sess)
)
.getOrElse(
CompletableFuture.completedFuture("unauthorized")
)));
}
private Handler movePaddle() {
return ctx -> {
final String gameId = ctx.getPathTokens().get("id");
ctx.getRequest().getBody().then(body -> {
final float targetY = Float.parseFloat(body.getText());
renderSecure(ctx, session -> gamesRepo.movePaddle(gameId, session.userId, targe
});
};
}
https://github.com/javaFunAgain/ratpong/blob/mhttps://github.com/javaFunAgain/ratpong/blob/mhttps://github.com/javaFunAgain/ratpong/blob/m
What if want:What if want:What if want:
a function that is checked for permissions,
does database access,
and is monitored,
and ...
It can get messyIt can get messyIt can get messy
monitor( ()- >
renderSecure( securityContext, (user) ->
doInTransaction (dbSession -> {
}
....
AlternativeAlternativeAlternative
doInMyMightyStack( callContext -> {
System.out.printn("User is " + callContext.getUser
callContext.dataAccess.insert( )
})
But what if:But what if:But what if:
secure(
trx(
secure(
trx(
cache(
If we only had sth toIf we only had sth toIf we only had sth to chainchainchain such e ectssuch e ectssuch e ects
SIDE EFFECTSSIDE EFFECTS
public void notifyGroupOwner(int userId) {
final User user = userService.getUserFromDB(10001);
final Group group = user.getGroup();
final User owner = group.getGroupOwner();
owner.sendMessage("User "+ userId
+ " is not eating mushrooms");
}
side e ects everywhereside e ects everywhereside e ects everywhere
public void notifyGroupOwner(int userId) {
final User user = userService.getUserFromDB(10001);
//may return null, throw exception
final Group group = user.getGroup();
// may return null, is in transaction, throws sometimes excep
final User owner = group.getGroupOwner();
//may return null, may come from cache
owner.sendMessage("User "+ userId + " to straszny niejadek!");
//called async
}
NULLNULLNULL - I call it my billion-dollar mistake- I call it my billion-dollar mistake- I call it my billion-dollar mistake
handling missing datahandling missing datahandling missing data
User user = userService.getUserFromDB(10001);
if ( user != null) {
final Group group = user.getGroup();
if ( group != null ) {
final User owner = group.getGroupOwner();
if ( owner != null ) {
owner.sendMessage("hello there!!!");
}
}
}
WHAT IF WE COULD MAKEWHAT IF WE COULD MAKE
PROBLEMS VISIBLE?PROBLEMS VISIBLE?
root of the problemroot of the problemroot of the problem
User getUserFromDB(int userId) {....
Optional<User> UserService::getUserFromDB(int userId) ...
Optional<Group> User::getGroup(int groupId) ...
Optional<User> Grouo::getGroupOwner() ...
better,better,better,
Optional<User> user = userService.getUserFromDB(10001);
if ( user.isPresent()) {
final Optional<Group> group = user.get().getGroup();
if ( group.isPresent() ) {
final Optional<User> owner = group.get().getGroupOwner
if ( owner.isPresent() ) {
owner.sendMessage("hello there!!!");
}
}
}
better,better,better,
not smart at allnot smart at allnot smart at all
Optional<User> user = userService.getUserFromDB(10001);
if ( user.isPresent()) {
final Optional<Group> group = user.get().getGroup();
if ( group.isPresent() ) {
final Optional<User> owner = group.get().getGroupOwner
if ( owner.isPresent() ) {
owner.sendMessage("hello there!!!");
}
}
}
mapmapmap operationoperationoperation
Optional<String> str = "55";
Optional<Integer> x = str.map( v -> Integer.parseInt(v));
mapmapmap went wrongwent wrongwent wrong
Optional<User> user = userService.getUserFromDB(10001);
Optional<Optional<Group>> group = user.map(u -> u.getGroup());
mapmapmap went wrongwent wrongwent wrong
Optional<User> user = userService.getUserFromDB(10001);
Optional<Optional<Group>> group = user.map(u -> u.getGroup());
flatMapflatMapflatMap to the rescueto the rescueto the rescue
Optional<User> user = userService.getUserFromDB(10001);
Optional<Group> group = user.flatMap(u -> u.getGroup());
Awesome!Awesome!Awesome!
userService.getUserFromDB(10001)
.flatMap(user -> user.getGroup() )
.flatMap(group -> group.getGroupOwner() )
.ifPresent(user-> user.sendMessage("Hallo there Code4Life!
OptionalOptional
declaration of missing data side e ect
basic handling (code) in
map
flatMap
getOrDefault
THAT IS A POWER OFTHAT IS A POWER OF
MONADMONAD
FAILURE - EXCEPTIONSFAILURE - EXCEPTIONS
class Group {
User getGroupOwner() throws NoOwnerException {...
}
}
class User {
Group getGroup() throws NoGroupMemberException {...
}
void sendMessage(String msg) {...
}
}
class UserService {
User getUserFromDB(long key) throws NoSuchUserException {
}
Either<DataError, User> UserService.getUserFromDB(int userId);
// + other services
enum DataError {
NoOwner, NoGroup, NoUser
}
userService.getUserFromDB(10001)
.flatMap(user -> user.getGroup() )
.flatMap(group -> group.getGroupOwner() )
.forEach(user->
user.sendMessage("Hallo there Code4Life,"
+ "there was no problem !")); //on success
A MONADA MONAD
de nition 1 - formalde nition 1 - formalde nition 1 - formal
aaa monadmonadmonad in X is just a monoid in thein X is just a monoid in thein X is just a monoid in the
category of endofunctors of X, withcategory of endofunctors of X, withcategory of endofunctors of X, with
product × replaced by compositionproduct × replaced by compositionproduct × replaced by composition
of endofunctors and unit set byof endofunctors and unit set byof endofunctors and unit set by
the identity endofunctor.the identity endofunctor.the identity endofunctor.
MONADMONAD
de nition 2 - by codede nition 2 - by codede nition 2 - by code
Something that hasSomething that hasSomething that has
flatMap,
map
of (pure)
Something that hasSomething that hasSomething that has
flatMap,
map
of (pure)
TypeclassTypeclassTypeclass
MONADMONAD
de nition 3 - by comparisonde nition 3 - by comparisonde nition 3 - by comparison
CONTAINERSCONTAINERS
String
a type
List<String>
a container of String
Set<String>
a container of String
ArrayList<String>
a container of String
CONTAINERCONTAINER
aaa TypeTypeType + multiplicity+ multiplicity+ multiplicity e ecte ecte ect
MONADMONAD
aaa TypeTypeType +++ somesomesome e ecte ecte ect
EFFECTSEFFECTS
missing data
fails, exceptions
asynchronous computations
multiplicity
transactions
dependency injections
+ combinations+ combinations+ combinations
MORE THEORYMORE THEORY
FunctorFunctorFunctor - is a type(class) that has- is a type(class) that has- is a type(class) that has mapmapmap
Every monad is a functorEvery monad is a functorEvery monad is a functor
Some people call functorSome people call functorSome people call functor mappablemappablemappable
Applicative functorApplicative functorApplicative functor
Something between monad and functorSomething between monad and functorSomething between monad and functor
HasHasHas apapap functionfunctionfunction
ap(F<(A)->B>,F<A>)ap(F<(A)->B>,F<A>)ap(F<(A)->B>,F<A>) -> F<B>-> F<B>-> F<B>
MONADS, FUNCTOR MAKE SIDEMONADS, FUNCTOR MAKE SIDE
EFFECTS EXPLICITLY VISIBLE IN AEFFECTS EXPLICITLY VISIBLE IN A
TYPE SYSTEMTYPE SYSTEM
Applicative functor lets youApplicative functor lets youApplicative functor lets you sumsumsum inside e ectsinside e ectsinside e ects
(potentially parallel sequence)(potentially parallel sequence)(potentially parallel sequence)
Monadic atMap sequences operation one byMonadic atMap sequences operation one byMonadic atMap sequences operation one by
oneoneone
If You have 2 independent db transactions,If You have 2 independent db transactions,If You have 2 independent db transactions,
that can be called concurrently - You can usethat can be called concurrently - You can usethat can be called concurrently - You can use
apapap
If operations on db depend on some otherIf operations on db depend on some otherIf operations on db depend on some other
transaction (success/rollback) use atMaptransaction (success/rollback) use atMaptransaction (success/rollback) use atMap
Monadic transactionMonadic transactionMonadic transaction
It uses JDBI
one of many possible implementations (very
simple)
class Transaction<A> (private val action : (Handle) -> A ) {
fun run(dbi : Jdbi) : A = dbi.inTransaction<A, RuntimeExce
fun <B> map ( f: (A)->B) = Transaction {handle ->
f(action(handle))
}
fun <B> flatMap( f: (A)->Transaction<B>) = Transaction {h
f(action(handle)).action(handle)
}
companion object {
fun <T> pure (obj:T) = Transaction {
obj
Example useExample useExample use
internal fun selectNewJob () = Transaction{
dbHandle ->
dbHandle.createQuery(selectNextJobId)
.mapTo(Long::class.java)
.one()
.map { jobId ->
dbHandle.createUpdate(updateBatch).bind("b
.execute()
jobId
}
}.flatMap { jobId ->
selectNewTasks(jobId)
}
The same can be done with other aspectsThe same can be done with other aspectsThe same can be done with other aspects
/e ects/e ects/e ects
security
cache
diagnostics/monitoring
async
That is a lot ofThat is a lot ofThat is a lot of flatMapflatMapflatMap
Would be better if we hadWould be better if we hadWould be better if we had do notationdo notationdo notation
Four great things You can do to avoid bugsFour great things You can do to avoid bugsFour great things You can do to avoid bugs
stop using java.util. collections
prefer immutable objects
learn lambdas and higher order functions
use kotlin
use monads
use scala
How do we chain multiple e ects?How do we chain multiple e ects?How do we chain multiple e ects?
the worstthe worstthe worst flatMapflatMapflatMap ever...ever...ever...
Future<Secure<Transaction<Optional<Int>>>> myX;
Function<Int, Future<Secure<Transaction<Optional<String>>>>> f
myX.flatMap(f) // ...no go
Function<Secure<Transaction<Optional<Int>>>>, Future<Secure<Tr
Problem of stacking monadsProblem of stacking monadsProblem of stacking monads
Monad TransformersMonad TransformersMonad Transformers FutureTFutureTFutureT,,, OptionTOptionTOptionT
Monad transformers appeared to not be thatMonad transformers appeared to not be thatMonad transformers appeared to not be that
greatgreatgreat
We cannot have use them in kotlin or java (in aWe cannot have use them in kotlin or java (in aWe cannot have use them in kotlin or java (in a
sensible way)sensible way)sensible way)
AllMightyMonadAllMightyMonadAllMightyMonad
write the monad that has all the e ects for
you stack (cache, jdbc, async, etc.)
cry once (writing atMap) enjoy everywhere
Alternatives?Alternatives?Alternatives?
1. zioziozio
2. https://github.com/atnos-org/e /https://github.com/atnos-org/e /https://github.com/atnos-org/e /
No, we will not hack bytecodeNo, we will not hack bytecodeNo, we will not hack bytecode
Project neeProject neeProject nee
class Hasiok {
@Resource
val jdbcConnection: Connection
@Transactional
@Secure
@Cacheable
@Retryable
fun f(p:P) {
//code
}
}
class Hasiok {
val enterprisyF = Nee.pure(
secure
.and(retryable)
.and(cacheable)
.and(transactional)) {conn:ConnectionProvider ->
{p: P ->
//code
}
}
//declaration above means security is checked before retri
//and retrial is made before cache which happens before tr
}
fun readStone() = Nee.pure(cached.andThen(jdbc)) { jdbcProvide
{ id: StoneId ->
val dsl = DSL.using(jdbcProvider.getConnection().g
val record = dsl.selectFrom(Stones.STONES)
.where(Stones.STONES.ID.eq(id))
.fetchOneInto(Stones.STONES)
Stone(record.id, StoneData(record.name, record.pri
}
}.anyError()
fun addNewStone(newStone: StoneData) = seq.next().flatMap
addStone(it, newStone)
}
private fun addStone(stoneId: Long, newStone: StoneData) =
val dsl = DSL.using(jdbcProvider.getConnection().getRe
val insertedRows = dsl.insertInto(Stones.STONES)
.values(stoneId, newStone.name, newStone.price)
.execute()
if (insertedRows == 1) {
Option.some(stoneId)
} else {
Option.none()
}
}
Nee is a hobby, experimental project.Nee is a hobby, experimental project.Nee is a hobby, experimental project.
NeeNeeNee monadmonadmonad has 4 params:has 4 params:has 4 params:
Nee<R,E,P,A>Nee<R,E,P,A>Nee<R,E,P,A>
- R - environment (context)
- E - error type
- P - argument (for cache - I think this was a bas idea)
- A - the real result type
I am slowly adding e ects:I am slowly adding e ects:I am slowly adding e ects:
jdbc,
any transactional resource,
cache,
security,
diagnostic,
Fun!Fun!Fun!
DEPENDENCYDEPENDENCY
INJECTIONINJECTION
open class DocsModule(val config: ConfigModule) {
open val dbProvider: HandleProvider by lazy {
DBProvider(config.cfg)
}
open val timeProvider: TimeProvider by lazy { RealTime }
open val jobsRepository by lazy { JobsRepository(dbProvide
open val zipWriter : StatusZipWriter by lazy {
StatusZipWriter(config)
}
open val docsStatusRepository : DocsStatusRepository by la
MATERIALSMATERIALS
Project ZIOProject ZIOProject ZIO
Fun(c) 2018.7: John De Goes - FP to the MaxFun(c) 2018.7: John De Goes - FP to the MaxFun(c) 2018.7: John De Goes - FP to the Max
The Book of MonadsThe Book of MonadsThe Book of Monads Alejandro Serrano MenaAlejandro Serrano MenaAlejandro Serrano Mena
(experiment)(experiment)(experiment)
https://github.com/ziohttps://github.com/ziohttps://github.com/zio
https://www.youtube.com/watch?https://www.youtube.com/watch?https://www.youtube.com/watch?
v=sxudIMiOo68v=sxudIMiOo68v=sxudIMiOo68
https://github.com/nee ect/neehttps://github.com/nee ect/neehttps://github.com/nee ect/nee
FUNCTIONAL PROGRAMMINGFUNCTIONAL PROGRAMMING
Power of abstractionsPower of abstractionsPower of abstractions
we want to have less bugs
we want testability
we want to have more fun
@jarek000000@jarek000000@jarek000000
Thank youThank youThank you

Transaction is a monad

  • 1.
    TRANSACTION IS AMONADTRANSACTION IS A MONAD
  • 2.
    JAREK RATAJSKIJAREK RATAJSKI SoftwareDeveloper, Wizard, AnarchitectSoftware Developer, Wizard, AnarchitectSoftware Developer, Wizard, Anarchitect at Engenius GmbHat Engenius GmbHat Engenius GmbH
  • 3.
  • 6.
    public static voidmain(String[] args) { //take mushrooms from the baskets, // but not more than 20 from each List<Integer> mushrooms = new ArrayList<>( Arrays.asList(14, 52, 31, 62, 71, 22, 34)); int sum = 0; int i = 0; while (i < 6) { if (mushrooms.get(i++) > 20) { sum = sum + 20; } else { sum = sum + mushrooms.get(i); } } System.out.println(sum); }
  • 7.
    And there arebugsAnd there are bugsAnd there are bugs public static void main(String[] args) { //take mushrooms from the baskets, // but not more than 20 from each List<Integer> mushrooms = new ArrayList<>( Arrays.asList(14, 52, 31, 62, 71, 22, 34)); int sum = 0; int i = 0; while (i < 6) { if (mushrooms.get(i++) > 20) { sum = sum + 20; } else { sum = sum + mushrooms.get(i); } } System.out.println(sum); }
  • 8.
    bugs in thiscode have their own bugsbugs in this code have their own bugsbugs in this code have their own bugs
  • 9.
    HOW NOT TOHOWNOT TO HAVE BUGS?HAVE BUGS?
  • 11.
    You have tobe carefulYou have to be carefulYou have to be careful
  • 12.
    It does notworkIt does not workIt does not work
  • 13.
    One cannot becareful 8One cannot be careful 8One cannot be careful 8 hours a dayhours a dayhours a day ve days a weekve days a weekve days a week
  • 14.
    Week after weekWeekafter weekWeek after week
  • 15.
    Errors will happenErrorswill happenErrors will happen
  • 16.
  • 17.
  • 18.
  • 19.
    PREVENTING BUGSPREVENTING BUGS writetests use the power of type system
  • 20.
    PREVENTING BUGSPREVENTING BUGS writetests use the power of type system use safer language features
  • 21.
    PREVENTING BUGSPREVENTING BUGS writetests use the power of type system use safer language features use safer languages
  • 22.
    PREVENTING BUGSPREVENTING BUGS writetests use the power of type system use safer language features use safer languages ...
  • 23.
    TESTSTESTS they do work additionalcode refactoring paradox
  • 26.
    Safer languages /safer featuresSafer languages / safer featuresSafer languages / safer features
  • 27.
    LAMBDASLAMBDAS public static voidmain(String[] args) { //take mushrooms from the baskets, // but not more than 20 from each final List<Integer> mushrooms = new ArrayList<>( Arrays.asList(14, 52, 31, 62, 71, 22, 34)); final int sum = mushrooms.stream() .map(m -> Math.min(m, 20)) .reduce(0, (a, b) -> a + b); System.out.println(sum); }
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
    BETTERBETTER less imperative more declarative nomutation separation of concerns
  • 33.
    THIS IS ONLYTHE BEGINNINGTHIS IS ONLY THE BEGINNING
  • 34.
    MUTANTSMUTANTS public static voidmain(String[] args) { //take mushrooms from the baskets, // but not more than 20 from each final List<Integer> mushrooms = new ArrayList<>( Arrays.asList(14, 52, 31, 62, 71, 22, 34) ); int avg = calcAverage(mushrooms); final int sum = mushrooms.stream() .map(m -> Math.min(m, 20)) .reduce(0, (a, b) -> a + b); System.out.println(sum); }
  • 35.
    FIGHTING MUTANTS WITHFIGHTINGMUTANTS WITH IMMUTABLE LISTSIMMUTABLE LISTS
  • 37.
    List<String> a =[...] ; a.add("sth"); //no change on a (futile operation)
  • 38.
    List<String> a =[...] ; List<String> b = a.add("sth");
  • 39.
    VAVRVAVR final io.vavr.List<Integer> mushrooms= List.of ( 14, 52, 31, 62, 71, 22, 34); int avg = calcAverage(mushrooms); //no problem final int sum = mushreooms .map(m -> Math.max(m, 20)) .fold(0, (a,b)-> a+b); System.out.println(sum);
  • 40.
  • 43.
  • 44.
    public class Person{ private String firstName; private String lastName; private BigDecimal salary; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public BigDecimal getSalary() { return salary; }
  • 46.
    Immutability in javaImmutabilityin javaImmutability in java public class PersonImmutable { public final String firstName; public final String lastName; public final BigDecimal salary; public PersonImmutable( String firstName, String lastName, BigDecimal salary) { this.firstName = firstName; this.lastName = lastName; this.salary = salary; } PersonImmutable withSalary(BigDecimal newSalary) { return new PersonImmutable( this.firstName, this.lastName, newSalary); } }
  • 47.
    Immutability inImmutability inImmutabilityin kotlinkotlinkotlin data class Person( val firstName: String, val lastName : String, val salary: BigDecimal) val x:Person = ... val y = x.copy( salary = BigDecimal("15000"))
  • 48.
    Java recordsJava recordsJavarecords record Person( String firstName, String lastName, BigDecimal salary ) var x = ... //val y = x.copy( salary = BigDecimal("15000"))
  • 49.
    Two great thingsYou can do to avoid bugsTwo great things You can do to avoid bugsTwo great things You can do to avoid bugs stop using java.util. collections prefer immutable objects use Kotlin
  • 50.
  • 51.
  • 52.
  • 53.
    Fantastic frameworksFantastic frameworksFantasticframeworks What do they promise:What do they promise:What do they promise: concentrate on business logic let the magic work inject whatever wherever
  • 54.
    Fantastic frameworksFantastic frameworksFantasticframeworks What they really do:What they really do:What they really do: leaking abstractions mess in code hard to debug problems null pointer exceptions slow tests
  • 55.
  • 56.
  • 57.
  • 58.
    Is retry insidetransaction or transaction insideIs retry inside transaction or transaction insideIs retry inside transaction or transaction inside retry?retry?retry? @Transactional @Retryable void myMethod () { }
  • 59.
    Libraries > FrameworksLibraries> FrameworksLibraries > Frameworks
  • 60.
    What typical coderthinks when you say you doWhat typical coder thinks when you say you doWhat typical coder thinks when you say you do not use framework to do transactionnot use framework to do transactionnot use framework to do transaction code fromcode fromcode from public void updateCoffeeSales(HashMap<String, Integer> salesForWeek) throws SQLException { PreparedStatement updateSales = null; PreparedStatement updateTotal = null; String updateString = "update " + dbName + ".COFFEES " + "set SALES = ? where COF_NAME = ?"; String updateStatement = "update " + dbName + ".COFFEES " + "set TOTAL = TOTAL + ? " + "where COF_NAME = ?"; try { con.setAutoCommit(false); updateSales = con.prepareStatement(updateString); updateTotal = con.prepareStatement(updateStatement); for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) { updateSales.setInt(1, e.getValue().intValue()); updateSales.setString(2, e.getKey()); updateSales.executeUpdate(); updateTotal.setInt(1, e.getValue().intValue()); updateTotal.setString(2, e.getKey()); updateTotal.executeUpdate(); con.commit(); } } catch (SQLException e ) { JDBCTutorialUtilities.printSQLException(e); if (con != null) { try { System.err.print("Transaction is being rolled back"); con.rollback(); } catch(SQLException excep) { JDBCTutorialUtilities.printSQLException(excep); } } } finally { https://docs.oracle.com/javase/tutorial/jdbc/bashttps://docs.oracle.com/javase/tutorial/jdbc/bashttps://docs.oracle.com/javase/tutorial/jdbc/bas
  • 61.
  • 62.
    JOOQ ExampleJOOQ ExampleJOOQExample create.transaction(configuration -> { AuthorRecord author = DSL.using(configuration) .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .values("George", "Orwell") .returning() .fetchOne(); DSL.using(configuration) .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) .values(author.getId(), "1984") .values(author.getId(), "Animal Farm") .execute(); // Implicit commit executed here });
  • 63.
    Hibernate can dothat as wellHibernate can do that as wellHibernate can do that as well TransactionUtil.doInHibernate( this::sessionFactory, session - ... session.persist( item ); ... } );
  • 64.
    SECURITYSECURITY private <T> voidrenderSecure(Context ctx, Function<Session, CompletionStage<T>> async ) { final Option<String> bearer = Option.of(ctx.getRequest().getHeaders().get("Authorizatio final Option<String> sessionId = bearer.map(b -> b.replace("Bearer ", "")); final Option<Session> session = sessionId.flatMap(sessionsRepo::getSession); ctx.render(JsonMapping.toJsonPromise(session.map( sess -> (CompletionStage<Object>) async.apply(sess) ) .getOrElse( CompletableFuture.completedFuture("unauthorized") ))); } private Handler movePaddle() { return ctx -> { final String gameId = ctx.getPathTokens().get("id"); ctx.getRequest().getBody().then(body -> { final float targetY = Float.parseFloat(body.getText()); renderSecure(ctx, session -> gamesRepo.movePaddle(gameId, session.userId, targe }); }; } https://github.com/javaFunAgain/ratpong/blob/mhttps://github.com/javaFunAgain/ratpong/blob/mhttps://github.com/javaFunAgain/ratpong/blob/m
  • 65.
    What if want:Whatif want:What if want: a function that is checked for permissions, does database access, and is monitored, and ...
  • 66.
    It can getmessyIt can get messyIt can get messy monitor( ()- > renderSecure( securityContext, (user) -> doInTransaction (dbSession -> { } ....
  • 67.
    AlternativeAlternativeAlternative doInMyMightyStack( callContext ->{ System.out.printn("User is " + callContext.getUser callContext.dataAccess.insert( ) })
  • 68.
    But what if:Butwhat if:But what if: secure( trx( secure( trx( cache(
  • 70.
    If we onlyhad sth toIf we only had sth toIf we only had sth to chainchainchain such e ectssuch e ectssuch e ects
  • 73.
  • 74.
    public void notifyGroupOwner(intuserId) { final User user = userService.getUserFromDB(10001); final Group group = user.getGroup(); final User owner = group.getGroupOwner(); owner.sendMessage("User "+ userId + " is not eating mushrooms"); }
  • 75.
    side e ectseverywhereside e ects everywhereside e ects everywhere public void notifyGroupOwner(int userId) { final User user = userService.getUserFromDB(10001); //may return null, throw exception final Group group = user.getGroup(); // may return null, is in transaction, throws sometimes excep final User owner = group.getGroupOwner(); //may return null, may come from cache owner.sendMessage("User "+ userId + " to straszny niejadek!"); //called async }
  • 76.
    NULLNULLNULL - Icall it my billion-dollar mistake- I call it my billion-dollar mistake- I call it my billion-dollar mistake
  • 77.
    handling missing datahandlingmissing datahandling missing data User user = userService.getUserFromDB(10001); if ( user != null) { final Group group = user.getGroup(); if ( group != null ) { final User owner = group.getGroupOwner(); if ( owner != null ) { owner.sendMessage("hello there!!!"); } } }
  • 78.
    WHAT IF WECOULD MAKEWHAT IF WE COULD MAKE PROBLEMS VISIBLE?PROBLEMS VISIBLE?
  • 79.
    root of theproblemroot of the problemroot of the problem User getUserFromDB(int userId) {....
  • 80.
    Optional<User> UserService::getUserFromDB(int userId)... Optional<Group> User::getGroup(int groupId) ... Optional<User> Grouo::getGroupOwner() ...
  • 81.
    better,better,better, Optional<User> user =userService.getUserFromDB(10001); if ( user.isPresent()) { final Optional<Group> group = user.get().getGroup(); if ( group.isPresent() ) { final Optional<User> owner = group.get().getGroupOwner if ( owner.isPresent() ) { owner.sendMessage("hello there!!!"); } } }
  • 82.
    better,better,better, not smart atallnot smart at allnot smart at all Optional<User> user = userService.getUserFromDB(10001); if ( user.isPresent()) { final Optional<Group> group = user.get().getGroup(); if ( group.isPresent() ) { final Optional<User> owner = group.get().getGroupOwner if ( owner.isPresent() ) { owner.sendMessage("hello there!!!"); } } }
  • 83.
    mapmapmap operationoperationoperation Optional<String> str= "55"; Optional<Integer> x = str.map( v -> Integer.parseInt(v));
  • 84.
    mapmapmap went wrongwentwrongwent wrong Optional<User> user = userService.getUserFromDB(10001); Optional<Optional<Group>> group = user.map(u -> u.getGroup());
  • 85.
    mapmapmap went wrongwentwrongwent wrong Optional<User> user = userService.getUserFromDB(10001); Optional<Optional<Group>> group = user.map(u -> u.getGroup());
  • 86.
    flatMapflatMapflatMap to therescueto the rescueto the rescue Optional<User> user = userService.getUserFromDB(10001); Optional<Group> group = user.flatMap(u -> u.getGroup());
  • 87.
    Awesome!Awesome!Awesome! userService.getUserFromDB(10001) .flatMap(user -> user.getGroup()) .flatMap(group -> group.getGroupOwner() ) .ifPresent(user-> user.sendMessage("Hallo there Code4Life!
  • 88.
    OptionalOptional declaration of missingdata side e ect basic handling (code) in map flatMap getOrDefault
  • 89.
    THAT IS APOWER OFTHAT IS A POWER OF MONADMONAD
  • 90.
    FAILURE - EXCEPTIONSFAILURE- EXCEPTIONS class Group { User getGroupOwner() throws NoOwnerException {... } } class User { Group getGroup() throws NoGroupMemberException {... } void sendMessage(String msg) {... } } class UserService { User getUserFromDB(long key) throws NoSuchUserException { }
  • 91.
    Either<DataError, User> UserService.getUserFromDB(intuserId); // + other services enum DataError { NoOwner, NoGroup, NoUser }
  • 92.
    userService.getUserFromDB(10001) .flatMap(user -> user.getGroup()) .flatMap(group -> group.getGroupOwner() ) .forEach(user-> user.sendMessage("Hallo there Code4Life," + "there was no problem !")); //on success
  • 93.
    A MONADA MONAD denition 1 - formalde nition 1 - formalde nition 1 - formal
  • 94.
    aaa monadmonadmonad inX is just a monoid in thein X is just a monoid in thein X is just a monoid in the category of endofunctors of X, withcategory of endofunctors of X, withcategory of endofunctors of X, with product × replaced by compositionproduct × replaced by compositionproduct × replaced by composition of endofunctors and unit set byof endofunctors and unit set byof endofunctors and unit set by the identity endofunctor.the identity endofunctor.the identity endofunctor.
  • 95.
    MONADMONAD de nition 2- by codede nition 2 - by codede nition 2 - by code
  • 96.
    Something that hasSomethingthat hasSomething that has flatMap, map of (pure)
  • 97.
    Something that hasSomethingthat hasSomething that has flatMap, map of (pure) TypeclassTypeclassTypeclass
  • 98.
    MONADMONAD de nition 3- by comparisonde nition 3 - by comparisonde nition 3 - by comparison
  • 99.
    CONTAINERSCONTAINERS String a type List<String> a containerof String Set<String> a container of String ArrayList<String> a container of String
  • 100.
    CONTAINERCONTAINER aaa TypeTypeType +multiplicity+ multiplicity+ multiplicity e ecte ecte ect
  • 101.
    MONADMONAD aaa TypeTypeType +++somesomesome e ecte ecte ect
  • 102.
    EFFECTSEFFECTS missing data fails, exceptions asynchronouscomputations multiplicity transactions dependency injections + combinations+ combinations+ combinations
  • 103.
  • 104.
    FunctorFunctorFunctor - isa type(class) that has- is a type(class) that has- is a type(class) that has mapmapmap Every monad is a functorEvery monad is a functorEvery monad is a functor Some people call functorSome people call functorSome people call functor mappablemappablemappable
  • 105.
    Applicative functorApplicative functorApplicativefunctor Something between monad and functorSomething between monad and functorSomething between monad and functor HasHasHas apapap functionfunctionfunction ap(F<(A)->B>,F<A>)ap(F<(A)->B>,F<A>)ap(F<(A)->B>,F<A>) -> F<B>-> F<B>-> F<B>
  • 106.
    MONADS, FUNCTOR MAKESIDEMONADS, FUNCTOR MAKE SIDE EFFECTS EXPLICITLY VISIBLE IN AEFFECTS EXPLICITLY VISIBLE IN A TYPE SYSTEMTYPE SYSTEM
  • 107.
    Applicative functor letsyouApplicative functor lets youApplicative functor lets you sumsumsum inside e ectsinside e ectsinside e ects (potentially parallel sequence)(potentially parallel sequence)(potentially parallel sequence) Monadic atMap sequences operation one byMonadic atMap sequences operation one byMonadic atMap sequences operation one by oneoneone
  • 108.
    If You have2 independent db transactions,If You have 2 independent db transactions,If You have 2 independent db transactions, that can be called concurrently - You can usethat can be called concurrently - You can usethat can be called concurrently - You can use apapap If operations on db depend on some otherIf operations on db depend on some otherIf operations on db depend on some other transaction (success/rollback) use atMaptransaction (success/rollback) use atMaptransaction (success/rollback) use atMap
  • 109.
  • 110.
    It uses JDBI oneof many possible implementations (very simple) class Transaction<A> (private val action : (Handle) -> A ) { fun run(dbi : Jdbi) : A = dbi.inTransaction<A, RuntimeExce fun <B> map ( f: (A)->B) = Transaction {handle -> f(action(handle)) } fun <B> flatMap( f: (A)->Transaction<B>) = Transaction {h f(action(handle)).action(handle) } companion object { fun <T> pure (obj:T) = Transaction { obj
  • 111.
    Example useExample useExampleuse internal fun selectNewJob () = Transaction{ dbHandle -> dbHandle.createQuery(selectNextJobId) .mapTo(Long::class.java) .one() .map { jobId -> dbHandle.createUpdate(updateBatch).bind("b .execute() jobId } }.flatMap { jobId -> selectNewTasks(jobId) }
  • 112.
    The same canbe done with other aspectsThe same can be done with other aspectsThe same can be done with other aspects /e ects/e ects/e ects security cache diagnostics/monitoring async
  • 113.
    That is alot ofThat is a lot ofThat is a lot of flatMapflatMapflatMap
  • 114.
    Would be betterif we hadWould be better if we hadWould be better if we had do notationdo notationdo notation
  • 115.
    Four great thingsYou can do to avoid bugsFour great things You can do to avoid bugsFour great things You can do to avoid bugs stop using java.util. collections prefer immutable objects learn lambdas and higher order functions use kotlin use monads use scala
  • 117.
    How do wechain multiple e ects?How do we chain multiple e ects?How do we chain multiple e ects? the worstthe worstthe worst flatMapflatMapflatMap ever...ever...ever... Future<Secure<Transaction<Optional<Int>>>> myX; Function<Int, Future<Secure<Transaction<Optional<String>>>>> f myX.flatMap(f) // ...no go Function<Secure<Transaction<Optional<Int>>>>, Future<Secure<Tr
  • 118.
    Problem of stackingmonadsProblem of stacking monadsProblem of stacking monads
  • 119.
    Monad TransformersMonad TransformersMonadTransformers FutureTFutureTFutureT,,, OptionTOptionTOptionT
  • 120.
    Monad transformers appearedto not be thatMonad transformers appeared to not be thatMonad transformers appeared to not be that greatgreatgreat We cannot have use them in kotlin or java (in aWe cannot have use them in kotlin or java (in aWe cannot have use them in kotlin or java (in a sensible way)sensible way)sensible way)
  • 121.
    AllMightyMonadAllMightyMonadAllMightyMonad write the monadthat has all the e ects for you stack (cache, jdbc, async, etc.) cry once (writing atMap) enjoy everywhere
  • 122.
  • 123.
    1. zioziozio 2. https://github.com/atnos-org/e/https://github.com/atnos-org/e /https://github.com/atnos-org/e /
  • 125.
    No, we willnot hack bytecodeNo, we will not hack bytecodeNo, we will not hack bytecode
  • 127.
  • 128.
    class Hasiok { @Resource valjdbcConnection: Connection @Transactional @Secure @Cacheable @Retryable fun f(p:P) { //code } }
  • 129.
    class Hasiok { valenterprisyF = Nee.pure( secure .and(retryable) .and(cacheable) .and(transactional)) {conn:ConnectionProvider -> {p: P -> //code } } //declaration above means security is checked before retri //and retrial is made before cache which happens before tr }
  • 130.
    fun readStone() =Nee.pure(cached.andThen(jdbc)) { jdbcProvide { id: StoneId -> val dsl = DSL.using(jdbcProvider.getConnection().g val record = dsl.selectFrom(Stones.STONES) .where(Stones.STONES.ID.eq(id)) .fetchOneInto(Stones.STONES) Stone(record.id, StoneData(record.name, record.pri } }.anyError()
  • 131.
    fun addNewStone(newStone: StoneData)= seq.next().flatMap addStone(it, newStone) } private fun addStone(stoneId: Long, newStone: StoneData) = val dsl = DSL.using(jdbcProvider.getConnection().getRe val insertedRows = dsl.insertInto(Stones.STONES) .values(stoneId, newStone.name, newStone.price) .execute() if (insertedRows == 1) { Option.some(stoneId) } else { Option.none() } }
  • 132.
    Nee is ahobby, experimental project.Nee is a hobby, experimental project.Nee is a hobby, experimental project. NeeNeeNee monadmonadmonad has 4 params:has 4 params:has 4 params: Nee<R,E,P,A>Nee<R,E,P,A>Nee<R,E,P,A> - R - environment (context) - E - error type - P - argument (for cache - I think this was a bas idea) - A - the real result type
  • 133.
    I am slowlyadding e ects:I am slowly adding e ects:I am slowly adding e ects: jdbc, any transactional resource, cache, security, diagnostic,
  • 134.
  • 135.
  • 136.
    open class DocsModule(valconfig: ConfigModule) { open val dbProvider: HandleProvider by lazy { DBProvider(config.cfg) } open val timeProvider: TimeProvider by lazy { RealTime } open val jobsRepository by lazy { JobsRepository(dbProvide open val zipWriter : StatusZipWriter by lazy { StatusZipWriter(config) } open val docsStatusRepository : DocsStatusRepository by la
  • 137.
    MATERIALSMATERIALS Project ZIOProject ZIOProjectZIO Fun(c) 2018.7: John De Goes - FP to the MaxFun(c) 2018.7: John De Goes - FP to the MaxFun(c) 2018.7: John De Goes - FP to the Max The Book of MonadsThe Book of MonadsThe Book of Monads Alejandro Serrano MenaAlejandro Serrano MenaAlejandro Serrano Mena (experiment)(experiment)(experiment) https://github.com/ziohttps://github.com/ziohttps://github.com/zio https://www.youtube.com/watch?https://www.youtube.com/watch?https://www.youtube.com/watch? v=sxudIMiOo68v=sxudIMiOo68v=sxudIMiOo68 https://github.com/nee ect/neehttps://github.com/nee ect/neehttps://github.com/nee ect/nee
  • 138.
    FUNCTIONAL PROGRAMMINGFUNCTIONAL PROGRAMMING Powerof abstractionsPower of abstractionsPower of abstractions we want to have less bugs we want testability we want to have more fun @jarek000000@jarek000000@jarek000000 Thank youThank youThank you