It sounds like you're building an API to provide documentation, but instead
why not just document the data structure directly?

Once you have your data structure design, add functions for non-trivial
tasks. So something like "red?" seems a little too trivial and specific.
The raw s-expression, (= (:color fruit) :red), is just as clear, if not
clearer, than (red? fruit).

On the other hand, something like "packingvol" seems non-obvious enough to
have a function for it.

Think about constructors in the same way. Use them when they simplify the
code, don't use them if they're trivial or obvious.

In a language with immutable data structures, you can sometimes use
constants instead of constructors. In one of the projects I'm currently
working on, rather than have a "make-system" function, I've created an
"empty-system" constant that can be built upon.

- James


On 26 June 2014 04:37, Mark P <pierh...@gmail.com> wrote:

> Thanks Ryan, Mike and James for your comments and info.  Ryan I will
> follow up the links you posted.
>
> In the meantime, a request for some clarification...
>
> I have read / watched clojure stuff along these lines... ie that data
> hiding (in an immutable data context) is bad.  I thought my approach was
> taking this into account.  Ie, I am actually using a hash-map for my fruit
> instance data (rather than say closures for true data hiding) and this is
> not hidden - it is freely accessible to users of my fruit library.  Users
> can choose to use getter functions or use more direct map access
> techniques.  The getter functions sort of provide self documentation about
> interface intent, without restricting/forcing users to be limited to this
> if the interface has weaknesses.  I guess I was trying to "take the best
> from both approaches".  But maybe you are telling me that even this
> watered-down-encapsulation is not good?
>
> Ryan you are suggesting that maybe defrecord is the way to go.  I haven't
> read about that yet, so I will look into that.
>
> But just suppose I wanted to stick with using raw hash-maps for my data.
>  Would you suggest that instead of my current approach, I should...
>
>    - Not implement any getter function when a keyword accessor will do.
>     Eg don't implement (defn color [fru] (:color fru)), just let library users
>    do (:color fru) themselves.
>    - Do still implement other getters, eg (defn red? [fru] (= :red fru))
>    and eg (defn packingvol [{:keys [size]}] (* size size size)).
>    - What about "make"?  Do I still implement a fruit constructor
>    function, or do I leave it to users to use usual hash-map constructors?
>     (And if I should not implement a fruit constructor, how do I indicate to
>    users the shape that a fruit hash-map should take?  Do I just document this
>    in comments?)
>    - Was I right to construct a new namespace for my fruit-related data
>    and functions?
>
> Thanks,
>
> Mark.
>
> On Thursday, 26 June 2014 11:42:01 UTC+9:30, Ryan Schmitt wrote:
>>
>> In object-oriented programming, encapsulation is always and everywhere
>> regarded as a highly significant design virtue, but the Clojure people have
>> a bit of a different assessment, particularly when it comes to information.
>> In one talk, Rich Hickey pointed out that encapsulation is for hiding
>> implementation details, but information doesn't *have* an
>> implementation; there are no innards to encapsulate *unless you add them*.
>> Stuart Halloway gave one talk where he made a brilliant point about how
>> abstraction is valuable for *commands*, because then it limits what you
>> have to know, but it's not valuable for *queries*, because then the
>> abstraction (i.e. the custom accessors that "encapsulate" your data) only
>> limits what you can perceive.
>>
>> And of course, there's Alan Perlis's old saw that "it is better to have a
>> hundred functions operate on one data structure than to have ten functions
>> operate on ten data structures"--that is to say, by using generic data
>> structures (e.g. maps) to directly represent information in your domain,
>> you can reuse all of the generic functions that operate on those data
>> structures, whereas encapsulated data screws your clients because it
>> renders all of their generic collections libraries useless. (This is one of
>> the ways in which OOP has failed to deliver on its promise of pervasive
>> code reuse.)
>>
>> Some of Rich Hickey's talks that touch on the subject:
>>
>> http://www.infoq.com/presentations/Value-Values
>> http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey
>>
>> As for your particular example, I recommend looking at defrecord, which
>> generates a data structure that behaves like a map (it can be perceived and
>> manipulated generically), as well as various constructors and readers and
>> so forth for that type. defrecord also lets you specify protocols for your
>> data type. See:
>>
>> http://clojure.org/datatypes
>>
>> Also be sure to check out Prismatic's survey of Clojure's choices for
>> doing "object"-like things:
>>
>> https://github.com/Prismatic/eng-practices/blob/master/
>> clojure/20130926-data-representation.md#data-types
>>
>> On Wednesday, June 25, 2014 6:34:50 PM UTC-7, Mark P wrote:
>>>
>>> I've only recently started real clojure development, so very much still
>>> learning what styles work and what don't.  I have a question about naming
>>> attribute getters...
>>>
>>> Suppose I want to model "fruit" entities.  I will use a hash-map to
>>> represent the data for each such entity, and will defn a "make" function to
>>> construct fruit instances.  I will put "make" and any other fruit-related
>>> functions within a new namespace "myprog.fruit".
>>>
>>> Because my data representation is a map, strictly speaking I don't need
>>> any attribute getter functions.  Because I could simply use the attribute
>>> keywords in the map as my getter functions, eg (:color fru).  But I will
>>> create actual getter functions within my namespace anyway, for a few
>>> reasons.
>>>
>>>    1. It will signal to any users of my fruit library, which attributes
>>>    are "official" and are expected to be used / supported, versus attributes
>>>    which are more of an implementation detail.
>>>    2. Some attributes will simply be (defn color [fru] (:color fru)),
>>>    whereas others are less direct, eg (defn red? [fru] (= :red fru)) or
>>>    another eg (defn packingvol [{:keys [size]}] (* size size size)).
>>>    3. Down the track I can change my data representation, and just
>>>    reimplement the getter functions.  Users of my fruit library who have 
>>> stuck
>>>    to my getter functions interface will not need to change a thing.
>>>
>>> This approach seems okay to me so far, though I am open to critiques and
>>> alternative suggestions.  But one issue has come to mind... leading to the
>>> question of this post...
>>>
>>> At the end of 2. above, I have defined a local let symbol "size" as part
>>> of the map destructuring.  But it so happens I have already defined a size
>>> getter, namely (defn size [fru] (:size fru)).  So within the definition of
>>> packingvol my size getter is masked by the local symbol "size".  Now I'm
>>> not sure this masking causes much harm... I don't actually need to use the
>>> size getter here, and if I really needed it I could always use
>>> "myprog.fruit/size"...  But something still makes me feel uncomfortable
>>> about it, maybe because this masking could end up happening a lot within my
>>> fruit.clj source code file.
>>>
>>> Now I could just switch to a new naming convention for my getters, ie
>>> "color-get", "red?-get", "size-get", "packingvol-get" etc.  But I'm not
>>> sure I like the extra verbosity.  And for users of my fruit library - who
>>> will be working in a different namespace - this problem mostly goes away as
>>> they will probably be namespace-qualifying access to my getters.
>>>
>>> Is using self-named attribute getters a good idea?
>>>
>>> Thanks,
>>>
>>> Mark.
>>>
>>>  --
> 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/d/optout.
>

-- 
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/d/optout.

Reply via email to