Fluokitten Extensions of Clojure Core

This article is a guide to using Clojure core artifacts as implementations of Fluokitten protocols. By requiring org.uncomplicate.fluokitten.jvm namespace in your namespace, you activate Fluokitten’s extensions to Clojure core types so they can act as functors, applicatives, monads etc.

To be able to follow this article, you’ll have to have Clojure installed and Fluokitten library included as a dependency in your project, as described in Getting Started Guide. Obviously, you’ll need a reasonable knowledge of Clojure (you don’t have to be an expert, though). So, after checking out Getting Started Guide, start up Clojure REPL and open this article and we are ready to go.

The complete source code of the examples used in this article is available here in the form of midje tests.

Data structures

The following is a list of common Clojure core data structures and their behavior as various Fluokitten protocols implementations. Generally, any data structure can be considered as a specific context that can hold zero, one or more values.

Functor

fmap behaves in the same way as map for most data structures, but takes care that the resulting data structure is of the same type as the first argument after the function. The number of arguments that the function can accept have to match the number of functors.

(fmap inc [])
;=> []

(fmap inc [1 2 3])
;=> [2 3 4]

(fmap + [1 2] [3 4 5] [6 7 8])
;=> [10 13]

(fmap inc (list))
;=> (list)

(fmap inc (list 1 2 3))
;=> (list 2 3 4)

(fmap + (list 1 2) (list 3 4 5) (list 6 7 8))
;=> (list 10 13)

(fmap inc (empty (seq [1]))) => (empty (seq [2]))

(fmap inc (seq [1 2 3]))
;=> (seq [2 3 4])

(fmap + (seq [1 2]) (seq [3 4 5]) (seq [6 7 8]))
;=> (seq [10 13])

(fmap inc (lazy-seq []))
;=> (lazy-seq [])

(fmap inc (lazy-seq [1 2 3]))
;=> (lazy-seq [2 3 4])

(fmap + (lazy-seq [1 2]) (lazy-seq [3 4 5]) (lazy-seq [6 7 8]))
;=> (lazy-seq [10 13])

