I apologize for the length of this message, but I've run into a problem that I think I might approach in any of several ways, each of which leads to questions about things that I'd like to know more about in any event. Some are dev environment questions and some (re: circular lists near the end) are core Clojure language questions.
The root problem is that I think I have an infinite loop somewhere in my code and I'm having a hard time tracking it down. My system has a lot of randomness in it and I've been unable to produce a repeatable or minimal example, but in many full-scale runs I eventually get a hang that smells like an infinite loop. I found one that I tracked down to some recursive zipper code, and I fixed that, but there's at least one more and I can't find it. One of the reasons this is particularly tricky is that I'm dynamically generating and executing lots of calls in random/evolved orders with random/evolved arguments, so unexpected sequences of events can arise. (And they do arise, which is the point! :-) In Common Lisp I would wait until I think I'm in trouble, break execution with a keyboard interrupt, look at the function call stack (and local vars, if I'm in a nice CL environment), and probably figure it out pretty quickly. What would experienced Clojurians suggest in a situation like this? Is there is a way to break and look around under slime? I just finally got slime working with Clojure (THANKS to Lau Jens, http://www.bestinclass.dk/index.php/2009/12/clojure-101-getting-clojure-slime-installed/), but I don't yet know my way around it. Can anyone recommend a good concise description of all of the relevant key commands? I've never used slime much for Common Lisp either -- I've used mostly IDEs like MCL and various lisp machines way back in the last century -- so I need to start near the beginning on slime. Knowing that I could break and examine state would certainly motivate that. In general I've been running a REPL in a separate terminal window using the clj script that comes with ClojureX (on macs running OS X 10.6.2). I don't know how to force a break or get a break loop in this environment but when I hit an error I can do (.printStackTrace *e) and this does show me some of my own function calls amid the other stuff. Even though this is pretty minimal it has been very helpful at times. Two problems with this, however: 1) I can only get this from an error; I don't know how to force a break so I can look at this when I seem to be stuck in a loop, and 2) The stack trace info is always elided, with lines saying something like "... 31 more". I want all the trace information that it can give me, but I've been unable to figure out how to keep it from eliding. I've tried to force clj-initiated runs to break by sending signals to the java process from the Mac OS X Activity Monitor app, but everything that I have tried (e.g. SIGINT) either does nothing or kills the whole process. Is there a signal that I could sent to abort execution but not quit java, as happens with errors, so that I could look at a backtrace? Is there a handler that I could add to my code to allow this to happen? It'd only help if it allowed me to see what else was going on when the signal was received; if it only told me that the error was from a signal arriving then that would defeat the purpose. I've also been doing some runs under MCLIDE, with help from Terje Norderhaug, and there I can indeed issue a break from a menu and get a backtrace of sorts... but so far I have not been able to get a backtrace that shows me what I need -- that is, I don't see any of my own functions, presumably because the break changed the context... I'm not sure what's going on there. I've also briefly experimented with Eclipse/Counterclockwise, but found Eclipse fairly confusing in general... but does it provide a better approach to breaking/exploring running code? If so then perhaps I should give that another shot. An alternative: Is there a way to watch my running Clojure program without breaking it, that is to observe the recent call history (of my own definitions, either all of them or specifically marked ones) from outside the process? I can think of some clumsy ways to roll my own version of this, e.g. using trace and sending the trace output to a file, but I don't think this will be practical because I generally only get these errors after long stretches of intensive computation that will involve many many calls to the potentially problematic functions. So I think there'll be too much trace output to save on disk, and the slowdown from tracing all of the calls may prevent me from ever reaching a problematic state (although I haven't tried this to be sure of the timing). I guess I could keep a bounded trace on disk but maintaining the bound would slow things even more... So this doesn't feel like the best approach. Might there be -- wishful thinking, I know -- some already-existing way to eavesdrop on a running Clojure program dynamically, from another process, and see what functions are being called? Some of this is complicated by the fact that I'm running multiple threads in Clojure while I never do so under Common Lisp... So there's the question of which threads get interrupted and which stacks get looked at. But the backtrace produced by (.printStackTrace *e) has nonetheless produced useful clues so I'm hopeful... One last set of questions stemming from this problem concerns circular lists. In Common Lisp my unintentional infinite loops have often stemmed from accidental circular lists (which were created by dynamically random/evolved sequences of list-manipulation instructions). I know how these can arise in Common Lisp and I also know how to prevent them there, generally with lots of (usually unnecessary and expensive) list copying. In Clojure I'm not even sure if/how such lists can arise, and I'd be interested in any general comments that anyone has on that. To make matters more confusing I'm not sure how to copy a list, to prevent such problems if they are indeed possible. I realize that copying is generally not necessary in Clojure because of immutability, but if one wants to copy a list how can one do it? Thanks for your patience in reading this. I'd appreciate any feedback on any parts of it. And in spite of the problems listed above I'm having a great time in the Clojureverse! -Lee -- Lee Spector, Professor of Computer Science School of Cognitive Science, Hampshire College 893 West Street, Amherst, MA 01002-3359 lspec...@hampshire.edu, http://hampshire.edu/lspector/ Phone: 413-559-5352, Fax: 413-559-5438 Check out Genetic Programming and Evolvable Machines: http://www.springer.com/10710 - http://gpemjournal.blogspot.com/ -- 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