defun, core.match, tagged vectors - seems like I can emulate Elixir
function pattern match behaviour. I took some simple code I found online
(https://twitter.com/Xzilend/status/640282621042233344) and rewrote it to
1) use only tagged vectors (not quite) and 2) use defun and tagged vectors.
I am eager to hear your thoughts on the rewrite.
Original
(defn- register
[db mailer email password]
(if (users/valid? email password)
(if (users/taken? db email)
(response/bad-request {:errors [{:message "That email is taken."}]})
(do (users/do-create! db email password)
(users/notify mailer email activate-subject activate-text)
(response/ok (auth/authenticate email))))
(response/bad-request {:errors [{:message errors/invalid-creds}]})))
Lets ignore the possible concurrency issues for now.
Rewrite without using core.match
(defn register
([db mailer email password]
(register [:start {:db db :mailer mailer :email email :password password
}]))
([status {:keys [db mailer email password] :as args}]
(cond
(= status :start)
(if (users/valid? email password)
(register [:check-unique args])
(register [:err {:res "Invalid credentials"}]))
(= status :check-unique)
(if (users/taken? db email)
(register [:create args])
(register [:err {:res "User already exisits"}]))
(= status :create)
(do (users/do-create! db email password)
(users/notify mailer email activate-subject activate-text)
(register [:ok {:res (auth/authenticate email)}]))
(= status :ok) (response/ok (:res args))
(= status :err) (response/bad-request {:errors [{:message (:res args
)}]}))))
With no pattern matching I had to wrap the actual arguments into a map so
that all recursive calls hit the two argument arity.
Using defun and tagged vectors,
(defn register [& args] (register' (into [:start] args)))
(defun- register'
([:start db mailer email password]
(if (users/valid? email password)
(register' [:check-unique db mailer email password])
(register' [:err "Invalid credentials"])))
([:check-unique db mailer email password]
(if (users/taken? db email)
(register' [:create db mailer email password])
(register' [:err "User already exists"])))
([:create db mailer email password]
(do (users/do-create! db email password)
(users/notify mailer email activate-subject activate-text)
(register' [:ok (auth/authenticate email)])))
([:ok res] (response/ok (:res args)))
([:err msg] (response/bad-request {:errors [{:message (:res args)}]})))
I couldn't find a way to refer to the arguments vector from within the body
of a case. I would have preferred writing the :start case like this,
([:start _ _ email password]
(if (users/valid? email password)
(register' (into [:check-unique] it)
(register' [:err "Invalid credentials"])))
I find the defun version better compared to the plain if else version. Step
transitions are explicit. Early exit is a transition to err state. The
`response/bad-request` call occurs only once as opposed to at each early
exit point.
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.