James Reeves has got it. It's same for me. All this boils down to really knowing the language. Knowing how to make music with the instruments provided. For me Clojure's namespaces has been one of the most wonderful things that has happened to me in programming. It has facilitated all kinds of evaluation environments. I've never had such issues as yours. Configuration management is one of the easiest things to do in Clojure. I think what I would offer in this case is the word, Refactor. One thing I noticed about your examples is that your functions lack composition. Think that way and maybe you'll stop having the problems you're having because if you're not careful it won't be long before you start thinking of inheritance as well.
On Friday, December 27, 2013 7:08:49 PM UTC+1, puzzler wrote: > > I ended up accidentally injecting a completely different thread of > discussion into the "Is Clojure right for me?" thread. I'm breaking it > into a separate thread here. > > Here's where we left off: > > On Fri, Dec 27, 2013 at 6:34 AM, Stuart Halloway > <stuart....@gmail.com<javascript:> > > wrote: > >> Yes, thanks Mark. It seems to me that you are saying "namespaces make >> poor maps". Stipulated. So why not use records or maps? >> >> > This is close, but not exactly what I'm saying. It's not so much about > wanting namespaces to be maps. It's about wanting a robust mechanism for > functions to share information other than threading that information > through the functions. > > In Structure and Interpretation of Computer Programming, a large chunk of > the programs are written in a pseudo-object-oriented style, a style which > simulates objects by having functions' closures share the same local > context. > > Sure, you could do that with maps: > > For example: > > (defn create-functions [init-info] > (let [shared-info init-info] > {:foo (fn foo [x] ... body uses shared-info in some way ...), > :bar (fn bar [x] ... body uses shared-info in some way ..)})) > > Then, to access these functions, you'd do something like this: > (def instance (create-functions default-info)) > ((:foo instance) 2) ; the equivalent of instance.foo(2) in OO > ((:bar instance) 3) ; the equivalent of instance.bar(3) in OO > > Of course, SICP's main point is that even bare-bones Scheme is rich enough > to simulate objects, but the other point is that it is important for > functions to be able to share stat*e* which can be initialized for a > group of functions, > > *even if that state is immutable. * > The above pattern is important enough that most languages provide some > kind of mechanism for doing that. Classes are one such pattern. > > In Clojure, the above code would be horribly un-idiomatic of course. > Actually, depending on the functions and definitions, it might not even be > possible to structure the code that way (because Clojure's local definition > capabilities are not as rich as what is possible at the global level, for > example, regarding mutual references -- in some contexts, letfn might help, > but not all contexts). > > The above example uses associative containers for the shared-info, and > associative containers to hold the group of related functions that are > outputted, but that doesn't make this solution any more attractive -- > again, emphasizing that this is not about associative containers. > > I claim that Clojure only provides two idiomatic solutions to the above > problem of functions sharing the same immutable information, which might > need to be set prior to using those functions: > > Solution 1: > > (def ^:dynamic shared-info default-info) > (defn foo [x] ... body uses shared-info) > (defn bar [x] ... body uses shared-info) > > Call these functions via: > > (foo 2) and (bar 3) if you're happy with the defaults or > > (binding [shared-info info] (foo 2)) and > (binding [shared-info info] (bar 3)) otherwise. > > This is awkward for several reasons, and sometimes this solution isn't > really an option since functions that return lazy structures won't work > with the above mechanism. > > Solution 2: > > (defn foo [shared-info x] ... body uses shared-info) > (defn bar [shared-info x] ... body uses shared-info) > > Call these functions via: > > (foo info 2) > (bar info 3) > > > My argument is that both these solutions are unsatisfying. Solution 2 is > irritating because if you use a map for shared-info, you end up having to > repeat the destructuring in every single function. Also, you need to > thread this information through all the functions, not only the functions > that directly use, but also the functions that call other functions that > use it. It makes all the functions bulky and awkward, and at the end of > that, you still don't have something that reflects the notion of getting > back a group of functions that share the *same* info -- instead, you have > to remember to always pass in that same info with every function call. > Also, with the threading-the-state version, you don't have a convenient > notion of "default state", unless you are willing to take cases on the > number of arguments for every single one of your functions. > > My other claim is that "Solution 2" feels too heavy-handed for small > projects, or scenarios where it isn't clear you'll ever want to initialize > the shared-info to something other than the defaults, but the path of > transition from Solution 1 to Solution 2 is a painful one. > > > I think that where my experience differs from yours is summarized in your >> comment "In Clojure, a project often begins with a bunch of functions >> sharing some immutable state or "magic constants" stored in vars." I >> *never* do that. If there are a group of related things, I put them in an >> associative container (maybe just a map) on day one. >> > > Even if you share all those constants in an immutable container, I > maintain that the threading of that information through all the functions > can be awkward. > > >> >> The analogy with OO is misleading here. If you started an OO project >> putting everything in static/singleton members, you would have the same >> pain you attribute to Clojure's namespaces. >> > > Not necessarily. Sure, you'd have to delete all the "static" words from > your methods. And you'd need to make sure you then accessed the methods > through an instance, but fundamentally, you wouldn't need to change the > body of any of the methods or the number of inputs in callers or callees. > I suspect in a language like Scala, you could probably even find a way to > leave behind a "companion object" that forwards to a default instance, so > your old static/singleton calls still work. > > So yes, I agree that there would be some similar issues in moving from a > singleton to an instance-based scheme in an OO language, but I don't think > the change would be as dramatic as the same transition in Clojure. > > >> >> I find this to be a "my car doesn't have wings" argument, and the kind of >> thing that leads into casually complecting everything with everything else. >> >> > If there is an idiomatic way in Clojure to solve this problem other than > the two solutions I've outlined above, I'd love to see it. > -- -- 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 --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.