Skip to content

Instantly share code, notes, and snippets.

@svs
Created April 24, 2012 09:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save svs/2478214 to your computer and use it in GitHub Desktop.
Save svs/2478214 to your computer and use it in GitHub Desktop.
Question about Rich Hickey's 'use hashes not classes' statement

So I don't have a model called Person, but I use hashes like {:name => "Siddharth", :sex => "Y"}. Great. Now I want to validate the presence of certain fields before persisting to the database. What do I do?

Module PersonValidation
  function validate_presence_of_name(person_hash)
    person_hash[:errors][:name] = "EEENKKK!" unless person_hash[:name]
  end
  
  ...
  ...

end

So I must be missing something because how is this different from a class in a dynamic language? The above approach means you can easily swap out functions in one module for another and thus not tie the validation implementation to the structure of the loan hash, but this is trivially simple in Ruby even using classes. Is there some other benefit?

@ghoseb
Copy link

ghoseb commented Apr 24, 2012

Clojure maps (aka hashes) are suitable for modelling domain values when you don't need fast type based, polymorphic dispatch (which is most of the times for me, but YMMV). If you don't need type based dispatch, you could just use maps. Then your domain values will be able to participate in Clojure's sequence abstractions without much hassle. You could stick in the validator functions, for example, in the metadata like this -

(def svs ^{:validator (fn [x] (every? #{:name :sex} (keys x)))} {:name "SVS" :sex "Y"})

You can then write a generic function which can persist any entity after validating it -

(defn valid-obj?
  [o]
  (let [valid? (:validator (meta o) identity)]
    (valid? o)))

(defn persist-to-db!
  [entity]
  {:pre [(valid-obj? entity)]}
  ;;; DB stuff
  )

Note that in here the persist-to-db! function will throw an exception if the precondition fails. This is just an example, you don't really need to use it like so.

All of this is still better than a class because of two reasons -

  • You are not mixing data and behaviour
  • The so-called objects are not opaque, they are still just data with behaviour supplied from the "outside"

Also, take a look at Trammel, a very nice contracts-programming library for Clojure.

You can also use Clojure's defrecord feature to create map-like types and introduce similar validation behaviour using protocols.

PS - When you talk about "swapping out functions" from classes in Ruby being trivial, do you mean Monkey Patching?

@yeban
Copy link

yeban commented Apr 24, 2012

I don't seen any advantage. You might as well have passed a person_obj instead of person_hash and said unless person_obj.name. In Ruby, a class gives you two things: a container to attach the defined methods to (klass), and a scope to look up method and variable names. Hash is a container; using a Hash instead of a class is not the same.

@svs
Copy link
Author

svs commented Apr 24, 2012

Not monkey-patching the class, rather extending actual object instances by dynamically mixing in modules. The two are very different ;-)
I did not know about clojure :pre and :post. Certainly very powerful feature baked into the language.

Now what else is it that I don't know.

Guess it's time to get Joy of Clojure!

@nipra
Copy link

nipra commented Apr 24, 2012

BG has already given an excellent answer. Personally I don't like OOP much for the same reason I don't like [much] languages with mutable data structures. As Alan Kay famously said: ``When you have a 'setter' on an object, you have turned an object back into a data structure'' [Copy-pasted from the web] [Watch this presentation: http://tele-task.de/archive/video/flash/14029/] . Personally I'll be much happier programming in a language with a good namespace design and immutable data structures. Such a language is much simpler and better to reason about. Clojure is one such language and I absolutely love it because of that. If I ever need a usecase for multiple dispatch I can use multimethods. That said OOP as per Alan Kay is more about message passing than anything else.

@ghoseb
Copy link

ghoseb commented Apr 24, 2012

Not monkey-patching the class, rather extending actual object instances by dynamically mixing in modules.

In that case, you just need an immutable data-structure and some metadata. No need to mess around with opaque, mutable objects.

@svs
Copy link
Author

svs commented Apr 24, 2012 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment