clojure-dev 2025-10-24

This seems... unexpected? I don't recall anything that says bare 'do' (not in list-head position) should get any special wrapping treatment at the start of let / fn bodies

Clojure 1.12.0
user=> (let [] do)
nil
user=> (let [] do 123)
123
user=> (let [] 123 do)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: do in this context
user=> ((fn [] do))
nil

👏 1

I think i had a fix for this in jira somewhere

Closest thing I found was some past discussions about shadowing of special forms http://ask.clojure.org/index.php/1132/special-symbols-can-be-shadowed-inconsistently which from what I can tell is about the Lisp-2-ish special treatment of these special-symbols at the head of list - but not when they're in value-position, where it appears legal to use them as local binding names

(let [do 123] do) ;; => nil
(let [do 123] do do) ;; => 123
(let [do 123] [do]) ;; => [123]

(let [if :abc] if) ;; => :abc
; but not 
(let [if (constantly nil)] (if)) ;=> throws

I think you may have found a bug

yeah, seems that clojurescript handles it differently too

ClojureScript 1.11.132
cljs.user=> (let [] do)
                    ^
WARNING: Use of undeclared Var cljs.user/do at line 1
nil
cljs.user=> (let [do :something] do)
:something

Is behavior of special forms defined in situations like these? E.g. (let [] def).

https://clojure-doc.org/articles/language/macros/ ahh, this guide points out the exact failure case ('Ouch!') and says 'Never use special names as local binding or global variable names.' It's a community guide though, not sure how definitive that statement is - are we to take it as undefined behaviour?

I think this one is special to do ?

special forms are just overridden in call sites, not as local names

special forms as globals is definitely already a thing in the wild, eg. clojure.spec.alpha/def

yep, empirically it's just do which behaves weird in value position

user=> (doseq [sym (sort (keys clojure.lang.Compiler/specials))
        :let [f (list `let* [sym :ok] sym)]]
  (println sym "\t" (try (eval f) (catch Exception e (ex-message e)))))
& 	 :ok
. 	 Syntax error compiling fn* at (1:1).
case* 	 :ok
catch 	 :ok
def 	 :ok
deftype* 	 :ok
do 	 nil
finally 	 :ok
fn* 	 :ok
if 	 :ok
let* 	 :ok
letfn* 	 :ok
loop* 	 :ok
monitor-enter 	 :ok
monitor-exit 	 :ok
new 	 :ok
quote 	 :ok
recur 	 :ok
reify* 	 :ok
set! 	 :ok
throw 	 :ok
try 	 :ok
var 	 :ok
clojure.core/import* 	 Syntax error compiling let* at (1:1).
nil

open an ask about this

👍 1

I've gotta ask: how did you discover this??? 🤯

Strangely I don't even remember the context 😦 just found it in a "huh that's weird" note to self while cleaning up some old fiddle files earlier, and decided to investigate a bit further