Clojure parser confusion

This is mostly to document something that was confusing me with Clojure. It feels like an inconsistency in the language but maybe it’s just me.

Sample A:

(my-function 1.41 (slow-operation))

Sample B:

(if (discriminator-returns-false)
    (slow-operation)
    "result"
    )

Sample C:

(case selector
    :match-a (slow-operation)
    :match-b "result"
    )

My intuition was that:

  • In Sample A, (slow-operation) must obviously be evaluated first to provide an input to (my-function) regardless of whether (my-function) uses it because Clojure is not a lazy language.
  • In sample B, (slow-operation) won’t be executed because that’s how if statements work.
  • In Sample C, it seemed intuitive that (slow-operation) would be evaluated because it’s an argument to the (case) function in the same way as sample A.

In a fully lazy language (Scheme, Haskell) the intuition is that whatever the operation, it won’t be evaluated until something tries to use the result. In sample C, (case) is not a defined special form so it feels like (slow-operation) needs to be executed to generate an argument for (case), but that would be horribly inefficient. Therefore, it feels like it should be a ‘(slow-operation) or #(slow-operation) that will be evaluated after the (case) decides which option to use.

In the end, this works in the most reasonable, probably obvious way - (slow-operation) is correct and only evaluated in the match-a branch. (case) is a macro which does some trickery to avoid unnecessary execution. I was hoping there was an intuitive way to tell which syntax to use but “convention” and reasonable guesses appears to be the answer.