A Smooth Introduction to TCL for Web Nerds

for Web Publishing with Databases

by Martin Elsman last modified Feb 03, 2002


Preface

These notes are for use in the course Web Publishing with Databases held at the IT University in Copenhagen, Denmark. The notes supplement the TCL for Web Nerds notes, written by Hal Abelson, Philip Greenspun, and Lydia Sandon, used in the MIT course 6.916. The present notes require no programming experience. The goal is to introduce the reader to tcl programming with Web applications in mind.

All the examples in these notes can be run using the tclsh interpreter. Versions of the tclsh interpreter exist for both UNIX and Windows. Under UNIX, simply type tclsh at the shell prompt, which will bring up the tcl prompt:

 %
Under Windows, download and run the self-extracting executable tcl823.exe; a tclsh icon should appear on your desktop. Do not worry about the gray window that pops up when you click on the icon. Simply iconize this window; it is of no use to us because we will be constructing the user interface in HTML.

Introduction

Why would a Web publisher want to learn tcl? Because tcl is a simple language that can be used to build powerful and efficient server-backed Web sites with little effort!

One Web server that makes the efficient implementation of Web services with tcl possible is AOLserver. AOLserver has a built-in tcl interpreter. The Web publisher implements pages in the tcl programming language, which when interpreted produces HTML code as output. Then, when a client with a browser requests a tcl page, AOLserver interprets the contents of the page and sends the output from interpreting the tcl code back to the client. In this way the Web publisher can program the content of Web pages.

In the following sections, we introduce the tcl language. First, we give an overview of what tcl commands look like. We then proceed with sections on Arithmetics, Variables, Nested Commands, Grouping, Escape Sequences, Comments, Conditionals, While-Loops and For-Loops, Strings, and Procedures.

Commands

A command in tcl is of the form
  cmd arg1 arg2 ... argN
where cmd is a command identifier and arg1, arg2, ..., and argN are arguments to the command. Commands can be typed directly at the tclsh prompt:
  % puts "I'm alive!"
  I'm alive
Here, the command puts takes only one argument, the string "I'm alive!", and prints it. The double-quotes are needed to group the string as one argument to puts instead of two (see the section on Grouping, below).

Arithmetics - computing with numbers

Arithmetics is easy in tcl. Arithmetic commands start with the command identifier expr:
  % expr 3+4
  7
This expr command computes the result of adding 3 and 4. When commands are entered at the %-prompt, the tclsh interpreter prints the result of the command--in this case, 7. There are several other operators than + that you can use, including -, *, and /.

Variables

Variables are used to hold values, like 4, 100, and 4.3, and even values of other types than numbers, like strings and lists. Variables are set with the set command:
  % set age 30
  30
  % puts "I'm $age years old"
  I'm 30 years old
The set command takes two arguments, the name of the variable to set and the value to which the variable should be set. There are several things to notice here. First, because the set command returns the content of the variable, the tclsh interpreter prints the value 30. Second, it is possible to refer to the content of a variable by placing a $-sign in front of the variable name. Here this feature is used in the argument to the puts command, in which the value 30 is substituted for the variable age.

As mentioned earlier, variables can be used to hold values other than numbers. Here is an example where a variable my_name is used to hold the name of a person:

  % set my_name Martin
  Martin
  % puts "$my_name is $age years old"
  Martin is 30 years old
Notice again that before the string is passed to the puts command, the contents of the variables my_name and age are substituted for the occurrences of $my_name and $age in the string.

Nested Commands

A nested command is a command wrapped in square brackets ([...]). Nested commands provide an easy way to use the result of a command as an argument to another command:
  % set expected_time_to_reincarnation [expr 90 - $age]
  60
Here the set command is used to set a variable expected_time_to_reincarnation to the result of evaluating the nested command [expr 90 - $age]. Notice that the nested command is evaluated before being passed to the set command. Because the variable age contains the value 30, the variable expected_time_to_reincarnation is set to 90 - 30, which equals 60. Notice also that variable names can have underscore (_) characters in them.

Programs

A tcl program is a sequence of commands separated by newlines and semicolons. Here are two commands separated by semicolons:
  % puts Hello; puts World
  Hello
  World
A useful tcl command is the source command, which reads tcl commands from a file. Assume that you have a file hello.tcl in the directory in which you started tclsh and that this file contains the tcl code
  set firstname Martin
  set lastname Elsman
  set age 30
  set email mael@itu.dk
  puts "My name is $firstname $lastname and my email address is $email."
Using the source command, it is possible to execute the commands in the hello.tcl file:
  % source hello.tcl
  My name is Martin Elsman and my email address is mael@itu.dk.

Grouping: Double Quotes ("...") and Curly Braces ({...})

