Macros work under compilation, and handle interpretation situations too. (Though not all of them; certain trompe d'oeil tricks are possible with fexprs, since they can have access to the contents of the run-time lexical environment, and the syntax, simultaneously, making it possible to interpret the syntax in a way that depends on the run-time value of a variable.)
You're not going to make anything better than Lisp in a programming language by resurrecting hacks that were consciously discarded as a bad design decades ago.
The point is probably that the author regarded it as a disadvantage if a language has types other than the character string.
"Hey look, TCL is better than Lisp for some of the same reasons it is better than Ruby and Python: no damn types."
No, the point is that almost all TCL values are represented as strings which are also valid literals for those values. This is where the similarity with Lisp comes from: Lisp is a language where the syntax is the representation (you write the AST), whereas TCL is a language where the representation is the syntax (you operate on the string).
To expand upon this, Lisp can write its lists to files (and most other things out of the box) which it can then read back from files and then eval. This is what Lisps mean by a printable/readable object.
In Clojure:
(spit "foo.clj" '(defn foo [] "CONGRATS! YOU CALLED FOO!!!"))
(load-file "foo.clj")
(foo) ; => "CONGRATS! YOU CALLED FOO!!!"
In Common Lisp: (with-open-file (f "foo.lisp" :direction :output)
(pprint
'(defun foo ()
"CONGRATS! YOU CALLED FOO!!!!") f))
(with-open-file (f "foo.lisp" :direction :input)
(eval (read f)))
(foo) ; => "CONGRATS! YOU CALLED FOO!!!!" CL-USER> (with-open-file (f "/tmp/makapaka"
:direction :output
:if-exists :supersede)
(pprint '(a potentially (complex data structure)
mixing lists #(and vectors) 2/3
#C(2 5) and stuffs) f))
; No value
CL-USER> (with-open-file (f "/tmp/makapaka")
(read f))
(A POTENTIALLY (COMPLEX DATA STRUCTURE) MIXING LISTS #(AND VECTORS) 2/3 #C(2 5)
AND STUFFS)
In this example, I printed and read back a list containing symbols, lists, vectors, rationals and complex numbers.Basically: the reader converts strings to Lisp objects[0]. The printer converts Lisp objects into strings. When we say that an object has a "readable representation", it means that if you feed the result of the printer into reader, you get the same object back.
The evaluator (which I didn't use here, but OP did) evaluates Lisp objects (not strings!), and can e.g. introduce a new function to the environment when evaluating a defun form, i.e. a list of form (defun name (args...) "docstring" code). It is not needed (in fact, it is discouraged[1]) when all you care about is writing and reading back Lisp objects.
--
[0] - Note it's the same reader that actually reads your code when you compile/load file or use a REPL.
[1] - The usual eval-on-untrusted-data code execution vulnerability. Note that since the reader is mostly used to process code, by default it allows you to use the #. read-time-value reader macro. If you READ a string like: "(1 2 #.(* 2 2))", you'll get a Lisp list (1 2 4), since #. basically means "pipe the next s-expression to eval, and return that". When you use reader/printer for serialization/deserialization, you usually want to disable #. for those invocation of read function, by setting
*READ-EVAL*
variable to NIL.
You can write normal procedures in Lisp too -- for new control structures even. In fact, macro use in Lisp is usually discouraged unless you really need them, and in most cases you really don't. When you really do, they're nice to have, though. It's too bad that when you really need them in other languages, they're not there.
"Lisp has types, more or less like Python and Ruby. For example in order to use a number as argument of a string manipulation function, there is to convert from one type to the other."
I'm not sure I see the point. You have to convert strings to numbers and numbers to strings in Lisp too. Lisp has types. You can even get static types in Lisp, if you want them - like with Typed Racket.[1]
"In Tcl serialization just happens for many types because almost everything has a string representation. You can write a Tcl list in a file just with: puts $fd $list. This is not possible in Lisp, where you need to perform serialization."
You can write a TCL list to a file? You mean a TCL string to a file? Lisp can just write strings to files too.
"Lisp central data type is the list, while Tcl central data type is the string. Tcl also makes a lot of use of lists that are just special strings. In today's world, to have strings as base type is usually more useful, because many thinks like HTML, XML, Networking Protocols, are about strings more than they are about lists. To deal with strings in Lisp is not as natural as with Tcl."
The thing is, you don't necessarily want to deal with strings in Lisp (and arguably even in TCL). You want to deal with higher level abstractions such as tags, elements, and so on. The underlying representation as string or list is less important, and can sometimes even get in the way (such as in some languages which allow buffer overflows when dealing with strings).
[1] - http://docs.racket-lang.org/ts-guide/index.html