macrosschemelispcommon-lispcdr

Can a macro be used to make c[...]r combinations with any arbitrary number of car and cdr calls, such as cadaddr?


I recently discovered that all of my implementations of Scheme throw an error when I try to use (cadaddr (list 1 3 (list 5 7) 9)). Apparently, by default Scheme does not allow any car and cdr combinations in the single-function form that use more than four abbreviated car and cdrcalls. I originally blamed this on Scheme's minimalism, but then I discovered that Common Lisp also shares this defect.

Can this be solved with a macro? Can we write a macro that allows an arbitrary amount of a and d in its c[...]r calls and returns the expected result, while also having the Common Lisp-like compatibility with macros like setf? If not, why not? And if so, has a reason ever been given for this is not a default feature in any lisp that I've seen?


Solution

  • Such a macro is described in Let Over Lambda for common lisp. You must wrap your code with (with-cxrs ...) to bring them all into scope, but it walks your code to see which combinators you need. I wrote a Clojure port of it years ago for fun, though of course nobody (including me) has ever wanted to use it for real. You could port it to Scheme if you liked.

    (defn cxr-impl [name]
      (when-let [op (second (re-matches #"c([ad]+)r" name))]
        `(comp ~@(map {\a `first \d `rest} op))))
    
    (defmacro with-cxrs [& body]
      (let [symbols (remove coll? (tree-seq coll? seq body))]
        `(let [~@(for [sym symbols
                       :let [impl (cxr-impl (name sym))]
                       :when impl
                       thing [sym impl]]
                   thing)]
           ~@body)))
    
    user> (macroexpand-1 '(with-cxrs (inc (caadaaddadr x))))
    (let [caadaaddadr (comp first first rest first first rest rest first rest)]
      (inc (caadaaddadr x)))
    

    https://groups.google.com/g/clojure/c/CanBrJPJ4aI/m/S7wMNqmj_Q0J

    As noted in the mailing list thread, there are some bugs you'd have to work out if you wanted to use this for real.