Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Hands-On Reactive Programming with Clojure

You're reading from   Hands-On Reactive Programming with Clojure Create asynchronous, event-based, and concurrent applications

Arrow left icon
Product type Paperback
Published in Jan 2019
Publisher Packt
ISBN-13 9781789346138
Length 298 pages
Edition 2nd Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Konrad Szydlo Konrad Szydlo
Author Profile Icon Konrad Szydlo
Konrad Szydlo
Leonardo Borges Leonardo Borges
Author Profile Icon Leonardo Borges
Leonardo Borges
Arrow right icon
View More author details
Toc

Table of Contents (20) Chapters Close

Title Page
Copyright and Credits
About Packt
Contributors
Preface
1. What is Reactive Programming? FREE CHAPTER 2. A Look at Reactive Extensions 3. Asynchronous Programming and Networking 4. Introduction to core.async 5. Creating Your Own CES Framework with core.async 6. Building a Simple ClojureScript Game with Reagi 7. The UI as a Function 8. A New Approach to Futures 9. A Reactive API to Amazon Web Services 10. Reactive Microservices 11. Testing Reactive Apps 12. Concurrency Utilities in Clojure 1. Appendix - The Algebra of Library Design 2. Other Books You May Enjoy Index

Applicative functors


Like functors, applicative functors are a sort of container, and they define two operations:

(defprotocol Applicative 
  (pure [av v]) 
  (fapply [ag av])) 

The pure function is a generic way to put a value inside of an applicative functor. So far, we have been using the option helper function for this purpose. We will be using it a little later.

The fapply function will unwrap the function contained in the applicative ag, and will apply it to the value contained in the applicative av.

The purpose of both of the functions will become clear with an example, but first, we need to promote our option functor to an applicative functor, as follows:

(extend-protocol fkp/Applicative 
  Some 
  (pure [_ v] 
    (Some. v)) 
 
  (fapply [ag av] 
    (if-let [v (:v av)] 
      (Some. ((:v ag) v)) 
      (None.))) 
   None 
  (pure [_ v] 
    (Some. v)) 
 
  (fapply [ag av] 
    (None.))) 

The implementation of pure is the simplest. All it does is wrap the value, v, into an instance of Some. Equally simple is the implementation of fapply for None. As there is no value, we simply return None again.

The fapply implementation of Some ensures that both arguments have a value for the :v keyword; strictly speaking, they both have to be instances of Some. If :v is not nil, it applies the function contained in ag to v, finally wrapping the result. Otherwise, it returns None.

This should be enough to try our first example, using the applicative functor API:

(fkc/fapply (option inc) (option 2)) 
;; #library_design.option.Some{:v 3} 
 
(fkc/fapply (option nil) (option 2)) 
;; #library_design.option.None{} 

We are now able to work with functors that contain functions. Additionally, we have also preserved the semantics of what should happen when any of the functors don't have a value.

We can now revisit the average-age example from before, as follows:

(def age-option (comp (partial fkc/fmap age) option pirate-by-name)) 
 
(let [a (age-option "Jack Sparrow") 
      b (age-option "Blackbeard") 
      c (age-option "Hector Barbossa")] 
  (fkc/<*> (option (fkj/curry avg 3)) 
           a b c)) 
;; #library_design.option.Some{:v 56.666668} 

Note

The vararg function, <*>, is defined by fluokitten and performs a left-associative fapply on its arguments. Essentially, it is a convenience function that makes (<*> f g h) equivalent to (fapply (fapply f g) h).

We start by defining a helper function, to avoid repetition. The age-option function retrieves the age of a pirate for us as an option.

Next, we curry the avg function to 3 arguments and put it into option. Then, we use the <*> function to apply it to the options a, b, and c. We get the same result, but have the applicative functor take care of nil values for us.

Note

Function currying: Currying is the technique of transforming a function of multiple arguments into a higher-order function of a single argument that returns more single-argument functions until all of the arguments have been supplied.

Roughly speaking, currying makes the following snippets equivalent:

(def curried-1 (fkj/curry + 2)) 
(def curried-2 (fn [a] 
                 (fn [b] 
                   (+ a b)))) 
 
((curried-1 10) 20) ;; 30 
((curried-2 10) 20) ;; 30 

Using applicative functors this way is so common that the pattern has been captured as the function alift, as shown in the following code snippet:

 (defn alift 
  "Lifts a n-ary function `f` into a applicative context" 
  [f] 
  (fn [& as] 
    {:pre  [(seq as)]} 
    (let [curried (fkj/curry f (count as))] 
      (apply fkc/<*> 
             (fkc/fmap curried (first as)) 
             (rest as))))) 

The alift function is responsible for lifting a function in such a way that it can be used with applicative functors without much ceremony. Because of the assumptions that we are able to make about applicative functors (for instance, that it is also a functor), we can write generic code that can be reused across any applicatives.

With alift in place, our age average example turns into the following:

(let [a (age-option "Jack Sparrow") 
      b (age-option "Blackbeard") 
      c (age-option "Hector Barbossa")] 
  ((alift avg) a b c)) 
;; #library_design.option.Some{:v 56.666668} 

We lift avg into an applicative-compatible version, making the code look remarkably like a simple function application. And, since we are not doing anything interesting with the let bindings, we can simplify it further, as follows:

((alift avg) (age-option "Jack Sparrow") 
             (age-option "Blackbeard") 
             (age-option "Hector Barbossa")) 
;; #library_design.option.Some{:v 56.666668} 
 
((alift avg) (age-option "Jack Sparrow") 
             (age-option "Davy Jones") 
             (age-option "Hector Barbossa")) 
;; #library_design.option.None{} 

As with functors, we can take the code as it is, and we can simply replace the underlying abstraction, preventing repetition once again:

((alift avg) (i/future (some-> (pirate-by-name "Jack Sparrow") age)) 
             (i/future (some-> (pirate-by-name "Blackbeard") age)) 
             (i/future (some-> (pirate-by-name "Hector Barbossa") age))) 
;; #<Future@17b1be96: #<Success@16577601: 56.666668>> 
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime
Visually different images