LEONARDO BORGES • SENIOR CLOJURE ENGINEER • @LEONARDO_BORGES
O futuro chegou:
Programação concorrente com futures
SOBRE
Um pouco sobre mim
• Senior Clojure Engineer na
Atlassian, Sydney
• Fundador do Grupo de Usuários
Clojure de Sydney
• Autor de Clojure Reactive
Programming - http://bit.ly/cljRp
* QCon discount code: CRP10
CONCORRÊNCIA
FUTURES
O quê?
ABSTRAÇÃO
COMPOSIÇÃO
Futures em Java <= 1.7
FUTURES EM JAVA <= 1.7
static ExecutorService es = Executors.newCachedThreadPool();
static Integer doubler(Integer n) {
return 2 * n;
}
static Future<Integer> serviceA(Integer n) {
return es.submit(() -> {
Thread.sleep(1000);
return n;
});
}
static Future<Integer> serviceB(Integer n) {
return es.submit(() -> {
Thread.sleep(1500);
return Double.valueOf(Math.pow(n, 2)).intValue();
});
}
static Future<Integer> serviceC(Integer n) {
return es.submit(() -> {
Thread.sleep(2000);
return Double.valueOf(Math.pow(n, 3)).intValue();
});
}
FUTURES EM JAVA <= 1.7
Integer doubled = doubler(serviceA(10).get());
System.out.println("Couldn't do anything else while the line above was being executed...");
System.out.println("Result: " + serviceB(doubled).get() + " - " + serviceC(doubled).get());
Bloqueia a thread
Bloqueia a thread Bloqueia a thread
• Desperdício de processamento
Problemas
• Desperdício de processamento
• Baixo nível de composição
Problemas
E no Java 8?
FUTURES NO JAVA 8
final CompletableFuture<Integer> doubled = serviceA(10).thenApply(CompletableFutures::doubler);
final CompletableFuture<Integer> resultB = doubled.thenCompose(CompletableFutures::serviceB);
final CompletableFuture<Integer> resultC = doubled.thenCompose(CompletableFutures::serviceC);
CompletableFuture<Void> allFutures = CompletableFuture.allOf(resultB, resultC);
allFutures.whenComplete((v, ex) -> {
try {
System.out.println("Result: " + resultB.get() + " - " + resultC.get());
} catch (Exception e) {}
});
System.out.println("Doing other important things...");
FUTURES NO JAVA 8
final CompletableFuture<Integer> doubled = serviceA(10).thenApply(CompletableFutures::doubler);
final CompletableFuture<Integer> resultB = doubled.thenCompose(CompletableFutures::serviceB);
final CompletableFuture<Integer> resultC = doubled.thenCompose(CompletableFutures::serviceC);
CompletableFuture<Void> allFutures = CompletableFuture.allOf(resultB, resultC);
allFutures.whenComplete((v, ex) -> {
try {
System.out.println("Result: " + resultB.get() + " - " + resultC.get());
} catch (Exception e) {}
});
System.out.println("Doing other important things...");
Não bloqueia a thread
Esses
combinadores são
familiares?
STREAMS NO JAVA 8
List<Integer> ns = Arrays.asList(1, 2, 3, 4);
Function<Integer, Integer> doubler = (i) -> i * 2;
System.out.println(ns.stream().map(doubler).collect(Collectors.toList()));
// [2, 4, 6, 8]
Function<Integer, Stream<? extends Integer>> toRange = (i) -> IntStream.range(0, i).boxed();
Stream<Integer> combined = ns.stream()
.map(doubler)
.flatMap(toRange);
System.out.println(combined.collect(Collectors.toList()));
// [0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7]
Streams vs Futures
Stream<R> map(Function<? super T, ? extends R> mapper) {…}
Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {…}
CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {…}
CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {…}
Streams vs Futures
Stream<R> map(Function<? super T, ? extends R> mapper) {…}
Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {…}
CompletableFuture<U> thenApply (Function<? super T,? extends U> fn) {…}
CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {…}
E se quisermos
escrever funções
que funcionem com
Streams e Futures?
SEQUENCING FUTURES
CompletableFuture<Collection<Integer>> result =
sequence(serviceA(10), serviceB(10), serviceC(10));
// java.util.concurrent.CompletableFuture[10, 100, 1000]
SEQUENCING FUTURES
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) ->
acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) ->
a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
SEQUENCING FUTURES
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) ->
acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) ->
a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
SEQUENCING FUTURES
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) ->
acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) ->
a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
SEQUENCING FUTURES
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) ->
acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) ->
a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
SEQUENCING FUTURES
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) ->
acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) ->
a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
SEQUENCING STREAMS
Stream<Integer> s1 = Arrays.asList(1).stream();
Stream<Integer> s2 = Arrays.asList(2).stream();
Stream<Integer> s3 = Arrays.asList(3).stream();
sequenceS(s1, s2, s3)
// [[1, 2, 3]]
SEQUENCING STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
SEQUENCING STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
SEQUENCING STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
SEQUENCING STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
Perceberam alguma
semelhança?
FUTURES VS STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) -> acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) -> a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
FUTURES VS STREAMS
static <A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
Stream.of(Stream.empty()),
(acc, coll) ->
acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, Stream.of(x)))),
(acc, coll) -> acc.flatMap((xs) ->
coll.map((x) ->
Stream.concat(xs, x))));
}
static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) {
return Arrays.asList(cfs).stream().reduce(
CompletableFuture.completedFuture(new ArrayList<>()),
(acc, future) -> acc.thenCompose((xs) ->
future.thenApply((x) -> {
xs.add(x);
return xs;
})),
(a, b) -> a.thenCompose((xs) ->
b.thenApply((ys) -> {
xs.addAll(ys);
return xs;
})));
}
FlatMappable
FLATMAPPABLE
<M extends FlatMappable, A> M<List<A>> sequence(M<A>... ma) {
…
}
• Java não suporta tipos de alta
espécie (higher kinded types)
• Tipos de alta espécie são
indispensáveis ao se implementar
tais abstrações
Chegando no limite do sistema
de tipos
Colocando nome nos
bois
FlatMappable se chama Monad
trait Monad[F[_]] {
def point[A](a: => A): F[A]
def bind[A, B](a: F[A])(f: A => F[B]): F[B]
def map[A, B](a: F[A])(f: A => B): F[B] = bind(a)(b => point(f(b)))
}
FlatMappable se chama Monad
trait Monad[F[_]] {
def point[A](a: => A): F[A]
def bind[A, B](a: F[A])(f: A => F[B]): F[B]
def map[A, B](a: F[A])(f: A => B): F[B] = bind(a)(b => point(f(b)))
}
Tipos de alta espécie em ação
MONADS EM SCALA
O Monad de Futures
implicit def FutureMonad: Monad[Future] = new Monad[Future] {
def point[A](a: => A) = Future.successful(a)
def bind[A, B](a: Future[A])(f: A => Future[B]): Future[B] = a flatMap f
}
MONADS EM SCALA
O Monad de Listas
implicit def ListMonad: Monad[List] = new Monad[List] {
def point[A](a: => A) = List(a)
def bind[A, B](a: List[A])(f: A => List[B]): List[B] = a flatMap f
}
MONADS EM SCALA
Implementando sequence
def sequence[M[_] : Monad, A](ma: List[M[A]]): M[List[A]] = {
ma.foldLeft(Monad[M].point(List(): List[A]))((acc, m) =>
acc.flatMap((xs) =>
m.map((x) =>
xs :+ x))
)
}
Being abstract is something profoundly
different from being vague … The purpose of
abstraction is not to be vague, but to create a
new semantic level in which one can be
absolutely precise.
EDSGER W. DIJKSTRA
”
“
MONADS EM SCALA
Sequencing
val resultF: Future[List[Integer]] = sequence(List(serviceA(10), serviceB(10), serviceC(10)))
println(Await.result(resultF, Duration(2, "seconds")))
// List(10, 100, 1000)
val resultL: List[List[Int]] = sequence(List(List(1,2,3), List(4,5,6), List(7,8,9)))
println(resultL)
// List(List(1, 4, 7), List(2, 4, 7), List(3, 4, 7), List(1, 5, 7), ...)
Demais! O quê mais
podemos fazer??
Folding
FOLDING
List(2, 3, 4).reduce(_+_)
//9
FOLDING
List(2, 3, 4).reduce(_+_)
//9
val intFutures = List(Future.successful(1),
Future.successful(2),
Future.successful(3))
val result: Future[Int] = sequence(intFurures).map((x) => x.reduce(_ + _))
//…Future[9]
Existe algo em
comum?
Introduzindo Foldable
trait Foldable[F[_]] { self =>
…
def fold[M: Monoid](t: F[M]): M = ???
}
Introduzindo Monoids
trait Monoid[F] { self =>
def zero: F
def append(f1: F, f2: => F): F
}
Introduzindo Monoids: Ints
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
def zero: Int = 0
def append(f1: Int, f2: => Int): Int = f1 + f2
}
Introduzindo Monoids: Ints
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
def zero: Int = 0
def append(f1: Int, f2: => Int): Int = f1 + f2
}
Foldable[List].fold(List(2, 3, 4)))
//9
Introduzindo Monoids: Futures
Introduzindo Monoids: Futures
implicit def futureFreeMonoid[A] = new Monoid[Future[List[A]]] {
def zero: Future[List[A]] = Future.successful(List())
def append(f1: Future[List[A]], f2: => Future[List[A]]) = for {
a1 <- f1
a2 <- f2
} yield a1 ++ a2
}
Introduzindo Monoids: Futures
implicit def futureFreeMonoid[A] = new Monoid[Future[List[A]]] {
def zero: Future[List[A]] = Future.successful(List())
def append(f1: Future[List[A]], f2: => Future[List[A]]) = for {
a1 <- f1
a2 <- f2
} yield a1 ++ a2
}
Foldable[List].fold(List(Future.successful(2),
Future.successful(3),
Future.successful(4)))
//…Future[9]
Monad, Foldable e
Monoid são apenas
o começo
Em Scala, muitas
delas já foram
implementadas em
Scalaz
A Teoria das
Categorias pode ter
um impacto grande
na criação de
bibliotecas
Being abstract is something profoundly
different from being vague … The purpose of
abstraction is not to be vague, but to create a
new semantic level in which one can be
absolutely precise.
EDSGER W. DIJKSTRA
”
“
Referências
• Clojure Reactive Programming - http://bit.ly/cljRp
• Java 8 CompletableFuture - http://bit.ly/j8Future
• Java 8 Streams - http://bit.ly/j8stream
• Category Theory - http://amzn.to/1NfL08U
• Free Monoids - http://en.wikipedia.org/wiki/Free_monoid
• Scalaz - https://github.com/scalaz/scalaz
• Fluokitten (Clojure) - https://github.com/uncomplicate/fluokitten
Obrigado!
LEONARDO BORGES • SENIOR CLOJURE DEVELOPER • @LEONARDO_BORGES
Q&A
We are hiring!
LEONARDO BORGES • SENIOR CLOJURE DEVELOPER • @LEONARDO_BORGES

