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

+1 vote
ago in Compiler by
retagged ago 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

+1 vote
ago by
selected ago by
0 votes
ago by

What needs to be doc'ed about it?

ago 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
ago 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.
ago 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
...