;; 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 language I’m using in this post is Lamber, my Lambda Calculus compiling language. It features a minimalist syntax with only functions, values, if-s, and operators like Wisp’s colon nesting operator and terminating period (similar to Lua’s end.)
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.
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-...
Utilities for functions: https://arrow-kt.io/learn/collections-functions/utils/
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 language I’m using in this post is Lamber, my Lambda Calculus compiling language. It features a minimalist syntax with only functions, values, if-s, and operators like Wisp’s colon nesting operator and terminating period (similar to Lua’s end.)
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.