The question of how to specify the "shape" of your data is an important one, and I think one of the contributions of Clojure is the way it isolates that problem, instead of complecting it with object-oriented design or static type checking. You might look at Prismatic Schema, a library that offers declarative data description and validation without the baggage of OOP or the complexity of static typing:
https://www.youtube.com/watch?v=o_jtwIs2Ot8 https://github.com/Prismatic/schema There's also core.typed, an optional static type system for Clojure that allows you to specify your data's shape as a type of map, called a heterogeneous map: https://github.com/clojure/core.typed/wiki/Types#heterogeneous-maps There's also my own dynamic-object, which is an attempt to bring these ideas to Java development without ruining them: https://github.com/rschmitt/dynamic-object (see in particular https://github.com/rschmitt/dynamic-object#schema-validation) You can implement all the convenience functions you want; as long as you're not using them to encapsulate or abstract away your data, they're probably harmless. A function like 'red?' doesn't seem like it's adding too much value, but a less trivial function could easily be worth providing as part of your API. Just because your clients *can* use generic collections operations doesn't necessarily mean that they should *have to*. I'm not sure I understand your question about namespacing. Creating a namespace for your code is basically mandatory--are you asking about the ideal granularity of namespaces? Prismatic has another eng-practices document dealing with namespace organization: https://github.com/Prismatic/eng-practices/blob/master/clojure/20130927-ns-organization.md On Wednesday, June 25, 2014 8:37:26 PM UTC-7, Mark P 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.