cats.core
Category Theory abstractions for Clojure
->=
macro
(->= expr & forms)
Like ->
, but with monadic binding instead of pure application. A mnemonic for the name is a pun on >>=
, the monadic bind operator, and clojure’s regular arrow macros.
You can think of it as generalizing the some->
thread macro to all Monads instead of just Maybe.
Alternatively, if you think of the regular thread macro as sugar for let
:
(-> :a b (c (other args)) d) => (let [res (b :a) res (c res (other args)) res (d res)] res)
Then ->=
is sugar for cats.core/mlet:
(->= m-a b (c (other args)) d) (mlet [res m-a res (c res (other args)) res (d res)] (return res))
Note that extra args in this context are assumed pure, and will be evaluated along with the function itself; this also matches the behavior of some->
wrt extra args.
Threading through pure functions is somewhat awkward, but can be done:
(->= m-a monadic-fn (-> pure-fn other-pure-fn m/return) other-monadic-fn)
->>=
macro
(->>= expr & forms)
Like ->>, but with monadic binding instead of pure application. See cats.labs.sugar/->=
for more in-depth discussion.
<=<
(<=< mg mf x)
Right-to-left composition of monads. Same as >=>
with its first two arguments flipped.
>>
(>> mv mv')
(>> mv mv' & mvs)
Perform a Haskell-style left-associative bind, ignoring the values produced by the monadic computations.
>>=
(>>= mv f)
(>>= mv f & fs)
Perform a Haskell-style left-associative bind.
Let’s see it in action:
(>>= (just 1) (comp just inc) (comp just inc))
;; => #<Just [3]>
alet
macro
(alet bindings & body)
Applicative composition macro similar to Clojure’s let
. This macro facilitates composition of applicative computations using fmap
and fapply
and evaluating applicative values in parallel.
Let’s see an example to understand how it works. This code uses fmap for executing computations inside an applicative context:
(fmap (fn [a] (inc a)) (just 1)) ;=> #<Just [2]>
Now see how this code can be made clearer by using the alet macro:
(alet [a (just 1)] (inc a)) ;=> #<Just [2]>
Let’s look at a more complex example, imagine we have dependencies between applicative values:
(join (fapply (fmap (fn [a] (fn [b] (fmap (fn [c] (inc c)) (just (+ a b))))) (just 1)) (just 2))) ;=> #<Just [4]>
This is greatly simplified using alet
:
(alet [a (just 1) b (just 2) c (just (+ a b))] (inc c)) ;=> #<Just [4]>
The intent of the code is much clearer and evaluates a
and b
at the same time, then proceeds to evaluate c
when all the values it depends on are available. This evaluation strategy is specially helpful for asynchronous applicatives.
ap
macro
(ap f & args)
Apply a pure function to applicative arguments, e.g.
(ap + (just 1) (just 2) (just 3)) ;; => #<Just [6]> (ap str [“hi” “lo”] [“bye” “woah” “hey”]) ;; => [“hibye” “hiwoah” “hihey” “lobye” “lowoah” “lohey”]
ap
is essentially sugar for (apply fapply (pure f) args)
, but for the common case where you have a pure, uncurried, possibly variadic function.
ap
actually desugars in alet
form:
(macroexpand-1 `(ap + (just 1) (just2))) ;; => (alet [a1 (just 1) a2 (just 2)] (+ a1 a2))
That way, variadic functions Just Work, without needing to specify an arity separately.
If you’re familiar with Haskell, this is closest to writing “in Applicative style”: you can straightforwardly convert pure function application to effectful application by with some light syntax (<$> and <*> in case of Haskell, and ap
here).
See the original Applicative paper for more inspiration: http://staff.city.ac.uk/~ross/papers/Applicative.pdf
ap->
macro
(ap-> x & forms)
Thread like ->
, within an applicative idiom.
Compare:
(macroexpand-1 `(-> a b c (d e f))) => (d (c (b a) e f)
with:
(macroexpand-1 `(ap-> a b c (d e f)) => (ap d (ap c (ap b a) e f))
ap->>
macro
(ap->> x & forms)
Thread like ->>
, within an applicative idiom. See cats.labs.sugar/ap->
for more in-depth discussion.
as->=
macro
(as->= expr name & forms)
Like as->
, but with monadic binding instead of pure application. See cats.labs.sugar/->=
for more in-depth discussion.
as-ap->
macro
(as-ap-> expr name & forms)
Thread like as->
, within an applicative idiom. See cats.labs.sugar/ap->
for more in-depth discussion.
bimap
(bimap f g)
(bimap f g bv)
Map over both arguments at the same time.
Given functions f
and g
and a value wrapped in a bifunctor bv
, apply f
to a first argument or g
to a second argument.
(bimap dec inc (either/right 1)
;; => #<Right 2>
(bimap dec inc (either/left 1)
;; => #<Left 0>
bind
(bind mv f)
Given a monadic value mv
and a function f
, apply f
to the unwrapped value of mv
.
(bind (either/right 1) (fn [v]
(return (inc v))))
;; => #<Right [2]>
For convenience, you may prefer to use the mlet
macro, which provides a beautiful, let
-like syntax for composing operations with the bind
function.
curry
macro
(curry f)
(curry n f)
Given either a fixed arity function or an arity and a function, return another which is curried.
With inferred arity (function must have one fixed arity):
(defn add2 [x y] (+ x y))
(def cadd2 (curry add2))
((cadd2 1) 3)
;; => 4
(cadd2 1 3)
;; => 4
With given arity:
(def c+ (curry 3 +))
((c+ 1 2) 3)
;; => 6
((((c+) 1) 2) 3)
;; => 6
fapply
(fapply af & avs)
Given a function wrapped in a monadic context af
, and a value wrapped in a monadic context av
, apply the unwrapped function to the unwrapped value and return the result, wrapped in the same context as av
.
This function is variadic, so it can be used like a Haskell-style left-associative fapply.
filter
(filter p mv)
Apply a predicate to a value in a MonadZero
instance, returning the identity element when the predicate does not hold.
Otherwise, return the instance unchanged.
(require '[cats.monad.maybe :as maybe])
(require '[cats.core :as m])
(m/filter (partial < 2) (maybe/just 3))
;=> <Just [3]>
(m/filter (partial < 4) (maybe/just 3))
;=> <Nothing>
fmap
(fmap f)
(fmap f fv)
Apply a function f
to the value wrapped in functor fv
, preserving the context type.
foldm
(foldm f z xs)
(foldm ctx f z xs)
Given an optional monadic context, a function that takes two non-monadic arguments and returns a value inside the given monadic context, an initial value, and a collection of values, perform a left-associative fold.
(require '[cats.context :as ctx]
'[cats.core :as m]
'[cats.monad.maybe :as maybe])
(defn m-div [x y]
(if (zero? y)
(maybe/nothing)
(maybe/just (/ x y))))
(m/foldm m-div 1 [1 2 3])
(m/foldm maybe/context m-div 1 [1 2 3])
;; => #<Just 1/6>
(m/foldm maybe/context m-div 1 [1 0 3])
;; => #<Nothing>
(foldm m-div 1 [])
;; => Exception
(m/foldm maybe/context m-div 1 [])
(ctx/with-context maybe/context
(foldm m-div 1 []))
;; => #<Just 1>
forseq
(forseq vs mf)
Same as mapseq
but with the arguments flipped.
Let’s see a little example:
(m/forseq [2 3] maybe/just)
;; => <Just [[2 3]]>
Yet an other example that fails:
(m/forseq [1 2]
(fn [v]
(if (odd? v)
(maybe/just v)
(maybe/nothing))))
;; => <Nothing>
left-map
(left-map f)
(left-map f bv)
Map covariantly over the first argument.
Given a function f
and a value wrapped in a bifunctor bv
, apply f
to the first argument, if present, otherwise leave bv
unchanged.
(left-map dec (either/right 1)
;; => #<Right 1>
(left-map dec (either/left 1)
;; => #<Left 0>
lift-a
macro
(lift-a f)
(lift-a n f)
Lift a function with a given fixed arity to an applicative context.
(def app+ (lift-a 2 +))
(app+ (maybe/just 1) (maybe/just 2)) ;; =>
(app+ (maybe/just 1) (maybe/nothing)) ;; =>
(app+ [0 2 4] [1 2]) ;; => [1 2 3 4 5 6]
lift-m
macro
(lift-m f)
(lift-m n f)
Lift a function with a given fixed arity to a monadic context.
(def monad+ (lift-m 2 +))
(monad+ (maybe/just 1) (maybe/just 2)) ;; => <Just [3]>
(monad+ (maybe/just 1) (maybe/nothing)) ;; =>
(monad+ [0 2 4] [1 2]) ;; => [1 2 3 4 5 6]
mapseq
(mapseq mf coll)
Given a function mf
that takes a value and puts it into a monadic context, and a collection, map mf
over the collection, calling sequence
on the results.
(require '[cats.context :as ctx]
'[cats.monad.maybe :as maybe]
'[cats.core :as m])
(m/mapseq maybe/just [2 3])
;=> <Just [[2 3]]>
(m/mapseq (fn [v]
(if (odd? v)
(maybe/just v)
(maybe/nothing)))
[1 2])
;; => #<Nothing>
(ctx/with-context maybe/context
(mapseq #(maybe/just (* % 2)) []))
;; => #<Just [()]>
mlet
macro
(mlet bindings & body)
Monad composition macro that works like Clojure’s let
. This facilitates much easier composition of monadic computations.
Let’s see an example to understand how it works. This code uses bind to compose a few operations:
(bind (just 1)
(fn [a]
(bind (just (inc a))
(fn [b]
(return (* b 2))))))
;=> #<Just [4]>
Now see how this code can be made clearer by using the mlet macro:
(mlet [a (just 1)
b (just (inc a))]
(return (* b 2)))
;=> #<Just [4]>
pure
(pure v)
(pure ctx v)
Given any value v
, return it wrapped in the default/effect-free context.
This is a multi-arity function that with arity pure/1
uses the dynamic scope to resolve the current context. With pure/2
, you can force a specific context value.
Example:
(with-context either/context
(pure 1))
;; => #<Right [1]>
(pure either/context 1)
;; => #<Right [1]>
right-map
(right-map g)
(right-map g bv)
Map covariantly over the second argument.
Given a function g
and a value wrapped in a bifunctor bv
, apply g
to the second argument, if present, otherwise leave bv
unchanged.
(right-map inc (either/right 1)
;; => #<Right 2>
(right-map inc (either/left 1)
;; => #<Left 1>
sequence
(sequence mvs)
Given a collection of monadic values, collect their values in a seq returned in the monadic context.
(require '[cats.context :as ctx]
'[cats.monad.maybe :as maybe]
'[cats.core :as m])
(m/sequence [(maybe/just 2) (maybe/just 3)])
;; => #<Just [[2, 3]]>
(m/sequence [(maybe/nothing) (maybe/just 3)])
;; => #<Nothing>
(ctx/with-context maybe/context
(m/sequence []))
;; => #<Just [()]>
traverse
(traverse f tv)
(traverse ctx f tv)
Map each element of a structure to an action, evaluate these actions from left to right, and collect the results.
(defn inc-if-even
[n]
(if (even? n)
(maybe/just (inc n))
(maybe/nothing)))
(ctx/with-context maybe/context
(m/traverse inc-if-even [2 4]))
;; => #<Just [3 4]>
unless
macro
(unless b mv)
(unless ctx b mv)
Given an expression and a monadic value, if the expression is not logical true, return the monadic value. Otherwise, return nil in a monadic context.
when
macro
(when b mv)
(when ctx b mv)
Given an expression and a monadic value, if the expression is logical true, return the monadic value. Otherwise, return nil in a monadic context.