I guess Self was the same way. Self & Smalltalk also give you access to the entire runtime.
Most languages of this sort, like Smalltalk, Lisp, and especially slower, more liberal implementations like PicoLisp, allow for compile-time and/or runtime AST transformation, and other sorts of metaprogramming.
in TCL, everything is a string. Or at least, everything behaves like a string in the proper context. When you pass code blocks into a command (like if, or while, or whatever), you're not passing code objects: you're passing unevaled strings. This is why expr works in TCL: there's nothing special about expr, it's just actually implementing a DSL (sort of) rather than evaling your code straight up.
The practical upshot of this is that unlike smalltalk (I think: Can an ST user actually answer this?), and to a greater degree than LISP and FORTH (;immediate and readtables are a lot more painful to wrangle), you can not only modify the semantics of the language: you can modify the syntax.
Also you can at any time just completely replace one object by other via the becomes: message.
There are also some cool tricks when metaclasses are used, many of each one can see in Python as well.
However, you know ST better than me. Am I right?
But I think there is already quite a few things possible via messages and metaclasses, even if one cannot do actual AST transformations.
After all, the whole image is accessible, so you can dynamically ask any object for its definition, or even compiled code (bytecode or JIT) and change them.
That's true. In fact, there are things that messages and metaclasses can do that you can't do with AST transformations without implementing those abstractions.
>After all, the whole image is accessible, so you can dynamically ask any object for its definition, or even compiled code (bytecode or JIT) and change them.
I still miss this in Lisp. Some Lisps had that, once, but it's uncommon nowadays. it happens in the commercial CLs, but those are expensive. OS CL implementations rarely have good image support (in SBCL, image saving actually corrupts the RAM state to the point of nonrecoverability, and the docs recomend fork(2)ing if you want to save an image and continue your app).
Aside from the proprietary CLs, PicoLisp is the only modern lisp environment that has this kind of dynamic capability AFAICT (and while it does sort of have images, in the form of external symbols, the language doesn't encourage using them like this. Also, calling it modern is a stretch: it has more in common with LISP 1.5 than, say, CL). And the Schemes? Don't make me laugh. Scheme has many strengths, but reflection isn't one of them. It's something that I really wish the Lisps had.
One of the reasons I want to try my hand at implementing Lisp on the Spur VM at some point.
Seriously, Picolisp is absolutely insane.
[Edit] I should say "one of the qualities." The other important one is that Tcl has no types. Even all the lisps I know have types.
And before you ask, yes, picolisp has list interpolation (or quasiquoting, in lisp parlance), so you can control which parts of a certain chunk of code will be run when, and in which contexts. It also includes the `macro` fexpr, which makes interpolation more convenient.
Is it that it has no types or that everything is a string? asking, not stating.
set x 5
expr { $x + 3 }
'expr' receives x as the string 5 but knows to treat it as an int.I'm not sure that's a great explanation. Maybe a decent summary of the concept of 'no types' is "the language does not presume to tell you how you can or cannot use your data."
But you might be interested to know there is currently an effort to get Tcl to compile to native/near-native code. Here is a paper on the new techniques being developed and a link to a talk given by one of the lead tcl core team members.
http://www.tcl-lang.org/community/tcl2015/assets/talk14/TheT...
https://www.youtube.com/watch?v=RcrqmZV88PY&t=38s