4 ms·
> Tcl is a macro language, it derives from macro-assemblers, the C preprocessor, and older, text-oriented languages like TRAC Then why doesn’t it have macros?
by celeritascelery 5y ago
> Tcl is a macro language, it derives from macro-assemblers, the C preprocessor, and older, text-oriented languages like TRAC
Then why doesn’t it have macros? This is one of my biggest complaints with tcl, the other being that you have to escape newlines in forms. This makes much less pleasant then lisp.
For example if I have this Tcl code
[set foo [foofn bar [barfn baz [bazfn quz 1]]]]
I would love to write it like this
[set foo
[foofn bar
[barfn baz
[bazfn quz 1]]]]
But I can’t without escaping every newline (you better hope you didn’t forget one). If tcl had macros I could write it like this
[set foo
[thread-last
1
[bazfn quz]
[barfn baz]
[foofn bar]]
But instead I end up create a bunch of temporary variables so that my lines don’t get super long. It just feels less elegant then it could have been.
- mjevans 5y agoI dislike that too about most of the languages which are in some way white-space sensitive. (Python as the go-to worst example, but other modern languages where ; or a statement termination value is optional also fit.) Newlines only can work, but then the question revolves around implicit newline escapes (if there's an unclosed statement) versus a required escape and things can quickly get out of hand and readability if really long strings need to be passed around; like with nightmare long SQL queries or default / example config file blocks.
- tyingq 5y agoIt's a bit odd, because you can skip newlines in many cases. This works: #!/usr/bin/tclsh set foo [ lindex [ expr 2 * 3 ] 0 ] puts $foo
- theamk 5y agoTCL does not need macros because every function can be a macro. Here is an answer to your question: proc multiline {expr} {uplevel [regsub "\n" $expr " "]} Use it as following: multiline { set foo [ foofn bar [ barfn baz [ bazfn quz 1 ]]] } try it in action: https://jdoodle.com/ia/jka https://jdoodle.com/ia/jka The hardest part to understand when writing TCL "macros", is that it's all strings. You might be used to "inputs are s-expressions" from lisp, or "inputs are closure objects with pointers to code and local variables", but not in TCL. {} produces a string parameter. It is interpreted as an expression to be evaluated in parent context when passed to "uplevel". So "proc" takes 3 strings as arguments, and syntactically no different from "regexp".
- celeritascelery 5y agoBut that won't work if I am using variables correct? because {} does not interpolate.
- theamk 5y agosure it will, "uplevel" will interpolate for you. There is a link to online playground, I encourage you to visit it and try various changes. No installation or signin required.
- celeritascelery 5y agoI stand corrected, it works just as you describe.
- blacksqr 5y agoIt will work because the value passed to proc multiline within the curly braces is treated as a string without substitutions, that value is then passed to the regsub command which replaces all newlines in the string with spaces, then the result is passed to uplevel, which is basically "eval this string in the scope context where multiline was called". It's uplevel that does all the variable substitution, using any values defined in the scope where multiline was executed.
- jjnoakes 5y agoThis is not quite right... First off I think you want "regsub -all ...". Second, since your regsub isn't really replacing newlines (except the first), the example isn't so good - your "set foo ..." works without the multiline proc because the opening square brackets are on the same line as the commands. So this also works: jdoodle.com/ia/jnc Third, if you do change the example so that square brackets are on their own lines, as in the original request, then your version would work (with "regsub -all") but it might strip too much - like newlines inside nested quoting. See, for example: jdoodle.com/ia/jne
- blacksqr 5y agoIn Tcl newlines only have special meaning at the end of a command -- at the beginning of a command they're just whitespace. So you can break up a long line as follows: set foo [ foofn bar [ barfn baz [ bazfn quz 1 ] ] ] Elegant!