Tcl is foremost used for processing strings, which are sequences of letters, numbers, and other characters, like &, $, #, and so on. There are two ways in which a sequence of characters can be grouped as an argument to a command, by using double quotes ("...") and by using curly braces ({...}). Here is the difference:
  % puts "My name is $firstname"
  My name is Martin
  % puts {My name is $firstname}
  My name is $firstname
In both examples, grouping of characters is needed to treat a sequence of characters containing spaces as one argument to the puts command. The difference is that if curly braces are used to group characters as arguments to a command then elements within the arguments are not evaluated before the command is called. Contrary, if double quotes are used to group arguments to a command then dollar signs and square brackets are interpreted inside the arguments.

Escape Sequences - the backslash (\)

``This is great'', you say. ``Now I can do almost everything! But how do I write a program that prints a string containing dollar signs ($) and curly braces ({...})?'' Here is how:
  % puts "I can now write characters like \$, \{, \}, \[, \], and \""
  I can now write characters like $, {, }, [, ], and " 
There are other special characters that you can write using backslash, like newline (\n) and tab (\t):
  % puts "Sale items\ncar\t\$800\nbicycle\t\$200"
  Sale items
  car     $800
  bicycle $200

Comments

Comments in tcl code starts with the character # placed in the beginning of a line or immediately after a semicolon. Comments end with a newline. Here are a few examples of comments in tcl code:
  % # Let's set the variable year
  % set year 2000   ;# we're in year 2000
  2000
It is a good habit to always document (with lots of comments) the tcl code that you write; good documentation is critical to understanding other people's code--and sometimes, even your own!

Conditionals

The conditional construct is fundamental for programming. It is the conditional construct that provides for the possibility of executing different code dependent on certain conditions. As an example, here is code that, depending on the variable number_of_courses prints a suitable sentence:
  set number_of_courses 2
  if {$number_of_courses == 0} {
    puts "I'm taking no courses this semester"
  } elseif {$number_of_courses == 1} {
    puts "I'm taking one course this semester"
  } else {
    puts "I'm taking $number_of_courses courses this semester"
  }
In addition to the operator ==, there are several other operators that can be used in conditionals, including >=, >, <=, <, and !=.

In principle, the if command is just like any other command, except that it takes a non-fixed number of arguments; in the example above the if command takes seven arguments. Here is a use of the if command that takes only two arguments:

  set email "mael@it.edu"
  if { $email == "mael@it.edu" } {
    puts "Welcome home Martin"
  }

While-Loops and For-Loops

While-loops and for-loops provide ways of implementing repetitions. Here is a simple program that uses a while-loop to print the string ``Soon, I'm a Web programmer...'' three times:
  set i 0
  while {$i < 3} {
    incr i
    puts "Soon, I'm a Web programmer..."
  }
The while command takes two arguments. It is essential for this example that both arguments are wrapped in curly braces. The evaluation of a while-loop results in evaluating the second argument until the evaluation of the first argument returns 0. For the present example, the variable i is initially set to 0. Now, because the first argument to while evaluates to 1 ([expr $i < 3] returns 1), the second argument to while is evaluated as a sequence of commands. This evaluation results in the variable i to be increased by one (by the incr command), such that it now has the value 1. Before the while-loop is evaluated two more times, the string ``Soon, I'm a Web programmer...'' is printed by the puts command.

With for-loops, it is possible to do exactly the same things that one can do with while-loops. A for command takes four arguments. The first argument is a command to be executed before the loop is entered; this command typically initializes a variable used in the loop. The second argument is a test, which is executed at each repetition; if the result of this test is false, the loop is terminated and evaluation proceeds after the loop. The third argument is a command, which is executed after each repetition. The fourth argument is the body of the loop. Here is an example for-loop, which prints a multiplication table:

  # after each repetition, x is increased by one
  for {set x 0} {$x<10} {incr x} {
    puts "$x times 9 is [expr $x * 9]"
  }
See the man pages for for and while if you need more information about these commands.

Strings

An essential part of Web programming has to do with strings. When you receive input from a user, the inputs are stored as strings, which it is then up to you, the Web programmer, to analyse and use in some way. Similarly, when you want to send the user a response, the response is a HTML page, which essentially is a string, albeit it must take the form of a valid HTML page.

Tcl is well-suited for processing strings; it has a large set of built-in commands for string manipulation.

A string is basically a sequence of characters, so given a string, one can ask about the length of the string:

  % set mystring "This is a fairly long string..."
  This is a fairly long string...
  % string length $mystring
  31
This example makes use of the string command, which implements many different string-manipulation commands, called sub-commands. The first argument to the string command specifies the sub-command. Here, we're interested in the length sub-command of the string command. Additional arguments to the string command are then arguments to the sub-command. The length sub-command of the string command takes one extra argument, which is the string of which the length is to be returned. From the example, we see that the string in the mystring variable is 31 characters long.

