Recently, I have been hanging out at #clojure, talking to experts in clojure about various things and using that to learn something practical about clojure. So far, the experience has been invaluable. I believe I learnt at a much better rate from my two days at #clojure than I did this year through various sources of clojure. Anyway, I wanted to simply model data “the clojure way” & sought out some help from the experts. In this post, I will present the code I came up with and then the comments I received followed by the revised code, etc.
Say, I want to model a vacation trip. A trip can be described by having a name, a description, a start date, an end date & a group of people participating in the trip.
After some discussions and learning a few bits about resisting the urge to model data in an OO way, the gist of the advice which I agree with is - start with basic structures such as lists, maps, vectors & sets. Model your data using those. Use prismatic/schema, if required, to validate the shape of the date you receive. As you progress, you can replace maps with records as all operations on maps are supported on records.
I took the advice and modeled my trip using maps. But since there is no class definition or anything like that, I started with a “default trip” which is supposed to provide a “shape” for reference along with the default values. Then, I gave helper methods from which you can build your own trip. That looked something like:
(ns trip.core | |
(:require [clj-time.core :as t])) | |
(def default-trip | |
{ :name "New Trip" | |
:description "Enter your trip description here" | |
:start-date (t/today) | |
:end-date (-> 5 t/days t/from-now) | |
:people #{}}) | |
(defn +people | |
"Adds a person to the trip" | |
[trip & persons] | |
(assoc trip :people | |
(into (:people trip) persons))) | |
(defn -people | |
"Removes a person from the trip" | |
[trip & persons] | |
(assoc trip :people | |
(into (:people trip) persons))) | |
(defn start-date | |
"sets the start date" | |
[trip start-date] | |
(assoc trip :start-date start-date)) | |
(defn end-date | |
"sets the end date for the trip" | |
[trip end-date] | |
(assoc trip :end-date end-date)) | |
(defn name | |
[trip tripname] | |
(assoc trip :name tripname)) | |
(defn description | |
[trip desc] | |
(assoc trip :description desc)) | |
;;; usage examples | |
(-> default-trip | |
(name "NYC Trip") | |
(description "Fall trip to NYC") | |
(start-date (-> 2 t/days t/from-now)) | |
(end-date (-> 9 t/days t/from-now)) | |
(+people "John" "Jason" "Krishna" "Raju")) |
After I applied the above comments, I have removed the helper methods for start-date, end-date, name & description.
(ns trip.core | |
(:require [clj-time.core :as t])) | |
(defn default-trip | |
"returns a default trip with current date as start date & end date as 5 days from now" | |
[] | |
{ :name "New Trip" | |
:description "Enter your trip description here" | |
:start-date (t/today) | |
:end-date (-> 5 t/days t/from-now) | |
:people #{}}) | |
(defn +people | |
"Adds a person to the trip" | |
[trip & persons] | |
(update-in trip [:people] | |
into persons)) | |
(defn -people | |
"Removes a person from the trip" | |
[trip & persons] | |
(update-in trip [:people] | |
clojure.set/difference persons)) | |
(comment | |
(-> (default-trip) | |
(assoc :name "NYC Trip") | |
(assoc :description "Fall trip to NYC") | |
(assoc :start-date (-> 2 t/days t/from-now)) | |
(assoc :end-date (-> 9 t/days t/from-now)) | |
(+people "John" "Jason" "Krishna" "Raju")) | |
) |
After making above small changes, I ended with:
(ns trip.core | |
(:require [[clj-time.core :as t] | |
[clojure.set :as set]])) | |
(defn default-trip | |
"returns a default trip with current date as start date & end date as 5 days from now" | |
[] | |
{ :name "New Trip" | |
:description "Enter your trip description here" | |
:start-date (t/today) | |
:end-date (-> 5 t/days t/from-now) | |
:people #{}}) | |
(defn +people | |
"Adds a person to the trip" | |
[trip & persons] | |
(update-in trip [:people] | |
into persons)) | |
(defn -people | |
"Removes a person from the trip" | |
[trip & persons] | |
(update-in trip [:people] | |
set/difference persons)) | |
(comment | |
(-> (default-trip) | |
(assoc :name "NYC Trip" | |
:description "Fall trip to NYC" | |
:start-date (-> 2 t/days t/from-now) | |
:end-date (-> 9 t/days t/from-now)) | |
(+people "John" "Jason" "Krishna" "Raju")) | |
) |
As you can see, there is only default data that describes the shape of the data & a couple of helper methods to åssist with nested structures. We have a trip!
Overall, its highly recommended for those trying to learn clojure to join the #clojure on webchat.freenode.net & ask away your questions. The folks over there are very helpful (justin_smith, tbaldridge, ToxicFrog and a lot of other very helpful folks). Looking forward to learn more …