This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-26
Channels
- # aleph (9)
- # announcements (31)
- # babashka (23)
- # beginners (35)
- # biff (2)
- # calva (5)
- # cider (10)
- # clara (11)
- # clerk (114)
- # clj-kondo (18)
- # cljdoc (37)
- # clojars (7)
- # clojure (24)
- # clojure-austin (10)
- # clojure-europe (27)
- # clojure-nl (1)
- # clojure-norway (23)
- # clojure-uk (2)
- # clojurescript (18)
- # conjure (2)
- # core-async (6)
- # cursive (21)
- # datomic (3)
- # fulcro (15)
- # introduce-yourself (7)
- # lsp (32)
- # malli (57)
- # meander (5)
- # music (1)
- # nbb (2)
- # off-topic (17)
- # pathom (6)
- # rdf (4)
- # reagent (8)
- # releases (2)
- # shadow-cljs (4)
- # slack-help (23)
- # spacemacs (6)
- # tools-build (32)
Trying to understand syntax quoting when used with read-string
.
Evaluating (read-string "
(x)")` produces (clojure.core/seq (clojure.core/concat (clojure.core/list 'autodiff.core/x))
, i would expect it to produce either (x)` or perhaps
(user.core/x)
(assuming expression is evaluated in namespace user.core
).
Why does it produce the long expression with namespaced seq, concat and list?
https://clojure.org/reference/reader#syntax-quote For all forms other than Symbols, Lists, Vectors, Sets and Maps, `x is the same as 'x. For Symbols, syntax-quote resolves the symbol in the current context, yielding a fully-qualified symbol (i.e. namespace/name or fully.qualified.Classname).
That explains why the namespaces are added, but I don't understand why seq
, concat
and list
are generated. It's really when used together with read-string
, evaluating (x)` gives
(user.core/x)
.
See this thread: https://clojurians.slack.com/archives/C03S1L9DN/p1662157629261129?thread_ts=1662147036.291109&cid=C03S1L9DN
I see, the resulting form is equivalent to (user.core/x)
, and necessary to support splicing somehow (have to understand that later). However, I don't get the same result for the other examples in that thread. When I put ''foo
in my repl I get 'foo
back (not (quote foo)
), or '#'foo
gives me back #'foo
(not (var foo)
). Is that a clojure/clojurescript difference?
Ah, but:
(read-string "''foo")
=> (quote (quote foo))
Oops. Mine was clojure. But
(cljs.reader/read-string "''foo")
''foo
My Clojure result as shown stands. We have to be careful with Lisp REPLs; sometimes they try to be nice on the P aspect. Common-lisp has print-readably, which ironically prints human-unreadably, because the reader they have in mmind is the Lisp reader.
I just checked for that in the CLJS repl:
cljs.user=> (str "test: " (cljs.reader/read-string "''foo"))
"test: ''foo"
Do you mean that when the repl prints ''foo
, it's actually sugaring the underlying expression to make it more human readable?
That is my concern, yes. But over here:
~/dev/matrix/cljc/whoshiring [main] $ clj
Clojure 1.11.1
user=> (read-s(read-string "''foo")
(quote (quote foo))
user=> (read-s(read-string "``foo")
(clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list (quote user/foo))))
user=>
No idea why the "(read-s" is getting echoed into my expressions, but I am not typing them.Ok this is interesting. Everything I typed above was through Cider in Emacs. If I run it like you do with clj, I get the same results!
That is why I switched to CLJ. My results originally were using a REPL launched in IntelliJ, thought that might be a factor.
This shows the myriad printer control variables in clojure: https://clojuredocs.org/clojure.pprint/write FWIW
And even with the CLJ console, who knows how ... well...
user=> *print-*print-readably*
true
The moral: looking at the console can be misleading at the edges of any Lisp syntax. What we see is not always what we have.
As for the backticks, hmm, we do not have a backquote
. So all that code is what the reader generates when it encounters a backtick. Guessing.
Come to think of it, `foo is not much of an expression to be backquoting. All that generated code makes more sense for sth like:
(defmacro prog1 [& body]
`(let [result# ~(first body)]
~@(rest body)
result#))
I see. Yeah, expression like `foo is just to boil down to understanding backquote. What I am actually doing is to parse a backquoted expression from source code, and it blows up to this huge datastructure, which got me really confused.
Indeed, I would expect the syntactic backtick sugar to expand into something that is more 1-1 with the source code, such as a backquote
special form. Now I know things get expanded more than that during the read-step!
What you see is what syntax quote expands too I believe. Syntax quote and quote are not special forms, they're just reader macros.
Right. Please explain more if you can, I am still struggling to understand this well 🙂 As I understand it, quote (`'x`) expands into the special form (quote x)
in the reader (which is a special form right?). I still don't understand why (x)` and
'
(x)` expands into different things (the latter being the same as you get when used together with read-string
, I think).
Well, to be more precise
` and ' are not special forms, they are reader macros.
quote
is a special form. The ' reader macro expands to quote
.
The reader macro on the other hand expands to code that will create a seq of what's inside it and possibly have some stuff in it wrapped in
unquote` or unquote-splice
See this stackoverflow for more details: https://stackoverflow.com/a/3704703/172272
Like the Stackoverflow says, probably syntax quote could have also expanded into a special form or even a call to a syntax-quote macro that then performs the same transformation of returning that seq concat code. But instead they did it all inside the reader macro, which in Clojure is Java code.
Yes, but I wouldn't say it's hidden black magic. The reader takes text and returns code. It's allowed to return whatever code it wants for whatever text it gets. In other Lisps, you can even add your own reader macros, Clojure doesn't let you, it has a restricted set of them. It's kind of similar to how macros take code and return code and are allowed to return any transformed code they want. In a way each macro is always just black magic that does whatever it wants and you need to learn about each macro to figure out what they do. But at the same time, it's a regular pattern: Text goes into the reader, certain tokens invoke Reader macros that transform the following text in a special way, code is returned. Then code is macro expanded with normal macros, meaning code that follows macro forms are transformed into different code by the macro, and finally the code is evaluated.
So nothing is more special about than about ' , they both take text and returns code. The only surprising thing is how complicated and not intuitive the code returned by
is in comparison to '
Correct. That's the difference between reader macros and normal macros. It's also why writing a reader macro is a lot harder, but technically a lot more powerful. A reader macro could for example take JavaScript source text and return parsed Clojure code from it. It's kind of why Clojure chose not to support them, already macros can make different code base feel like different programming languages since they can add so much new semantics. Reader macros can also change the fundamental syntax , which can make working in two code base really different. So Clojure didn't allow them to try and keep things a bit more consistent.
Aha! Now when you mention it, I've heard about sorcery like that being used in Scheme. Now I think I understand what that means also.