Futures e abstração - QCon São Paulo 2015

  • 1.
    LEONARDO BORGES •SENIOR CLOJURE ENGINEER • @LEONARDO_BORGES O futuro chegou: Programação concorrente com futures
  • 2.
    SOBRE Um pouco sobremim • Senior Clojure Engineer na Atlassian, Sydney • Fundador do Grupo de Usuários Clojure de Sydney • Autor de Clojure Reactive Programming - http://bit.ly/cljRp * QCon discount code: CRP10
  • 3.
  • 4.
  • 5.
    FUTURES EM JAVA<= 1.7 static ExecutorService es = Executors.newCachedThreadPool(); static Integer doubler(Integer n) { return 2 * n; } static Future<Integer> serviceA(Integer n) { return es.submit(() -> { Thread.sleep(1000); return n; }); } static Future<Integer> serviceB(Integer n) { return es.submit(() -> { Thread.sleep(1500); return Double.valueOf(Math.pow(n, 2)).intValue(); }); } static Future<Integer> serviceC(Integer n) { return es.submit(() -> { Thread.sleep(2000); return Double.valueOf(Math.pow(n, 3)).intValue(); }); }
  • 6.
    FUTURES EM JAVA<= 1.7 Integer doubled = doubler(serviceA(10).get()); System.out.println("Couldn't do anything else while the line above was being executed..."); System.out.println("Result: " + serviceB(doubled).get() + " - " + serviceC(doubled).get()); Bloqueia a thread Bloqueia a thread Bloqueia a thread
  • 7.
    • Desperdício deprocessamento Problemas
  • 8.
    • Desperdício deprocessamento • Baixo nível de composição Problemas
  • 9.
  • 10.
    FUTURES NO JAVA8 final CompletableFuture<Integer> doubled = serviceA(10).thenApply(CompletableFutures::doubler); final CompletableFuture<Integer> resultB = doubled.thenCompose(CompletableFutures::serviceB); final CompletableFuture<Integer> resultC = doubled.thenCompose(CompletableFutures::serviceC); CompletableFuture<Void> allFutures = CompletableFuture.allOf(resultB, resultC); allFutures.whenComplete((v, ex) -> { try { System.out.println("Result: " + resultB.get() + " - " + resultC.get()); } catch (Exception e) {} }); System.out.println("Doing other important things...");
  • 11.
    FUTURES NO JAVA8 final CompletableFuture<Integer> doubled = serviceA(10).thenApply(CompletableFutures::doubler); final CompletableFuture<Integer> resultB = doubled.thenCompose(CompletableFutures::serviceB); final CompletableFuture<Integer> resultC = doubled.thenCompose(CompletableFutures::serviceC); CompletableFuture<Void> allFutures = CompletableFuture.allOf(resultB, resultC); allFutures.whenComplete((v, ex) -> { try { System.out.println("Result: " + resultB.get() + " - " + resultC.get()); } catch (Exception e) {} }); System.out.println("Doing other important things..."); Não bloqueia a thread
  • 12.
  • 13.
    STREAMS NO JAVA8 List<Integer> ns = Arrays.asList(1, 2, 3, 4); Function<Integer, Integer> doubler = (i) -> i * 2; System.out.println(ns.stream().map(doubler).collect(Collectors.toList())); // [2, 4, 6, 8] Function<Integer, Stream<? extends Integer>> toRange = (i) -> IntStream.range(0, i).boxed(); Stream<Integer> combined = ns.stream() .map(doubler) .flatMap(toRange); System.out.println(combined.collect(Collectors.toList())); // [0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7]
  • 14.
    Streams vs Futures Stream<R>map(Function<? super T, ? extends R> mapper) {…} Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {…} CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {…} CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {…}
  • 15.
    Streams vs Futures Stream<R>map(Function<? super T, ? extends R> mapper) {…} Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {…} CompletableFuture<U> thenApply (Function<? super T,? extends U> fn) {…} CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {…}
  • 16.
    E se quisermos escreverfunções que funcionem com Streams e Futures?
  • 17.
    SEQUENCING FUTURES CompletableFuture<Collection<Integer>> result= sequence(serviceA(10), serviceB(10), serviceC(10)); // java.util.concurrent.CompletableFuture[10, 100, 1000]
  • 18.
    SEQUENCING FUTURES static <A>CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 19.
    SEQUENCING FUTURES static <A>CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 20.
    SEQUENCING FUTURES static <A>CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 21.
    SEQUENCING FUTURES static <A>CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 22.
    SEQUENCING FUTURES static <A>CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 23.
    SEQUENCING STREAMS Stream<Integer> s1= Arrays.asList(1).stream(); Stream<Integer> s2 = Arrays.asList(2).stream(); Stream<Integer> s3 = Arrays.asList(3).stream(); sequenceS(s1, s2, s3) // [[1, 2, 3]]
  • 24.
    SEQUENCING STREAMS static <A>Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); }
  • 25.
    SEQUENCING STREAMS static <A>Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); }
  • 26.
    SEQUENCING STREAMS static <A>Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); }
  • 27.
    SEQUENCING STREAMS static <A>Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); }
  • 28.
  • 29.
    FUTURES VS STREAMS static<A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); } static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 30.
    FUTURES VS STREAMS static<A> Stream<Stream<A>> sequenceS(Stream<A>... cfs) { return Arrays.asList(cfs).stream().reduce( Stream.of(Stream.empty()), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, Stream.of(x)))), (acc, coll) -> acc.flatMap((xs) -> coll.map((x) -> Stream.concat(xs, x)))); } static <A> CompletableFuture<Collection<A>> sequence(CompletableFuture<A>... cfs) { return Arrays.asList(cfs).stream().reduce( CompletableFuture.completedFuture(new ArrayList<>()), (acc, future) -> acc.thenCompose((xs) -> future.thenApply((x) -> { xs.add(x); return xs; })), (a, b) -> a.thenCompose((xs) -> b.thenApply((ys) -> { xs.addAll(ys); return xs; }))); }
  • 31.
  • 32.
    FLATMAPPABLE <M extends FlatMappable,A> M<List<A>> sequence(M<A>... ma) { … }
  • 33.
    • Java nãosuporta tipos de alta espécie (higher kinded types) • Tipos de alta espécie são indispensáveis ao se implementar tais abstrações Chegando no limite do sistema de tipos
  • 34.
  • 35.
    FlatMappable se chamaMonad trait Monad[F[_]] { def point[A](a: => A): F[A] def bind[A, B](a: F[A])(f: A => F[B]): F[B] def map[A, B](a: F[A])(f: A => B): F[B] = bind(a)(b => point(f(b))) }
  • 36.
    FlatMappable se chamaMonad trait Monad[F[_]] { def point[A](a: => A): F[A] def bind[A, B](a: F[A])(f: A => F[B]): F[B] def map[A, B](a: F[A])(f: A => B): F[B] = bind(a)(b => point(f(b))) } Tipos de alta espécie em ação
  • 37.
    MONADS EM SCALA OMonad de Futures implicit def FutureMonad: Monad[Future] = new Monad[Future] { def point[A](a: => A) = Future.successful(a) def bind[A, B](a: Future[A])(f: A => Future[B]): Future[B] = a flatMap f }
  • 38.
    MONADS EM SCALA OMonad de Listas implicit def ListMonad: Monad[List] = new Monad[List] { def point[A](a: => A) = List(a) def bind[A, B](a: List[A])(f: A => List[B]): List[B] = a flatMap f }
  • 39.
    MONADS EM SCALA Implementandosequence def sequence[M[_] : Monad, A](ma: List[M[A]]): M[List[A]] = { ma.foldLeft(Monad[M].point(List(): List[A]))((acc, m) => acc.flatMap((xs) => m.map((x) => xs :+ x)) ) }
  • 40.
    Being abstract issomething profoundly different from being vague … The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. EDSGER W. DIJKSTRA ” “
  • 41.
    MONADS EM SCALA Sequencing valresultF: Future[List[Integer]] = sequence(List(serviceA(10), serviceB(10), serviceC(10))) println(Await.result(resultF, Duration(2, "seconds"))) // List(10, 100, 1000) val resultL: List[List[Int]] = sequence(List(List(1,2,3), List(4,5,6), List(7,8,9))) println(resultL) // List(List(1, 4, 7), List(2, 4, 7), List(3, 4, 7), List(1, 5, 7), ...)
  • 42.
    Demais! O quêmais podemos fazer??
  • 43.
  • 44.
  • 45.
    FOLDING List(2, 3, 4).reduce(_+_) //9 valintFutures = List(Future.successful(1), Future.successful(2), Future.successful(3)) val result: Future[Int] = sequence(intFurures).map((x) => x.reduce(_ + _)) //…Future[9]
  • 46.
  • 47.
    Introduzindo Foldable trait Foldable[F[_]]{ self => … def fold[M: Monoid](t: F[M]): M = ??? }
  • 48.
    Introduzindo Monoids trait Monoid[F]{ self => def zero: F def append(f1: F, f2: => F): F }
  • 49.
    Introduzindo Monoids: Ints implicitdef intMonoid: Monoid[Int] = new Monoid[Int] { def zero: Int = 0 def append(f1: Int, f2: => Int): Int = f1 + f2 }
  • 50.
    Introduzindo Monoids: Ints implicitdef intMonoid: Monoid[Int] = new Monoid[Int] { def zero: Int = 0 def append(f1: Int, f2: => Int): Int = f1 + f2 } Foldable[List].fold(List(2, 3, 4))) //9
  • 51.
  • 52.
    Introduzindo Monoids: Futures implicitdef futureFreeMonoid[A] = new Monoid[Future[List[A]]] { def zero: Future[List[A]] = Future.successful(List()) def append(f1: Future[List[A]], f2: => Future[List[A]]) = for { a1 <- f1 a2 <- f2 } yield a1 ++ a2 }
  • 53.
    Introduzindo Monoids: Futures implicitdef futureFreeMonoid[A] = new Monoid[Future[List[A]]] { def zero: Future[List[A]] = Future.successful(List()) def append(f1: Future[List[A]], f2: => Future[List[A]]) = for { a1 <- f1 a2 <- f2 } yield a1 ++ a2 } Foldable[List].fold(List(Future.successful(2), Future.successful(3), Future.successful(4))) //…Future[9]
  • 54.
    Monad, Foldable e Monoidsão apenas o começo
  • 55.
    Em Scala, muitas delasjá foram implementadas em Scalaz
  • 56.
    A Teoria das Categoriaspode ter um impacto grande na criação de bibliotecas
  • 57.
    Being abstract issomething profoundly different from being vague … The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. EDSGER W. DIJKSTRA ” “
  • 58.
    Referências • Clojure ReactiveProgramming - http://bit.ly/cljRp • Java 8 CompletableFuture - http://bit.ly/j8Future • Java 8 Streams - http://bit.ly/j8stream • Category Theory - http://amzn.to/1NfL08U • Free Monoids - http://en.wikipedia.org/wiki/Free_monoid • Scalaz - https://github.com/scalaz/scalaz • Fluokitten (Clojure) - https://github.com/uncomplicate/fluokitten
  • 59.
    Obrigado! LEONARDO BORGES •SENIOR CLOJURE DEVELOPER • @LEONARDO_BORGES Q&A
  • 60.
    We are hiring! LEONARDOBORGES • SENIOR CLOJURE DEVELOPER • @LEONARDO_BORGES