Sorry for the length of this post--I feel I have to spell out the details 
in order to head off irrelevant responses.  I'm saving you the trouble of 
reading a long chain posts just focused on misunderstandings.

For anything below, I'd be happy to find out that I'm wrong, confused, 
misguided, etc.

I've started using MASON, a library for agent-based models (ABMs) written 
in Java.  Using gen-class to follow the MASON model works pretty well, even 
if the result is somewhat "Java-esque" Clojure.

Using MASON in the normal way involves cyclic dependencies.  This is no 
problem if I let Clojure figure out the types at runtime--but I can get a 
speed improvement of 11 times the original speed if I add type hints, after 
a bit of code reorganization to avoid almost all of the cyclic type 
dependencies.  And by adding one additional type hint--more on this 
below--I get a further 6.5X speed improvement, for a total improvement due 
to type hints of roughly 70X.  Note that speed often matters for ABMs that 
involve stochastic effects; it's typically necessary to run the same 
simulation many times, and it's useful to be able to do the needed runs in 
minutes or hours rather than days.

However, the last type hint involves a cyclic type dependency.  It doesn't 
generate a cyclic dependency error, but gives class not found errors no 
matter which of two interdependent classes is compiled first. My solution 
is to delete the type hint, compile all of the Clojure files, add the type 
hint back, and recompile that one modified file. That's how I get the full 
85X speed improvement.  (*That* is an ugly solution.)

The cyclic dependencies are due to decisions by MASON's designers that seem 
entirely reasonable for an agent-based modeling framework--even if I would 
do things differently--and *have* done things differently (in a 
narrowly-focused, application-specific ABM framework that's purely 
functional except for reporting).

******

So: In order to use a well-designed Java library, I have to choose between 
slow code or awkward workarounds for a cyclic dependency.

******

Let me emphasize that I *do not* think that Clojure should incorporate 
every feature that someone thinks useful.  I certainly don't think that 
Clojure should provide every feature offered by Java.  Ugh.  I love Clojure 
because it doesn't include everything plus the kitchen sink, but instead 
provides a small set of flexible, simple, well-thought out functions.

However, the lack of cyclic dependencies is not the absence of a 
substantial language feature.  It's an arbitrary limitation placed on 
compile time that's not present at run time.  Allowing cyclic dependencies 
wouldn't make Clojure less simple or elegant, because it wouldn't really 
add anything to the language.

(Yes, if cyclic dependencies are allowed, then people can abuse them. 
People can also abuse type hints, interwoven side effects and laziness, 
macros, and non-idiomatic coding styles.  The Clojure community teaches 
routinely teaches novices how to avoid these mistakes.)

(If cyclic dependencies are really considered so harmful, one could hide 
them behind a special compiler option that emits a warning when used.)

---------------------------

Optional reading: Why there's a cyclic dependency in MASON simulations.

MASON simulations typically consist of 

(a) One or more "agents", which implement an interface (Steppable) with a 
single method, step().

(b) An instance of a class that extends SimState, which contains a 
scheduler that repeatedly calls step() in every agent that's registered 
with it.

The agents have to interact with each other, and the way that they find out 
about each others' states is typically through the the SimState object 
that's automatically passed to step().  The point is that the only way for 
teach Student to know about other instances of Students is by knowing about 
an object that also has to know about Students. There's the cycle.  (An 
alternative might be to have each Student maintain a collection of all 
other Students, but that's ugly and unnecessary.)

I use gen-class in order to implement Steppable and to extend SimState. The 
type hint that I delete and restore is for the second argument to the 
step() function required by Steppable:

(defn -step [^students.Student this ^students.StudentsSimState sim-state]

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to