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 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
--- 
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 clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to