Share your thoughts in the 2021 Clojure Community Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Macros by

The docstring for clojure.core/binding says that it "executes the exprs in an implicit do". I would thus expect the elements of the body to be executed in order and the value of the last be returned (c.f. the docstring for do).

However, the body expressions are not wrapped in a do or a let, but rather a try. This allows for some surprising behavior:

(binding [clojure.core/*print-length* 10]
  (/ 10 0)
  (catch Exception e
    :failure))

The above expression returns :failure, while the following more obviously throws a compiler exception because catch can't be used without a try.

(binding [clojure.core/*print-length* 10]
  (do 
    (/ 10 0)
    (catch Exception e
      :failure)))

As far as I can tell from the git history, this implementation detail hasn't changed since 2008, so maybe there's some good reason for it. If there is, what is it? If not, can we change the implementation of binding to actually have a do around ~@body?

1 Answer

+2 votes
by
selected by
 
Best answer

try has an implicit do in it - what do you want to do that doesn't work?

(binding []
  (println "1")
  (println "2")
  3)
;; 1
;; 2
;;=> 3
by
"what do you want to do that doesn't work?" That's a fair question, and the answer is "nothing." Rather, there is something that works (the first code block in my original question) that I would expect to fail.
by
Well, don't do that.
by
(And when I say "works" I mean it returns a value rather than throwing an exception)
by
Presumably, it has to use try/finally so that it can pop the bindings regardless of whether the wrapped code succeeds or fails?
by
@Sean Corfield:
Right, but it could have an extra `do` around the body to prevent callers from adding extra `catch` clauses at the end of the `try`.

@Alex Miller:
Obviously, I'd never do that on purpose. :)

So I did some more reading and realized you're right. `binding` expects the body to be a list of "expr(ession)s". According to https://clojure.org/reference/evaluation, every form is an expression except for special forms. `(catch Exception e)` is a special form and is thus not necessarily an expression. Therefore it is not necessarily valid input to `binding`. It seems that `binding` could be modified to handle this input without invalidating the docstring, but the current docstring matches the current behavior, which is what I was really worried about.
...