Composition and point-free style
IntermediatetheoryComposing functions is the basic move of functional programming: you build big behaviors by combining small ones. Clojure ships three core combinators — compclojure.core/compRight-to-left function composition: (comp f g) = #(f (g %)).view on clojuredocs →, partialclojure.core/partialPre-fill leading arguments and return a new function.view on clojuredocs →, and juxtclojure.core/juxtRun each given fn on the same input; return a vector of results.view on clojuredocs → — which together let you assemble new functions without naming intermediate arguments. Code written this way is called point-free or tacit: there are no "points" (argument names) cluttering the picture.
Minimal example
compclojure.core/compRight-to-left function composition: (comp f g) = #(f (g %)).view on clojuredocs → glues functions right-to-left: (comp f g) is "first g, then f":
partialclojure.core/partialPre-fill leading arguments and return a new function.view on clojuredocs → pre-fills the first arguments of a function and returns a new function:
juxtclojure.core/juxtRun each given fn on the same input; return a vector of results.view on clojuredocs → runs several functions on the same input and returns a vector of their results — a tiny "fork":
Practical example
Combine all three to build a small pipeline without ever naming the intermediate values:
Reading right-to-left at the inner compclojure.core/compRight-to-left function composition: (comp f g) = #(f (g %)).view on clojuredocs →: pull :score, multiply by 2.
juxtclojure.core/juxtRun each given fn on the same input; return a vector of results.view on clojuredocs → then forks each person into [name, doubled-score].
Point-free, with restraint
Point-free style is powerful but can become hard to read. A good rule of thumb: prefer point-free for short compositions of well-known functions; switch to a named lambda or letclojure.core/letLocal bindings: (let [k v …] body).view on clojuredocs → when the chain starts to obscure intent.
Order arguments for composition
Argument order is an API decision, not an afterthought. partialclojure.core/partialPre-fill leading arguments and return a new function.view on clojuredocs →, compclojure.core/compRight-to-left function composition: (comp f g) = #(f (g %)).view on clojuredocs →, and ->>clojure.core/->>Thread the value through forms by inserting it as the last arg.view on clojuredocs → all assume the data you keep varying comes last — which is exactly why every core sequence function takes its collection as the final argument:
Put the most constant arguments first and the most variable one (usually the collection) last; partialclojure.core/partialPre-fill leading arguments and return a new function.view on clojuredocs → then pre-fills the stable arguments and hands you a function of the thing that changes. Two code smells say a function will resist composition: it takes too many arguments, or it's too specific to a single caller — both hint it's doing too much. Whether extracting such a helper buys a real abstraction or merely a detour is the subject of Indirection vs abstraction.
Check yourself
? quiz
`(comp f g h) x` is equivalent to which expression?
Exercise
Without using fnclojure.core/fnAnonymous function: (fn [args] body).view on clojuredocs → or #(), build a single function summary that maps a
person {:name ... :score N} to [name (* 2 N)]. Use juxtclojure.core/juxtRun each given fn on the same input; return a vector of results.view on clojuredocs →, compclojure.core/compRight-to-left function composition: (comp f g) = #(f (g %)).view on clojuredocs →,
and partialclojure.core/partialPre-fill leading arguments and return a new function.view on clojuredocs →.