Fork me on GitHub
#babashka
<
2023-07-30
>
John Doe16:07:27

Hi, any idea why this macro works in clj but not in bb, I'm trying to def some stuff at compile time, but bb complains clojure.lang.ExceptionInfo: Var name should be simple symbol.

(defmacro mydef
  []
  (let [some-vals-need-computing [1 2 3]]
    `(def myvar ~some-vals-need-computing)))

Bob B16:07:17

I would guess that it's because def is a special form that gets special treatment in clj... I think the symbol in the syntax quote should be ~'myvar to ensure that the argument to def is an unqualified symbol (because syntax quote would typically try to qualify the symbol)

borkdude16:07:19

Apparently this is allowed in Clojure:

user=> (def user/foobar 1)
#'user/foobar

borkdude16:07:31

but I think officially it's not allowed, probably just under-checked

John Doe16:07:48

wow..thanks Bob. It works.

John Doe17:07:48

when i macroexpland on the original version, it did expand to a full qualified var in bb, but it blow up when I actually use it. werid

John Doe17:07:12

Sorry I don't quiet follow, what is exactly allowed but not officially allowed? And how is that related to my case here?

John Doe17:07:47

I'm under a namesapce

borkdude17:07:57

user/foo is not a simple symbol but a qualified symbol

John Doe17:07:34

yes... but macroexpand expand to myns/myvar

John Doe17:07:54

which is exactly right and what i want right?

didibus17:07:22

no, you should quote it

Bob B17:07:05

according to <https://clojure.org/guides/learn/syntax#_def>, the argument to def should be a 'simple' symbol (no namespace), and def should create that var is the current ns. So, the fact that syntax quoting puts an ns on the symbol should probably 'break', but in the special case where the ns happens to be the current ns, it works in clj (but not in bb). So, the ~'myvar ensures that the def statement that gets created doesn't have a ns on the symbol.

❤️ 4
borkdude17:07:49

Consider this:

(defmacro mydef
  []
  (let [some-vals-need-computing [1 2 3]]
    `(def myvar ~some-vals-need-computing)))
#'user/mydef
user=> (ns foo)
nil
foo=> (user/mydef)
Syntax error compiling def at (REPL:1:1).
Can't refer to qualified var that doesn't exist

borkdude17:07:12

So while your macro works, it only works if you use it from the same namespace in clj, which is pretty brittle. Best to avoid it

❤️ 2
John Doe17:07:39

Thanks! I think I understand now.

borkdude17:07:36

I can relax the check in SCI to make it behave like JVM Clojure though, but it's not the case today and I doubt it is a good relaxation

borkdude17:07:18

I just read this code: https://github.com/clojure/clojure/blob/501348deb1e49736f3c49f2c177bcff947fe3a17/src/jvm/clojure/lang/Compiler.java#L532-L543 and it does seem an intended choice. I'd also check with CLJS too see what they do

borkdude17:07:49

Apparently CLJS supports it too:

cljs.user=> (def cljs.user/dude 1)
#'cljs.user/dude
cljs.user=> (def user/dudex 2)
Unexpected error (ExceptionInfo) compiling at (<cljs repl>:1:1).
Can't def ns-qualified name in namespace user at line 1
cljs.user=> (ns user)
WARNING: user is a single segment namespace at line 1
nil
user=> (ns cljs.user)
nil
cljs.user=> (def user/dudex 2)
Unexpected error (ExceptionInfo) compiling at (<cljs repl>:1:1).
Can't def ns-qualified name in namespace user at line 1

borkdude17:07:15

I'll make a SCI issue to support it in SCI too then, but currently you have to use ~'my-var

John Doe17:07:01

Thanks Michiel. It's been a while since I touched any clojure macro, I'm pretty rusty at macro and comply forget about the unqualified symbol syntax, which is why I made the mistake. Now that you explained, I think it's good not to relax it actually. 😀

borkdude17:07:22

My name is Michiel :)

John Doe17:07:34

Opps, sorry (