Circular Dependancy Question

80 views
Skip to first unread message

Dan Larkin

unread,
Feb 14, 2009, 1:40:31 PM2/14/09
to clo...@googlegroups.com
So I've got a circular dependency problem.  There's a few ways to move functions and (require )s around but the problem remains -- these three files fundamentally depend on one another:

parser.clj - lexer & parser (using joshua choi's excellent fnparse library)
defaulttags.clj - multimethods for handing conditionals, looping, including other templates, etc.
template.clj - "user facing" code. Has functions to load templates from disk and helper functions for rendering templates.

And here's a dependency breakdown:

defaulttags.clj
  depends on template.clj for loading new templates from disk from an "include" template tag
  depends on parser.clj for parsing the templates it loads

template.clj
  depends on parser.clj for parsing/rendering templates it loads from disk
  also loads defaulttags.clj so the multimethods get registered


There's also another file, right now called foo.clj, which defines two functions (represent and invoke-templatetag) and has no dependencies, but is required by all 3 files.  I had to create the foo.clj file to get around an earlier circular dependency issue, and that works and there's no problem with it right now... just saying that's how parser.clj can call the tags registered in defaulttags.clj even though it doesn't import it.


So anyway, I guess that's a long-winded explanation of my current circular dependency problem. It'd be great if someone could suggest a remedy.

But as an aside, does this seem to anyone else like a wart on an otherwise great language? Thinking about file layout should not be one of my priorities... the language should not encourage me to put everything together in one file just to get it to work.  I should be able to separate functionality in a way that makes sense to the app I'm building.

Thanks for reading,
Dan

Stephen C. Gilardi

unread,
Feb 14, 2009, 2:14:58 PM2/14/09
to clo...@googlegroups.com

On Feb 14, 2009, at 1:40 PM, Dan Larkin wrote:

But as an aside, does this seem to anyone else like a wart on an otherwise great language? Thinking about file layout should not be one of my priorities... the language should not encourage me to put everything together in one file just to get it to work.  I should be able to separate functionality in a way that makes sense to the app I'm building.

Within one namespace, we use declare to avoid this problem. Perhaps declare could be extended (or another function created) to work with namespace-qualified symbols that refer to namespaces and vars that may not yet exist. I would imagine this would involve creating the namespace and var within it if either didn't yet exist and pushing the namespace name onto some "deferred load" queue so it's guaranteed loaded by the time the top level load is complete.

For this to be effective in our preferred way to declare dependencies, it should be a new clause in within "ns".

Any thoughts?

--Steve

Dan Larkin

unread,
Feb 14, 2009, 3:09:54 PM2/14/09
to clo...@googlegroups.com
What you describe is the first way I thought about it.  Some sort of :external-depends or something...

But thinking about it more, would it be possible to emulate the way python handles circular dependencies?  When a file is imported in python the interpreter "evaluates" top-level forms to create a list of exports. Of course the clojure reader has fundamental differences from the python reader, but couldn't clojure do something similar?  After all, (read (PushbackReader. (StringReader. "(def a (foo 5))"))) just returns a list, couldn't each (require )'d namespace be read, searched for exports and dependencies (which would have the same thing done to them) and then they could be evaluated? That way all the vars necessary for "linking" are present.

Dan

Stephen C. Gilardi

unread,
Feb 14, 2009, 4:25:19 PM2/14/09
to clo...@googlegroups.com

On Feb 14, 2009, at 3:09 PM, Dan Larkin wrote:

But thinking about it more, would it be possible to emulate the way python handles circular dependencies?  When a file is imported in python the interpreter "evaluates" top-level forms to create a list of exports. Of course the clojure reader has fundamental differences from the python reader, but couldn't clojure do something similar?  After all, (read (PushbackReader. (StringReader. "(def a (foo 5))"))) just returns a list, couldn't each (require )'d namespace be read, searched for exports and dependencies (which would have the same thing done to them) and then they could be evaluated? That way all the vars necessary for "linking" are present.

One problem with that is that in Clojure, things you define in other namespaces (macros) can be used *at compile time* in compiling your code. That's fundamentally different from less dynamic languages. Reading about the solution to this in (say) the rationale for the R6RS Scheme spec may at least give us an idea of how hard the problem is we're trying to solve.

--Steve

Reply all
Reply to author
Forward
0 new messages