I can’t believe I’m praising Tcl
yosefk.com
yosefk.com
http://jim.tcl.tk/index.html/doc/www/www/index.html (source code is here: https://github.com/msteveb/jimtcl)
Now maintained by Steve Bennett, and actively used by some embedded folks.
The ease with which one can quickly create interfaces between different tools I think is one of the contributing factors to its success. That and of course, the legacy codebase.
To imply:
(define (fn a b)
(list (car a) (car (car b))))
You could type: define: fn a b
list:
car a
car (car b)
As it's a console, in practice you'll mostly be typing commands like this: load x y z
but you have good mechanisms for going deeper. And if you just want to write in s-expressions you still can.There are some good observations about design pressures on command languages vs. programming languages in Olin Shivers's "A Scheme Shell", as well.
* Erlang VM
* sqlite
* Linux kernel
* Clojure functional data structures
That list may look very pretentious, but I don't try to fully understand the packages. What interests me is (1) the style, and (2) the larger strokes. What kind of idioms are there, and how does it fit together. I think it is pretty hard to get the idea of a software package from reading the code. For that some higher level description or book makes more sense. Generally I think reading source code is very helpful in becoming a better programmer. I don't think the above list is anywhere near a general recommendation. I did rather pick a domain that was intimidating to me, and the reduce ignorance there.
The Go compilers and stdlib are also a great read. Is no coincidence that Ken Thompson was involved in all those projects.
More on topic: I agree it is sad how underrated Tcl is, perhaps not the best language ever created, but deserves much more attention and credit than the latest JS-flavour-of-the-week.
Tk is also great and IMHO still the best portable GUI toolkit around.
As an aside, LTK, which is basically just lisp bindings for talking over a socket to wish is the only lisp gui that I've had work on all platforms and all lisp implementations.
Can I get one of the Tcl adherents in this thread to comment- have I got things completely wrong? Need I simply become better with Tcl? Or does it truly lack things like powerful string handling and hash tables?
Can you give me an example of powerful string handling?
string[3] for the usual string manipulations.
subst[4] when you want weird string substitutions (works like a template).
regsub[5] and regexp[6] for regular expressions.
[1] http://www.tcl.tk/man/tcl/TclCmd/dict.htm
[2] http://www.tcl.tk/man/tcl/TclCmd/array.htm
[3] http://www.tcl.tk/man/tcl/TclCmd/string.htm
[4] http://www.tcl.tk/man/tcl/TclCmd/subst.htm
For example the interpreter can basically say "there's a syntax error somewhere in this giant block of yours" and not have any idea where.
A related issue is that a single delimiter (the brace) is bad for readability, even if it's easy to parse. Just try reading a gigantic dictionary; it isn't anywhere near as clear as Python/JSON-style can be (where there are obvious commas, colons, quoted strings and more to show you exactly what you're looking at).
Another headache for debugging is that commands frequently accept the names of variables. In unfamiliar code you can't even answer simple questions like "where are all the places 'xyz' is used?" because a reference to 'xyz' could be hiding almost anywhere. You can change something and not fully understand the effects that your change could have. While I'm sure this allows for very clever code to be quickly written, it fails the more-important test of producing code that is easy to read.
It is even possible for commands to silently accept mistakes and seem correct, with major consequences. Suppose "x" just happens to be a one-element list containing a number, '{0}'. With this input, "lindex 0 $x" is legal (even though "lindex $x 0" is the correct argument order). Worse, the wrong form returns something that seems reasonable without error. In this case what it actually does is grab the index that it found by magically looking inside the list ($x) and return one of the values from the implicit list containing "0" that was given on the command line; it returns a "0" but not the "0" that you'd think it does!
While one can argue that each language has "best practices" and intended usage, the examples above are largely pulled directly from built-in commands and data types. These are things that people encounter all the time, they are not caused by any particular TCL programming practice.
Tcl might have been too easy. I also recall ESR dismissing it at one point in favor of Python.
But I didn't know how much I liked it until I Googled for "PHP upvar" and found a forum thread with bunch of people telling some guy that he was a language fascist for wanting such a thing.
I still don't know why Tcl had problems entering the WWW age. It started out pretty well, the aolserver was/is a pretty capable and performant system and they even had browser plugins (i.e. as a Java rival), but once the plethora of web services hit the landscape and we all went "Web 2.0", the lack of a proper CPAN equivalent and the slightly outdated Tk look caused some exodus to more hip scripting languages. The sad thing is that since quite a while Tcl overcame all those troubles (modules, packages, native look and feel), but probably a bit too late.
Bugzilla, for example, was originally written in Tcl; but for reasons I have never been able to discover, it was completely redone in Perl. Anybody here know why?
Then you've got to remember that for a while, Sun owned Tcl. Pretty much the same period when they were hyping Java…
Also, the core Tcl distribution was still focused on scripting (embedded or not) and GUI programming with Tk. Modularity back in the days often involved separate interpreters with added featurs (TclX for example).
Compare that to PHP: Basically the same situation regarding additional modules, but they had everything related to simple web development in one package - parsing requests, handling pictures… You could use PHP to do scripting and even GUIs, and you could use Tcl to do web development. But once you're written off as a niche language, it's hard to escape that trap.
We've got a whole bunch of interpreted languages out there, but only a few of them are considered all-purpose languages. Ruby and Python mostly, even Perl has to fight a bit against the Unix scripting preconceptions.
Tcl wouldn't be the first scripting language of that period "lost". Anyone remember Pike or Icon?
I think the general consensus at our company is that Tcl was the right choice, although our programmers do occasionally gripe at some Tcl shortcomings.
foreach animal { "dog" "cat" "bird" } color { "brown" "black" "red" } { puts "See the $color $animal?" }
for animal, color in zip(['dog', 'cat', 'bird'], ['brown', 'black', 'red']):
print 'See the %s %s?' % (color, animal)Still, I wish more languages directly imitated the TCL foreach loop. I've found that when I code, I wish I could just do some simple task 3-4 times. I am always tempted to create a little function, and to call that function repeatedly. But that seems like overkill. With TCL, I just write a simple loop statement instead of the function. I've found that my code is more compact, and the number of functions that I declare is reduced.
['dog', 'cat', 'bird'].zip(['brown', 'black', 'red']).each {|animal, color| puts "See the #{color} #{animal}?"}for animal, color in zip(['dog', 'cat', 'bird'], ['brown', 'black', 'red']): print 'See the %s %s?' % (color, animal)
edit: eurleif beat me too it.
a:`dog`cat`bird
c:`brown`black`red
{`0:,//("See the ";$x;" ";$y;"?\n")}'[c;a]
The string formatting is a bit ugly because I'm not using a dedicated format function, but foreach with multiple data structures is just f'[list;of;arguments].In other words, it's one character: ' ("each").
(loop :for animal :in '("dog" "cat" "bird")
:for color :in '("brown" "black" "red")
:do (format t "See the ~a ~a?~%" color animal))
See the brown dog?
See the black cat?
See the red bird?
How far can we take the example? (loop :for animal :in '("dog" "cat" "bird")
:for color :in '("brown" "black" "red")
:for count :from 2
:do (format t "See the ~a ~a ~as?~%" count color animal))
See the 2 brown dogs?
See the 3 black cats?
See the 4 red birds?
How about this? (loop :for animal :in '("dog" "cat" "bird")
:for color :in '("brown" "black" "red")
:for count :from 2 :sum count :into total
:do (format t "See the ~a ~a ~as?~%" count color animal)
:finally (format t "See all ~a?~%" total))
See the 2 brown dogs?
See the 3 black cats?
See the 4 red birds?
See all 9?
Anybody?People either love or hate Common Lisp's LOOP. Lispy types who prefer recursion often abhor it. I'm a lover.
(doseq [animal ["dog" "cat" "bird"
color ["brown" "black" "red"]]
(printf "see the %s %s?\n" color animal))(doseq [[a c] (zip ["dog" "cat" "bird"] ["brown" "black" "red"])] (print "See the " c a "?"))
Haskell:
mapM_ (\(a, c) -> putStrLn $ "See the " ++ c ++ " " ++ a ++ "?") (zip ["dog", "cat", "bird"] ["brown", "black", "red"])
edit: one can do way more complicated stuff with the list monad in Haskell and the very comprehensive core lib in Clojure; I'm sure even these examples can still be made a little bit shorter and more readable.
for <dog cat bird> Z <brown black red> -> $animal, $color {
say "See the $color $animal?"
}+ By this I mean you can get a lot done, in not very much code, and maintain it afterwards, and read and modify other people's code easily.
Even Alan Kay has been complaining about it since 1997 [1]. Looks like even our complaints get recycled.
1. http://video.google.com/videoplay?docid=-2950949730059754521
1. Code is data, data is code
Implications: everything should be a first class citizen of the language, able to be passed by reference, altered by functions elsewhere.
Examples: s-expression in lisp? Subclassing built-in types of oo language (python new style objects)
2. Configs are applied to code, so that the same code can run unchanged on dev beta and live. (this is my favourite reinvented wheel du jour - devops. In fact the whole 12factor app recently on HN is a great example of rediscovered wheels - or perhaps more fairly a great example of doing this guide to future generations too)
3. iPads are no way to type into unresizeable text boxes
... Edits coming when I get a keyboard
I find it fascinating, it's as if kids these days believe that their fathers had all the things they take for granted, like smartphones, tablets, AJAX, whatever, and then, because they were stupid, chose to use green screens and FORTRAN instead.
http://journal.dedasys.com/2010/03/30/where-tcl-and-tk-went-...
Half the libs needed a source code change to the tcl interpreter. There were two main add ons (I want to remember one was called green eggs and ham???) which were incompatible, so if your lib needed A and another bit needed B you were stuck.
One thing it did have was very easy to write C plugins because everything is a string
To pick Lua (my favorite of the technologies you listed), it has the following things that Tcl does not have:
- strict ANSI C implementation
- small implementation (15k sloc vs. 110k-160k for Tcl, depending on how you count)
- a fast implementation, with an even faster JIT
If you follow your argument to its logical conclusion then everything is just a reinvention of Lisp (including Tcl). But we have tools available to us today (like Lua) that are strictly better than Lisp or Tcl for some use cases. - only one dialect (sometimes changes between major versions,
but there aren't multiple dialects evolving in parallel).
- infix notation is preferred by many programmers, and is easier
to use for non-programmers.
- the language is small (more like Scheme than Lisp)
The Lua and LuaJIT implementations have the following advantages over SBCL (you didn't say which Lisp, so I'm just picking what seems to be the most popular one): - extremely portable
- extremely small
- easy to sandbox
- very good integration with existing C and C++While it's good as a standalone language, it's an outstanding extension language. It's small, trivially portable, and has excellent interop with C. It's not competition for Lisp in general so much as Emacs Lisp or vimscript, designed for systems that have a core written in C but need a lot of customization layered on top. Configuration for window managers, scripting enemy behavior for game engines written in brutally optimized C++, that kind of thing. Systems with a "hard and soft layer" architecture (http://c2.com/cgi/wiki?AlternateHardAndSoftLayers). (Incidentally, Tcl was also designed with this use case in mind.)
That said, if you're using Lua as your primary language but are proficient in C, you can add a lot of power to it pretty easily. The emphasis on C interop goes both ways. (Also, the implementation is very intelligently engineered, and is worth study if you're interested in language design or virtual machines.)
Lua is probably going to be faster, but one wouldn't exactly use a scripting language for math heavy stuff etc either.
It's kind of the opposite of Javascript, which went from 0 to release in a few weeks and had a bunch of design errors become permanent as a result. Lua and Javascript have a lot in common, and studying Lua may be a good way to see where Javascript is going in the long term.
Lua is significantly smaller than ECL, and a bit smaller than PicoLisp. According to sloccount, they weigh in at 432,818 (ECL), 15,018 (PicoLisp), and 14,309 (Lua).
This is strictly a limitation of languages like Lua, but in day-to-day use it sometimes becomes an advantage.
He has a point there. Lua is nice enough to write, but it is `func(arg, 'strarg')` where Tcl is appearantly `func $arg arg`. If I have to type that into an interactive prompt 200 times, I'd take the latter. If I want to write some complex program, I'd take the former. In other words, Tcl is optimized for writing, Lua is optimized for reading.
- everything is a literal
- everything is an expression
Is there a Tcl variation that's a functional language?