Let's talk about Clojure's 'if-let' form

The if-let and when-let forms are mini violations of SRP!

October 25, 2021

This post bears a similar message to a previous post: Assignments and Ifs

That post lays out a case against the feature provided by the Go programming language of inlining assignment statements into if statements. There are somewhat similar constructs in Clojure:

Let's experience a short programming adventure:

Ok, we'd like to invoke an external API to generate a token needed by our client-side code:

(if-let [token (external-api/generate-token (:customer-id user))]
  (http/ok token)
  (http/fail "Failure"))

Hmm, it would be nice to extract a separate binding for the id...

(if-let [id    (:customer-id user)
         token (external-api/generate-token id)]
  (http/ok token)
  (http/fail "Failure"))

Uh-oh, it doesn't compile because if-let only allows a single binding. Maybe define an outer let?

(let [id (:customer-id user)]
  (if-let [token (external-api/generate-token id)]
    (http/ok token)
    (http/fail "Failure")))

Ugh, now there are two 'layers' of lets. That's a bit weird. Maybe combine both bindings in a single let and define a separate if statement?

(let [id    (:customer-id user)
      token (external-api/generate-token id)]
  (if (some? token)
    (http/ok token)
    (http/fail "Failure")))

Ok, let's talk about the final code listing above. In that snippet the bindings and the logic have been decoupled. While this resulted in more code overall, I believe we are left with code that is easier to change and maintain.

"Good Design Is Easier to Change Than Bad Design." -The Pragmatic Programmer

My conclusion: The if-let and when-let forms are mini violations of SRP!

What do you think?