[HN Gopher] Functional Threading "Macros"
       ___________________________________________________________________
        
       Functional Threading "Macros"
        
       Author : GarethX
       Score  : 58 points
       Date   : 2025-10-06 10:46 UTC (1 days ago)
        
 (HTM) web link (aartaka.me)
 (TXT) w3m dump (aartaka.me)
        
       | kazinator wrote:
       | 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:
       | 3> [chain [bind1 * 2] succ]       #<intrinsic fun: 0 param +
       | variadic>       4> [*3 10]       21
       | 
       | 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;                 static void
       | qquote_init(void)       {         val eq_to_list_f =
       | pa_12_1(eq_f, list_s);         val eq_to_quote_f = pa_12_1(eq_f,
       | quote_s);         val cons_f = func_n2(cons);
       | protect(&consp_f, &second_f, &list_form_p_f,
       | &quote_form_p_f, &xform_listed_quote_f, convert(val *, 0));
       | eq_to_list_f = pa_12_1(eq_f, list_s);         consp_f =
       | func_n1(consp);         second_f = func_n1(second);
       | list_form_p_f = andf(consp_f,
       | chain(car_f, eq_to_list_f, nao),
       | nao);         quote_form_p_f = andf(consp_f,
       | chain(cdr_f, consp_f, nao),
       | chain(cdr_f, cdr_f, null_f, nao),
       | chain(car_f, eq_to_quote_f, nao),
       | nao);         xform_listed_quote_f = iffi(andf(consp_f,
       | chain(car_f, eq_to_list_f, nao),
       | chain(cdr_f, consp_f, nao),
       | chain(cdr_f, cdr_f, null_f, nao),
       | chain(cdr_f, car_f, consp_f, nao),
       | chain(cdr_f, car_f, car_f, eq_to_quote_f, nao),
       | nao),                                     chain(cdr_f, car_f,
       | cdr_f,                                           pa_12_1(cons_f,
       | nil),                                           pa_12_2(cons_f,
       | quote_s),                                           nao),
       | nil);       }
       | 
       | "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.
        
       | adityaathalye wrote:
       | 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-...                 ;; 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))))
        
         | adityaathalye wrote:
         | 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...
        
         | adityaathalye wrote:
         | Oh and, the arrow-kt library bolts this onto kotlin.
         | 
         | Utilities for functions: https://arrow-kt.io/learn/collections-
         | functions/utils/
        
       ___________________________________________________________________
       (page generated 2025-10-07 23:01 UTC)