Search code examples
clojure

Creating mutually recursive local functions with metadata in Clojure


Suppose I want to define two mutually recursive functions within a local scope. I can do this with letfn:

(letfn 
  [(f [x] (if (= x 0) (g x) true))
   (g [x] (if (= x 1) (f x) false))]
 (f 0))

But letfn is quite restricted, compared to let, as it accepts only "function specs," not arbitrary expressions. My question is: what if I want to attach metadata (using with-meta) to both f and g, so that within f, I can read g's metadata, and within g, I can read f's metadata? Is this possible in Clojure?

(For context, I am trying to implement a fn-like macro that automatically attaches certain metadata to the function being created. I'd like these auto-annotated fns to be instantiable wherever a normal Clojure function is, including inside a letfn. But I don't see how I can define a letfn-like macro that attaches the metadata, because it would ultimately have to desugar to letfn, which cannot attach metadata.)


Solution

  • Don't forget about with-local-vars:

      (with-local-vars [f (fn [x] (if (= x 0) (g x) true))
                        g (fn [x] (if (= x 1) (f x) false))]
        (reset-meta! f {:f 3})
        (reset-meta! g {:g 2})
    

    with results:

    (f 0) => false
    (f 1) => true
    
    f           => #<Var: --unnamed-->
    (var-get f) => #object[tst.demo.core$fn__20698$fn__20699 0x1eb2d718 "tst.demo.core$fn__20698$fn__20699@1eb2d718"]
    (meta f)    => {:f 3}
    (meta g)    => {:g 2}
    

    You can also use var-get and var-set to access/change the value of the local vars.