I can think of a couple more alternatives to consider: ;; Turns out you don't need vars for everything. (let [state {:some :data}] (defn do-stuff [] (func-of state)))
;; Shifts some burden to the client code (defn do-stuff-fn [state] (fn [] (func-of state)) The issue as I see it is a complection of namespaces (DAG of bags of functions) and individual object lifecycles. The grid size stuff impl is a function of that particular object (which starts out with a global lifecycle), whether that's exposed in their signatures or not. If I have to make it explicit later, I'm willing to accept that work as stemming from an incorrect initial design. It doesn't bother me to refactor the old in terms of the new, explicit, versions when I identify something like that, deleting the old eventually (or not). Why should functions share information? Parameters and return values give you inputs and outputs, and if it weren't a little cumbersome to add new ones we'd be abusing it to bits. Vars are easy, but wrong at times, thus the tension. Is there something in between the purity and the ease? I guess I'm willing to accept the immediate costs because of a perceived larger benefit, namely decoupled designs. But, I think if we had more functions to dynamically modify scopes as data, that would get us closer, like 'environment-as-a-value'. Right now, it's all bound up in private or complicated implementation details. On Fri, Dec 27, 2013 at 6:35 AM, Softaddicts <lprefonta...@softaddicts.ca>wrote: > We use a lot of configuration information in our product and I mean lot :) > Immutable in regard to the application cycle. > > We chose to remove this from each name spaces and instead use a name > space to act as an intermediate to acces it. > > It's not as pure as it should be (it's contextually outside of any fn > referring to it) > but since it does not appear to change in the life cycle of the app it's > bearable. > > It also useful because we can decide were it's stored. In prod it's in > zookeeper. When running tests, it comes from a memory based implementation > and can be stubbed at will within the tests. > > It's also versionned, a change clones the current config and creates a new > one with whatever delta needs to be applied to it. > > Passing this stuff around using one of the two methods you have shown > would have been cumbersome and unmaintainable. Most of the time > our configuration information comes from top level fns so the lower > layers remain even more insensitive to how this info is pulled in. > > I would not however share state this way using this outside of Clojure > constructs (atom, refs, ...) to handle mutation explicitly. > > When the configuration changes it's because the app ends its life cycle and > restarts anyway. > > My 2 cents :) > > Luc P. > > > > 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.hallo...@gmail.com>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. > > > -- > Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my ipad! > > -- > -- > 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. > -- -- 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.