A Clojure function that prints the “fully expanded form before evaluation”?

Viewed 175

Is there a Clojure function that does for any function what macroexpand-all does for a macro?

In SICP, Abelson & Sussman give a demonstration of this that they call the “linear recursive process”.

In other words, if we give:

(factorial 6)

the function would print (and not evaluate):

(* 6 (* 5 (* 4 (* 3 (* 2 (1))))))

Essentially, I am trying to “see” the data structure any function “builds up” just prior to evaluation.


I think this would be an interesting way for beginners (that’s me :) to see what the Reader(?) is building up just before it passes things on to the Evaluator. (I’m not sure I’m using the right terms, here.)

3 Answers

Function tracing can be done without programming. It is, for example, available in the Cider extension to Emacs. https://docs.cider.mx/cider/debugging/tracing.html

I have loaded my factorial program into Emacs, and placed my cursor on the factorial function.

 (ns factorial.core
    (:gen-class))

 (defn factorial [n]
    (if (zero? n)
     1
     (* n (factorial (dec n)))))

 (defn -main
    "A factorial program."
    [& args]

    (let [n 6] 
       (println "The factorial of " n "is" (factorial n))))

             M-x cider-jack-in

 REPL:  (in-ns 'factorial.core)

             C-c M-t v, select factorial when prompted

        (factorial 3)

enter image description here

This doesn't exist because that's not how evaluation works. The form (* 6 (* 5 (* 4 (* 3 (* 2 1))))) never exists while computing (factorial 6), so the runtime can't just give it to you "in passing".

Your question actually boils down to how the function is implemented. For instance, if factorial looked like this, (can be done with macro as well)

(defn factorial
  [n]
  (if (<= n 1) 1 (concat `(~'* ~n) (list (factorial (dec n))))))

Upon calling the function,

(map #(-> % factorial) (range 10)) =>
(1
 1
 (* 2 1)
 (* 3 (* 2 1))
 (* 4 (* 3 (* 2 1)))
 (* 5 (* 4 (* 3 (* 2 1))))
 (* 6 (* 5 (* 4 (* 3 (* 2 1)))))
 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 1))))))
 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 1)))))))
 (* 9 (* 8 (* 7 (* 6 (* 5 (* 4 (* 3 (* 2 1)))))))))

This would be called. You can think of macro as function that transforms a function. Only when the function factorial transforms an expression to look like the nested multiplication as above will the macroexpansion give what you are looking for.

(map #(-> % factorial eval) (range 10)) ; will give the evaluated values
Related