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

+1 vote
in Compiler by
retagged by

It is a relatively well-known fact that do, when used at the top level of a namespace, lifts its children to the top-level. Which makes it suitable for e.g. writing macros that emit code that requires some namespace and immediately uses it.

That behavior is relied upon by existing libraries and is described online in various places, but it's not documented at https://clojure.org.

Perhaps it makes sense to explicitly document this particular quirk of do and its usage.

2 Answers

+2 votes
by
selected by
0 votes
by

What needs to be doc'ed about it?

by
The fact that it is an expected behavior that can be relied upon and not a happenstance that should be avoided. See the original discussion with differing opinions here: https://clojurians.slack.com/archives/C03S1KBA2/p1752422724214819
by
Specifically, that top-level forms in a do are compiled separately. Consider:

```
(ns example1)

(when true
  (require '[clojure.set :as set])
  (set/difference #{1} #{2}))
```

and

```
(ns example2)

(do
  (require '[clojure.set :as set])
  (set/difference #{1} #{2}))
```

The namespace `example2` compiles and runs fine, because `do` fully evaluates each of its forms sequentially. The namespace `example1` fails, because the compiler tries to resolve the symbol `set/difference` before the `require` form has been evaluated.

There is a difference in behaviour there that is not necessarily intuitive. One can argue that the observed behaviour is consistent with existing documentation, but since it's not called out, it is unclear whether this is intended behaviour and can be relied upon moving forward.
by
Note that the whole do form is read (so quotes and reader macros are handled at that point), then the forms in the do are evaluated. Saying they are "compiled separately" isn't accurate.

user=> (ns example3)
nil
example3=>
example3=> (do
      #_=>   (require '[clojure.set :as set])
      #_=>   `set/difference)
set/difference ;; quoted symbol does not resolve ns alias here b/c it isn't available when the form is read
example3=> `set/difference ;; but now the set alias is available
clojure.set/difference
...