Welcome! Please see the About page for a little more info on how this works.

+1 vote
ago in Compiler by
edited ago by

I was finding a way to have co-recursive reified instances, and after looking at the implementation of clojure.lang.Compiler I've found out that this can actually be achieved with letfn*. Consider the following proof-of-concept:

(defmacro thunk [& body]
  `(reify* [clojure.lang.IFn]
      (invoke [this] ~@body)))

(letfn* [g (thunk (+ 1 2 3 (f)))
         f (thunk (+ 1 2 3))]
    (f))

This works, and seems to be faster than other state-based approaches (e.g. using atom to resolve forward declarations).

This said, I recognize that this may not be standard behavior. for instance using (reify* ...) directly in the letfn* bindings will cause the reify* block to be parsed as a MetaExpr which breaks a cast in the implementation of LetFnExpr. For example:

(letfn* [g (reify* [clojure.lang.IFn] (invoke [this] (+ 1 2 3 (f))))
         f (reify* [clojure.lang.IFn] (invoke [this] (+ 1 2 3)))] 
  (f))

But since this would implicitly add metadata to the reify* forms we get this error:

class clojure.lang.Compiler$MetaExpr cannot be cast to class clojure.lang.Compiler$ObjExpr (clojure.lang.Compiler$MetaExpr and clojure.lang.Compiler$ObjExpr are in unnamed module of loader 'app')

The only way I've managed to create a reify* form without metadata is by creating the form in macro, such as the thunk macro defined in the first example.

Is using reify* within letfn* bindings supported? If not, are there any other methods that avoid mutable state that could be employed in similar cases?

Please log in or register to answer this question.

...