Search code examples
clojuredestructuring

Destructuring argument in Clojure


Newbie enjoying Clojure a lot here. So, I have an HTTP route:

 (POST   "/login"    request   (post-login request))

here, "request" is a map with a lot of http things inside. And the "post-login" function:

(defn post-login
  ;; Login into the app
  [{{email "email" password "password"} :form-params session :session :as req}]
    (let [user (modusers/get-user-by-email-and-password email password)]
     ;; If authenticated
     (if-not (nil? (:user user))
      (do
       (assoc (response/found "/")
         :session (assoc session :identity (:user user)) :flash "Bitte schön!"))
       (assoc (response/found "/login") :flash "Etwas stimmt nicht mit deinem Zugriffsprozess"))))

but I don't understand how "request" is being destructuring. How "email" and "password" are now available inside the function?, and what ":as req" means? Is there a name to this "technique"?


Solution

  • If it's still not clear from the comments, here's a quick answer:

    Your post-login function receives a single argument which is a map representing HTTP request. This argument is stored in req with the help of the special :as keyword. So (defn post-login [req] ...) and (defn post-login [{:as req}] ...) are exactly the same.

    A (one) way that map destructuring works is that you provide a map key on the right side and the thing to which it should be bound on the left side, e.g. [{form-params :form-params :as req}]. Now the :form-params key in req map will be bound to the form-params symbol available inside the function body.

    Things get more interesting when you do "nested" destructuring. In your case, [{{email "email" password "password"} :form-params :as req} just means that :form-params key inside the request will be bound to {email "email" password "password"} which again is a destructuring itself. Eventually, the "email" key in :form-params will be bound to the email symbol and the same for password. As before, if you need access to the whole request (not just some params inside it) you may get that via :as.

    Note: there's an alternative syntax which you can use (a kind of shortcut): [{{:strs [email password]} :form-params session :session :as req}]