MetaCard Corporation

Part 3: Architecture of the languages


In all three languages, variables can be used without being declared. In Tcl and MetaTalk variables are local unless explicitly declared to be global, while the opposite is true for ksh. ksh is also the only dynamically scoped language: variables declared in a function have scope that includes all functions called by that function. This characteristic means that it is very important to declare all variables used in a function. For example, consider a function that uses the variable i (a common name for a looping variable) that calls another function that uses i as a looping variable. If i is not declared to be local in the called function, it will alter the calling functions i and a very difficult to find bug will be the result.

In MetaTalk variables are automatically interpreted as numeric values when arithmetic operations are performed on them. In ksh and Tcl, this conversion must be forced by using a special syntax (enclosing the expression in double parentheses in ksh and using the expr command in Tcl). Tcl and ksh also require the variable name be preceded by the $ character when the variable is referenced, but not when it's set. Ksh also doesn't require the $ when a variable is referenced inside a double-parenthesis arithmetic evaluation.

MetaTalk (like Perl) uses double-precision floating point values for all internal calculations, whereas in ksh variables are treated as integers unless they are explicitly declared to be floating point (only single precision floating point variables are supported). Tcl takes a middle ground, treating values as 32-bit integers unless they are initialized with a number containing a decimal point, or an operation is performed on them that requires conversion to a double-precision real. This requires extra care in programming because integer overflow is not one of the conditions that cause conversion, and because there are some built-in functions that only accept integers. Ksh has a similar limitation when passing floating point values to functions: pass too large an integer, and the value will be truncated without warning.

All three languages have the ability to use a variable as an array. Arrays can be numerically-indexed as in third-generation languages, but strings can also be used as array indices. These "associative" arrays are a powerful feature that can in many cases make up for a language's lack of support for structures and classes. For example, suppose you needed a sorted list of the words used in a document, and the number of times each was used. Assuming the variable "somevariable" contains the text to be counted, this short MetaTalk script is all you need:

  repeat for each word w in somevariable
# automatically creates an array element
# and put a number into it
    add 1 to count[w]
  end repeat
# get a list of the array elements
  put keys(count) into sorted
  sort lines of sorted
  repeat for each word w in sorted
    write format("%20s %d\n", w, count[w]) to stdout
  end repeat
A C/C++/Java program to do this would be many, many times longer, and would take much, much longer to write. Even worse, it's quite likely that it would run slower than the MetaTalk script because in most cases it wouldn't be practical to optimize the associative array and sorting operations to the degree done to the MetaTalk engine.

As is required for numeric variables, arrays must by declared in ksh whereas any variable can be used as an array in Tcl and MetaTalk. In MetaTalk and ksh, accessing an array element that doesn't exist returns an empty string. In Tcl this causes an error, which means you must include extra commands to check for this case when using arrays as dictionaries.

All three languages have constructs that allows access to variables declared outside the current function. This feature can be used to implement call-by-reference. For example, in Tcl the name of an array can be passed in to a function and the upvar command executed to allow access to the elements of that array. The ksh language supports hierarchical variables for which a root name can be passed to a function and subvariables within it set individually. ksh also supports a nameref keyword that can make one variable a reference to another which is used like the Tcl upvar command. The MetaTalk language uses a more natural parameter list syntax to denote variables passed by reference (it works just like the C++ "reference" construct).

MetaTalk is the only language that supports the creation of static variables that have scope only within the script in which they are declared. Like global variables, these static variables retain their value across function calls, but unlike globals, the risk of name-space collisions with variables in other scripts is eliminated.

Tcl and ksh both allow variables to be unset, removing them from the environment. MetaTalk lacks this feature, and globals must be set to empty if their memory is to be reclaimed. Memory used by local variables, of course, is reclaimed as they go out of scope in all three languages.

All three support the full range of regular expression pattern matches available in languages like awk and Perl, although the syntax used by ksh is incompatible with the standard regular expression syntax used by vi and grep. All three also support the "globbing" type regular expressions used in the shell languages. MetaTalk also has validation operators that make it easier to determine things like whether or not a string is an integer, real number, or x,y coordinate.

