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?