SlideShare uma empresa Scribd logo
1 de 76
Baixar para ler offline
The algebra of library
design
#cljsyd - October 2014
Leonardo Borges
@leonardo_borges
www.leonardoborges.com
www.thoughtworks.com
Algebra?
More specifically…
• Algebraic structures, studied in Abstract Algebra
• a set with one or more operations on it
• Category theory is another way to study these
structures
Overview
• The problem with Clojure futures
• A better futures library
• Functors, Applicative Functors and Monads
• What about core.async (and others?)
The beginning…
(def age (future (do (Thread/sleep 2000)!
31)))!
(prn "fetching age...")!
!
(def double-age (* 2 @age))!
(prn "age, doubled: " double-age)!
!
(prn "doing something else important...")
Do you see any issues?
The beginning…
(def age (future (do (Thread/sleep 2000)!
31)))!
(prn "fetching age...")!
!
(def double-age (* 2 @age))!
(prn "age, doubled: " double-age)!
!
(prn "doing something else important...")!
!
;; "fetching age..."!
;; "age, doubled: " 62!
;; "doing something else important..."!
It looks like we would like to execute
something once the Future has completed
A first glance at imminent
(require '[imminent.core :as i])!
!
(def age (i/future (do (Thread/sleep 2000)!
31)))!
(prn "fetching age...")!
(def double-age (i/map age #(* % 2)))!
(i/on-success double-age #(prn "age, doubled: " %))!
!
(prn "doing something else important...")
A first glance at imminent
(require '[imminent.core :as i])!
!
(def age (i/future (do (Thread/sleep 2000)!
31)))!
(prn "fetching age...")!
(def double-age (i/map age #(* % 2)))!
(i/on-success double-age #(prn "age, doubled: " %))!
!
(prn "doing something else important...")!
!
;; "fetching age..."!
;; "doing something else important..."!
;; "age, doubled: " 62!
Functor
class Functor f where!
fmap :: (a -> b) -> f a -> f b
Another example
(def age (future (do (Thread/sleep 2000)!
31)))!
(def name (future (do (Thread/sleep 2000)!
"Leonardo")))!
(prn "fetching name...")!
(prn (format "%s is %s years old" @name @age))!
(prn "more important things going on...")
Same as before, only this time we
want to execute the code once
both futures have completed
Rewriting it in imminent
(def age (i/future (do (Thread/sleep 2000)!
31)))!
(def name (i/future (do (Thread/sleep 2000)!
"Leonardo")))!
(prn "fetching name...")!
(def both (i/sequence [name age]))!
(i/on-success both !
(fn [[name age]]!
(prn (format "%s is %s years old" name age))))!
!
(prn "more important things going on...")!
!
Rewriting it in imminent
(def age (i/future (do (Thread/sleep 2000)!
31)))!
(def name (i/future (do (Thread/sleep 2000)!
"Leonardo")))!
(prn "fetching name...")!
(def both (i/sequence [name age]))!
(i/on-success both !
(fn [[name age]]!
(prn (format "%s is %s years old" name age))))!
!
(prn "more important things going on...")!
!
;; "fetching name..."!
;; "more important things going on..."!
;; "Leonardo is 31 years old"!
Monad
class Monad m where!
(>>=) :: m a -> (a -> m b) -> m b!
return :: a -> m a
Monad
class Monad m where!
(>>=) :: m a -> (a -> m b) -> m b!
return :: a -> m a
(>>=) is also called bind, flatmap, selectMany
and mapcat…
Monad - derived functions
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Monad - derived functions
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
(defn mlift2!
[f]!
(fn [ma mb]!
(flatmap ma!
(fn [a]!
(flatmap mb!
(fn [b]!
(pure (f a b))))))))
Monad - derived functions
sequence :: Monad m => [m a] -> m [a]
Monad - derived functions
sequence :: Monad m => [m a] -> m [a]
(defn sequence!
[ms]!
(reduce (mlift2 conj)!
(pure [])!
ms))
Monad - derived functions
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
Monad - derived functions
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
(defn mmap!
[f vs]!
(sequence (map f vs)))
Monad - derived functions
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
(defn mmap!
[f vs]!
(sequence (map f vs)))
plus a bunch of others…
Let’s look at a more concrete
example
Aggregating movie data
(defn cast-by-movie [name]!
(future (do (Thread/sleep 5000)!
(:cast movie))))!
!
(defn movies-by-actor [name]!
(do (Thread/sleep 2000)!
(->> actor-movies!
(filter #(= name (:name %)))!
first)))!
!
(defn spouse-of [name]!
(do (Thread/sleep 2000)!
(->> actor-spouse!
(filter #(= name (:name %)))!
first)))!
!
(defn top-5 []!
(future (do (Thread/sleep 5000)!
top-5-movies)))!
The output
({:name "Cate Blanchett",!
:spouse "Andrew Upton",!
:movies!
("Lord of The Rings: The Fellowship of The Ring - (top 5)"...)}!
{:name "Elijah Wood",!
:spouse "Unknown",!
:movies!
("Eternal Sunshine of the Spotless Mind"...)}!
...)
One possible solution
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (pmap movies-by-actor @cast)!
spouses (pmap spouse-of @cast)!
top-5 (top-5)]!
(prn "Fetching data...")!
(pprint (aggregate-actor-data spouses movies @top-5)))
One possible solution
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (pmap movies-by-actor @cast)!
spouses (pmap spouse-of @cast)!
top-5 (top-5)]!
(prn "Fetching data...")!
(pprint (aggregate-actor-data spouses movies @top-5)))
;; "Elapsed time: 10049.334 msecs"
We face the same problems
as before
Re-arranging the code
would improve it
But the point is not having to
do so
In imminent
(defn cast-by-movie [name]!
(i/future (do (Thread/sleep 5000)!
(:cast movie))))!
!
(defn movies-by-actor [name]!
(i/future (do (Thread/sleep 2000)!
(->> actor-movies!
(filter #(= name (:name %)))!
first))))!
!
(defn spouse-of [name]!
(i/future (do (Thread/sleep 2000)!
(->> actor-spouse!
(filter #(= name (:name %)))!
first))))!
!
(defn top-5 []!
(i/future (do (Thread/sleep 5000)!
top-5-movies)))
In imminent
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (i/flatmap cast #(i/map-future movies-by-actor %))!
spouses (i/flatmap cast #(i/map-future spouse-of %))!
result (i/sequence [spouses movies (top-5)])]!
(prn "Fetching data...")!
(pprint (apply aggregate-actor-data!
(i/dderef (i/await result)))))
In imminent
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (i/flatmap cast #(i/map-future movies-by-actor %))!
spouses (i/flatmap cast #(i/map-future spouse-of %))!
result (i/sequence [spouses movies (top-5)])]!
(prn "Fetching data...")!
(pprint (apply aggregate-actor-data!
(i/dderef (i/await result)))))
;; "Elapsed time: 7087.603 msecs"
Everything is asynchronous
by default
We can optionally block and wait
for a result using the “i/await”
function
Ok, that’s cool!
But what about Applicative Functors?
Writing this sucks
(defn int-f [n]!
(i/future (do (Thread/sleep 2000)!
(* 2 n))))!
!
!
(-> (i/bind!
(int-f 10)!
(fn [a] (i/bind!
(int-f a)!
(fn [b] (i/bind!
(int-f b)!
(fn [c] !
(i/pure (+ a b c)))))))
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f a)!
c (int-f b)]!
(i/pure (+ a b c)))
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f a)!
c (int-f b)]!
(i/pure (+ a b c)))
How long does this computation take?
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f a)!
c (int-f b)]!
(i/pure (+ a b c)))
How long does this computation take?
;; "Elapsed time: 6002.39 msecs"
What if the computations don’t
depend on each other?
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f 20)!
c (int-f 30)]!
(i/pure (+ a b c)))
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f 20)!
c (int-f 30)]!
(i/pure (+ a b c)))
How long does this take now?
Monadic “do” notation
(i/mdo [a (int-f 10)!
b (int-f 20)!
c (int-f 30)]!
(i/pure (+ a b c)))
How long does this take now?
;; "Elapsed time: 6002.39 msecs"
?
The ‘do-notation’ is used to sequence monadic
steps so we remain serial, but still asynchronous
Applicative Functor
class Functor f => Applicative f where!
pure :: a -> f a!
(<*>) :: f (a -> b) -> f a -> f b!
Previous example, rewritten in applicative style
(i/<*> (i/map (int-f 10) (curry + 3))!
(int-f 20)!
(int-f 30))
;;"Elapsed time: 2001.509 msecs"
It turns out this pattern is a derived
function from Applicative Functors
Applicative Functor - derived functions
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
Applicative Functor - derived functions
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
(defn alift!
[f]!
(fn [& as]!
{:pre [(seq as)]}!
(let [curried (curry f (count as))]!
(apply <*>!
(fmap curried (first as))!
(rest as)))))
“alift” assumes every Applicative
Functor is also Functor
Previous example, rewritten using “alift"
;;"Elapsed time: 2001.509 msecs"
((i/alift +) (int-f 10) (int-f 20) (int-f 30))
It looks a lot like function application
:)
Applicatives give us the parallel
semantics without giving up convenience
Ok, this is awesome! But we have
core.async!
The movies example, revisited
(defn cast-by-movie [name]!
(let [c (chan)]!
(go (<! (timeout 5000))!
(>! c (:cast movie))!
(close! c))!
c))!
!
(defn movies-by-actor [name]!
(let [c (chan)]!
(go (<! (timeout 2000))!
(>! c (->> actor-movies!
(filter #(= name (:name %)))!
first))!
(close! c))!
c))
The movies example, revisited
(defn spouse-of [name]!
(let [c (chan)]!
(go (<! (timeout 2000))!
(>! c (->> actor-spouse!
(filter #(= name (:name %)))!
first))!
(close! c))!
c))!
!
(defn top-5 []!
(let [c (chan)]!
(go (<! (timeout 5000))!
(>! c top-5-movies)!
(close! c))!
c))!
!
!
(defn async-pmap [f source]!
(go (->> (map f (<! source))!
async/merge!
(async/into [])!
<!)))
The movies example, revisited
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
m-cast (mult cast)!
movies (async-pmap movies-by-actor (tap m-cast (chan)))!
spouses (async-pmap spouse-of (tap m-cast (chan)))!
top-5 (top-5)]!
(prn "Fetching data...")!
(pprint (<!! (go (aggregate-actor-data (<! spouses)!
(<! movies)!
(<! top-5))))))!
!
;; "Elapsed time: 7088.834 msecs"
“mult” and “tap” have no business
being in that code
they are necessary because
channels are single-take containers
Exceptions
core.async swallows them by default
(defn movies-by-actor [name]!
(let [c (chan)]!
(go (<! (timeout 2000))!
(throw (Exception. (str "Error fetching movies for actor " name)))!
(>! c (->> actor-movies!
(filter #(= name (:name %)))!
first))!
(close! c))!
c))
Exceptions
core.async swallows them by default
(defn movies-by-actor [name]!
(let [c (chan)]!
(go (<! (timeout 2000))!
(throw (Exception. (str "Error fetching movies for actor " name)))!
(>! c (->> actor-movies!
(filter #(= name (:name %)))!
first))!
(close! c))!
c))
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
m-cast (mult cast)!
movies (async-pmap movies-by-actor (tap m-cast (chan)))!
spouses (async-pmap spouse-of (tap m-cast (chan)))!
top-5 (top-5)]!
(prn "Fetching data...")!
(pprint (<!! (go (aggregate-actor-data (<! spouses)!
(<! movies)!
(<! top-5))))))!
!
;; nil - WTF???"
Exceptions - workaround
(defn throw-err [e]!
(when (instance? Throwable e) (throw e))!
e)!
!
(defmacro <? [ch]!
`(throw-err (<! ~ch)))!
!
!
(defn movies-by-actor [name]!
(let [c (chan)]!
(go (try (do (<! (timeout 2000))!
(throw (Exception. (str "Error fetching movies for actor " name)))!
(>! c (->> actor-movies!
(filter #(= name (:name %)))!
first))!
(close! c))!
(catch Exception e!
(do (>! c e)!
(close! c)))))!
c))
Exceptions - workaround
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
m-cast (mult cast)!
movies (async-pmap movies-by-actor (tap m-cast (chan)))!
spouses (async-pmap spouse-of (tap m-cast (chan)))!
top-5 (top-5)]!
(prn "Fetching data...")!
(pprint (<!! (go (try!
(aggregate-actor-data (<? spouses)!
(<? movies)!
(<? top-5))!
(catch Exception e!
e)))))))
A lot of effort for not much benefit
Exceptions - the imminent way *
* not really unique to imminent. Other libraries use this same approach
(defn movies-by-actor [name]!
(i/future (do (Thread/sleep 2000)!
(throw (Exception. (str "Error fetching movies for actor
" name)))!
(->> actor-movies!
(filter #(= name (:name %)))!
first))))
Exceptions - the imminent way # 1
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (i/flatmap cast #(i/map-future movies-by-actor %))!
spouses (i/flatmap cast #(i/map-future spouse-of %))!
results (i/sequence [spouses movies (top-5)])!
_ (prn "Fetching data...")!
result (deref (i/await results))]!
!
(i/map result #(pprint (apply aggregate-actor-data %)))!
(i/map-failure result #(pprint (str "Oops: " %)))))
;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
Exceptions - the imminent way # 1
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (i/flatmap cast #(i/map-future movies-by-actor %))!
spouses (i/flatmap cast #(i/map-future spouse-of %))!
results (i/sequence [spouses movies (top-5)])!
_ (prn "Fetching data...")!
result (deref (i/await results))]!
!
(i/map result #(pprint (apply aggregate-actor-data %)))!
(i/map-failure result #(pprint (str "Oops: " %)))))
imminent results are themselves functors :)
;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
Exceptions - the imminent way # 2
(let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")!
movies (i/flatmap cast #(i/map-future movies-by-actor %))!
spouses (i/flatmap cast #(i/map-future spouse-of %))!
result (i/sequence [spouses movies (top-5)])]!
(prn "Fetching data...")!
(i/on-success result #(prn-to-repl (apply aggregate-actor-data %)))!
(i/on-failure result #(prn-to-repl (str "Oops: " %))))!
;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
Final Thoughts
Category Theory helps you design libraries with higher
code reuse due to well known and understood
abstractions
Final Thoughts
Imminent takes advantage of that and provides an
asynchronous and composable Futures library for
Clojure
Final Thoughts
Even when compared to core.async, imminent still makes
sense unless you need the low level granularity of
channels and/or coordination via queues
Questions?
Leonardo Borges
@leonardo_borges
www.leonardoborges.com
www.thoughtworks.com

Mais conteúdo relacionado

Mais procurados

CompletableFuture
CompletableFutureCompletableFuture
CompletableFuture
koji lin
 
AST Transformations
AST TransformationsAST Transformations
AST Transformations
HamletDRC
 
JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6
Solution4Future
 

Mais procurados (20)

A deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio moduleA deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio module
 
Building native Android applications with Mirah and Pindah
Building native Android applications with Mirah and PindahBuilding native Android applications with Mirah and Pindah
Building native Android applications with Mirah and Pindah
 
JVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in KotlinJVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in Kotlin
 
CompletableFuture
CompletableFutureCompletableFuture
CompletableFuture
 
AST Transformations
AST TransformationsAST Transformations
AST Transformations
 
JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2
 
ES6: The Awesome Parts
ES6: The Awesome PartsES6: The Awesome Parts
ES6: The Awesome Parts
 
50 new things we can do with Java 8
50 new things we can do with Java 850 new things we can do with Java 8
50 new things we can do with Java 8
 
Riga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with JavassistRiga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with Javassist
 
Introduction to Debuggers
Introduction to DebuggersIntroduction to Debuggers
Introduction to Debuggers
 
Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
 
node ffi
node ffinode ffi
node ffi
 
JavaOne 2015 - Having fun with Javassist
JavaOne 2015 - Having fun with JavassistJavaOne 2015 - Having fun with Javassist
JavaOne 2015 - Having fun with Javassist
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
Kotlin
KotlinKotlin
Kotlin
 
Explaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to ComeExplaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to Come
 

Destaque

Functional Reactive Programming / Compositional Event Systems
Functional Reactive Programming / Compositional Event SystemsFunctional Reactive Programming / Compositional Event Systems
Functional Reactive Programming / Compositional Event Systems
Leonardo Borges
 

Destaque (9)

Functional Reactive Programming
Functional Reactive ProgrammingFunctional Reactive Programming
Functional Reactive Programming
 
High Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScriptHigh Performance web apps in Om, React and ClojureScript
High Performance web apps in Om, React and ClojureScript
 
What is Reactive programming?
What is Reactive programming?What is Reactive programming?
What is Reactive programming?
 
Functional Reactive Programming / Compositional Event Systems
Functional Reactive Programming / Compositional Event SystemsFunctional Reactive Programming / Compositional Event Systems
Functional Reactive Programming / Compositional Event Systems
 
ASP.NET MVC Core
ASP.NET MVC CoreASP.NET MVC Core
ASP.NET MVC Core
 
02 - [ASP.NET Core] ASP.NET Core MVC
02 - [ASP.NET Core] ASP.NET Core MVC 02 - [ASP.NET Core] ASP.NET Core MVC
02 - [ASP.NET Core] ASP.NET Core MVC
 
Microservices: Architecture for the Real-time Organization
Microservices: Architecture for the Real-time OrganizationMicroservices: Architecture for the Real-time Organization
Microservices: Architecture for the Real-time Organization
 
Real world functional reactive programming
Real world functional reactive programmingReal world functional reactive programming
Real world functional reactive programming
 
Angular, ASP.NET Core, and Visual Studio Code - Oh My!
Angular, ASP.NET Core, and Visual Studio Code - Oh My!Angular, ASP.NET Core, and Visual Studio Code - Oh My!
Angular, ASP.NET Core, and Visual Studio Code - Oh My!
 

Semelhante a The algebra of library design

Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
Jan Kronquist
 
From Python to Scala
From Python to ScalaFrom Python to Scala
From Python to Scala
FFunction inc
 

Semelhante a The algebra of library design (20)

Frege - consequently functional programming for the JVM
Frege - consequently functional programming for the JVMFrege - consequently functional programming for the JVM
Frege - consequently functional programming for the JVM
 
Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012
 
Clojure: Simple By Design
Clojure: Simple By DesignClojure: Simple By Design
Clojure: Simple By Design
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Intro
 
Music as data
Music as dataMusic as data
Music as data
 
Arduino programming of ML-style in ATS
Arduino programming of ML-style in ATSArduino programming of ML-style in ATS
Arduino programming of ML-style in ATS
 
Clojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVMClojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVM
 
(first '(Clojure.))
(first '(Clojure.))(first '(Clojure.))
(first '(Clojure.))
 
Things about Functional JavaScript
Things about Functional JavaScriptThings about Functional JavaScript
Things about Functional JavaScript
 
Halogen: Past, Present, and Future
Halogen: Past, Present, and FutureHalogen: Past, Present, and Future
Halogen: Past, Present, and Future
 
Software Dendrology by Brandon Bloom
Software Dendrology by Brandon BloomSoftware Dendrology by Brandon Bloom
Software Dendrology by Brandon Bloom
 
From Java to Parellel Clojure - Clojure South 2019
From Java to Parellel Clojure - Clojure South 2019From Java to Parellel Clojure - Clojure South 2019
From Java to Parellel Clojure - Clojure South 2019
 
From Python to Scala
From Python to ScalaFrom Python to Scala
From Python to Scala
 
Go Containers
Go ContainersGo Containers
Go Containers
 
Go Containers
Go ContainersGo Containers
Go Containers
 
All I Needed for Functional Programming I Learned in High School Algebra
All I Needed for Functional Programming I Learned in High School AlgebraAll I Needed for Functional Programming I Learned in High School Algebra
All I Needed for Functional Programming I Learned in High School Algebra
 
EcmaScript unchained
EcmaScript unchainedEcmaScript unchained
EcmaScript unchained
 
Testing stateful, concurrent, and async systems using test.check
Testing stateful, concurrent, and async systems using test.checkTesting stateful, concurrent, and async systems using test.check
Testing stateful, concurrent, and async systems using test.check
 
Go Concurrency
Go ConcurrencyGo Concurrency
Go Concurrency
 

Mais de Leonardo Borges

Clouds against the Floods (RubyConfBR2011)
Clouds against the Floods (RubyConfBR2011) Clouds against the Floods (RubyConfBR2011)
Clouds against the Floods (RubyConfBR2011)
Leonardo Borges
 

Mais de Leonardo Borges (16)

Realtime collaboration with Clojure - EuroClojure - Barcelona, 2015
Realtime collaboration with Clojure - EuroClojure - Barcelona, 2015Realtime collaboration with Clojure - EuroClojure - Barcelona, 2015
Realtime collaboration with Clojure - EuroClojure - Barcelona, 2015
 
Parametricity - #cljsyd - May, 2015
Parametricity - #cljsyd - May, 2015Parametricity - #cljsyd - May, 2015
Parametricity - #cljsyd - May, 2015
 
Futures e abstração - QCon São Paulo 2015
Futures e abstração - QCon São Paulo 2015Futures e abstração - QCon São Paulo 2015
Futures e abstração - QCon São Paulo 2015
 
Programação functional reativa: lidando com código assíncrono
Programação functional reativa: lidando com código assíncronoProgramação functional reativa: lidando com código assíncrono
Programação functional reativa: lidando com código assíncrono
 
Monads in Clojure
Monads in ClojureMonads in Clojure
Monads in Clojure
 
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
 
Intro to Clojure's core.async
Intro to Clojure's core.asyncIntro to Clojure's core.async
Intro to Clojure's core.async
 
Clojure/West 2013 in 30 mins
Clojure/West 2013 in 30 minsClojure/West 2013 in 30 mins
Clojure/West 2013 in 30 mins
 
Clojure Reducers / clj-syd Aug 2012
Clojure Reducers / clj-syd Aug 2012Clojure Reducers / clj-syd Aug 2012
Clojure Reducers / clj-syd Aug 2012
 
The many facets of code reuse in JavaScript
The many facets of code reuse in JavaScriptThe many facets of code reuse in JavaScript
The many facets of code reuse in JavaScript
 
Heroku addons development - Nov 2011
Heroku addons development - Nov 2011Heroku addons development - Nov 2011
Heroku addons development - Nov 2011
 
Clouds against the Floods (RubyConfBR2011)
Clouds against the Floods (RubyConfBR2011) Clouds against the Floods (RubyConfBR2011)
Clouds against the Floods (RubyConfBR2011)
 
Clouds Against the Floods
Clouds Against the FloodsClouds Against the Floods
Clouds Against the Floods
 
Arel in Rails 3
Arel in Rails 3Arel in Rails 3
Arel in Rails 3
 
Testing with Spring
Testing with SpringTesting with Spring
Testing with Spring
 
JRuby in The Enterprise
JRuby in The EnterpriseJRuby in The Enterprise
JRuby in The Enterprise
 

Último

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Último (20)

Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
JohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptxJohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptx
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 

The algebra of library design

  • 1. The algebra of library design #cljsyd - October 2014 Leonardo Borges @leonardo_borges www.leonardoborges.com www.thoughtworks.com
  • 3. More specifically… • Algebraic structures, studied in Abstract Algebra • a set with one or more operations on it • Category theory is another way to study these structures
  • 4. Overview • The problem with Clojure futures • A better futures library • Functors, Applicative Functors and Monads • What about core.async (and others?)
  • 5. The beginning… (def age (future (do (Thread/sleep 2000)! 31)))! (prn "fetching age...")! ! (def double-age (* 2 @age))! (prn "age, doubled: " double-age)! ! (prn "doing something else important...")
  • 6. Do you see any issues?
  • 7. The beginning… (def age (future (do (Thread/sleep 2000)! 31)))! (prn "fetching age...")! ! (def double-age (* 2 @age))! (prn "age, doubled: " double-age)! ! (prn "doing something else important...")! ! ;; "fetching age..."! ;; "age, doubled: " 62! ;; "doing something else important..."!
  • 8. It looks like we would like to execute something once the Future has completed
  • 9. A first glance at imminent (require '[imminent.core :as i])! ! (def age (i/future (do (Thread/sleep 2000)! 31)))! (prn "fetching age...")! (def double-age (i/map age #(* % 2)))! (i/on-success double-age #(prn "age, doubled: " %))! ! (prn "doing something else important...")
  • 10. A first glance at imminent (require '[imminent.core :as i])! ! (def age (i/future (do (Thread/sleep 2000)! 31)))! (prn "fetching age...")! (def double-age (i/map age #(* % 2)))! (i/on-success double-age #(prn "age, doubled: " %))! ! (prn "doing something else important...")! ! ;; "fetching age..."! ;; "doing something else important..."! ;; "age, doubled: " 62!
  • 11. Functor class Functor f where! fmap :: (a -> b) -> f a -> f b
  • 12. Another example (def age (future (do (Thread/sleep 2000)! 31)))! (def name (future (do (Thread/sleep 2000)! "Leonardo")))! (prn "fetching name...")! (prn (format "%s is %s years old" @name @age))! (prn "more important things going on...")
  • 13. Same as before, only this time we want to execute the code once both futures have completed
  • 14. Rewriting it in imminent (def age (i/future (do (Thread/sleep 2000)! 31)))! (def name (i/future (do (Thread/sleep 2000)! "Leonardo")))! (prn "fetching name...")! (def both (i/sequence [name age]))! (i/on-success both ! (fn [[name age]]! (prn (format "%s is %s years old" name age))))! ! (prn "more important things going on...")! !
  • 15. Rewriting it in imminent (def age (i/future (do (Thread/sleep 2000)! 31)))! (def name (i/future (do (Thread/sleep 2000)! "Leonardo")))! (prn "fetching name...")! (def both (i/sequence [name age]))! (i/on-success both ! (fn [[name age]]! (prn (format "%s is %s years old" name age))))! ! (prn "more important things going on...")! ! ;; "fetching name..."! ;; "more important things going on..."! ;; "Leonardo is 31 years old"!
  • 16. Monad class Monad m where! (>>=) :: m a -> (a -> m b) -> m b! return :: a -> m a
  • 17. Monad class Monad m where! (>>=) :: m a -> (a -> m b) -> m b! return :: a -> m a (>>=) is also called bind, flatmap, selectMany and mapcat…
  • 18. Monad - derived functions liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
  • 19. Monad - derived functions liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r (defn mlift2! [f]! (fn [ma mb]! (flatmap ma! (fn [a]! (flatmap mb! (fn [b]! (pure (f a b))))))))
  • 20. Monad - derived functions sequence :: Monad m => [m a] -> m [a]
  • 21. Monad - derived functions sequence :: Monad m => [m a] -> m [a] (defn sequence! [ms]! (reduce (mlift2 conj)! (pure [])! ms))
  • 22. Monad - derived functions mapM :: Monad m => (a -> m b) -> [a] -> m [b]
  • 23. Monad - derived functions mapM :: Monad m => (a -> m b) -> [a] -> m [b] (defn mmap! [f vs]! (sequence (map f vs)))
  • 24. Monad - derived functions mapM :: Monad m => (a -> m b) -> [a] -> m [b] (defn mmap! [f vs]! (sequence (map f vs))) plus a bunch of others…
  • 25. Let’s look at a more concrete example
  • 26. Aggregating movie data (defn cast-by-movie [name]! (future (do (Thread/sleep 5000)! (:cast movie))))! ! (defn movies-by-actor [name]! (do (Thread/sleep 2000)! (->> actor-movies! (filter #(= name (:name %)))! first)))! ! (defn spouse-of [name]! (do (Thread/sleep 2000)! (->> actor-spouse! (filter #(= name (:name %)))! first)))! ! (defn top-5 []! (future (do (Thread/sleep 5000)! top-5-movies)))!
  • 27. The output ({:name "Cate Blanchett",! :spouse "Andrew Upton",! :movies! ("Lord of The Rings: The Fellowship of The Ring - (top 5)"...)}! {:name "Elijah Wood",! :spouse "Unknown",! :movies! ("Eternal Sunshine of the Spotless Mind"...)}! ...)
  • 28. One possible solution (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (pmap movies-by-actor @cast)! spouses (pmap spouse-of @cast)! top-5 (top-5)]! (prn "Fetching data...")! (pprint (aggregate-actor-data spouses movies @top-5)))
  • 29. One possible solution (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (pmap movies-by-actor @cast)! spouses (pmap spouse-of @cast)! top-5 (top-5)]! (prn "Fetching data...")! (pprint (aggregate-actor-data spouses movies @top-5))) ;; "Elapsed time: 10049.334 msecs"
  • 30. We face the same problems as before
  • 32. But the point is not having to do so
  • 33. In imminent (defn cast-by-movie [name]! (i/future (do (Thread/sleep 5000)! (:cast movie))))! ! (defn movies-by-actor [name]! (i/future (do (Thread/sleep 2000)! (->> actor-movies! (filter #(= name (:name %)))! first))))! ! (defn spouse-of [name]! (i/future (do (Thread/sleep 2000)! (->> actor-spouse! (filter #(= name (:name %)))! first))))! ! (defn top-5 []! (i/future (do (Thread/sleep 5000)! top-5-movies)))
  • 34. In imminent (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (i/flatmap cast #(i/map-future movies-by-actor %))! spouses (i/flatmap cast #(i/map-future spouse-of %))! result (i/sequence [spouses movies (top-5)])]! (prn "Fetching data...")! (pprint (apply aggregate-actor-data! (i/dderef (i/await result)))))
  • 35. In imminent (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (i/flatmap cast #(i/map-future movies-by-actor %))! spouses (i/flatmap cast #(i/map-future spouse-of %))! result (i/sequence [spouses movies (top-5)])]! (prn "Fetching data...")! (pprint (apply aggregate-actor-data! (i/dderef (i/await result))))) ;; "Elapsed time: 7087.603 msecs"
  • 37. We can optionally block and wait for a result using the “i/await” function
  • 38. Ok, that’s cool! But what about Applicative Functors?
  • 39. Writing this sucks (defn int-f [n]! (i/future (do (Thread/sleep 2000)! (* 2 n))))! ! ! (-> (i/bind! (int-f 10)! (fn [a] (i/bind! (int-f a)! (fn [b] (i/bind! (int-f b)! (fn [c] ! (i/pure (+ a b c)))))))
  • 40. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f a)! c (int-f b)]! (i/pure (+ a b c)))
  • 41. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f a)! c (int-f b)]! (i/pure (+ a b c))) How long does this computation take?
  • 42. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f a)! c (int-f b)]! (i/pure (+ a b c))) How long does this computation take? ;; "Elapsed time: 6002.39 msecs"
  • 43. What if the computations don’t depend on each other?
  • 44. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f 20)! c (int-f 30)]! (i/pure (+ a b c)))
  • 45. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f 20)! c (int-f 30)]! (i/pure (+ a b c))) How long does this take now?
  • 46. Monadic “do” notation (i/mdo [a (int-f 10)! b (int-f 20)! c (int-f 30)]! (i/pure (+ a b c))) How long does this take now? ;; "Elapsed time: 6002.39 msecs"
  • 47. ?
  • 48. The ‘do-notation’ is used to sequence monadic steps so we remain serial, but still asynchronous
  • 49. Applicative Functor class Functor f => Applicative f where! pure :: a -> f a! (<*>) :: f (a -> b) -> f a -> f b!
  • 50. Previous example, rewritten in applicative style (i/<*> (i/map (int-f 10) (curry + 3))! (int-f 20)! (int-f 30)) ;;"Elapsed time: 2001.509 msecs"
  • 51. It turns out this pattern is a derived function from Applicative Functors
  • 52. Applicative Functor - derived functions liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
  • 53. Applicative Functor - derived functions liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c (defn alift! [f]! (fn [& as]! {:pre [(seq as)]}! (let [curried (curry f (count as))]! (apply <*>! (fmap curried (first as))! (rest as)))))
  • 54. “alift” assumes every Applicative Functor is also Functor
  • 55. Previous example, rewritten using “alift" ;;"Elapsed time: 2001.509 msecs" ((i/alift +) (int-f 10) (int-f 20) (int-f 30))
  • 56. It looks a lot like function application :)
  • 57. Applicatives give us the parallel semantics without giving up convenience
  • 58. Ok, this is awesome! But we have core.async!
  • 59. The movies example, revisited (defn cast-by-movie [name]! (let [c (chan)]! (go (<! (timeout 5000))! (>! c (:cast movie))! (close! c))! c))! ! (defn movies-by-actor [name]! (let [c (chan)]! (go (<! (timeout 2000))! (>! c (->> actor-movies! (filter #(= name (:name %)))! first))! (close! c))! c))
  • 60. The movies example, revisited (defn spouse-of [name]! (let [c (chan)]! (go (<! (timeout 2000))! (>! c (->> actor-spouse! (filter #(= name (:name %)))! first))! (close! c))! c))! ! (defn top-5 []! (let [c (chan)]! (go (<! (timeout 5000))! (>! c top-5-movies)! (close! c))! c))! ! ! (defn async-pmap [f source]! (go (->> (map f (<! source))! async/merge! (async/into [])! <!)))
  • 61. The movies example, revisited (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! m-cast (mult cast)! movies (async-pmap movies-by-actor (tap m-cast (chan)))! spouses (async-pmap spouse-of (tap m-cast (chan)))! top-5 (top-5)]! (prn "Fetching data...")! (pprint (<!! (go (aggregate-actor-data (<! spouses)! (<! movies)! (<! top-5))))))! ! ;; "Elapsed time: 7088.834 msecs"
  • 62. “mult” and “tap” have no business being in that code
  • 63. they are necessary because channels are single-take containers
  • 64. Exceptions core.async swallows them by default (defn movies-by-actor [name]! (let [c (chan)]! (go (<! (timeout 2000))! (throw (Exception. (str "Error fetching movies for actor " name)))! (>! c (->> actor-movies! (filter #(= name (:name %)))! first))! (close! c))! c))
  • 65. Exceptions core.async swallows them by default (defn movies-by-actor [name]! (let [c (chan)]! (go (<! (timeout 2000))! (throw (Exception. (str "Error fetching movies for actor " name)))! (>! c (->> actor-movies! (filter #(= name (:name %)))! first))! (close! c))! c)) (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! m-cast (mult cast)! movies (async-pmap movies-by-actor (tap m-cast (chan)))! spouses (async-pmap spouse-of (tap m-cast (chan)))! top-5 (top-5)]! (prn "Fetching data...")! (pprint (<!! (go (aggregate-actor-data (<! spouses)! (<! movies)! (<! top-5))))))! ! ;; nil - WTF???"
  • 66. Exceptions - workaround (defn throw-err [e]! (when (instance? Throwable e) (throw e))! e)! ! (defmacro <? [ch]! `(throw-err (<! ~ch)))! ! ! (defn movies-by-actor [name]! (let [c (chan)]! (go (try (do (<! (timeout 2000))! (throw (Exception. (str "Error fetching movies for actor " name)))! (>! c (->> actor-movies! (filter #(= name (:name %)))! first))! (close! c))! (catch Exception e! (do (>! c e)! (close! c)))))! c))
  • 67. Exceptions - workaround (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! m-cast (mult cast)! movies (async-pmap movies-by-actor (tap m-cast (chan)))! spouses (async-pmap spouse-of (tap m-cast (chan)))! top-5 (top-5)]! (prn "Fetching data...")! (pprint (<!! (go (try! (aggregate-actor-data (<? spouses)! (<? movies)! (<? top-5))! (catch Exception e! e)))))))
  • 68. A lot of effort for not much benefit
  • 69. Exceptions - the imminent way * * not really unique to imminent. Other libraries use this same approach (defn movies-by-actor [name]! (i/future (do (Thread/sleep 2000)! (throw (Exception. (str "Error fetching movies for actor " name)))! (->> actor-movies! (filter #(= name (:name %)))! first))))
  • 70. Exceptions - the imminent way # 1 (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (i/flatmap cast #(i/map-future movies-by-actor %))! spouses (i/flatmap cast #(i/map-future spouse-of %))! results (i/sequence [spouses movies (top-5)])! _ (prn "Fetching data...")! result (deref (i/await results))]! ! (i/map result #(pprint (apply aggregate-actor-data %)))! (i/map-failure result #(pprint (str "Oops: " %))))) ;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
  • 71. Exceptions - the imminent way # 1 (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (i/flatmap cast #(i/map-future movies-by-actor %))! spouses (i/flatmap cast #(i/map-future spouse-of %))! results (i/sequence [spouses movies (top-5)])! _ (prn "Fetching data...")! result (deref (i/await results))]! ! (i/map result #(pprint (apply aggregate-actor-data %)))! (i/map-failure result #(pprint (str "Oops: " %))))) imminent results are themselves functors :) ;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
  • 72. Exceptions - the imminent way # 2 (let [cast (cast-by-movie "Lord of The Rings: The Fellowship of The Ring")! movies (i/flatmap cast #(i/map-future movies-by-actor %))! spouses (i/flatmap cast #(i/map-future spouse-of %))! result (i/sequence [spouses movies (top-5)])]! (prn "Fetching data...")! (i/on-success result #(prn-to-repl (apply aggregate-actor-data %)))! (i/on-failure result #(prn-to-repl (str "Oops: " %))))! ;; Oops: java.lang.Exception: Error fetching movies for actor Cate Blanchett
  • 73. Final Thoughts Category Theory helps you design libraries with higher code reuse due to well known and understood abstractions
  • 74. Final Thoughts Imminent takes advantage of that and provides an asynchronous and composable Futures library for Clojure
  • 75. Final Thoughts Even when compared to core.async, imminent still makes sense unless you need the low level granularity of channels and/or coordination via queues