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.