I just started hacking around with Clojure, and although I adore the language, I can't understand how to do certain things idiomatically.
Writing a web-app using compojure, here's one of my controller actions:
(defn create [session params]
(let [user (user/find-by-email (params :email))]
(if user
(if (user/authenticate user (params :password))
(do (sign-in session user)
(resp/redirect "/home?signed-in=true"))
(resp/redirect "/?error=incorrect-password"))
(let [new-user (user/create params)]
(sign-in session new-user)
(resp/redirect "/home?new-user=true")))))
I'm writing this in a very imperative way. Using so many let
s/if
s/do
s, I can't help but think I'm doing something very wrong. How would I write this functionally?
Here's the psuedocode for what I'm trying to do
look if user exists
if user exists, try to sign user in using password provided
if password is wrong, redirect to "/?error=incorrect-password"
if password is correct, sign user in and redirect to "/home?signed-in=true"
else create user, sign user in, and redirect to "/home?new-user=true"
Thanks so much!
There isn't anything non-functional about if
- it's a perfectly good, purely functional construct that evaluates an expression conditionally. Too many if
s in one place might be a warning sign though, that you should be using a different construct (e.g. cond? polymorphism with protocols? multimethods? a composition of higher order functions?)
do
is more tricky: if you have a do then it implies that you are doing something for a side effect, which is definitely non-functional. In your case sign-in
and user/create
appear to be the side-effectful culprits here.
What should you do about side-effects? Well they are sometimes necessary, so the challenge is how do your structure your code to ensure that side effects are controlled and manageable (ideally refactored out into a special state-handling danger zone so that the rest of your code can stay clean and purely functional).
In your case you might consider:
Functional programming styles encourage using higher level functions like map, reduce and filter, also they force you to deal with immutable data structures most of the time. Nothing is wrong with your code so far, since you haven't broken any single rule in functional programming. However, you can improve your code a little bit, like combining let and if using if-let.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With