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!

Hi Mark,

You might find "Lexical contexts; or, Lambda, the Ultimate Module?"
post by Scott Burson inspiring.

http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/ef3b989e8dabad62/a6ceac2011b8b66c?hl=en&#a6ceac2011b8b66c

It does not use CL package and dynamic variables (similar to clojure
ns and vars) to achieve modularity, rather criticize them as wrong
constructs for modularity. Instead, It builds a semi-module system
using CL lexical constructs (let, lables, macrolet, symbol-macrolet) +
macro.

Source code is available here.

http://common-lisp.net/cgi-bin/viewcvs.cgi/devel/src/?root=misc-extensions

The implementation is very short. However, it's beyond my skill to
tell whether it can be ported to clojure or not.

Cheers,
- Feng



--~--~---------~--~----~------------~-------~--~----~
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