1. 47
  1. 12

    Tcl is pretty fancy. I think it deserves more praise. ;-)

    1. 7

      I hadn’t done much/anything with it before today, but my work today has made me like it a lot. Writing bare words instead of quoted strings improves (interactive) usability far more than it ought to. I always suspected as much, because R’s wonderful tidyverse does a similar thing; but I never dared to trust that gut feeling. Now that I’ve experienced it in the tidyverse, the shell, and Tcl I’m pretty sure that feature is just that nice (for me).

      (For an example of unquoted strings (actually column names) in the tidyverse, see this analysis of the diamonds dataset. It’s the code block that starts with library(modelr), just below the scatter plot of Old Faithful data. Fantastic for exploratory data analysis.)

      1. 0

        Writing bare words instead of quoted strings improves (interactive) usability

        Is it because you usually work in an editor that colorizes the quoted strings like tropical fruits?

        Strings as distinctive syntactical elements are more useful.

        1. 2

          That and the overhead of actually typing the quote marks.

          1. 2

            That these tools operate without quotes make me think the editors could do the same thing, too.

      2. 3

        How does Tcl compare to something like Bash, which also seems to be optimised for typeability?

        Hm, I suppose that Bash (the language) doesn’t really do much for you. What’s really useful for typing things quickly is the Unix CLI interface paradigm, but even that can be inconsistent. I wonder what a scripting language that brings the concept of CLI tools and arguments to the language level would feel like (e.g. let the user define their own functions, used like foo -a baz --bar baf output.txt

        1. 11

          One thing Tcl does better than bash: if you use a variable containing spaces, that won’t get exploded into multiple words. (unless you ask for it)

          $ tclsh
          % proc dashit {first rest} {puts "$first -- $rest"}
          % set x [list one two done]
          one two done
          % dashit $x "more text"
          one two done -- more stuff
          

          Contrast that with bash:

          $ bash
          $ x="one two done"
          $ function dashit() { echo $1 -- $2; }
          $ dashit $x "more text"
          one -- two
          

          Hm, I suppose that Bash (the language) doesn’t really do much for you. What’s really useful for typing things quickly is the Unix CLI interface paradigm.

          If we subtract the Unix CLI interface paradigm from bash/dash/sh/fish/zsh/ksh/csh, is there a language left over? If bash didn’t conform to that paradigm for you, it wouldn’t be bash but a completely different language, less suitable for shell use.

          cp my/file $myarchive  # Bash
          
          run(['cp', 'my/file', myarchive]  # Python, after `from subprocess import run`
          

          (By the Unix CLI paradigm, I suppose you mean mrules like

          • first word is a command (or look for an executable of the same name)
          • subsequent words are command arguments
          • words are space-separated, so quoting arguments is unneccesary
          • refer to variables with $varname.

          I wonder what a scripting language that brings the concept of CLI tools and arguments to the language level would feel like (e.g. let the user define their own functions, used like foo -a baz --bar baf output.txt

          That’s pretty much exactly what Tcl is. You’re even allowed to redefine built-in names! Have a look at “Tcl the misunderstood”, especially the headers “Eval and uplevel” and the following bit from “Radical language modifications = DSL”:

          Tcl looks like a configuration file by default:

          disable ssl
          validUsers jim barbara carmelo
          hostname foobar {
              allow from 2:00 to 8:00
          }
          

          The above is a valid Tcl program, once you define the commands used, disable, validUsers and hostname.

          1. 2

            Wow this was a very thorough comment, you’re making me want to learn Tcl now!

          2. 7

            FWIW I started to morph shell/bash into a sane language with Oil, while still retaining its typeability.

            • You can opt into a mode that removes the need for double quotes in almost all cases. In other words, there will be less of a difference between the “interactive session pasted into a file” and the “right thing”.
            • I improved the semantics of arrays and associative arrays. The lack of any real data types besides strings is something that makes shell seem like “not a programming language”. It has loops, functions, and conditionals, but it doesn’t have good data types like Python. (Or even Tcl, which doesn’t have data types per se but has predictable parsing of strings into data types!)
              • A nice typeability improvement is that you can opt into @a as an alias substitute for "${a[@]}" (which is the correct way to use arrays). The latter is really hard to type correctly and I didn’t even know it before I started implementing a shell! I find that most people don’t use arrays in shell (because they’re not POSIX, and they have bad semantics.)
            • I’m adding a JS-like var to which you can think of as a better and builtin version of Tcl and shell’s expr
            • He says that Matlab arrays [1 2 3] are better than lists [1, 2, 3]. Oil will have lists for JSON compatibility, but it also has arrays like this @[1 2 3] or @[bare words], rather than [1, 2, 3] and ['quoted', 'words'].
              • Some people suggest using Python for shell-like tasks, but I do think it would be silly if you had to type ['ls', '-l'] instead of ls -l.

            I linked to some details here: https://lobste.rs/s/pk64xp/p_idea_faq#c_lkpane

            I am looking for feedback on these new features! Right now the best way to provide feedback is to login to Zulip, which supports a few login services like Github so you don’t have to create a new password.

            BTW shell functions work great and have a decent syntax. They let you define something that behaves just like an external tool or builtin. They can be transparently put in pipelines, which no other language provides (you can think of shell functions as having “proper” stdin, stdout, stderr, unlike Perl, Ruby, or Python functions).

            However one funny problem is that the shell builtin getopts is difficult to use and inflexible. It has both “hidden state” and implicit variables which are hard to get used to (e.g. if you’re coming from Python, Ruby, JS, etc.)

            So fixing/enhancing getopts is also on my TODO list. There is a bunch more and I’m looking for people to try rewriting some shell snippets with them and give feedback. I rewrote a couple small programs, shown in the linked Zulip posts.

            1. 2

              One confusing thing about shell functions is that you can define global variables inside them but that doesn’t work if you call the function in a pipeline or an expansion, so given

              foo() { x=1; echo foo; }
              

              you can do

              foo; echo $x
              

              but not

              foo | grep o; echo $x
              

              or

              printf "%s bar\n" $(foo); echo $x
              

              The explanation is that running the function in a pipeline or substitution implies running it in a subshell which can’t set variables in the outer shell, but this semantics can cause weird problems when you’re refactoring…

              1. 1

                Yes, one idea I had a long time ago to address that was to force a prefix on shell functions in pipelines, to make the subshell explicit, sort like:

                ls | subshell myfunc | wc -l
                
                instead of 
                
                ls | myfunc | wc -l
                
                Or more conventionally
                
                ls | (myfunc) | wc -l
                

                But I’m not sure if it’s a good idea now, and there are a bunch of other pitfalls I think are more common to address first.

                Other things that Oil does better that can mitigate it:

                • variables aren’t global by default. Usually in functions you want to mutate locals.
                • variable names are statically known, e.g. something like local $1 where $1 is var=val isn’t idiomatic. So you could warn about this in a lint tool. I think that might end up being the best option.
            2. 5

              How does Tcl compare to something like Bash, which also seems to be optimised for typeability?

              I’d say that it’s about the same, maybe better. Most simple shell commands are also legitimate TCL commands. The substitution rules are cleaner though, function definition is nicer &c.:

              echo [ls -l /etc]
              proc foo {bar baz} {exec echo $baz $bar}
              

              versus:

              echo $(ls -l /etc)
              function foo () { echo $2 $1 }
              

              (Typed on my phone, not tested, might not be right, I haven’t used Tcl in a year or two)

              1. 2

                It’s simplicity also makes it easy for 3rd parties to implement on their own. That makes it more popular in appliances and embedded, esp for administration. Cisco is probably the most famous use case. Here’s a tiny implementation that runs in 10KB on a microcontroller. In the 1990’s, it was also used by ArsDigita for web apps.