Abstract
Scripting languages such as Perl and Tcl represent a very different style of programming than system programming languages such as C or Java. Scripting languages are designed for "gluing" applications; they use typeless approaches to achieve a higher level of programming and more rapid application development than system programming languages. Increases in computer speed and changes in the application mix are making scripting languages more and more important for applications of the future.
Scripting languages are designed for different tasks than system programming languages, and this leads to fundamental differences in the languages. System programming languages were designed for building data structures and algorithms from scratch, starting from the most primitive computer elements such as words of memory. In contrast, scripting languages are designed for gluing: they assume the existence of a set of powerful components and are intended primarily for connecting components together. System programming languages are strongly typed to help manage complexity, while scripting languages are typeless to simplify connections between components and provide rapid application development.
Scripting languages and system programming languages are complementary, and most major computing platforms since the 1960's have provided both kinds of languages. However, several recent trends, such as faster machines, better scripting languages, the increasing importance of graphical user interfaces and component architectures, and the growth of the Internet, have greatly increased the applicability of scripting languages. These trends will continue over the next decade, with scripting languages used for more and more applications and system programming languages used primarily for creating components.
By the late 1950's higher level languages such as Lisp, Fortran, and Algol began to appear. In these languages statements no longer correspond exactly to machine instructions; a compiler translates each statement in the source program into a sequence of binary instructions. Over time a series of system programming languages evolved from Algol, including such languages as PL/1, Pascal, C[4], C++[9], and Java[1]. System programming languages allow applications to be developed much more quickly than assembly languages while providing nearly the same level of efficiency. As a result, they have almost completely replaced assembly language for the development of large applications.
while
and if
for control structures; the compiler generates all the detailed instructions to implement the control structures.
The second difference between assembly language and system programming languages is typing. I use the term "typing" to refer to the degree to which the meaning of information is specified in advance of its use. In a strongly typed language the programmer declares how each piece of information will be used and the language prevents the information from being used in any other way. In a weakly typed language there are no a priori restrictions on how information can be used: the meaning of information is determined solely by the way it is used, not by any initial promises.1
Today's system programming languages are strongly typed. For example:
To summarize, system programming languages are designed to handle the same tasks as assembly languages, namely creating applications starting from scratch. System programming languages are higher level and much more strongly typed than assembly languages. This allows applications to be created more rapidly and managed more easily with only a slight loss in performance. See Figure 1 for a graphical comparison of assembly language and several system programming languages.
A typeless language makes it much easier to hook together components. There are no a priori restrictions on how things can be used, and all components and values are represented in a uniform fashion. Thus any component or value can be used in any situation; components designed for one purpose can be used for totally different purposes never foreseen by the designer. For example, in the Unix shells, all filter programs read a stream of bytes from an input and write a string of bytes to an output; any two programs can be connected together by attaching the output of one program to the input of the other.
The strongly typed nature of system programming languages discourages reuse. Typing encourages programmers to create a variety of incompatible interfaces ("interfaces are good; having more interfaces is better"). Each interface requires objects of specific types and the compiler prevents any other types of objects from being used with the interface, even if that would be useful. In order to use a new object with an existing interface, conversion code must be written to translate between the type of the object and the type expected by the interface. This in turn requires recompiling part or all of the application, which isn't possible in the common case where the application is distributed in binary form.
To see the advantages of a typeless language, consider the following Tcl command:
button .b -text Hello! -font {Times 16} -command {puts hello}This command creates a new button control that displays a text string in a 16-point Times font and prints a short message when the user clicks on the control. It mixes six different types of things in a single statement: a command name (
button
), a button control (.b
), property names (-text
, -font
, and -command
), simple strings (Hello!
and hello
), a font name (Times 16
) that includes a typeface name (Times
) and a size in points (16
), and a Tcl script (puts hello
). Tcl represents all of these things uniformly with strings. In this example the properties may be specified in any order and unspecified properties are given default values; more than 20 properties were left unspecified in the example.The same example requires 7 lines of code in two methods when implemented in Java. With C++ and Microsoft Foundation Classes, it requires about 25 lines of code in three procedures (see [7] for the code for these examples). Just setting the font requires several lines of code in Microsoft Foundation Classes:
CFont *fontPtr = new CFont();Much of this code is a consequence of the strong typing: variable
fontPtr->CreateFont(16, 0, 0,0,700, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH|FF_DONTCARE, "Times New Roman");
buttonPtr->SetFont(fontPtr);
fontPtr
must be declared, then a new object of type CFont
must be created in order to match the interface of the button's SetFont
method; CreateFont
has a rigid interface that requires 14 different arguments to be specified. In Tcl, the essential characteristics of the font (typeface Times, size 16 points) can be used immediately with no declarations or conversions. Furthermore, Tcl allows the behavior for the button to be included directly in the command that creates the button, while C++ and Java require it to be placed in a separately declared method.2Another key difference between scripting languages and system programming languages is that scripting languages are usually interpreted whereas system programming languages are usually compiled. Interpreted languages provide rapid turnaround during development by eliminating lengthy compile times. Interpreters also allow powerful effects to be achieved by generating code on the fly. For example, a Tcl-based Web browser can parse a Web page by translating the HTML for the page into a Tcl script using a few regular expression substitutions. It then executes the Tcl script to render the page on the screen. Interpreters also allow applications to be extended by downloading code into them.
Scripting languages tend to be less efficient than system programming languages, in part because they use interpreters instead of compilers but also because their basic components are chosen for power and ease of use rather than an efficient mapping onto the underlying hardware. For example, scripting languages often use variable-length strings in situations where a system programming language would use a binary value that fits in a single machine word, and scripting languages often use hash tables where system programming languages use indexed arrays.
Fortunately, the performance of a scripting language isn't usually a major issue. Applications for scripting languages are generally smaller than applications for system programming languages, and the performance of a scripting application tends to be dominated by the performance of the components, which are typically implemented in a system programming language.
Scripting languages are higher level than system programming languages, in the sense that a single statement does more work on average. A typical statement in a scripting language executes hundreds or thousands of machine instructions, whereas a typical statement in a system programming language executes about five machine instructions (see Figure 1). Part of this difference is because scripting languages use interpreters, which are less efficient than the compiled code for system programming languages. But much of the difference is because the primitive operations in scripting languages have greater functionality. For example, in Perl it is about as easy to invoke a regular expression substitution as it is to invoke an integer addition. In Tcl, a variable can have traces associated with it so that setting the variable causes side effects; for example, a trace might be used to keep the variable's value updated continuously on the screen.
Because of the features described above, scripting languages allow very rapid development for applications that are gluing in nature. Table 1 provides anecdotal support for this claim. It describes several applications that were implemented in a system programming language and then reimplemented in a scripting language, or vice versa.
The scripting versions typically required 5-10x less code or development time than the system programming versions. Applications that involved more complex algorithms or data structures, such as the last example in the table, benefited less from a scripting language than applications that are mostly gluing.
In deciding whether to use a scripting language or a system programming language for a particular task, consider the following questions:
sh
and csh
for scripting. In the PC world of the 1990's, C and C++ are used for system programming and Visual Basic for scripting. In the Internet world that is taking shape now, Java is used for system programming and languages such as JavaScript, Perl, and Tcl are used for scripting.Scripting and system programming are symbiotic. Used together, they produce programming environments of exceptional power: system programming languages are used to create exciting components which can then be assembled using scripting languages. For example, much of the attraction of Visual Basic is that system programmers can write ActiveX components in C and less sophisticated programmers can then use the components in Visual Basic applications. In Unix it is easy to write shell scripts that invoke applications written in C. One of the reasons for the popularity of Tcl is the ability to extend the language by writing C code that implements new commands.
Graphical user interfaces (GUIs) first began to appear in the early 1980's and became widespread by the end of the decade; GUIs now account for half or more of the total effort in many programming projects. GUIs are fundamentally gluing applications: the goal is not to create new functionality, but to make connections between a collection of graphical controls and the internal functions of the application. I am not aware of any rapid-development environments for GUIs based on a system programming language. Whether the environment is Windows, Macintosh Toolbox, or Unix Motif, GUI toolkits based on languages like C or C++ have proven to be hard to learn, clumsy to use, and inflexible in the results they produce. Some of these systems have very nice graphical tools for designing screen layouts that hide the underlying language, but things become difficult as soon as the designer begins writing code to provide the behaviors for the interface elements. All of the best rapid-development GUI environments are based on scripting languages: Visual Basic on the PC, HyperCard on the Macintosh, and Tcl on Unix. Thus scripting languages have risen in popularity as the importance of GUIs has increased.
The growth of the Internet has also popularized scripting languages. The Internet is a gluing environment. It doesn't create any new computations or data; it simply makes a huge number of existing things easily accessible. The ideal language for most Internet programming tasks will be one that makes it possible for all the connected components to work together, i.e. a scripting language. For example, Perl has become popular for writing CGI scripts and JavaScript is popular for scripting in Web pages.
The third example of scripting-oriented applications is component frameworks such as ActiveX, OpenDoc, and JavaBeans. Although system programming languages work well for creating components, the task of assembling components into applications is better suited to scripting. Without a good scripting language to manipulate the components, much of the power of a component framework is lost. This may explain in part why component frameworks have been more successful on PCs (where Visual Basic provides a convenient scripting tool) than on other platforms such as Unix/CORBA where scripting is not included in the component framework.
Another reason for the increasing popularity of scripting languages is improvements in scripting technology. Modern scripting languages such as Tcl and Perl are a far cry from early scripting languages such as JCL. For example, JCL didn't even provide basic iteration and most Unix shells don't support procedures. Scripting technology is still relatively immature even today. For example, Visual Basic isn't really a scripting language at heart; it was originally implemented as a simple system programming language, then modified to make it more suitable for scripting. Future scripting languages will be even better than those available today.
Scripting technology has also benefited from the ever-increasing speed of computer hardware. It used to be that the only way to get acceptable performance in an application of any complexity was to use a system programming language. In some cases even system programming languages weren't efficient enough, so the applications had to be written in assembler. However, machines today are 100-500 times faster than the machines of 1980 and they continue to double in performance every 18 months. Today, many applications can be implemented in an interpreted language and still have excellent performance; for example, a Tcl script can manipulate collections with hundreds or thousands of objects and still provide good interactive response. As computers get faster, fewer applications will need to be implemented in a system programming language to get adequate performance.
One final reason for the increasing use of scripting languages is a change in the programmer community. Twenty years ago most programmers were sophisticated programmers working on large projects. Programmers in that day expected to spend several months to master a language and its programming environment, and system programming languages were designed for such programmers. However, since the arrival of the personal computer, more and more casual programmers have joined the programmer community. For these people, programming is not their main job function; it is a tool that they use occasionally to help with their main job. Examples of casual programming are simple database queries or macros for a spreadsheet. Casual programmers are not willing to spend months learning a system programming language, but they can often learn enough about a scripting language in a few hours to write useful programs. Scripting languages are easier to learn because they have simpler syntax than system programming languages and because they omit complex features like objects and threads. For example, compare Visual Basic with Visual C++; few casual programmers would attempt to use Visual C++, but many have been able to build useful applications with Visual Basic.
Even today the number of applications written in scripting languages is much greater than the number of applications written in system programming languages. On Unix systems there are many more shell scripts than C programs, and under Windows there are many more Visual Basic programmers and applications than C or C++. Of course, most of the largest and most widely used applications are written in system programming languages, so a comparison based on total lines of code or number of installed copies may still favor system programming languages. Nonetheless, scripting languages are already a major force in application development and their market share will increase in the future.
How much benefit has object-oriented programming actually provided? Unfortunately I haven't seen enough quantitative data to answer this question definitively. In my opinion objects provide only a modest benefit: perhaps a 20-30% improvement in productivity but certainly not a factor of two, let alone a factor of 10. C++ now seems to be reviled as much as it is loved, and some language experts are beginning to speak out against object-oriented programming [3]. The rest of this section describes why objects don't provide the same benefits as scripting and argues that many of the benefits of object-oriented programming can be achieved in scripting languages.
Another problem with object-oriented languages is their emphasis on inheritance. Implementation inheritance, where one class inherits part of its implementation from another, is a bad idea that makes software harder to manage and reuse. It binds the implementations of classes together so that neither can be understood without the other: a subclass cannot be understood without knowing how the inherited methods are implemented in its superclass, and a superclass cannot be understood without knowing how its methods are inherited in subclasses. In a complex class hierarchy, no individual class can be understood without understanding all the other classes in the hierarchy. Even worse, a class cannot be separated from its hierarchy for reuse. Multiple inheritance only makes these problems worse. Implementation inheritance causes the same intertwining and brittleness that have been observed when goto
statements are overused. As a result, object-oriented systems often suffer from complexity and lack of reuse.
Scripting languages, on the other hand, have actually generated significant software reuse. They use a model where interesting components are built in a system programming language and then glued together into applications using the scripting language. This division of labor provides a natural framework for reusability. Components are designed to be reusable, and there are well-defined interfaces between components and scripts that make it easy to use components. For example, in Tcl the components are custom commands implemented in C; they look just like the builtin commands so they are easy to invoke in Tcl scripts. In Visual Basic the components are ActiveX extensions, which can be used by dragging them from a palette onto a form.
Regardless of the strengths and weaknesses of object-oriented programming, objects are largely orthogonal to scripting. Most of the benefits of objects, such as better encapsulation, can be achieved in scripting languages as well as system programming languages. For example, Python is an object-oriented scripting language, Perl version 5 includes support for objects, Object Rexx is an object-oriented version of Rexx, and Incr Tcl is an object-oriented extension to Tcl. One difference is that objects in scripting languages tend to be typeless, while objects in system programming languages tend to be strongly typed.
However, I doubt that system programming languages will disappear to the same extent that assembly languages have. Scripting languages are not designed to serve all purposes. They work particularly well for gluing together components, but they are not as suitable for creating the components. The strong typing of system programming languages makes it easier to build complex data structures and algorithms; for example, trees and other pointer-rich structures are easier to implement in a system programming language than a scripting language. A scripting language is great for writing a database query, but if you're writing a database engine you're better off with a system programming language.
Thus system programming languages will continue to play an important role in computers, but they will be used primarily for creating components and fixed-function applications such as database servers. Many of the more glue-like areas where system programming languages are used today, such as GUIs and enterprise applications, will come to be dominated by scripting languages.
Scripting technology is old in years but young in maturity. In the future I expect to see more powerful scripting languages that allow a larger community of programmers to create exciting applications even more quickly than is possible today.
[2] F. Brooks, The Mythical Man-Month, Addison-Wesley, ISBN 0-201-00650-2, 1975.
[3] S. Johnson, Objecting To Objects, Invited Talk, USENIX Technical Conference, San Francisco, CA, January 1994.
[4] B. Kernighan and D. Ritchie, The C Programming Language, Second Edition, Prentice Hall, ISBN 0-13-110362-8, 1988.
[5] Netscape Inc., "JavaScript in Navigator 3.0", http://home.netscape.com/eng/mozilla/3.0/handbook/javascript/atlas.html#taint_dg
.
[6] R. O'Hara and D. Gomberg, Modern Programming Using REXX, Prentice Hall, ISBN 0-13-597329-5, 1988.
[7] J. Ousterhout, Additional Information for Scripting White Paper, http://www.sunlabs.com/people/john.ousterhout/scriptextra.html
.
[8] J. Ousterhout, Tcl and the Tk Toolkit, Addison-Wesley, ISBN 0-201-63337-X, 1994.
[9] B. Stroustrup, The C++ Programming Language, Addison-Wesley, ISBN 0-201-12078-X, 1987.
[10] L. Wall, T. Christiansen, and R. Schwartz, Programming Perl, Second Edition, O'Reilly and Associates, ISBN 1-56592-149-6, 1996.
2 In practice, a trivial example like this would probably be handled with a graphical development environment that hides the complexity of the underlying language: the user enters property values in a form and the development environment outputs the code. However, in more complex situations such as conditional assignment of property values or interfaces generated programmatically, the developer must write code in the underlying language.
john.ousterhout@eng.sun.comLast updated: 05/10/97 15:40:42