Language Paradigms and machine models


1 Programming-language paradigms
2 Machine models for the language paradigms
3 Designing your own language to fit the job

A paradigm is a pattern or stylistic approach to doing or stating something. For example, when you prepare your resumé, you use a standard pattern, a paradigm, for formatting it. Another paradigm is the layout of the engine in a car --- although there are many different models of car, virtually all of them use the same design for their engine, frame, and drive train. Paradigms are useful to architects, who design skyscrapers with one paradigm and family residences with another. Writers use paradigms for stories, novels, and newspaper articles. Paradigms are used by scientists and engineers, too.

There are paradigms for thinking about and solving problems. We call these programming (language) paradigms.


1 Programming-language paradigms

There programming paradigms have been developed over the history of computing:


2 Machine models for the language paradigms

The previous section introduced the standard language paradigms in terms of how they express solutions to problems. There is another, useful characterization of the paradigms: the machine models for the languages.

We review the four paradigms and focus on their machine models and storage layouts.

Imperative machine model

The critical characteristic of a machine that executes an imperative program is that storage is a single, shared global space. All data structures are kept in primary storage, and all the code fragments (procedures) share the data structures:

When one procedure wants to communicate or send data to another, the procedure places the data into one of the global data structures, calls the other procedure, and lets the other procedure look up the data in global storage. There is no notion of ownership of data, no notion of locality, and even no real need for arguments/parameters to procedures. The sequencing of variable lookups and updates is critical, and timing and race-condition errors often result.

It is easy to implement the imperative machine model on a von Neumann architecture because it is the von Neumann architecture! For this reason, the imperative model is widely used for writing system software.

One huge disadvantage of the imperative model is that there is no simple way to deallocate and reuse storage. Here is the solution discoverd in the 1950's: If we arrange the global storage like a stack, we can program the procedures so that they ``push'' new data structures onto the global storage stack and ''pop'' the structures when they are no longer needed. Programming languages like Algol60 and Pascal used blocks and local variables to indicate when data are pushed and popped:


Communicative (object) machine model

Since a communicative program consists of a collection of actors (objects) that know things, do things, and communicate their knowledge, the machine model for communicative programming consists of a collection of namespaces that model the actors by holding data structures and procedures (methods). There is no global storage, just namespaces, each with its local storage:

A namespace implements an actor/object; each actor ''knows'' the information in its owned data structure; it ``does'' the computations coded in its local methods; and it ''communicates'' knowledge by sending arguments/parameters to other methods and receiving answers in return. (Communication is needed when an actor requires knowledge from a data structure or method that it does not own.) Parameter passing and answer returning are essential to the paradigm; the arguments and results are usually small pieces of data --- numbers and words --- and not entire data structures.

This machine model reduces errors due to abuse of data structures. Sequencing errors can still arise when a data structure's updates are applied in the wrong order because methods are called in the wrong order --- it is important to write the protocols for communication between actors.

We implement the machine model on a von Neumann machine by means of heap storage: namespaces (''objects'') are carved out of heap storage; each object is a mix of data structures and (pointers to) method code. There must also be an ``activation structure'' that remembers which methods in which objects are activated (executing). When the computer is a single-processor machine, the activation structure is organized as an activation stack.

Functional machine model

In algebra, we learn that equations and functions are pleasant because there is no strict sequence that we must use to solve them. This is also true in functional programming, where sequencing errors are greatly reduced, because there are no updateable variables to maintain. A machine model for a functional language has no global variables, no local data structures. Instead, all knowledge is passed between functions as arguments and answers:

The arguments and answers used by functions are complex data structures that must be assembled and disassembled by the functions at each call and return. This is an inconvenience, but the payoff is that there is no sharing of global variables so no race errors nor sequencing errors can arise --- each argument/answer is private data that a called function uses as it wishes.

The functional machine model is implemented on a von Neumann machine by heap storage used to hold the argument- and answer-data-structures passed between functions. Since arguments and answers come-and-go frequently, there is a clean-up program, a garbage collector, that repeatedly scans heap storage and erases old, unused data structures.

There is a crucial efficiency trick here: since there are no assignment commands in a functional program, once a data structure argument/answer is assembled in the heap, it never changes. Thus, a constructed data structure can be shared and reused, reducing the time-space overhead of (re)building huge data structures as arguments and answers. All the pointer tricks you have learned come to good use when implementing the functional machine model.

Logical machine model

Here, a program is a crossword-like puzzle with clues that tell you how to fill in the puzzle's empty squares (cells). The corresponding machine model is a blackboard architecture with a shared global table:

Each clue is implemented as code that can fill some squares in the table with data and data structures. Because there are usually many different possibilities for filling in the empty squares, a conventional implementation strictly sequences the order of the clues updating the table. The updates are saved in a control stack that can be popped (''backtracked'') if the information in the table is filled in incorrectly or inconsistently; a backtrack step pops the control stack and erases from the table the information that was inserted by the popped update. Clues are tried repeatedly until a complete, consistent solution is entered into the table.


3 Designing your own language to fit the job

When you are established at a firm or a lab, you will become expert at solving problems in one application area, one domain. You will find that your solutions will follow some particular paradigm. You will also develop a library of components that you reuse when you build similar solutions to similar problems in the domain.

You will reach the point where it will be useful for you to share your knowledge, your libraries, your styles with others. Maybe you will share them with non-software engineers who use your work. At this point, you might wish to develop a language that is specialized to the domain and its problems. Such a language is called a domain-specific language. You use the language to talk about programs and their solutions. If you can state solutions (algorithms) in your domain-specific language, and if a computer can understand your domain-specific language (that is, you write an interpreter for your domain-specific language), then it is a domain-specific programming language.

You have probably hacked code in HTML --- it's a domain-specific language for web-page layout. You might have used a gamer package to create players and an environment for an interactive game; you have thus used a domain-specific language for gaming. And so it goes. Indeed, any nontrivial input format for an application is a domain-specific language, with its own a syntax and semantics --- language design and systems design go hand in hand.

When you design a domain-specific programming language, you will base it on a paradigm --- a machine model. The purpose of this course is to show you how to use some of standard paradigms and models to design your own languages.