Tcl the Misunderstood (2006)
antirez.com
antirez.com
1. Types in Python are implicit. It's really hard to explain that they need to always think about what type something is, especially when they haven't grasped the concept of types yet.
2. Indentation as part of the language. I thought it's a great thing for beginners, but it's actually confuses beginners that empty space affects your program, e.g. they think that spaces around assignment or arithmetic operators should affect their program too, which is not the case.
3. Ranges for iteration -- they just memorize the syntax blindly :(
So I started to wonder whether Pascal is better to learn for beginners -- you have to list all the types at the beginning, and no implicit conversions are done.
But now I think what about TCL? Maybe it'd be even easier for total beginners?
LOGO has real depth, an elegant syntax, and there's an alternate universe where it has Python's niche as everyone's favorite scripting language. But it's effectively a toy language so there's nowhere to go from "learning".
Here's my hot take: before you teach anyone "the basics of programming", teach them how to enter formulas in a spreadsheet.
Those who break past "the wall" and achieve any fluency at programming, can and will learn to start thinking in layered abstractions. And their programs will start to get bigger. Then it's time to move away from BASIC.
I moved to Pascal. Today, Python. But I agree with the above and about Python, that you are forced to handle abstractions, almost from the git-go. Perhaps what saves it is the ease of trying things, so if the functioning of a loop like "for i in range(10)" is confusing, just try it with a print() statement in there.
BASIC was also preferable on a timeshare machine. Waiting 10 seconds per line for a response seemed more tolerable than waiting 10 minutes for an entire program to compile and link.
And we shall not discuss the horrors of its build and dependency system, which I appreciate isn't the "language" but what good is a programming language if one cannot compile it?
- Go is also inspired by Pascal, but it has more of a foothold today, it shares the simplicity and the syntax is more natural and convenient than C and similar.
- It's a light language. You can approach it in terms of just functions (procedures) and data, as opposed to say Java (which unfortunately is one of the most used languages in to teach programming).
- The tooling is approachable, it compiles fast, there is no friction when it comes to automated testing, you get a static binary out of it etc.
- The std library is comprehensive and very easy to use. You can teach a ton of concepts with just that without ever having to worry about third party dependencies. A full web server, a CLI etc.
- It's low level enough so you can teach a bunch of important concepts such as pointers and constructs around pointers, allocation etc.
Nitpicks:
> compiles fast
That is totally irrelevant for beginners, as their programs <100 lines long. The best would be not having to compile at all.
> as opposed to say Java
Not really a big problem, actually. I watched how people learn Java in college (I was a pro at the time so helped them) -- they usually get main() definition from professor, and then just copy that file around. They write everything right in main() or in other static methods, so not a biggie it's all wrapped in some mystic "class Main {...}". But I think it's much better that:
1. They have to write types for all variables and no implicit conversions made.
2. If it's not in autocomplete -- it doesn't exist. Compare that to Pythons "len()", or Go's "append()" (and explain why result of append() needs to be assigned if it works without it).
But once you accept that there are rules to follow, and that they are somewhat arbitrary, languages do differ wildly in the amount and obscurity of syntactic forms you need to learn just to get started. Just count the amount of different syntactic concepts in this simple program:
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello world\n");
return 0;
}
and compare it to the Python version.I agree, and I think the problem you mention is a special case of the difficulty some people have to grasp the concept of definition in the mathematical sense.
Unlike "dictionary" definitions, they can be arbitrary: if I say "let S be the set of prime numbers greater than 13" you pretty much have to accept it, as long as it's well-formed and consistent.
On the other hand definitions need to be very precise. Natural language relies on "you know what I mean", which is a feature for most human-to-human communication, but it's not how mathematics and computer science work!
Later she taught programming. The first thing she told students was: "Computers are dumb. They will only do exactly what you tell them."
10 REM Computers are never wrong
20 PRINT "Please type a number"
30 INPUT A
40 LET A = A + 1
50 PRINT "The number you typed was"
60 PRINT A
After a few paragraphs of explanation I learned that computers do what you tell them to do, not what you want them to do.The differences do start to diminish if you add some requirements like "print out the name of the script and the passed arguments then some message to stderr".
main()
{
printf("Hello world");
}
Some details are hidden by the compiler, but Python isn’t exactly pure machine language either.Semicolons and bracket are conceptually equivalent to significant white space (but more obviously explicit) and main is also a concept in Python (that is much more syntactically brutal than even its verbose c equivalent) that is hidden by the interpreter — which you also have to explain to beginners.
Compare with Python:
import sys
def main(args):
print("hello %s" % (args[0]))
sys.exit(0)
if __name__ == "__main__":
main(sys.argv[1:])
Which (of course) breaks when cut and pasting onlinePeople are used to the idea of explicit delimiters, at least to some extent, thanks to punctuation. People are not at all used to whitespace having syntactic meaning. You can put as many spaces as you want between words, or sentences, or paragraphs, and it might look weird but it still means the same thing.
Semantic whitespace isn't a reduction in syntax, it just hides the syntax to fool you into thinking it isn't there, until it goes wrong and you suddenly have to debug it.
It's not a problem, it's just a tradeoff. Like 97 percent of all the other differences between programming languages. As to whitespace in Python -- it was counterintuitive at first, but once I grokked the rationale behind it, I've never been tripped up by it (to an extent that it would slow my thinking for more than a second or two).
Every language has random $foo that you have to get used to and step around once in a while. If you don't like Python, fine, go back to C and bash. They'll be happy to welcome you back with open arms.
it doesn't seem simpler, and any argument that it's faster or easier to type seems to fall apart the first time you need to paste code across different indentation levels, right? what's the upside?
What's the upside?
Readability. And freedom from silly debates about which indentation style is best.
You can put as many spaces as you want between words, or sentences, or paragraphs, and it might look weird but it still means the same thing.
No -- whitespace definitely has meaning in natural languages. It doesn't usually change the meaning much -- but it does affect it (where meaning of course includes tone and consideration for the reader's experience). Especially when it comes to the use of space between sentences and paragraphs.
As any good writer will tell you.
Though it also showed the problem with teaching languages: The students were not that eager on it. They would have preferred a languages with better job prospects.
As motivation is the key for success to learning, yeah sometimes the language that offers the quickest benefits is a good choice. The strength of Python is that it has great libraries in very trendy fields that allow to get stuff done with very little knowledge. That is why both Python and JS (easiest to get a job) are good beginner languages. (That really hurts me to write as learning should not be solely focused on external rewards but such is the world currently)
That said, if I were to choose a language to teach programming then Lua would be the perfect candidate. It avoids the syntax pitfalls of Python as well as having much lower complexity while still being very powerful. Plus really great for gamedev so many motivating examples.
E.g. if they learn carpentry, and the classes teach them how to do dining chairs (proper tools, wood characteristics, drilling and gluing techniques). But in real world they'll have to make tables. Does it really matter if they learned to make chairs not tables?
Tcl also does not have distinguished NULL value like Python, C, C++, C# and many other languages. You do not have to account for that.
The reduction of side effects and no universal no-value value puts Tcl in my hierarchy of programming languages right below Haskell and above pretty much everything else. Tcl is very easy to use when you know how to structure the program.
I still use it for various prototyping when I do not want strict type discipline.
[1] In practice things are typed in the interpreter internals for performance reasons, but the language behaves as if EIAS.
proc maybe {onno onjust val} {
if {[llength $val] == 2 && [string equal [lindex $val 0] Just]} {
lappend onjust [lrange $val 1 end]
return [uplevel $onjust]
}
# I cannot extract value
return [uplevel $onno]
}
maybe {puts "---"} {puts } {Just "huh?"}
maybe {puts "---"} {puts } {}
This is an example of custom control statement, modeled after Haskell's maybe [1].[1] https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-...
It is easy to use and write. You can follow algebraic data types convention and prefix values with tags and use something like control structure drafted above. I did that, it is relatively comfortable.
Note that special case of empty list also handled as if it is Nothing. So it is more useful than would appear. ;)
Of course some of these features were already present in other languages. However, steady improvements to Tcl have pushed the language well beyond how it was described in the article. Definitely not a "toy" but overall as capable as the other scripting tools out there.
Put differently: The command line ergonomics of POSIX shell, with the scripting ergonomics of Lisp
* The Birth of Tcl: https://news.ycombinator.com/item?id=31111220
* TclTutor 3: https://news.ycombinator.com/item?id=30327945
* Tcl/Tk Spline Editor: https://news.ycombinator.com/item?id=29772621
* Tcl library for CSP: https://news.ycombinator.com/item?id=29421786
* Why Tcl syntax is so weird: https://news.ycombinator.com/item?id=29143346
* Tcl: Lisp for the masses: https://news.ycombinator.com/item?id=28957701
int my_command (ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) { ... }
However, that might have been the only thing I liked about Tcl. For some binary data import tasks I ended up writing a Perl program that translated it into Tcl.If you meant a login shell... https://wiki.tcl-lang.org/page/Is+tclsh+or+wish+suitable+as+...
This seems very slightly disingenuous, if my memory is correct. I don't remember all the details, but I ran into an issue with a Tcl application a while back and Unicode support. Digging into it, I recall that the Tcl interpreter actually represents every character as a predefined number of bytes, set by a preprocessor definition. The default was 2, and cut off any Unicode character that needed more than 2 bytes to encode, unless you were willing to recompile the interpreter to use 4 bytes, at the cost of doubling memory consumption for every string.
Very nitpicky, but I think it's important to point out it's not "quite" utf-8, because Tcl needs each codepoint to be O(1) indexable in an array, something normal utf-8 can't do.
Ah, that’s exactly what old python did. Wonder of it was inspired by the tcl solution.
Fwiw because they rejected indexing recent cpython uses a variable encoding based on contents (possibilities are iso-8859-1, ucs2, or ucs4). That does mean adding an astral codepoint to an ASCII string quadruples its size.
Pypy, on the other hand, uses utf-8.
I assume its because the original unicode only supported 2 byte characters, and even after astral characters became a thing it took a while for them to be used for non-cjk things.
The fixed size, but I’m referring to the compile-time width switch.
I may be wrong but I was under the impression most older langages simply remained on their original character width (or left the issue ill defined and/or added an ancillary type like C).
> There are no types, and you don't need to perform conversions, however, you aren't likely to introduce bugs because the checks on the format of the strings are very strict.
You aren't likely to introduce bugs when literally every type is a string. Even lists. Lists are strings. Lists of strings are strings. Maps are strings.
It's pretty insane, and the interpreter does some horrible magic so that if you never access a list as a string it can store it internally as a real list (to avoid insanely bad performance) but to the programmer it's still a string and nothing prevents you from treating it as such.
I actually made a semi-funcitonal WASM-to-TCL transpiler to avoid having to write TCL. Worked kind of well! Unfortunately WASM is quite large and I lost interest before completing it.
It's the ways in which it is not like Lisp, the features that Lisp hackers discovered decades ago were nests of bugs and confusion that... arghhh.
My overall impression of Tcl, however, is quite positive -- and Tcl/Tk is a JATO bottle for rapid GUI development that no Lisp implementation has yet been able to match -- not without embedding or incorporating Tcl itself (e.g., PS/Tk: https://wiki.tcl-lang.org/page/PS%2FTk).
And then one stars wondering if it isn't better to migrate to something else.
Reminds me of Forth too... do people still make firmware with Forth?
Today zsh or fish is the better bash, really.
Here's another dead end unexplored though - dtksh! https://www.brendangregg.com/dtkshdemos.html
% interp alias {} = {} expr
=
% set x [= 2 + 3]
5
Also aliasing lindex to @ is nice % interp alias {} @ {} lindex
% set a {1 2 3}
1 2 3
% @ $a 1
2namespace path {::tcl::mathop ::tcl::mathfunc}
Then you get very LISP like math operators
% set x [+ 2 3 [- 5 20]]
-10
and math functions % set x [abs -12]
12Do you want your language to help you solve real problems, or do you just enjoy your weird toy from another era? Everything is a string..? Please.