Control Statements, Commands, and Procedures

All three languages support the common control structures such as if-then-else, switch-case, and repeat loops. All three languages have special statements for catching errors and for sending messages to other objects.

All three tools can send events (call functions) after a certain period of time has elapsed, although with dtksh this is done through the GUI toolkit instead of with a built-in language element. This capability makes managing some time-sensitive UI techniques (e.g, leaving an alert up for a certain period of time) much easier.

Unlike Tk and Motif, all MetaCard objects can have custom properties, and special handlers can be written that are called when these properties are retrieved or set. Furthermore, like all MetaCard object properties, these custom properties are persistent across invocations of the application. Like member functions in object-oriented languages combined with the power of an object-oriented database, this is a powerful feature that makes building large applications easier.

All three languages have the most commonly used mathematical functions built in. MetaTalk also has advanced features such as a random number generator, and functions that find the min, max, and average of a list of numbers.

Ksh has a very minimal set of built-in commands, functions, and constants (42), many of which are only useful when ksh is used as an interactive shell. This requires that it rely heavily on running Unix commands such as sort as subprocesses for high-level data management, an architecture that seriously degrades performance. Ksh scripts use operators and other syntactic elements to a much greater degree than scripts in the other languages. For example, ksh supports several different types of pattern matching as operators, and these can be used in a wider variety of situations than is possible with the other languages. For GUI management, dtksh supports most of the huge number of calls in the Xt and Xm (Motif) libraries, and also many COSE Desktop specific calls.

Tcl has slightly more commands built-in (44) than ksh, including high-level commands for searching and sorting. Most of the GUI management is done using a separate set of approximately 130 Tk library calls. Many of these calls serve several different purposes which are specified by the type and number of arguments passed.

MetaTalk has by far the largest range of commands (96) and functions (154), including support for high-level data management, multimedia and animation, and support for the most common GUI techniques (e.g. password, alert/message, and file selection dialogs can all be displayed with built-in commands). The MetaCard GUI objects are managed primarily by setting the predefined object properties (354).

All three languages have the ability to read and write files, and to run and manage processes. Ksh is the strongest language in this area, since process management is such an import part of interactive shell use.

Tcl and MetaTalk have built-in support for accessing individual elements (words) in strings. MetaTalk's "chunk" expressions can be used to access individual characters, words, items, or lines in a text string. Tcl "lists" only support word-level boundaries by default, but lists can be nested to an unlimited depth, something that is not possible in MetaTalk. The list element delimiters that have to be inserted into the text string to implement this ability makes it impossible to use lists for general purpose text processing, however. For example, the "word count" procedure given in MetaTalk above can't be implemented in Tcl using lists, since any " or { characters in the input stream will cause the list operations to fail.

Tcl, like Perl, also requires that text strings that need to be accessed using something other than word boundaries (e.g., line boundaries or the components in a directory path) be "split" into chunks with an explicit operation, whereas MetaTalk can access the individual items in a string directly. Ksh has an "IFS" variable that can be used to change the word delimiter used when reading from a file, but the only way to to access words, items, or lines from a text string stored in a GUI input field or variable is to use complex set of substring and pattern matching operations, or to loop over all of the elements using the for construct, extracting the required element when it comes around.

All three languages support user-defined procedures. These procedures can have a variable number of arguments, which are easily referenced from within the procedure. While building procedures with variable numbers of arguments is also possible in most third generation languages, it is always much more difficult. Tcl and MetaTalk name the variables using an argument list in the function definition, whereas ksh functions only get positional parameters ($1, $2, etc.) which must be renamed in the function body if readability is a concern.

