I posted this in response to the original gist, but it probably wasn't seen. Does this demonstrate the behavior you want?
https://gist.github.com/johnwalker/8142143 On Friday, December 27, 2013 1:08:49 PM UTC-5, 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.