[HN Gopher] Why is Tcl syntax so weird (2013)
       ___________________________________________________________________
        
       Why is Tcl syntax so weird (2013)
        
       Author : blacksqr
       Score  : 76 points
       Date   : 2021-11-07 20:44 UTC (1 days ago)
        
 (HTM) web link (wiki.tcl-lang.org)
 (TXT) w3m dump (wiki.tcl-lang.org)
        
       | AnonC wrote:
       | I've seen Tcl used to write testing systems a long time ago, but
       | never really worked with it.
       | 
       | Tangentially, I found that the primary author of SQLite and
       | Fossil SCM, D Richard Hipp, was also a core team member of Tcl.
       | The server side of Fossil uses a minimal dialect of Tcl and the
       | build/test system is built with Tcl. [1]
       | 
       | [1]: https://fossil-scm.org/home/doc/trunk/www/fossil-v-git.wiki
        
       | [deleted]
        
       | tracnar wrote:
       | The Tcl wiki is quite strange, it has interesting content but
       | most pages are discussions like this one and it's hard to find
       | what's relevant or even correct.
       | 
       | What's weird about the Tcl syntax is that there is barely any.
       | It's very dynamic so only the top-level of a file is parseable as
       | a list of commands, anything else like a procedure depends on the
       | implementation of the command which can be overridden at any
       | time.
       | 
       | It's even used by built-in commands like expr and if which
       | essentially implement a mini expression language which is
       | different from Tcl.
        
         | rodgerd wrote:
         | It's an accessible Lisp with square brackets.
        
           | int_19h wrote:
           | Not really. References are pretty fundamental to Lisp, but
           | are (at best) bolted on in Tcl.
        
         | natrys wrote:
         | I have been writing some Tcl for the last couple of years,
         | mostly in place of shell scripts. Going to Tcl wiki is always a
         | surreal experience. I think this old comment captured it
         | perfectly:
         | 
         | https://news.ycombinator.com/item?id=9098859
        
           | blacksqr wrote:
           | Equal time:
           | 
           | https://news.ycombinator.com/item?id=9102916
        
         | kevin_thibedeau wrote:
         | That's what wikis originally were. Wikipedia's curated ontology
         | is an exception that only looks like the rule because most OG
         | wikis have died off.
        
           | emodendroket wrote:
           | Emacswiki is one active one still like that and the effort to
           | replace it with MediaWiki was ultimately abortive. C2 is also
           | big but I think mostly dead.
        
       | lilyball wrote:
       | I've always loved Tcl because of its absolute purity and
       | simplicity at the language level. I love how you can implement
       | your own control flow operators that look like built-in ones. I
       | love how easily you can make DSLs.
       | 
       | I just wish there was a Tcl that was redesigned from a modern
       | perspective. Give me first-class lambdas. Give me unnamed objects
       | that get destroyed automatically (with a callback) when out of
       | scope[1] so I don't have to use a weird namespace hack to make
       | the object and rely on the caller to invoke the destroy method.
       | Give me a stdlib that offers more functional stuff. Heck, give me
       | async/await. And more too.
       | 
       | But even without all of that, Tcl is still a wonderful language
       | and I often lament how it's mostly died out.
       | 
       | [1] There's at least one library I've used that actually does
       | this, but AFAIK there's no way to do this in pure Tcl so it must
       | be a feature of the C API. Also I'm not sure if it actually
       | tracks the object or just the variable it's assigned to.
        
         | CornCobs wrote:
         | I believe a lot of what makes Tcl the way it is is precisely
         | because it does things different from most other languages with
         | regards to closures, lambdas, scoping etc.
         | 
         | If Tcl had the other stuff, would it need upvar/uplevel or its
         | funny braces and substitution rules? I'm no expert at Tcl but
         | when I learnt it (mainly to use tkinter) I really had to rewire
         | my brain to think about something as simple as _variables_ so
         | differently
        
         | valenterry wrote:
         | Checkout Scala. It allows you to do pretty much the same, i.e.
         | the syntax is so flexible that you can even make 'puts "Hello
         | World!"' work and implement your own "if" that looks like the
         | builtin one. It also has first-class lambdas and the other
         | stuff that you asked for, but obviously there are also
         | differences.
        
           | lilyball wrote:
           | Scala runs on the JVM, which makes it not at all suited for
           | scripting. Scala also seems to have something of a
           | racism/white supremacy/misogyny problem.
        
             | valenterry wrote:
             | > Scala runs on the JVM, which makes it not at all suited
             | for scripting
             | 
             | The JVM is one target. You can also generate and run
             | javascript code or create a native binary. But it will not
             | be as smooth as e.g. python, so I can't really recommend
             | that.
             | 
             | > Scala also seems to have something of a racism/white
             | supremacy/misogyny problem.
             | 
             | I'm a member of that community for a long time and I really
             | don't think so. I always thought the Scala community was a
             | rather welcoming one. There certainly have been incidents
             | in this community, just like they have been in other
             | communities, but I don't think "Scala" has more of a
             | problem here than any other language.
        
             | mrbukkake wrote:
             | >Scala also seems to have something of a racism/white
             | supremacy/misogyny problem.
             | 
             | lol what
        
         | tverbeure wrote:
         | Tcl has been used for decades as the language of choice in the
         | electronic design automation world.
         | 
         | I often lament how it has _not_ died out. It 's demise is long
         | overdue...
        
           | nomel wrote:
           | > as the language of choice
           | 
           | I'm not sure that's an appropriate phrasing. The word
           | "choice" implies that alternatives are available, and there a
           | decision was part of the selection. I think "Has been used
           | for the language that momentum has demanded" might be more
           | accurate. ;)
        
         | pjmlp wrote:
         | I love the language as well, we did lots of cool stuff with Tcl
         | in 1998 - 2003, we were already doing our own Rails, but a tiny
         | startup in Lisbon does not rock the world the same way.
         | 
         | However that experience lived on and gave birth to OutSystems.
        
         | int_19h wrote:
         | You can't really get full-fledged closures without ditching the
         | fundamental concept of Tcl, which is that "everything is a
         | string". It's the same problem as OOP, or rather, object
         | lifetime management - you can have that in Tcl, but only as
         | long as you manage the lifetimes manually. For closures, this
         | negates most of the benefits they provide.
        
           | lilyball wrote:
           | It would work if they basically copy the current value of the
           | identifiers (i.e. by-value, not by-reference). Or by
           | capturing the procedure context. You can synthesize the
           | variable capture by hand but that would indeed force
           | everything into a string, which is a potential performance
           | issue and would also break any sort of automatic lifetime
           | management.
           | 
           | Even just making it easier to have lambdas reference
           | variables from their creation scope while the creation scope
           | is still on the stack would be an improvement. Today you can
           | use [info level] and then construct a lambda body that uses
           | [upvar #$level ...] to reference variables from the original
           | scope, but it's a PITA. Or you could cram the level into a
           | temporary namespace variable to avoid having to manually
           | construct a body string, but it's still a PITA.
        
             | pooryorick wrote:
             | [list apply {args body} $value1 $value2 ...] can be used to
             | capture values without actually generating a string because
             | even though conceptually a list is a string, it's actually
             | stored internally as a list.
        
             | int_19h wrote:
             | The basic problem is, how do you even know that something
             | is a variable reference? Remember, _everything_ is a
             | string. This means that the body of the lambda is also a
             | string. Now you can require that it 's an eval'able string
             | at the point of creation, and parse it to identify any $var
             | references. But you can only do that for the topmost level
             | of the lambda, because it can contain nested strings - and
             | you don't know which of those strings are code, and which
             | aren't (since it's really the command to which the string
             | is passed that decides to treat something as code or not).
             | 
             | Furthermore, if you process $ like that, this won't work
             | for any other command that takes a variable name as an
             | argument, and does something to it.
        
               | rightbyte wrote:
               | > The basic problem is, how do you even know that
               | something is a variable reference?
               | 
               | Isn't that by convention? Like there is a "upvar" to grab
               | values of the prior stack.
        
               | int_19h wrote:
               | "upvar" is explicit, there's no guesswork involved there.
               | But how would you handle a lambda with a body like this?
               | if {$foo == 1} { puts $bar } else { puts $baz }
               | 
               | given that all the {}s are just non-expanded string
               | literals, and what makes them be interpreted as code is
               | the implementation of "if"? Sure, you can hardcode "if";
               | but the whole point of Tcl is that syntax constructs like
               | that can be easily implemented as a library, which breaks
               | if you have to special-case them in lambdas for them to
               | work.
               | 
               | Now if you used upvar, this kinda sorta works, but only
               | so long as the lambda is immediately invoked by the
               | function that it was passed to. If you want to pass it
               | on, you'd have to wrap it in another lambda. And, of
               | course, this only works for lambdas that don't escape.
        
         | verifex wrote:
         | It's not dead yet. There's still AOL Server [1] and there's at
         | least one game [2] that uses TCL that is online. :)
         | 
         | 1. http://www.aolserver.org/ 2. http://www.carnageblender.com/
        
           | doublerabbit wrote:
           | Long live aolserver, Naviserver [1] is the fork that's still
           | in very active development.
           | 
           | 1. https://wiki.tcl-lang.org/page/NaviServer
        
         | specialist wrote:
         | Imagine the timeline where Tcl, Scheme, xlisp, or any of the
         | dozen other existing, mature, available, non-turrible
         | programming languages was chosen for DHTML.
         | 
         | Versus the poorly conceived, poorly implemented, from scratch
         | rush job based on a fundamental misunderstanding of Self.
         | 
         | In fact, we could have ended up with Self. Imagine that
         | timeline.
         | 
         | Worst is better.
        
           | pcwalton wrote:
           | The Web would definitely not be better had Tcl been the
           | language of the Web instead of JavaScript. At least JS has
           | working closures.
        
         | teleforce wrote:
         | Tcl is one of the most underrated programming languages. I have
         | a friend who is working at Intel as tool development engineer
         | (equivalent to software engineer), and Tcl is being used
         | extensively at Intel for chip tooling and design since a very
         | long time.
         | 
         | The modern version of Tcl will be great and a seamless
         | integration with the OS shell will probably the killer niche
         | application. Hopefully Oil Shell can modernize Tcl to some
         | extent and incorporate the best features of Tcl with excellent
         | OS shell integration capabilities.
        
           | enygmata wrote:
           | Tcl is also at the heart of some F5 Networks products
        
           | tverbeure wrote:
           | As a tool development engineer, it's only normal that your
           | friend is using Tcl extensively: there is no other option.
           | 
           | The real question is: would they still be using it when given
           | the choice?
           | 
           | For me, Tcl is a cancer that just doesn't want to die.
        
             | evancox100 wrote:
             | I feel like there is an opening for an EDA company or
             | startup to really embrace Python and provide a Python
             | interface / command line to their tooling.
        
               | synergy20 wrote:
               | Tcl is deeply rooted in EDA and chip design industry, too
               | many code were written with it. It's like C++, no matter
               | how fancy/modern rust is, there is no way it's going to
               | replace C++ for the next few decades.
               | 
               | One way is to provide Python(python stdlib might be
               | enough for simplicity) gradually, e.g. providing a Python
               | binding for Tcl so for the future, you can write all new
               | piece in Python, and it is either used directly or
               | converted to Tcl behind-the-scene.
        
               | tverbeure wrote:
               | I don't think that opening is there. Tcl may be a
               | terrible scripting language, its usage is not a factor in
               | the value of this or that EDA tool.
               | 
               | If a startup wants to displace an entrenched tool from
               | Synopsys or Cadence, the underlying algorithms (millions
               | of lines of C++ code) must be better. (And once that
               | happens, they'll be acquired by one of these 2.)
        
               | evancox100 wrote:
               | I think you are probably right, it is not a make or break
               | factor. Especially when it comes to back-end flows,
               | PPA/TAT/capacity/design closure are the key metrics and
               | the big 2.5 have large moats in these areas.
               | 
               | On the front-end side though, areas like verification,
               | debug, and various lint checks, I think there is more
               | opportunity for smaller players to introduce point tools
               | with better customizability & scripting interfaces.
               | 
               | Ultimately I think the industry is stuck in a local
               | maximum, where the frictional costs of rethinking overall
               | methodology, e.g. from SystemVerilog to Chisel, is too
               | high to justify the change, even if the end result would
               | be marginally (or greatly) better. (Not an endorsement of
               | Chisel, just an example.)
        
               | _ph_ wrote:
               | There is Python support in some EDA frameworks, but
               | especially in the digital domain, Tcl is still the gold
               | standard. On the one side this means there are tons of
               | Tcl code around and any experienced engineer is well
               | versed in Tcl. Also, electrical engineers are usually
               | very focussed on electric engineering. While being being
               | very intelligent, they are often not into programming, so
               | keeping things very simple is an advantage to them.
        
           | lilyball wrote:
           | I haven't gotten around to looking into Oil Shell. Is it
           | actually drawing any inspiration from Tcl? The "A Tour of the
           | Oil Language" page[1] references Ruby and Python but not Tcl.
           | 
           | [1] http://www.oilshell.org/release/latest/doc/oil-language-
           | tour...
        
             | teleforce wrote:
             | Not yet but I've mentioned to Andy (Oil Shell author)
             | regarding the importance of adding command features into
             | Oil Shell and he is actually looking into it [1]. Hopefully
             | he will draw some inspirations from Tcl. Imagine a Cisco
             | like networking command wrapper on top of Linux OS with
             | eBPF backend. It will turn Linux into a ready-made
             | formiddable open source networking OS without the need for
             | NX-OS [2].
             | 
             | If you are looking into a modern version of Tcl, there is
             | TIL [3]. It's a Tcl inspired new scripting language on top
             | of D language. By having D language as the foundation it
             | can perform all the features that you've requested
             | including lambdas, async and then some more [4].
             | 
             | [1]https://news.ycombinator.com/item?id=28552998
             | 
             | [2]https://en.wikipedia.org/wiki/Cisco_NX-OS
             | 
             | [3]https://news.ycombinator.com/item?id=27167762
             | 
             | [4]https://dlang.org/spec/expression.html
        
               | lilyball wrote:
               | TIL looks interesting, but also both too simple and too
               | complicated. What's the difference between a SubList and
               | a SimpleList? Why does it require SimpleLists instead of
               | using variadic arguments (e.g. why [math ($a + $b)]
               | instead of [math $a + $b])? And why does the author
               | explicitly disallow multiple spaces in a row outside of
               | indentation when this is a purely artificial limitation
               | and does not simplify anything?[1]
               | 
               | But also, why can't I write modules in TIL? It's based on
               | D, sure, but I shouldn't need to write and compile
               | something in D just to be able to have some means of
               | namespacing things. This very much feels like "I want to
               | write most of my stuff in D but I just want to be able to
               | whip up short scripts that for whatever reason I don't
               | want to write in D".
               | 
               | And of course it seems to be ditching the pure simplicity
               | of Tcl. It's like Tcl in that it's a command language and
               | borrows syntax from Tcl, but it seems to be missing the
               | fundamental concepts of Tcl.
               | 
               | [1] The author seems to hate the idea of people lining up
               | equals signs in code or things like that. Ok sure, you
               | can have a style preference. But that's what a code
               | formatter is for, not an artificial and unprecedented
               | level of whitespace significance.
        
         | blacksqr wrote:
         | Re [1]: the libraries that delete an object when it goes out of
         | scope use Tcl's built-in "trace" command, which lets you
         | execute code when a command terminates or when a variable is
         | deleted, among other events. So no, it's all done in pure Tcl.
        
           | lilyball wrote:
           | Oh it looks like I misremembered. I thought the API I had
           | used was something like `set doc [foo parse $input]` and the
           | document would go away when the variable does. I found it,
           | it's the tDOM library, and it was actually `dom parse $xml
           | doc` that does the automatic freeing, whereas `set doc [dom
           | parse $xml]` requires manual deletion. So it probably is
           | using trace.
        
           | rkeene2 wrote:
           | This is how the "defer" [0] command (from tcllib) in Tcl
           | works.
           | 
           | [0] https://core.tcl-
           | lang.org/tcllib/doc/tcllib-1-19/embedded/ww...
        
         | isr wrote:
         | > I just wish there was a Tcl that was redesigned from a modern
         | perspective. Give me first-class lambdas. Give me unnamed
         | objects that get destroyed automatically (with a callback) when
         | out of scope[1] so I don't have to use a weird namespace hack
         | to make the object and rely on the caller to invoke the destroy
         | method.
         | 
         | If you take a look at jimtcl (minimalistic reimplementation of
         | tcl, started by antirez - he of redis fame), it did take some
         | steps towards the above.
         | 
         | - unified arrays with dicts
         | 
         | - proper lambdas
         | 
         | - (sort of) closures
         | 
         | In many ways, I personally feel its a shame that jimtcl has
         | felt the need to tie itself down to tcl-compatibility as much
         | as it has. A few more warts could have been fixed along the
         | way.
         | 
         | Anyway, check it out. It's manpage is quite comprehensive
        
       | int_19h wrote:
       | While we're at it - Tcl-like languages make for pretty decent
       | embedded scripting solutions if performance is not a major
       | requirement. There's one called LIL that's literally just a
       | single .c file (and the accompanying header) for ease of
       | embedding:
       | 
       | http://runtimeterror.com/tech/lil/
        
         | pmarin wrote:
         | My favorite implementations are partcl[1] (600 lines) and
         | Picol[2] (500 lines by Salvatore Sanfilippo).
         | 
         | [1] https://github.com/zserge/partcl
         | 
         | [2] http://oldblog.antirez.com/post/picol.html
        
           | int_19h wrote:
           | So far as I can tell, the reason why they're so small is
           | because they implement "everything is a string" literally -
           | so e.g. appending to a list is basically concat. This is good
           | enough as a proof of concept, and neatly showcases just how
           | simple the language itself really is; but perf would be
           | abysmal even by Tcl standards as soon as you get more than a
           | few dozen items per list.
           | 
           | LIL tries to be a minimalist but _practical_ embeddable
           | implementation, so it has basic optimizations like
           | representing lists more efficiently internally. The price, of
           | course, is code size, but also C API complexity.
        
       | [deleted]
        
       | cmacleod4 wrote:
       | I wrote a blog post last year trying to explain the under-
       | appreciated radical minimalism of Tcl, and in particular the
       | decoupling of syntax and semantics - https://colin-
       | macleod.blogspot.com/2020/10/why-im-tcl-ish.ht...
        
       | celeritascelery wrote:
       | > 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.
        
         | theamk wrote:
         | TCL 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
         | 
         | 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 wrote:
           | But that won't work if I am using variables correct? because
           | {} does not interpolate.
        
             | blacksqr wrote:
             | It 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.
        
             | theamk wrote:
             | sure 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.
        
         | blacksqr wrote:
         | In 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!
        
         | mjevans wrote:
         | I 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 wrote:
           | It'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
        
       | ahartmetz wrote:
       | The CMake language has the same absence of syntax, and everything
       | is a command as well - like assignments with set(). Oh, and all
       | variables are strings, too like in Tcl. (In CMake, even lists of
       | strings are strings. Don't ask.)
       | 
       | The CMake language was created in a haphazard way as well, with
       | some other important problems that Tcl probably doesn't have.
        
         | Y_Y wrote:
         | CMake is the modern MUMPS.
        
       | denton-scratch wrote:
       | Article doesn't do a good job of explaining why Tcl syntax isn't
       | weird.
       | 
       | I had to code in it once, can't remember what, something trivial;
       | but I had to learn Tcl. It was basically an obstacle to my work.
       | I never needed to use it again, so I've forgotten it, so I wasted
       | my time learning that language.
        
       ___________________________________________________________________
       (page generated 2021-11-08 23:01 UTC)