That does help.  thanks for the link.  good to get feedback, I figured 
there's no magic bullet but didn't want to go to far down a path without 
soliciting advice.

I'm going to try a few things, keep it simple and see how it holds up.


thanks again dave




On Thursday, February 27, 2014 9:43:32 AM UTC-5, David Della Costa wrote:
>
> Hi Robert, 
>
> I completely understand the desire to structure your code in a way that 
> isolates business logic.  It's something I struggle with all the time 
> and I think it's not simple when you're shifting from thinking in an OO 
> way (as you and I and so many folks are) to thinking more functionally 
> about things.  I mean, this is not simple, period--design is hard. 
>
> One point of advice I can make regarding this is that sometimes I've 
> found it useful to design a structure in an expedient way, asking the 
> question, "what makes it easiest to get at and manipulate my data?" Then 
> I ask questions about what that design implies: what concerns get mixed 
> up that I'd rather not get mixed up, what is more complex than it should 
> be, etc.  Then I see if I can maintain or increase the simplicity while 
> addressing the remaining concerns. 
>
> > my concern was that i was missing some clojure idiom for "hiding" 
> > whether a data structure was fully populated vs. requiring an explicit 
> > look ups. 
>
> You can still abstract this functionality without coupling your data and 
> the lookup functions.  It's fine to provide the API user with a function 
> that says, "get me this data, I don't care how, and here's the tree of 
> entity relationships."  Heck, you can even hide the entity relationship 
> data structure if really really need to, depending on the needs of the 
> API.  That maintains the abstraction of a lookup method which could be 
> doing anything from pulling it from memory to looking it up in a 
> database to making an HTTP request...etc.  But your relationships can 
> still be expressed in that one isolated data structure, living wherever 
> it lives, getting managed however it gets managed. 
>
> But I would start by designing the lowest layer to make everything 
> explicit and as pure as possible: a function which gets an entity id and 
> this resource and tries to look up the data here, a function which gets 
> the id and that other resource and tries to look it up there, etc.  And 
> then the "get-me-the-data-I-don't-care-how" function would simply be 
> wrapping those up in logic which figures out which one succeeds (for 
> example). 
>
> If you want something on top of all of that that is more semantically 
> meaningful and matches your domain vocabulary, you can then do that too. 
>  You can always wrap these functions in other functions. 
>
> Rick Hickey's comments in this interview (I could swear I saw this 
> interview on a different site, but can't find it...ah well) may be 
> pertinent here: 
>
> http://www.codequarterly.com/2011/rich-hickey/ 
>
> "Fogus: Following that idea—some people are surprised by the fact that 
> Clojure does not engage in data-hiding encapsulation on its types. Why 
> did you decide to forgo data-hiding? 
> Hickey: Let’s be clear that Clojure strongly emphasizes programming to 
> abstractions. At some point though, someone is going to need to have 
> access to the data. And if you have a notion of “private”, you need 
> corresponding notions of privilege and trust. And that adds a whole ton 
> of complexity and little value, creates rigidity in a system, and often 
> forces things to live in places they shouldn’t. This is in addition to 
> the other losing that occurs when simple information is put into 
> classes. To the extent the data is immutable, there is little harm that 
> can come of providing access, other than that someone could come to 
> depend upon something that might change. Well, okay, people do that all 
> the time in real life, and when things change, they adapt. And if they 
> are rational, they know when they make a decision based upon something 
> that can change that they might in the future need to adapt. So, it’s a 
> risk management decision, one I think programmers should be free to make." 
>
> I don't think there's any magic answer to how to design something 
> correctly, and there are always tradeoffs and loose ends.  But perhaps 
> the thoughts in the quote above can provide a bit more of a basis to 
> start from when thinking about these kinds of design problems in Clojure. 
>
> Anyways, I was long-winded here, my apologies.  I hope at least some of 
> this helps. 
>
> DD 
>
> (2014/02/27 0:48), Robert Quinn wrote: 
> > david, 
> > 
> > I think you're right.  I've complicated things by conflating the two 
> > concepts. 
> > 
> > the overall structure of the data is a graph. when you look at an entity 
> > and their relationships it's a tree.  entity (LHS) has a set of 
> > relationships.  that was my reason for even thinking a zipper might help 
> > to maintain the RHS state.   
> > 
> > i'm trying to pass data between the "business functions" but I need them 
> > to have access to the latest state.  I was trying not couple/expose the 
> > business functions to data access ("utility functions") so that's where 
> > the complexity got introduced. 
> > 
> > the embedded functions are doing exactly what you suggest, they look up 
> > the related entities using the references.  the advantage (i thought) of 
> > having the functions embedded in the data is that the business functions 
> > don't have any control logic to choose an appropriate utility function, 
> > they just invoke the function provided to them. 
> > 
> > my concern was that i was missing some clojure idiom for "hiding" 
> > whether a data structure was fully populated vs. requiring an explicit 
> > look ups.  i think the answer is no, unless I can figure out some way of 
> > changing the behavior of (:rels entity) to do the lookup, the business 
> > function has to know that it's getting a function that must be invoked. 
> > 
> > I appreciate your help. 
> > 
> > robert 
> > 
> > 
> > On Tuesday, February 25, 2014 10:18:52 PM UTC-5, David Della Costa 
> wrote: 
> > 
> >     You are saying a zipper may be a solution--so do you have a tree or 
> >     graph structure? 
> > 
> >     If your RHS value is something that changes over time but has an 
> >     identity independent of its relationship to other nodes, then 
> >     retrieving 
> >     or constructing its value could simply be a function or set of 
> >     functions--or just a lookup in a table--once you have access to its 
> >     key, 
> >     could it not? 
> > 
> >     At least based on your description, it seems like you are 
> complicating 
> >     the design by conflating two different things: accessing the most 
> >     recent 
> >     value of the RHS value and establishing ways to look up values based 
> on 
> >     their node relationships. 
> > 
> >     (2014/02/26 1:35), Robert Quinn wrote: 
> >     > David, 
> >     > 
> >     > thanks for the reply.  I started down that path.   
> >     > 
> >     > RHS is an entity itself.  Its state changes over time, with 
> Identity 
> >     > independent of its relationships, it can be related to many 
> entities 
> >     > (LHS).  So its state has to live in one place or employ 
> >     synchronization 
> >     > (complex watchers?).  I remember seeing a reference to zipper 
> >     > functionality that seemed to imply a possible solution, I'll try 
> >     to find 
> >     > that again.. 
> >     > 
> >     > Thanks, 
> >     > 
> >     > Robert 
> >     > 
> >     > 
> >     > 
> >     > 
> >     > 
> >     > On Tuesday, February 25, 2014 10:26:30 AM UTC-5, David Della Costa 
> >     wrote: 
> >     > 
> >     >     Is there no way to simply structure the maps inside your main 
> >     atom in 
> >     >     such a way that you can look up your entities? 
> >     > 
> >     >     {lhs-uuid1 {rhs-uuid1 ["values"] rhs-uuid2 ["values"]}} 
> >     > 
> >     >     (get-in @myatom [lhs-uuid1 rhs-uuid2]) 
> >     > 
> >     >     You can wrap this in a function that does some other work to 
> >     look it up 
> >     >     if need be, if the result of the above is nil (for 
> >     example)...etc. 
> >     > 
> >     >     Seems like you're making things more complicated than they 
> >     need to be 
> >     >     here, but it's hard to know without knowing more about your 
> data. 
> >     > 
> >     >     But if I were you I would start there, as I've found thinking 
> >     hard 
> >     >     about 
> >     >     the structure of your data and and using the facilities 
> >     provided by the 
> >     >     standard library usually helps simplify things tremendously. 
> >     > 
> >     >     (2014/02/25 23:14), Robert Quinn wrote: 
> >     >     > new to clojure, in the process of unlearning years of OO. 
> >      sorry 
> >     >     for the 
> >     >     > long post. 
> >     >     > 
> >     >     > struggling with a design/impl and hoping to get some outside 
> >     >     opinions so 
> >     >     > I can understand my options (clojure better) 
> >     >     > 
> >     >     > basic concepts are straightforward and in a nutshell I'm 
> >     modeling 
> >     >     > entities and relationships  (aren't we all) 
> >     >     > 
> >     >     > --- 
> >     >     > 
> >     >     > created a pool of entities (people and groups) implemented 
> >     as maps. 
> >     >     >  each map has a :rels which is set of relationships to other 
> >     >     entities. 
> >     >     >  the pool is an atom. 
> >     >     > 
> >     >     > first approach; relationships are in the entity, add RHS 
> >     entities 
> >     >     into 
> >     >     > the LHS :rels.  the state of the RHS can change, I need the 
> >     latest 
> >     >     state 
> >     >     > of RHS not as it was when the relationship was created. I 
> >     decided 
> >     >     not to 
> >     >     > make each entity a ref (ref, atom, agent), it seemed too 
> fine 
> >     >     grained. 
> >     >     > *thoughts?* 
> >     >     > 
> >     >     > each entity has a uuid 
> >     >     > 
> >     >     > second approach, a pool of relationships (:lhs uuid :rhs 
> uuid 
> >     >     :type x). 
> >     >     > pool has all the relationships, also an atom.  the pool has 
> >     lots of 
> >     >     > benefits it "improves" other parts of the code. 
> >     >     > 
> >     >     > it doesn't solve the original problem, just moves it around, 
> >     i still 
> >     >     > can't put the actual RHS, so i'm using the uuid as a 
> >     reference, i 
> >     >     look 
> >     >     > up RHS get the latest. (second guessing why didn't i make 
> them 
> >     >     atoms and 
> >     >     > use @uuid). 
> >     >     > 
> >     >     > now my relationships aren't embedded in my entity, that 
> >     doesn't feel 
> >     >     > right, anyone dealing with the entity should still be able 
> >     to say... 
> >     >     > (:rels entity) and get the relationships, so i add a 
> >     function to 
> >     >     :rels 
> >     >     > that returns all the relationship based on the uuid of the 
> >     LHS, this 
> >     >     > works great. syntax changes and requires me to use ((:rels 
> >     entity)). 
> >     >     > 
> >     >     > *the problem is... (assumes everything within a single 
> >     process)* 
> >     >     > 
> >     >     > I want to pass this data structure around and now 
> >     dereferencing is 
> >     >     > bleeding out.  Instead of passing around a data structure, 
> I'm 
> >     >     passing 
> >     >     > around a data structure that has functions embedded in it. 
> >      which 
> >     >     seems 
> >     >     > like a cool idea, but now the callee needs to know that 
> :rels 
> >     >     returns a 
> >     >     > function, when it's supposed to have data in it. (which is 
> >     the same 
> >     >     > thing?) but I don't know how to treat it seamlessly.  (i 
> >     have not 
> >     >     > considered eval). 
> >     >     > 
> >     >     > for the :rels it's not too bad.  doesn't feel like I'm using 
> >     all the 
> >     >     > power correctly, but it works. 
> >     >     > 
> >     >     > *additional problem is, my rels look like this.* 
> >     >     > 
> >     >     > (:lhs uuid :rhs uuid :type x) 
> >     >     > 
> >     >     >  :lhs and :rhs "should be" entities, not references.  more 
> >     >     functions?  I 
> >     >     > make :lhs and :rhs return functions which dereference 
> >     entities. of 
> >     >     > course that makes selecting from rel-pool much harder 
> >     >     > 
> >     >     > that's when I knew I had to seek professional help (smile) 
> >     >     > 
> >     >     > even with a long post hard to capture all the 
> requirements... 
> >     >     > 
> >     >     >  1. the pools aren't supposed to be "public" so I can't have 
> >     all the 
> >     >     >     callee functions understanding how to do the 
> dereference.   
> >     >     >  2. the :rels function isn't one function, the truth is the 
> >     rels-pool 
> >     >     >     doesn't have all relationships, some are virtual, the 
> >     function 
> >     >     >     creates them on the fly, each entity could have it's own 
> >     >     version of 
> >     >     >     "get-relationships" 
> >     >     > 
> >     >     > any help is appreciated. 
> >     >     > 
> >     >     > i've considered 
> >     >     > 
> >     >     >   * creating a function that "denormalizes" the data before 
> >     >     sending it 
> >     >     >     out to.  now its fully formed but i lose some recency 
> (not 
> >     >     much diff 
> >     >     >     i know) 
> >     >     >   * centralizing all the derefs functions - so the callee 
> knows 
> >     >     that all 
> >     >     >     the data isn't embedded but the functions don't live in 
> >     >     structure, 
> >     >     >     just the uuids... that works well for LHS/RHS deref, but 
> >     makes 
> >     >     the 
> >     >     >     per entity virtual relationships much harder.   
> >     >     >   * rethinking my atom granularity and using @ but i don't 
> >     think that 
> >     >     >     really helps 
> >     >     > 
> >     >     > 
> >     >     > thanks for reading 
> >     >     > 
> >     >     > -- 
> >     >     > You received this message because you are subscribed to the 
> >     Google 
> >     >     > Groups "Clojure" group. 
> >     >     > To post to this group, send email to clo...@googlegroups.com 
> >     >     <javascript:> 
> >     >     > Note that posts from new members are moderated - please be 
> >     patient 
> >     >     with 
> >     >     > your first post. 
> >     >     > To unsubscribe from this group, send email to 
> >     >     > clojure+u...@googlegroups.com <javascript:> 
> >     >     > For more options, visit this group at 
> >     >     > http://groups.google.com/group/clojure?hl=en 
> >     <http://groups.google.com/group/clojure?hl=en> 
> >     >     <http://groups.google.com/group/clojure?hl=en 
> >     <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+u...@googlegroups.com <javascript:>. 
> >     >     > For more options, visit 
> >     https://groups.google.com/groups/opt_out 
> >     <https://groups.google.com/groups/opt_out> 
> >     >     <https://groups.google.com/groups/opt_out 
> >     <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 clo...@googlegroups.com 
> >     <javascript:> 
> >     > Note that posts from new members are moderated - please be patient 
> >     with 
> >     > your first post. 
> >     > To unsubscribe from this group, send email to 
> >     > clojure+u...@googlegroups.com <javascript:> 
> >     > For more options, visit this group at 
> >     > http://groups.google.com/group/clojure?hl=en 
> >     <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+u...@googlegroups.com <javascript:>. 
> >     > For more options, visit https://groups.google.com/groups/opt_out 
> >     <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 clo...@googlegroups.com<javascript:> 
> > Note that posts from new members are moderated - please be patient with 
> > your first post. 
> > To unsubscribe from this group, send email to 
> > clojure+u...@googlegroups.com <javascript:> 
> > 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+u...@googlegroups.com <javascript:>. 
> > 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.

Reply via email to