Hi, The most modular I can think of right now is just about creating a gravity type and using multimethods for all your functions.
This way you would have dynamic resolution of methods that do not work with precompiled fns. 2009/5/17 Mark Engelberg <mark.engelb...@gmail.com>: > > Thanks for your questions. I'll try to explain better. > > First, I'll explain that my line of work is to build tools to generate > puzzles. I often have a module which generates the puzzles through > various random processes, using certain probabilities and parameters. > Then, I have another module that solves the puzzles. Another module > analyzes the solving process to measure its difficulty. A final > module filters and sorts the puzzles based on the analysis. > > For an initial rapid prototyping programming session, it often > suffices to just express each "module" as a separate file/namespace, > with the key parameters as globals at the top of the file. These > modules expressly refer to each other, with no indirection. > > But then, as I want to explore variations, it gets more complicated. > What if I want to try a different set of parameters in the generator? > What if I want to swap out the printing function in the analyzer? > What if I want to swap out the whole analysis module with something > different? > > For many years, my primary language for doing these sorts of programs > has been Python. In my first pass, I just have a bunch of functions > in a file with global variables at the top. For exploring simple > changes, I can just import a file and then mutate the global > parameters. When things get too complex for that, I reorganize into > objects, and express the variation through inheritance and overriding > the things I want to change. > > Right now, I'm working on my first sizeable project in Clojure. > Again, I began by expressing each "module" as a file with some global > parameters at the top. But now, I'm beginning to get to the point > where I need to explore variations, and it's not clear to me how to > reorganize. > > I don't want to post my actual code here, so let's run with this > *gravity* example based off of what Adrian posted as a simple > illustration. > > gravity.clj: > (ns gravity) > (def *gravity* 1.0) > (defn say-grav [grav] > (prn "Gravity is:" grav)) > > (defn halve-grav [] > (/ *gravity* 2.0)) > > (defn mult-grav [x] > (* *gravity* x)) > > (defn print-grav-stats [] > (say-grav *gravity*) > (say-grav (halve-grav)) > (say-grav (mult-grav 2))) > > > Now, let's say I want to explore the following: > > gravity_variation1.clj: > (ns gravity-variation1) > -- Copy of everything in gravity.clj except with the following changes -- > (def *gravity* 0.38) > (defn say-grav [grav] > (prn "Gravity on Mars is:" grav)) > > And now I want to compare the two variations in one consumer file: > consumer.clj: > (ns consumer) > (gravity/print-grav-stats) > (gravity-variation1/print-grav-stats) > > So, how to do this in a clean way? And then, if I have other consumer > files that use the gravity module, how do I set it up so that it is a > parameter as to which gravity module they use? > > I've explored some of the dynamic rebinding techniques that Adrian > suggested, but haven't been satisfied with the results. It's hard to > constantly rebind things at the point of function call, and difficult > to analyze what's going to happen as the code gets more complex, and > you're using a mixture of functions from both "variations". I can > start passing more and more things around as parameters, but that gets > to be a rather complicated refactoring, and then these modules get > much harder to use. > > So how to do this in other languages?: > > In Python, I might refactor as follows: > class Gravity: > gravity = 1.0 > def halveGravity(self): > return self.gravity/2.0 > etc. > > One nuisance of refactoring a module written as globals-plus-functions > into classes in Python is you have to explicitly insert self > everywhere, but at least it's a very straightforward translation. In > a language where the self reference is implied, it would be even > easier (although most OO languages force you to use objects from the > get-go, so there wouldn't really be any refactoring to do anyway). > > Then, you can express the variation through inheritance and > overriding. The consumer code might look like: > gravity = Gravity() > gravityVariation1 = GravityVariation1() > > gravity.printGravStats() > gravityVariation1.printGravStats() > > There are some tricks you can do to make these behave more like static > methods, so they can be called without an instance, but that's not > idiomatic Python. > > I haven't done a whole lot of Java, but I imagine that you could > accomplish the same thing by using a class with static methods to > represent a "module" of code. I've recently been reading the Scala > book, and it seems like they've taken the idea of singleton objects as > modules one step further, along with traits to handle mixins of > partial implementations, in order to address a lot of these issues > even better than vanilla Java. > > And what about functional languages? Well, I'm pretty sure that PLT > Scheme has a sophisticated modular mechanism called Units which handle > these sorts of issues. And I think that ML can parameterize modules > in the form of functors (or is that only a parameterization over > types?). > > So please, I'd love to hear more ideas about how to write my Clojure > code in a more modular way, making it easier to explore and maintain > variations, and dynamically link the sets of modules I want to use to > solve a given problem. > > Thanks! > > > > --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---