MetaTalk is the only language that makes a distinction between user-defined functions (which return values) and procedures (which don't). The other two languages have built-in functions for mathematical operations, but treat all user-defined functions as commands (procedures).

Tcl and ksh procedures can return values, but they must be called using a special syntax when used in argument expressions to other commands. Ksh functions can only return integers between 0 and 255, which means that most functions will have to use variable references (which changes the calling arguments) or command substitution (an alternate function call syntax which causes values printed to stdout to be redirected to a variable in the calling function). In MetaTalk, the same syntax is used to call both built-in functions and user-defined functions, and no syntactic gymnastics are required to return a value from a function.

MetaTalk is case-insensitive, although string comparisons can be made case sensitive by setting a property. The other two languages are case-sensitive. This can be problematic given the "camel capitalization" scheme used for property/resource naming used in all three GUI toolkits (the first letter of each word in a string is capitalized). This is especially likely to happen with dtksh since it uses the complex Xt/Motif naming scheme. For example, to add a callback for double clicks on a button the resource multiClick must be set. Write it as as the more natural multiclick and the interpreter will warn you that no such resource exists. To get around this, the examples in the standard reference book for dtksh, "Desktop KornShell Graphic Programming" by J. Stephen Pendergrast, Jr., all use the ksh alias command to rename all the Xt/Motif functions to all lower-case names.

All three languages use the '#' character to start a comment that extends to the end of the line, although ksh only recognizes the '#' character if it is preceded by a space and Tcl only accepts it if occurs where the interpreter expects to find a command. MetaTalk also accepts the HyperCard-standard sequence "--" to start a comment.


Of the three, only MetaTalk comes with a GUI script debugger. Command-line script debuggers are available for Tcl and ksh, but these are not part of the standard development environments. Since all three are interpreted languages, however, most errors can be located easily by examining the line reported when the interpreter reports an execution error (although Tcl does not report the line number of an error, complicating the location process somewhat). In addition, there are many ways to output status information while the scripts are executing. This is similar to using printf in a C program, but more powerful since the messages can be sent to several different destinations. Tcl and ksh make this process even easier since they have commands that cause the environment to print out statements as they are executed.

MetaCard's script debugger and profiling tool can be used to set breakpoints and trace through a script statement by statement. In addition, variables can be examined and set while script execution is stopped. The MetaCard profiler can be used to find performance bottlenecks in scripts and also to provide script coverage measurements when doing QA on an application. Tcl has a "time" command which can be used to measure the performance of scripts, though no graphic display of this data is available in the standard environment.

Calling C Functions

All three tools can be extended by writing external functions in C or other languages. The architectures are very different, however. As a sourceware tool, extending Tcl/Tk is the most straight forward since you merely link your new functions into the Tcl interpreter. The full range of Tcl utility functions are available from your functions, as opposed to the limited access available in the other tools. The disadvantage of this approach is that the application must be exited and restarted every time the user-written C code is changed. Tcl also has a "send" command that can be used to send messages to another Tcl process through X events, and the ability to dynamically load libraries, both of which can be used to reduce the need to restart the application when developing external functions in C.

External C functions are added to dtksh by making them into dynamic libraries and loading these libraries with dtksh scripts; each external library and each function within each must be explicitly loaded by a separate statement. The external functions can call back into the dtksh interpreter to execute scripts and to get and set global variables. The disadvantage to this approach is that it is not portable to other operating systems, or even to all versions of UNIX.

Unlike the other two tools, standard MetaCard externals are run as separate processes. MetaCard's external procedure call mechanism relies on the X property and event mechanism for communication. Although calls using this path are much slower than the mechanism used in the other two tools, there are a couple of advantages. First, the MetaCard process does not need to be exited and restarted during the testing phase, which improves productivity. Secondly, the API is portable to platforms that don't support shared libraries such as DESQview/X and many older version of UNIX. External procedures can also call back into the MetaCard interpreter to get and set global variables and to execute MetaTalk scripts. An embedded version of MetaCard is also available, to which C functions are linked directly, similar to Tcl architecture.

On to Architecture of the GUI interfaces

Back to IDE Origins

Up to Table of Contents