Another important string command, which you have in fact already read about, is the append command. This command is so important that it is not a sub-command to the string command, but a real command! The append command takes two or more arguments. The effect of the append command is to set the variable to the concatenation of the old content of the variable and the remaining arguments:

  % set myotherstring "This is not"
  This is not
  % append myotherstring " a very" " long string" "..." 
  This is not a very long string...
Let us use the append command to write a program that constructs a HTML page in a variable page and prints out the page:
  set page "<html>\n"
  append page " <head>\n"
  append page "   <title>Hello World</title>\n"
  append page " </head>\n"
  append page " <body>\n"
  append page "   Hello World\n"
  append page " </body>\n"
  append page "</html>"
  puts $page

Of course, to get the effect of printing the content of the variable page, we could just have printed the page with the one command:

  puts "<html>
          <head>
            <title>Hello World</title>
          </head>
          <body>
             Hello World
          </body>
        </html>"

Here is an example where it is practical to build up a string dynamically using the append command:

  set page "<html><head><title>Hello World</title></head>
            <body><center>\n"
  set i 1
  while { $i < 5 } {
    append page "<h$i>Haleluja</h$i>\n"
    incr i
  }
  append page "</center></body></html>"
  puts $page
And here is how it looks when a Web browser presents the resulting page:

Haleluja

Haleluja

Haleluja

Haleluja

In Web applications it is important to be able to compare strings. Comparisons are necessary, for instance, to act differently on different user inputs. To compare two strings in tcl, we use the string compare command. The string compare command takes two strings as arguments and returns 0 if the strings are identical. Suppose that some user has registered on your site with the form variables first_names, last_name, email, and passwd. Assume also that the error command sends a reasonable response back to the user, based on the argument. At-least, we want our program to check that the form variables are non-empty and that the password is suitable:

  # compare the first_names value to the empty string
  if { [string compare $first_names ""] == 0 } {
     error "You must provide a first name"
  }

  # compare the last_name value to the empty string
  if { [string compare $last_name ""] == 0 } {
     error "You must provide a last name"
  }
  
  ...

  # require a password with at-least four characters
  if { [string length $passwd] <= 4 } {
     error "You must provide a password with at-last four characters"
  }
To learn more about string commands, see the TCL for Web Nerds notes, which can also tell you how to check that an email address is of the form something@somethingelse. Also see the string man page available from http://dev.scriptics.com/man/tcl8.3/TclCmd/contents.htm for a detailed explanation of the string command.

Procedures

Procedures in tcl provide the programmer with a way of defining new commands, which can then be used just like other tcl commands. Procedures, which are also sometimes called functions, are defined with the proc command. Here is a procedure hyperlink, which takes as argument a string representing a URL address and returns a string containing HTML code for a hyperlink to the URL address:
   proc hyperlink {url} {
     set res "<a href=\""
     append res $url "\">" $url "</a>"
     return $res
   }
The proc command takes three arguments, the name of the procedure being defined, a list of arguments to the procedure, and the body of the procedure. Procedure names are case sensitive, as are variable names, so the procedure name Hyperlink is different than the procedure name hyperlink. It is good programming practice to always group procedure argument lists and procedure bodies in curly braces.

In the example procedure, the variable res is defined locally within the body of the procedure, which means that the variable can be accessed only in the body of the procedure. A variable named res defined outside of the procedure is not affected by the use of the hyperlink command. The return command at the end of the procedure body is used to return the result of the procedure, which in this case is the string contained in the res variable. The append command is used to construct the HTML hyper-link, based on the URL address, which is given as argument to the procedure.

The following tcl code uses the hyperlink command to output HTML code with a link to the much popular Google search-engine:

  % puts "Google has the URL [hyperlink {http://www.google.com}]"
  Google has the URL <a href="http://www.google.com">http://www.google.com</a>

The ability to define procedures allows the programmer to divide a programming task into sub-tasks. These sub-tasks can then be understood and implemented in isolation, maybe by somebody else than the programmer! Procedural abstraction is therefore very important for software engineering; a programming language that does not provide ways of defining procedures scales poorly to the construction and maintenance of large software systems.

Another important aspect of procedural abstraction is that a procedure defined once can be used in many different contexts. For instance, notice that in the body of the hyperlink procedure definition, the command append is used four times, each time with different arguments.

As another example, consider the following procedure:

  proc multi {word n} {
    set res "" 
    while {$n >= 1} {
      append res $word 
      set n [expr $n - 1]
    }
    return $res
  }
The procedure multi takes two arguments, word and n. When called, the procedure returns the result of appending n copies of the string word. Notice the use of the append command. The append command sets the variable, defined by the first argument, to the concatenation of the old value and the remaining arguments, in this case the string contained in the variable word. Each time around the while-loop, the counter n is decreased by one. Here is a use of multi:
  % puts "I'm [multi {very } 3]smart!"
  I'm very very very smart!

mael@it.edu