CMPT 383 is an introduction to the study of programming languages. The basic goal of the course is to provide you with a general body of knowledge about programming languages and the ability to reason critically about the application of programming languages in software engineering work. The course also serves as a foundation for further study in areas such as programming language design and language processor implementation.
One important practical objective for this course is to help you learn how to learn programming languages. Inevitably, if you are to have a career in software engineering work, you will have to learn new programming languages at various times.
One way to develop your ability to learn languages is simply to learn several of them. Learning the first one is hard, the next one often not so hard and so on. After you've learned several, you will feel that learning new ones is a snap: you just learn to cope with some new syntax and you're set.
But is that really true? It depends. The problem is that you may have learned to work with several languages that are only slightly different from each other. For example, if you've learned some older languages such as FORTRAN, BASIC, PL/I, Pascal, C and Modula-2, you will have seen only a small part of the spectrum of concepts underlying modern programming languages. In short, you will have considerable experience in learning the imperative or procedural style of programming, but very little knowledge of object-oriented, functional or logic programming languages.
In this course, we focus on a systematic way of helping you learn how to learn new languages. Part of this will be to survey the spectrum of different programming paradigms and how they may be supported by programming languages. A second aspect of our survey will be to examine important programming language issues: distinctions that may be found between existing languages in the same class. This foundation of broad knowledge about programming language paradigms and issues should provide you the best preparation for learning new languages quickly.
A second goal of the course is to help you learn how to read reference manuals that describe or define programming languages. These reference manuals typically define programming languages in terms of their syntax and semantics. Syntax is the form that language constructs take; while semantics is the "meaning" that we give those constructs. Typically, programming language syntax is described using context-free grammars, formal structures which precisely specify a set of allowable strings for the language. The syntactic description is generally augmented with rules regarding formatting conventions, comment conventions and resolution of grammatical ambiguities. Most often, the semantics of a programming language will be specified using precise and highly-structured English descriptions. Increasingly, formal methods, such as denotational semantics are also used to describe the meaning of programming language constructs.
Reading language reference manuals is often a difficult skill, but an important one nevertheless. As a programmer, how often have you been faced with a question about what will happen when you use a particular combination of features? How often have you been successful in resolving such questions by reading the reference manual? Have you ever resorted to just trying it out to see what the compiler will do? For most of us, the answers to these questions will be often, not so often and yes.
Why is it often so difficult to answer such questions by consulting the manual? In defining a programming language, its designers often try to keep the description short and concise. This is usually done to make sure that the description itself is as simple and self-consistent as possible. However, such concise language descriptions necessarily mean that most topics are addressed only by general rules; there will be little discussion of how these rules are combined in various circumstances. In order to interpret how the rules are applied in a particular case, one must often make careful logical deductions from a close reading of the manual.
Why is it so important that we be able to answer the questions by reading the manual? Why not just try it out and see? The answer is that the difference between specification and implementation is very important in software engineering work. The specification should tell you what a system should do and how to evaluate the correctness of an implementation. But if you can't read the specification, how can you judge the implementation?
In the case of programming languages and their compilers, this specification-implementation distinction has important ramifications on the issue of portability. It is often very important that programs be portable across platforms, i.e., be executable on different computers. If we have developed a program using one compiler on one platform, how do we know that it will function correctly when we use a different compiler on another platform? In general, this can be a very difficult problem for a number of reasons. But one important step is surely to ensure that both compilers are faithful implementations of the same specification. Otherwise, portability is just a pipe dream.
In studying description methods for programming languages, then, you will learn how to read reference manuals and judge the correctness of language implementations. But you might also take language reference manuals as models for how you can develop software specifications in general. The better language reference manuals are written by top professionals in their field with a view to precisely specifying how language processors are to function and what implementation-dependent features they may have. What better examples could you study for lessons in how to write specifications?
Another important objective of this course is to study programming languages with respect to their impact on the entire software life cycle, not just their ease of use for writing programs. What does the language do to prevent certain classes of error or allow for their early detection? How does the language support breaking up the development of large systems into independently manageable chunks? How easy is it for maintainers to analyze a program to determine the effects of a proposed change? Does the language support an effective methodology for addressing efficiency concerns when that is important?
Another practical objective of this course is to help you learn how to be a better programmer in whatever language you use. Often you will not be in a position to specify a language of your choice. Nevertheless, with a broad background in the paradigms and issues of programming languages you will be in a much better position to consider solutions to programming problems in any particular language that you must use. For example, you may find that a solution could easily be expressed using the facilities of some language other than that specified. You may then be able to develop creative ways to use that solution to guide you in working with the specified language.
With a broad knowledge of programming language concepts and issues, you should also be in a good position to perform a critical evaluation of the strengths and weaknesses of whatever language you are using. Using the strengths, you may find novel creative ways to solve problems. Focussing on the weaknesses, you will be able to identify obstacles or pitfalls in developing a solution and take appropriate measures to avoid them altogether or reduce their impact.
Ideally, I would like to tell you that it is an important skill to be able to choose between languages for a particular application. Unfortunately, it is almost never the case in practical software engineering work that you have that choice. Even if you do, the choice will likely be highly constrained. More importantly, the choice of programming languages is usually overwhelmingly dominated by practical concerns: the availability of trained programmers, the need to work with existing code in existing languages, mandates from above, and so on.
However, with a broad knowledge of the spectrum of programming languages, you will be in a position to be a continuous advocate for the use of improved programming languages. Over time, this advocacy will have an effect in industry; new languages will be gradually adopted as the potential software engineering benefits they convey overcome the resistance to change.
Many computing applications incorporate minilanguages as part of the overall "user interface". For example, spreadsheet programs often have a minilanguage for expressing calculations of various kinds. Database programs may have more sophisticated mini languages often known as fourth-generation languages. Editors can have minilanguages for expressing global find and replace operations using sophisticated patterns. Many kinds of application have highly structured input formats that may be considered as minilanguages. Even "command-line interfaces" to programs may be considered as miniature programming languages.
A breadth of knowledge of programming language issues can be useful in helping you learn and evaluate these minilanguages. Furthermore, you may be called upon to design and implement software systems that incorporate minilanguages. With a good background in programming language issues, you will be in a good position to consider design alternatives and the reasons for choosing between them.
This introduction to the study of programming languages may also be considered as preparatory for advance study in programming languages in several areas.