Created
July 4, 2014 16:11
-
-
Save Janiczek/576c553e700191fceb87 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; global state | |
;; pros/cons: | |
;; + the code looks nice | |
;; - can't control the state | |
(defn milk-the-cow [] | |
(if (have? :bucket) | |
(do | |
(item-remove! :bucket) | |
(item-add! :bucket-of-milk)) | |
(get-kicked!))) | |
;;; explicit state | |
;; pros/cons: | |
;; + can debug better by putting in our own state (no hidden parameters) | |
;; - have to pass the state as an argument everywhere | |
;; - we still change the state under our feet (thus, impure function) | |
(defn milk-the-cow [s] | |
(if (have? s :bucket) | |
(do | |
(item-remove! s :bucket) | |
(item-add! s :bucket-of-milk)) | |
(get-kicked! s))) | |
;; (maybe returning state at the end of function could help a bit?) | |
;;; return-changed-state fn | |
;; pros/cons | |
;; + referential transparency | |
;; + pure function | |
;; - ? | |
(defn milk-the-cow [s] | |
(if (have? s :bucket) | |
(do | |
(-> s | |
(item-remove! :bucket) | |
(item-add! :bucket-of-milk))) | |
(get-kicked! s))) ;; or (-> s get-kicked!), if you prefer |
I don't see a pure function in the third example. item-remove! still looks like a mutable version (bang in the end). Anyway, you can add one more drawback to the mutable version, it contains race conditions, as the state is not changed atomically. My preferred solution is
(defn milk-the-cow [cowboy]
(if (contains? cowboy :bucket)
(do
(-> cowboy
(dissoc :bucket)
(assoc :bucket-of-milk true)))
(update-in cowboy [:kicked] #(inc (or % 0))))
(def cowboy (atom {:bucket true}))
(swap! cowboy milk-the-cow)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When I look at it from a distance, it looks like the last two examples are almost the same.
The difference is that the 2nd example passes the old state (
s
) to both of the functions, instead of passing the return value fromitem-remove!
toitem-add!
, like this:Thus the 2nd example has the hidden assumption in it that the functions mutate the state (otherwise why would we even call the
item-remove!
?).The last example, on the other hand, CAN have the assumption that the functions don't mutate anything - they return the modified state and expect their caller to do something with it.