On Wednesday, June 13, 2012 2:52:15 AM UTC-6, Vinzent wrote: > > I do use pre-conditions where the test condition is simple, ie is this a >> string? does the map have a :field-type? however I get a lot of my input >> data from http requests as json which have similar structures but different >> semantics, so I often do not have preconditions where type is not >> explicit. For example a string could be an list id or a contact id or an >> encoded json document. While it is possible to try to parse a string for >> json to verify its type this is seems very computationally expensive and >> therefore usually inferred from the context. >> > > Why do you care about computational expensiveness of pre and post > conditions? They'll be turned off in production anyway. >
How does one turn off pre/post conditions in production? I agree with Sean however and generally think that assertions in production a bad thing. > > >> For example, my first contact model I had {... :fields [{:field-type >> :email :value "" ...}]} I later realized that the endpoint to get the >> contact information provided them grouped and I was filtering a lot based >> on field-type anyway so a more effective data structure was {:emails >> [{:value "" ...}]}, :field-type was just an implementation detail, the >> email object still has a value and associated behavior but the map no >> longer contains a :field-type. However, phone numbers have exactly the >> same {:value ""} structure so the only way to determine if it is an email >> or a phone number is from context or by parsing the string. >> > > I believe using records or adding :type metadata would be an ideal fit in > this case. > I agree, an explicit type field makes dispatching easy. However this data structure returned by (http/get ... :as json) so if I want to add type information I need to walk the tree and rewrite it. Not necessarily a bad idea, but in some cases the only thing I need is the eTag and so the additional processing may in some cases unnecessary. One could easily make data conversions lazy by doing something like (defrecord Contact [contact]) (defmethod emails Contact [contact] (map map->Email (:emails contact)) to delay the computation until the values are actually requested. However, note that emails is now a multimethod method not a value and the consumer needs to use (emails contact) rather than (:emails contact)... Thus as I was saying previously is that (def emails :emails) gives you the flexibility to delay computation if desired. > I also tried using multimethods instead of protocols, but I still found I >> needed to restart the JVM frequently. I think whenever I would re-eval the >> namespace with the record type and (defmethod ...) a new class file would >> be emitted and the objects in my swank session need to be recreated. >> > > defmulti has defonce semantic; you can use (def foo nil) (defmulti foo > ...) to workaround this. A topic about this problem was recently created in > dev mail list, so it'll probably be fixed soon. > > I disagree. Ironically, if you told a java developer that getters were a >> premature optimization and he should use fields instead he would look at >> you funny. Getters are not an optimization and if anything have a minor >> performance penalty. However, using fields makes one very dependent on >> implementation details doing so is considered bad practice. I don't see how >> this is any different in clojure. >> > > In clojure, structure of your map is a part of the contract. It's not an > implementation detail - it's the same as getters\setters in java. > EXACTLY my point! Clojure does not distinguish between properties and data representation and these are NOT the same thing. There are many different ways to represent data. For example the area of a shape can be represented in many different ways, square inches, square miles, a rectangle, circles, polygons, or perhaps complex geometry requiring calculus all of which could be asked what is your area in square feet. Area is a property of the object, the width, radius, number of sides, etc is an implementation detail. You may then ask so why don't you just pass in {:area } as square feet instead of the radius of the circle? Because the value may not be used by the function. If its not used then why is it part of the contract? Because it may be used conditionally, for example, maybe the function needs to find the first shape that will fit within a region once that limit is reached it no longer requires the area for any other shapes. So if the shape requires complex calculus which has been written in another programming language and thus requires a rpc call to a network service to compute the value that is only used sometimes seems wasteful and inefficient if the value is only sometimes computed. This example is somewhat contrived, but it is not that different from what I am doing. For each contact record I need to perform data enrichment, normalization, cleansing, and feature extraction and calculate similarity scores to determine if contacts are likely to be the same and some things only need to be calculated when other conditions are met. For example name similarity can be very complex. We actually have a service that uses US census data to determine how common or uncommon a persons name is within a region, however if based on other information the contacts can be classified with sufficient precision this computation is unnecessary. My point is that properties with getter functions allow you to defer computation, keywords do not. Keywords are not like java getters they are like java fields. Instead of (:property themap), one should use (def property :property) (property themap). This allows you to defer computation and change data representation if necessary or desired. It is a bad practice in java and IMHO it should be considered bad practice in clojure for EXACTLY the same reasons as they are bad practice in OO. And with macros programming it is really easy to many properties at once with only a line or two, so I don't see why there is so much resistance to using them. > >> For example, my email data structure currently has just {:value " >> u...@domain.com"} however for various reasons it might actually be >> beneficial for me to use {:user "user" :domain "domain.com"}. >> > > Well, this is a contrived example, since you'd use a plain string instead > of a map with the single :value key :) Given that, user and domain would > probably be functions. > Actually this is only somewhat contrived. It is not uncommon for a user to the same nickname in his email nickn...@domain.com and in twitter handle, and this is a useful similarity feature when this computation is performed for each *pair* of field in each *pair* of contacts this computation may need to be performed millions of times. > > There are still a lot of things I like about clojure, immutable persistent >> data structures, equality semantics, meta programming and embedded >> languages such core logic, cascalog, etc. Abstraction and data hiding are >> different things. In OO I too think private methods are a code smell, >> (they should be moved to public methods within another class), but the >> clojure community seems to believe encapsulation is only about data hiding >> and does not currently seem to value the age old abstraction principle. >> > > I think it's rather in the process of finding it's own way of creating > abstractions. > Perhaps lisp programmers already did? CLOS and OO was born? -- 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