clojure-dev

yuhan 2025-10-24T08:44:02.508499Z

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
bronsa 2025-10-25T13:22:01.393109Z

I think i had a fix for this in jira somewhere

2025-10-25T14:49:01.667349Z

indeed you did: https://clojure.atlassian.net/browse/CLJ-1216

yuhan 2025-10-24T08:53:51.498909Z

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

borkdude 2025-10-24T08:59:09.790359Z

I think you may have found a bug

yuhan 2025-10-24T09:00:16.989739Z

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

daveliepmann 2025-10-24T09:14:26.703579Z

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

yuhan 2025-10-24T09:15:06.583609Z

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?

borkdude 2025-10-24T09:15:50.846359Z

I think this one is special to do ?

borkdude 2025-10-24T09:16:33.915059Z

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

yuhan 2025-10-24T09:22:10.475219Z

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

yuhan 2025-10-24T09:29:17.616649Z

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

2025-10-24T13:36:21.431769Z

open an ask about this

👍 1
seancorfield 2025-10-24T15:38:39.855159Z

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

yuhan 2025-10-24T17:07:09.168129Z

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