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