"Unlike most programming languages, Lisp supports parse-time execution of programs, called "read macros" or "reader macros". These are used to extend the syntax either in universal or program-specific ways."
read-string is a convenience over calling read itself as it will take a string rather than needing you to create a java.io.PushbackReader yourself.
As such read and read-string were implemented as core to Clojure's self interpretation and implementation, not as part of a general safe serialization API.
Unfortunately the temptation was to reach for read-string for de-serialization in general as it was so convenient and in the core. In a dev setting where you control and trust all input that is fine. In other contexts it definitely is not!
Yes you can, but they are a bit constrained in what they can do. For instance, it's easy to write a debug macro that prints the intermediate value of b+c:
(let [a #d (+ b c)] ...)
So the #d reader macro has access to the following form, but gets ignored at a higher level (i.e. when the "let" form is processed)
https://en.wikipedia.org/wiki/Lisp_reader
"Unlike most programming languages, Lisp supports parse-time execution of programs, called "read macros" or "reader macros". These are used to extend the syntax either in universal or program-specific ways."
read-string is a convenience over calling read itself as it will take a string rather than needing you to create a java.io.PushbackReader yourself.
As such read and read-string were implemented as core to Clojure's self interpretation and implementation, not as part of a general safe serialization API.
Unfortunately the temptation was to reach for read-string for de-serialization in general as it was so convenient and in the core. In a dev setting where you control and trust all input that is fine. In other contexts it definitely is not!