views:

109

answers:

2

I'm working on some Clojure code that has some circular dependencies between different namespaces and I'm trying to work out the best way of resolving them.

  • Basic issue is that I get a "No such var: namepace/functionname" error in one of the files
  • I tried to "declare" the function but then it complains with: "Can't refer to a qualified var that doesn't exist"
  • I could of course refactor the entire codebase but that seems impractical to do every time you have a dependency to resolve..... and might get very ugly for certain networks of circular dependencies
  • I could separate out a bunch of interfaces / protocols / declarations into a separate file and have everything refer to that.... but that seems like it would end up getting messy and spoil the current nice modular structure that I have with related functionality grouped together

Any thoughts? What is the best way to handle this kind of circular dependency in Clojure?

+2  A: 

I had a similar problem with some gui code, what I ended up doing is,

(defn- frame [args]
  ((resolve 'project.gui/frame) args))

This allowed me to resolve the call during runtime, this gets called from a menu item in frame so I was 100% sure frame was defined because it was being called from the frame itself, keep in mind that resolve may return nil.

Hamza Yerlikaya
+9  A: 

I remember a number of discussions on namespaces in Clojure -- on the mailing list and elsewhere -- and I have to tell you that the consensus (and, AFAICT, the current orientation of Clojure's design) is that circular dependencies are a design's cry for refactoring. Workarounds might occasionally be possible, but ugly, possibly problematic for performance (if you make things needlessly "dynamic"), not guaranteed to work forever etc.

Now you say that the circular project structure is nice and modular. But, why would you call it that if everything depends on everything...? Also, "every time you have a dependency to resolve" shouldn't be very often if you plan for a tree-like dependency structure ahead of time. And to address your idea of putting some basic protocols and the like in their own namespace, I have to say that many a time I've wished that projects would do precisely that. I find it tremendously helpful to my ability to skim a codebase and get an idea of what kind of abstractions it's working with quickly.

To summarise, my vote goes to refactoring.

Michał Marczyk
Thanks Michal for the insight and useful background! I'm still not yet convinced that always avoiding circular dependencies is necessarily the best design option for project structuring. Will have a look at the Clojure group and see if that can convince me otherwise :-)
mikera
A small update - putting the protocols in their own namespace has worked well and solved most of the problems, I usually end up adding a (:use [protocols]) to most other ns declarations and everything "just works". The only thing that I'm still finding ugly to work around are where you declare a class (e.g. a deftype) which you want to reference before it is declared (e.g. as a type hint in a protocol definition!!)
mikera
Thanks for the update, happy to hear that! I think that hinting protocol/interface functions with the names of actual implementing classes may not be a very good idea, though (actually I was under the impression that protocol methods cannot yet be hinted at all, but interface methods can and the argument is the same): hint with the name of the interface instead. If you're dealing with a `deftype`-created class, all its methods will be `Object` / interface / protocol methods anyway. The only time I'd use hints pointing to classes is when that's needed for interop.
Michał Marczyk
Still, out of curiosity, how do you work around not having a class around yet when it's needed for a hint...?
Michał Marczyk