Hi Mark,
Not sure if this helps, but I've found it useful to mediate access to
global vars through a single function.  Then I can swap in another
function to change the behavior.  Here's an example from 
http://tinyurl.com/qm8pj4

(defn get-property-function [name]
  (System/getProperty name))

(defn use-property-function [f]
  (alter-var-root #'get-property-function (fn [_] f)))

(defn get-property [name]
  (or (get-property-function name)
      (throw (Exception. (str "Missing property " name)))))

The public interface is (get-property name).  Additional functions
like (get-gravity) provide short aliases for individual properties.
The default definitions are returned by get-property-function, but I
can call use-property-function to globally replace those with a
different set of properties.

If I were to use (binding ...) on get-property-function, I could use
set! instead of alter-var-root, and the changes would only affect the
current thread.

-Stuart Sierra


On May 17, 3:24 pm, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> 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
-~----------~----~----~----~------~----~------~--~---

Reply via email to