I can't believe I'm praising Tcl (2008)
yosefk.com
yosefk.com
As an aside, I have to praise one particular, easily overlooked aspect of Tcl: its comm facility is, from a (non-Tcl) client implementation perspective, the easiest "foreign function interface"[1] I've ever worked with by far, due almost entirely to the simplicity and consistency of Tcl syntax.
[1] Technically, comm was designed as a remote execution facility, but it works just as well locally to run Tcl code from within a non-Tcl program without the hassle of embedding an interpreter.
[1]: https://github.com/macports/macports-ports/blob/master/lang/...
I bridged that gap recently with GoLang. Below [1] is an example of how I now use GoLang instead of Bash for my scripting needs. The development cycle is essentially just 2 steps, like scripting, 1) Edit file(s) 2) Run the script (./my.go).
This gives the same feeling of writing scripts, but with all the guarantees of GoLang and benefits of an IDE (VSCode). The trick is to have the first line of the file be a script (for the process loader to execute) as well as a comment (so that GoLang infrastructure ignores that line.
[1]: https://gist.github.com/gurjeet/3121b2e373e61873c5a6e96d72b6... (Gist version at the time of submission: v3)
The function err(), in addition to sending a message to stdout, also causes an exit of the process. But it does so only if ExitOnError is turned on; which is on by default, and can be turned off by setting environment variable EXIT_ON_ERROR=true.
I have converted that Gist into a standalone Git repo, and hope to update it to add more improvements (like the stderr change, above) to showcase how to leverage GoLang for scripting.
I use the Perl Expect.pm module (https://metacpan.org/pod/release/RGIERSIG/Expect-1.15/Expect...) since I create and maintain a lot of Perl scripts. It combines the utility of expect with the power of Perl regexes.
Although our tool itself is strictly in-house, when we get the time to give them a quality-control once-over we would like to open-source the bindings. Tcl and Go make a pretty good pair!
I would definitely reach for Tcl the next time I need to add a scripting language to a static-language project.
This author makes so many good points about key ergonomics with languages. I’d love to see more research into this, and the results being incorporated into languages.
A paper that tries to compute Halstead language level for Haskell. It was so high they had to change constants in the computation of Halstead volume (page 6 in the paper above). Basically, many spaces in Haskell program are "apply" operator and one have to count these spaces to even attempt to be compatible with the theory.
I also have to say that I like Tcl very much but for different reason: values in Tcl are immutable and passing a dictionary or list to a procedure will not change that dictionary in unpredictable way (will not "pull the rug").
I once ranked languages by their support for drunken programming (diminished higher thinking ability - how much thinking programming language really takes from you) and Haskell and Tcl came out first and second - both have immutable values and Haskell also helps with the type system. Other contenders like C, C++, C#, Python and some other languages were much worse to use when one is drunk. Mainly due to the need to remember effects on the arguments passed to a function and/or method, but also for infamous "null".
C# had third place - some type safety and garbage collection help a lot (and compiler accepts more code than Java). Everything else just brings you more headache when you try to decipher and make work what you wrote being drunk.
I did these experiments when Clojure was not all that hip, circa 2008-2010.
Then again, I also sometimes write code in my sleep if I left a problem unsolved during the workday and then I implement that solution when I wake up. Does anyone else also do this?
I had to update some code that a more talented coder than I, wrote while stoned. It was an adventure.
When I got the assignment back from the TA I had gotten everything correct, except for that problem, which he had marked up with red question marks and exclamation points. I looked at what I had written and found it to be incomprehensible nonsense...
The mutation operation can be seen as resource management optimization, premature (and thusly evil) more often than not.
If I could write code that was perfect and easy to read - but had to use nothing but my toes, I’d be investing in some fancy slippers and in a second.
Next time that you have a shell script that is too complicated for bash but not complicated enough for Python, give TCL a try!
I think Tcl would suffer from the lack of syntax for pipelines, for example.
The built in "exec" command supports pipelines and file redirections with the usual syntax so you can do
exec foo < bar | baz
and it works similarly to a bash command substitution $(exec foo < bar | baz)
The main catch is that if you have an argument that is "|" or "<" then TCL interprets it as part of a pipeline, even if it came from a variable. However, the flip side us that you don't need to worry about arguments with spaces or asterisks. There is no word splitting or glob expansion so it is OK to leave everything unquoted. set r "<"
set p "|"
exec foo $r bar $p baz
There are also a couple things that are slightly different from bash. One is that exec raises an exception if the command returns a nonzero exit code. If that is not what you want then you will need to use the TCL equivalent of try-catch. I wrote a small helper function for this common use case.Another thing is that exec returns a string with the output of the command, similarly to $() in bash. If you want to output to stdout then you need to work around that.
The main thing I dislike about migrating my bash scripts to python is that the syntax is more verbose. subproccess.call can be a bit of a mothfull and you "need" "to" "quote" "everything", "like“ "this". If the only fancy thing your script is doing is control flow then Tcl might be a good fit.
I agree subprocess.call is quite verbose, but I find you have to do it less in Python scripts anyway because you can things properly with libraries rather than hackily calling out to other programs (e.g. curl).
I am just learning about tcl today and am seriously considering replacing bash with it. Lack of quoting is looking very elegantly done in tcl, because of the way it handles {} and...it's not a scope, it's a single string argument. You can manipulate the string argument as a string, and you ultimately still know exactly whether you have space separated arguments or a single string because the grouping is explicit.
Tcl does not do automatic expansion on glob patterns unless explicitly requested with `glob`. Where bash might end up dynamically changing a space separated argument into two arguments when it gets passed around, tcl will give you a type failure for the equivalent scenario because the arguments do not fit the command given.
The ease of metaprogramming feats is really feeling incredibly LISPy, but without all the verbosity that turns me off from LISP...and with easy access to any commands in the host shell. That means LISPy control over a polyglot language context.
Consider:
puts "Hello world!"
# Hello world!
proc say {word msg} {puts "$word $msg!"}
say Hello world
# Hello world!
set greeting "Hello"
proc say$greeting {msg} {
say [uplevel {set greeting}] $msg
}
sayHello world
# Hello world!
set pyScript {
print "Python"
}
sayHello [exec python << $pyScript]
# Hello Python!
set nodeScript {
console.log("Node")
}
sayHello [exec node << $nodeScript]
# Hello Node!
I know this doesn't look this special, and basically just looks like doing normal bash stuff. But-- there's no special handling you need to do with the scripts that need an outside interpreter,
- super easy to build these interactively within your tclsh, you can do macro-like actions on any of this very easily,
- you can send functions "from whatever language" around as arguments (and augment them) totally painlessly and not need to worry about syntax issues outside of the normal language context. You're either "inside" a UTF-8 encoded {} or you are not (in which case you are in the tcl context).
That said, Tcl/Tk is still the fastest way I've seen to go from zero to GUI. Not even Visual Basic beats it in terms of quickness and simplicity.
http://learnrebol.com/rebol3_book.html
Also Powershell fully exposes WINFORMs, but it's not as streamlined as Tk, although seems to be more modern with more features.
But compared to most GUI toolkits especially anything cross platform, it’s really nice!
If any maintainers are reading: putting it in the F-Droid store would improve visibility / discoverability.
I've been learning TCL for fun lately and I both adore it and am frustrated by it.
Hahaha! This is excellent, made me crack a big IRL smile.
(Nice discussion on homoiconicity)
I heard that they added a JIT compiler somewhat recently. But I'm not sure if there's any kind of "true" compiler (or at least linter?), that can find your basic errors prior to runtime.
I used to dabble a lot with Tcl back in the 1990's and early 2000's. I eventually moved into Lisp to scratch that side-project itch, mostly just because I wanted to find more mistakes at build-time than runtime.
Does it allow for ahead of time compilation to bytecode? With build-time reporting of errors in the source code?
Source code is converted to bytecode on the procedure level, not the program level, so there's no program-wide checks for consistency. It's done at runtime.
There are some officially unsupported tools built in to the interpreter that let you dump bytecode of compiled procedures and reload it. You'd have to roll your own dumping and loading scripts if you wanted to deliver an entire program using those tools.
As a linter, I used nagelfar (http://nagelfar.sourceforge.net/). I had it set-up in vim with Syntastic. From what I remember it worked OK, definitely helped compared to no linting.