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 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.

Reply via email to