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.

Reply via email to