;; Clojure source code for `comp` (since Clojure v1.0)
(defn comp
"Takes a set of functions and returns a fn that is the composition
of those fns. The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc."
{:added "1.0"
:static true}
([] identity)
([f] f)
([f g]
(fn
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y z] (f (g x y z)))
([x y z & args] (f (apply g x y z args)))))
([f g & fs]
(reduce1 comp (list* f g fs))))
The main problem that threading macros solve is the lack of implicit partial application. If you farm that problem to partial-applicative combinators, you can do it:
This is the TXR Lisp interactive listener of TXR 302.
Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
1> (defun bind1 (fn a1) (lambda (a2) [fn a1 a2]))
bind1
2> (defun bind2 (fn a2) (lambda (a1) [fn a1 a2]))
bind2
We define bind1 and bind2 for binding the left or right argument of a binary function producing a unary function, then:
You can write functions that notice they have fewer arguments than ostensibly required and partially apply themselves. Obviously the standard * can't do that because it's usefully variadic already.
Anyway, in the implementation of TXR, I've done this kind of thing in C, just with function calls: no macros. E.g. in eval.c, certain functions are prepared that the quasiquote expander uses:
static val consp_f, second_f, list_form_p_f, quote_form_p_f;
static val xform_listed_quote_f;
"nao" means "not an object": it's a sentinel value used internally in the runt-time for various purposes, the most common of them being the termination of variadic argument lists.
andf is an and combinator: it returns a function which passes its argument(s) to each function in turn; if anything returns nil, it stops calling functions and returns nil. Otherwise it returns the value of the last function.
The pa_this_that functions are partial applicator combinators, generalizations of bind1 and bind2. E.g. pa_12_1 means take a fucntion with arguments 1 2, returning a function which just takes 1 (so 2 is bound: this is like bind2). A bunch of these exist, and more coud be added if needed: pa_1234_1, pa_1234_34 pa_123_1, pa_123_2, pa_123_3, pa_12_1 and pa_12_2.
If the language allows passing lists of functions, then `comp` can be implemented by hand: https://clojuredocs.org/clojure.core/comp
And re-implementing `comp` by hand can teach us more than we bargained for (all the way to compiler technology)... I blogged about it here: https://www.evalapply.org/posts/lessons-from-reimplementing-...
Oh and, the arrow-kt library bolts this onto kotlin.
Utilities for functions: https://arrow-kt.io/learn/collections-functions/utils/
fogus has a couple of nice little posts on the threading macro itself...
https://blog.fogus.me/2013/09/04/a-ha-ha-ha-aah.html
https://blog.fogus.me/2009/09/04/understanding-the-clojure-m...
The main problem that threading macros solve is the lack of implicit partial application. If you farm that problem to partial-applicative combinators, you can do it:
We define bind1 and bind2 for binding the left or right argument of a binary function producing a unary function, then: The only wart is you have that explicit bind1.You can write functions that notice they have fewer arguments than ostensibly required and partially apply themselves. Obviously the standard * can't do that because it's usefully variadic already.
Anyway, in the implementation of TXR, I've done this kind of thing in C, just with function calls: no macros. E.g. in eval.c, certain functions are prepared that the quasiquote expander uses:
static val consp_f, second_f, list_form_p_f, quote_form_p_f; static val xform_listed_quote_f;
"nao" means "not an object": it's a sentinel value used internally in the runt-time for various purposes, the most common of them being the termination of variadic argument lists.andf is an and combinator: it returns a function which passes its argument(s) to each function in turn; if anything returns nil, it stops calling functions and returns nil. Otherwise it returns the value of the last function.
The pa_this_that functions are partial applicator combinators, generalizations of bind1 and bind2. E.g. pa_12_1 means take a fucntion with arguments 1 2, returning a function which just takes 1 (so 2 is bound: this is like bind2). A bunch of these exist, and more coud be added if needed: pa_1234_1, pa_1234_34 pa_123_1, pa_123_2, pa_123_3, pa_12_1 and pa_12_2.