(fmap inc #{})
;=> #{}

(fmap inc #{1 2 3})
;=> #{2 3 4}

(fmap + #{1 2} #{3 4 5} #{6 7 8})
;=> #{10 13}

Clojure maps (data structure) have a richer structure than the previously described structures. While map would treat a map structure as a sequence of entries, fmap treats is as a context and just applies a function to the values, while taking care that keys are preserved and matched in the case of a modifying function that takes more than one argument:

(fmap inc {})
;=> {}

(fmap inc {:a 1 :b 2 :c 3})
;=> {:a 2 :b 3 :c 4}

(fmap + {:a 1 :b 2} {:a 3 :b 4 :c 5} {:a 6 :b 7 :c 8})
;=> {:a 10 :b 13 :c 13}

Map entries are functors, too. The modifying function is applied to the value(s) of map entry or entries, and the key of the first entry will be the key of the result:

(fmap inc (first {:a 1}))
;=> (first {:a 2})

(fmap + (first {:a 1})
        (first {:a 3})
        (first {:b 6}))
;=> (first {:a 10})

Finally, Clojure’s reducibles are also properly handled by fmap (but it can only accept one reducible at a time):

(into [] (fmap inc (r/map identity  [1 2 3])))
;=> [2 3 4]

(into [] (fmap + (r/map identity [1 2])
                 (r/map identity [3 4 5])
                 (r/map identity [6 7 8])))
;=> (throws UnsupportedOperationException)

Applicative

For most core structures, pure produces a collection of the required type, with the supplied value(s) as its element(s):

(pure [4 5] 1)
;=> [1]

(pure [4 5] 1 2 3)
;=> [1 2 3]

(pure (list) 1)
;=> (list 1)

(pure (seq [3]) 1)
;=> (seq [1])

(pure (lazy-seq [4]) 1)
;=> (lazy-seq [1])

(pure #{} 1)
;=> #{1}

For maps and map entries, pure uses nil to generate the default key for the pure entry:

(pure {} 1)
;=> {nil 1}

(pure (first {1 1}) 1)
;=> (first {nil 1})

If you provide enough elements to pure, it will put them in the appropriate structure.

(pure {} 1 2 3 4)
;=> {1 2 3 4}

pure handles Clojure reducibles:

(into (list) (pure (r/map identity [2 3]) 1))
 => (list 1)

We can establish an implicit context with the utils/with-context macro, and use return or unit functions (they are the same) instead of pure. return ant unit call pure with the implicit context.

(with-context []
  (return 1))
;=> [1]

(with-context {}
  (return 1))
;=> {nil 1}

fapply is pretty straightforward for most structures, except for maps and map entries, where it has a more complex behavior. It expect two structures of the same type as arguments: a function(s) structure and a data structure. Then it applies the functions to the data. The actual matching of the functions and the data depends on the data structure. For most core structures, it applies all combinations of functions and data:

(fapply [] [])
;=> []

(fapply [] [1 2 3])
;=> []

(fapply [inc dec] [])
;=> []

(fapply [inc dec] [1 2 3])
;=> [2 3 4 0 1 2]

(fapply [+ *] [1 2 3] [4 5 6])
;=> [5 7 9 4 10 18]

(fapply (list) (list))
;=> (list)

(fapply (list) (list 1 2 3))
;=> (list)

(fapply (list inc dec) (list))
;=> (list)

(fapply (list inc dec) (list 1 2 3))
;=> (list 2 3 4 0 1 2)

(fapply (list + *) (list 1 2 3) (list 4 5 6))
;=> (list 5 7 9 4 10 18)

(fapply (empty (seq [2])) (empty (seq [3])))
;=> (empty (seq [1]))

(fapply (empty (seq [33])) (seq [1 2 3]))
;=> (empty (seq [44]))

(fapply (seq [inc dec]) (empty (seq [1])))
;=> (empty (seq [3]))

(fapply (seq [inc dec]) (seq [1 2 3]))
;=> (seq [2 3 4 0 1 2])

(fapply (seq [+ *]) (seq [1 2 3]) (seq [4 5 6]))
;=> (seq [5 7 9 4 10 18])

(fapply (lazy-seq []) (lazy-seq []))
;=> (lazy-seq [])

(fapply (lazy-seq []) (lazy-seq [1 2 3]))
;=> (lazy-seq [])

(fapply (lazy-seq [inc dec]) (lazy-seq []))
;=> (lazy-seq [])

(fapply (lazy-seq [inc dec]) (lazy-seq [1 2 3]))
;=> (lazy-seq [2 3 4 0 1 2])

(fapply (lazy-seq [+ *])
        (lazy-seq [1 2 3])
        (lazy-seq [4 5 6]))
;=> (lazy-seq [5 7 9 4 10 18])

(fapply #{} #{})
;=> #{}

(fapply #{} #{1 2 3})
;=> #{}

(fapply #{inc dec} #{})
;=> #{}

(fapply #{inc dec} #{1 2 3})
;=> #{2 3 4 0 1}

(fapply #{+ *} #{1 2 3} #{4 5 6})
;=> #{5 7 9 4 10 18}

In the case of maps and map entries, it matches the functions and the data by map keys, while the nil key serves as a universal key, whose function applies to any data that does not have the specific matching function with the same key:

(fapply {} {})
;=> {}

(fapply {} {:a 1 :b 2 :c 3})
;=> {:a 1 :b 2 :c 3}

(fapply {:a inc} {})
;=> {}

(fapply {:a inc :b dec nil (partial * 10)}
        {:a 1 :b 2 :c 3 :d 4 nil 5})
;=> {:a 2 :b 1 :c 30 :d 40 nil 50}

(fapply {nil /  :a + :b *} {:a 1 :c 2} {:a 3 :b 4} {:c 2 :d 5})
;=> {:a 4 :b 4 :c 1 :d 1/5}

(fapply (first {:a inc}) (first {:b 1}))
;=> (first {:b 1})

(fapply (first {:a inc}) (first {:a 1}))
;=> (first {:a 2})

(fapply (first {nil inc}) (first {:a 1}))
;=> (first {:a 2})

(fapply (first {nil inc}) (first {nil 1}))
;=> (first {nil 2})

Monad

Similarly to the previous examples, bind works as expected for most core data structures, except maps and map entries that are a bit specific. It requires one or more data structures, and a function as the last parameter. That function should accept elements extracted from all provided data structures (one by one), and wrap the result(s) in the same type of structure. The bind function is automatically and transparently aware of the implicit context, so we can use return or unit to create agnostic monadic functions that can be applied to any monad. In the following example, the increment and add functions’ results will be packaged in appropriate contexts transparently.

Here are typical examples of sequences and friends:

(def increment (comp return inc))
(def add (comp return +))

(bind [] increment)
;=> []

(bind [1 2 3] increment)
;=> [2 3 4]

(bind [1 2 3] [4 5 6] add)
;=> [5 7 9]

(bind (list) increment)
;=> (list)

(bind (list 1 2 3) increment)
;=> (list 2 3 4)

(bind (list 1 2 3) (list 4 5 6) add)
;=> (list 5 7 9)

(bind (empty (seq [2])) increment)
;=> (empty (seq [3]))

(bind (seq [1 2 3]) increment) => (seq [2 3 4])

(bind (seq [1 2 3]) (seq [4 5 6]) add)
;=> (seq [5 7 9])

(bind (lazy-seq []) increment)
;=> (lazy-seq [])

(bind (lazy-seq [1 2 3]) increment)
;=> (lazy-seq [2 3 4])

(bind (lazy-seq [1 2 3]) (lazy-seq [4 5 6]) add)
;=> (lazy-seq [5 7 9])

(bind #{} increment)
;=> #{}

(bind #{1 2 3} increment)
;=> #{2 3 4}

(bind #{1 2 3} #{4 5 6} add)
;=> #{5 7 9}

With maps and map entries, bind treats keys as parts of the context, and feeds only the values of the corresponding entries to the function. Since the function returns a map for each entry, the results have to be flattened, so the final result is a map. It is easier to understand what happens with a few examples:

(bind {} #(hash-map :x %)) => {}

(bind {:a 1} #(hash-map :increment (inc %)))
;=> {[:a :increment] 2}

(bind {:a 1  :b 2 :c 3} #(hash-map :increment (inc %)))
;=> {[:a :increment] 2 [:b :increment] 3 [:c :increment] 4}

(bind {:a 1} {:a 2 :b 3} {:b 4 :c 5} (fn [& args] {:sum (apply + args)}))
;=> {[:a :sum] 3 [:b :sum] 7 [:c :sum] 5}

So, the function takes the corresponding values for each key from each map, feed it to the function, which returns the result wrapped in a context of a map. Then, all the resulting maps are joined by pairing the key of the input and the key of the output for a particular value.

Map entries do not care about the keys when feeding the values to the function. Only the key of the first map entry is used as the key of the result:

(bind (first {:a 1}) #(first {:increment (inc %)}))
=> (first {[:a :increment] 2})

(bind (first {:a 1}) (first {:a 2}) (first {:b 4}) (fn [& args] (first {:sum (apply + args)})))
=> {[:a :sum] 8}

The join function flattens one nesting level of the the data structure if it contains nested data structures of the same type, in a similar way as clojure’s flatten function (but only one level deep), for all collections except maps. For maps, it have to take account of the nesting of the map’s keys as parts of the context, by creating a vector of all the keys that were flattened and using it as the key for the value of the flattened entry, as shown in the following examples:

(join [[1 2] [3 [4 5] 6]])
;=> [1 2 3 [4 5] 6]

(join (list (list 1 2) (list 3 (list 4 5) 6)))
;=> (list 1 2 3 (list 4 5) 6)

(join (seq (list (list 1 2) (list 3 (list 4 5) 6))))
;=> (seq (list 1 2 3 (list 4 5) 6))

(join (lazy-seq (list (list 1 2) (list 3 (list 4 5) 6))))
;=> (lazy-seq (list 1 2 3 (list 4 5) 6))

(join #{#{1 2} #{3 #{4 5} 6}})
;=> #{1 2 3 #{4 5} 6}

(join {:a 1 :b {:c 2 :d {:e 3}}})
;=> {:a 1 [:b :c] 2 [:b :d] {:e 3}}

(join (first {:a (first {:b 1})}))
;=> (first {[:a :b] (first {:c 1})})

Magma

op is a pretty straightforward for Clojure core data structures - it is similar to the concat function. The difference is that concat turns everything into lazy sequences while op preserves the type of the data structure. op is associative for all Clojure core data structures, so they form semigroups.

(op [1 2 3] [4 5 6])
;=> [1 2 3 4 5 6]

(op [1 2 3] [4 5 6] [7 8 9] [10 11 12])
;=> [1 2 3 4 5 6 7 8 9 10 11 12]

(op (list 1 2 3) (list 4 5 6))
;=> (list 1 2 3 4 5 6)

(op (list 1 2 3) (list 4 5 6) (list 7 8 9) (list 10 11 12))
;=> (list 1 2 3 4 5 6 7 8 9 10 11 12)

(op (lazy-seq [1 2 3]) (lazy-seq [4 5 6]))
;=> (lazy-seq [1 2 3 4 5 6])

(op (lazy-seq [1 2 3]) (lazy-seq [4 5 6])
    (lazy-seq [7 8 9]) (lazy-seq [10 11 12]))
;=> (lazy-seq [1 2 3 4 5 6 7 8 9 10 11 12])

(op (seq [1 2 3]) (seq [4 5 6]))
;=> (seq [1 2 3 4 5 6])

(op (seq [1 2 3]) (seq [4 5 6])
    (seq [7 8 9]) (seq [10 11 12]))
;=> (seq [1 2 3 4 5 6 7 8 9 10 11 12])

(op #{1 2 3 6} #{4 5 6})
;=> #{1 2 3 4 5 6}

(op #{1 2 3 6} #{4 5 6} #{7 8 9} #{10 11 12})
;=> #{1 2 3 4 5 6 7 8 9 10 11 12}

(op {:a 1 :b 2} {:a 3 :c 4})
;=> {:a 3 :b 2 :c 4}

(op {:a 1 :b 2} {:a 3 :c 4} {:d 5} {:e 6})
;=> {:a 3 :b 2 :c 4 :d 5 :e 6}

(op (first {:a 1}) (first {:b 2}))
;=> (first {:ab 3})

(op (first {:a 1}) (first {:b 2}) (first {:b 3}))
;=> (first {:abb 6})

Monoid

id for Clojure’s data structures’ op is an empty structure of the same type as the id’s argument:

(id [2])
;=> []

(id (list 4 5 6))
;=> (list)

(id (seq [1 2]))
;=> (empty (seq [2]))

(id (lazy-seq [1 23]))
;=> (lazy-seq [])

(id #{2 3})
;=> #{}

(id {:1 2})
;=> {}

(id (first {:a 1}))
;=> [(keyword "") 0]

Foldable

The fold function aggregates the content of the core data structures into one value. All elements in the data structure must belong to the same monoid, i.e. they have the same type that implements Monoid protocol, or of types compatible with the first element’s type for the Magma’s op function. It is similar to Clojure’s reduce, but does not require the reduction function (since Monoid’s op is used).

(fold [1 2 3 4 5 6])
=> 21

(fold (list "a" "b" "c"))
;=> "abc"

(fold (seq [:a :b :c]))
;=> :abc

(fold (lazy-seq [[1] (list 2) (seq [3]])))
;=> [1 2 3]

(fold #{1 2 3})
;=> 6

(fold {:a 1 :b 2 :c 3})
;=> 6

(fold (first {:a 1}))
;=> 1

The fold function does not have to use op for folding (reducing). If you supply the function and an init value, it works as you’d expect from reduce.

(fold + 3 [1 2 3])
;=> 9

fold can handle multiple foldable data structures. In that case, it works as Clojure’s reduce on steroids - the folding (reduction) function is applied to the result of applying op to the corresponding elements of supplied collections.

 (fold + 0 [1 2 3] [4 5 6])
 ;=> 21

 (fold * 1 [1 2 3] [4 5 6])
 ;=> 315

 (fold * 2 [1 2 3] [4 5 6] [7 8 9] [1 2 1] [3 2 1])
 ;=> 12160

Sometimes the collection(s) that we would like to fold hold elements that are not Magmas - they do not have op. In that case, we can fold them with foldmap, which is a variant of fold that accept an additional function, which is applied to the elements of the collection being folded before folding. Of course, foldmap is useful even when elements have op - when we need to use some alternative function instead of op.

(foldmap str [1 2 3 4 5 6])
;=> "123456"

 (foldmap (fn [^String s] (.toUpperCase s))
          (list "a" "b" "c"))
 ;=> "ABC"

 (foldmap str (seq [:a :b :c]))
 ;=> ":a:b:c"

 (foldmap str {:a 1 :b 2 :c 3})
 ;=> "123"

 (foldmap str (first {:a 1}))
 ;=> "1"

 (foldmap + 3 inc [1 2 3])
 ;=> 12

 (foldmap * 5 / [1 2 3] [4 5 6])
 ;=> 1/4

 (foldmap * 2 / [1 2 3] [4 5 6] [7 8 9] [1 2 1] [3 2 1])
 ;=> 1/60480

Objects

Each Java object is a trivial implementation of Functor and Foldable, where the object is its own trivial context, unless there is a more specific implementation.

Functor

fmap simply applies the provided function to the object.

(fmap str (Object.))
;=> (str (Object.))

Foldable

fold returns the object itself:

(fold (Object.))
;=> (Object.)

String

String is an implementation of several Fluokitten protocols. The implementations help with many string functions that treat strings as sequences, so they assume that the string is in the sequence context.

Functor

fmap applies the function on the string, and then makes sure that the result is converted to string.

(fmap reverse "loWercase")
;=> "esacreWol"

Magma

op is equivalent to str function.

(op "some" "thing" " " "else")
;=> "something else"

Monoid

id returns an empty string.

(id "something")
;=> ""

Foldable

fold returns the string itself, as with any plain object.

(fold "something")
;=> "something"

Keyword

Keywords are functors, magmas, monoids, and foldables.

Functor

fmap applies the function to the keyword’s name, and then coerces the result to a keyword.

(fmap reverse :something)
;=> :gnihtemos

Magma

op aggregates names of all its keyword arguments to a new keyword.

(op :some :thing :else)
;=> :somethingelse

Monoid

id is an empty keyword.

(id :something)
;=> (keyword "")

Foldable

fold returns the keyword itself, as with plain objects.

(fold :something)
;=> :something

Numbers

Numbers are functors, magmas, monoids and foldables.

Functor

fmap works as with any plain object, by applying the function directly to the argument(s).

(fmap + 1 2 3)
;=> 6

Magma

op for numbers is the addition (+) function.

(op 1 2 3)
;=> 6

Monoid

id for addition is 0.

(id 4)
;=> 0

Foldable

fold simply returns its argument.

(fold 5)
;=> 5

Functions

In Haskell, functions are functors, monads, and many other categorical types. However, with Clojure functions, there is one big caveat: ordinary Clojure functions are not automatically curried, so the well known implementations of most categorical types, when translated to Clojure, do not obey all laws that monads, applicatives, etc. have to observe. Fortunately, Fluokitten provides the curry function that creates a curried version of any Clojure function.

While both ordinary Clojure functions and those curried by curry implement Fluokitten protocols, and can be used with all appropriate functions from Fluokitten core, only curried functions satisfy all appropriate laws. Most of the time, you can use fluokitten on ordinary functions regardless. However, be warned that those are not “kosher” and “halal” from the point of view of Haskellers, and may not be able to give the full benefit of “monadic” programming. The most obvious case would be if you translate examples that rely on Haskell’s currying.

Functor

Both plain and curried functions implement the Functor protocol. fmap composes its function arguments:

((fmap inc +) 1 2 3)
;=> ((comp inc +) 1 2 3)

What’s more, when used with multiple arguments, fmap offers a more advanced composition unavailable in Clojure:

((fmap + * /) 1 2 3)
;=> (+ (* 1 2 3) (/ 1 2 3))

Curried functions work in the following way, compared to plain functions:

(inc)
;=> clojure.lang.ArityException

((curry inc))
;=> a curried inc function

In the following example, we create two functions by using fmap. inc+ is a plain Clojure function created by applying the inc function to the result of applying the + function, in context. It first sums its arguments and then increments the sum. cinc+ does the same thing, but if called with insufficient number of arguments (two in this example, may be created with any arity) it fixes the provided arguments and creates a new curried function that expects the missing ones. This is in contrast with the plain function, which either accepts any number of arguments, or throws a Clojure compiler exception if called with less than minimal number of arguments.

(let [inc+ (fmap inc +)
      cinc+ (fmap inc (curry +))]

   (inc+ 1 2 3)
   ;=> 7

   (inc+ 1)
   ;=> 2

   (cinc+ 1 2 3)
   ;=> 7

   ((cinc+ 1) 2)
   ;=> 4

   ((inc+ 1) 2)
   ;=> (throws ClassCastException))

Called with multiple arguments, fmap creates appropriate compositions, depending of whether it is called with plain or curried argumens:

(let [inc*3+2 (fmap inc (partial * 3) (partial + 2))
      cinc*3+2 (fmap inc ((curry *) 3) ((curry +) 2))]

   (inc*3+2 7 3 1)
   ;=> 40

   (cinc*3+2 7 3 1)
   ;=> 40

   ((inc*3+2) 2)
   ;=> (throws ClassCastException)

   ((cinc*3+2) 2)
   ;=> 13

Applicative

pure creates a function that ignores its arguments and returns the content of its context.


(((pure identity inc)) 1)
;=> 2

((pure curried c+) 13 2)
;=> c+

The behavior of funciton as Applicatives is a bit tricky to understand, so we give a simple example that is clear once you understand how fapply works with (curried) functions, but if this is the first time you are reading about this, we recommend that you check out a chapter 11 from Learn You a Haskell for Great Good, which is dedicated to this topic.


((<*> (pure identity inc) (pure identity 1)))
;=> 2

(((pure curried inc) 100) 1)
;=> 2

((fapply (fapply (pure curried c+) (c+ 3)) (c* 100)) 6)
;=> 609

((<*> (pure curried c+) (c+ 3) (c* 100)) 6)
;=> 609

Monad

As in the case of applicatives, the behavior of bind, you need some experience to understand how it works with (curried) functions, so we recommend that you check out Learn You a Haskell for Great Good, and just give an example here for the reference.

((bind (curry +) (curry *)) 3 4)
;=> 84

Magma

Both plain and curried functions are magmas - op composes its arguments.

Monoid

id for plain functions is the identity function, while for curried functions it is curried identity function cidentity.

Foldable

Plain functions are foldable just as any object is - fold returns its argument. With curried functions, fold returns the plain function that was curried by the curry function.

References

Clojure synchronous references (atoms and refs) implement all categorical protocols. The reference is the (mutable!) context that hold an immutable value. Keep in mind that in this case, the preferred functions that work on the content are mutable (fmap!, fapply! etc.) , since they change the reference, instead of producing a new one (fmap etc.).

First, we describe regular fmap, fapply etc., but keep in mind that these do not make much sense with references, since the point of references is that they should mutate instead of producing new instances.

Functor

With refs and atoms, fmap is analogous to alter and swap!. The main difference from these, reference specific methods is that it returns the updated reference (context) itself, not the new value, consistently with other fmap implementations described earlier.

(fmap inc (atom 3))
;=> (atom 4)

(dosync (fmap inc (ref 5)))
;=> (ref 6)

(fmap + (atom 3) (atom 4) (ref 5))
;=> (atom 12)

(dosync (fmap + (ref 5) (atom 6) (ref 7)))
;=> (ref 18))

Applicative

pure creates new minimal reference context:

(pure (atom 8) 1)
;=> (atom 1)

(pure (ref 8) 2)
;=> (ref 2)

fapply expects a function and its argument to be inside references, extracts their contents, and updates the first value reference with the result of applying the function to the values.

(fapply (atom inc) (atom 1))
;=> (atom 2)

(dosync (fapply (ref inc) (ref 2)))
;=> (ref 3)

(fapply (atom +) (atom 1) (ref 2) (atom 3))
;=> (atom 6)

(dosync (fapply (ref +) (ref 1) (atom 2) (atom 3)))
;=> (check-eq (ref 6))

Monad

bind accepts variable number of arguments of which all are references except the last, which is a function that takes plain values and produces a reference. bind updates the first reference using this function.

(bind (atom 8) increment)
;=> (atom 9)

(dosync (bind (ref 8) increment))
;=> (ref 9)

(bind (atom 8) (ref 9) (atom 10) add)
;=> (atom 27)

(dosync (bind (ref 18) (ref 19) (atom 20) add))
;=> (ref 57)

join flattens the nested reference one level deep.

(join (atom (ref (atom 33))))
;=> (atom (atom 33))

(dosync (join (ref (ref (atom 33)))))
;=> (ref (atom 33))

Magma

Both refs and atoms form a semigroup with op working on their contents, as in the following example:

(op (atom 3) (atom 4))
;=> (atom 7)

(op (ref "some") (ref "thing"))
;=> (ref "something")

(op (atom 1) (ref 2) (atom 3))
;=> (atom 6)

Monoid

id produces a reference that holds the identity element of the monoid provided in the prototype argument:

(id (atom nil))
;=> (atom nil)

(id (ref 8))
;=> (ref 0)

(id (ref "something"))
;=> (ref "")

Foldable

fold extracts the content of the reference.

(fold (atom "something"))
;=> "something"

(fold (ref 2))
;=> 2

Mutable functions


 (let [a (atom 3)]
   (fmap! inc a) => a)

 (let [r (ref 5)]
   (dosync
    (fmap! inc r) => r))

 (fmap! + (atom 3) (atom 4) (ref 5)) => (check-eq (atom 12))

 (dosync
  (fmap! + (ref 5) (atom 6) (ref 7)) => (check-eq (ref 18)))

 (fapply! (atom inc) (atom 1)) => (check-eq (atom 2))

 (dosync (fapply! (ref inc) (ref 2))) => (check-eq (ref 3))

 (fapply! (atom +) (atom 1) (ref 2) (atom 3)) => (check-eq (atom 6))

 (dosync (fapply! (ref +) (ref 1) (atom 2) (atom 3)))
 => (check-eq (ref 6))

 (join! (atom (ref (atom 33)))) => (check-eq (atom (atom 33)))

 (dosync (join! (ref (ref (atom 33)))))
 => (check-eq (ref (atom 33)))

 (bind! (atom 8) increment) => (check-eq (atom 9))

 (dosync (bind! (ref 8) increment))
 => (check-eq (ref 9))

 (bind! (atom 8) (ref 9) (atom 10) add)
 => (check-eq (atom 27))

 (dosync (bind! (ref 18) (ref 19) (atom 20) add))
 => (check-eq (ref 57)

Tell Us What You Think!

Please take some time to tell us about your experience with the library and this site. Let us know what we should be explaining or is not clear enough. If you are willing to contribute improvements, even better!