Paradigms of Programming Languages
Paradigms
Although many different computer architectures are being developed, the most prevalent is still the traditional von Neumann architecture - consisting of a single sequential cpu separate from memory, with data piped between cpu and memory. This is reflected in the design of the dominant computer languages, with dynamic variables (representing memory cells with changing values); sequential iteration (reflecting the single sequential cpu); assignment statements (reflecting piping). This combination of choices gives rise to the imperative language paradigm - C, Ada, Pascal, FORTRAN, Cobol, Basic etc.
But other choices are possible - variables do not have to directly reflect memory cells (logic programming); repetition need not be iterative (logic, functional, concurrent); assignment need not be important (logic, functional); data and control need not be separated (object oriented, functional).
A paradigm is essentially a high level model of what computation is about - the unifying factor that makes Ada and C seem very similar, ignoring details such as what data or control structures are available, exactly how variables are passed to procedures, and so on.
Some Paradigms
- Procedural (COBOL, FORTRAN, Pascal, C)
- functional (LISP)
- logic (PROLOG)
- object-oriented (Smalltalk)
- 4GL (Dbase)
- Visual (Visual Basic)
1. In the Beginning -- Machine Languages
The first programmers had to program in machine language.
The language was extremely machine independent.
The programmer had to virtually think like the machine.
The programmer had to know both the instruction set of the
computer, and how data was represented.
So, the programmer was working under the basic premise of the
machine, which was: there is data, and there are instructions
that process the data.
2. Assembly Languages
As computers grew in power, assemblers and assembly language
came into being.
This created a level of abstraction between the base machine
and the programmer, allowing the programmer to think more like
a human being and less like the computer.
No longer did the programmer have to worry about getting the
zeros and ones right -- he or she could use mnemonics,
constants, literals and pseudo-ops.
Assembly languages also allowed functionality to be isolated
and reused through the use of subroutines.
Subroutines basically extended the instruction set of the
computer.
However, the assembly languages carried the basic premise of
the machine into its abstraction: There is data, and there
are instructions and subroutines that process the data.
3. Higher Level Languages
Around 1954, the first FORTRAN compiler was developed, and
higher level languages came into being.
These compiled and interpreted languages created another level
of abstraction between the programmer and the base machine.
The programmer could replace miles of assembly code with one
expression or statement. Again, programming languages took
another step towards the way people normally think, and the
way the computer thinks.
In addition, the higher level languages were (theoretically)
machine independent.
The compiled and interpreted languages also had functions and
subroutines, which further allowed the programmer to extend
the instruction set of the computer.
However, the higher level languages had one basic premise:
There is data, and there are functions / subroutines /
expressions / statements that process the data.
4. Structured Languages
In the late 1960s, structured languages came into being.
These languages (C, Pascal) eliminated the "goto" helping to
localize and clarify code. Programmer productivity went up by
a factor of 10 as a result.
However, the structured languages still carried the basic
premise of the Von Neuman machine: there is data, and there
are instructions / functions / subroutines that process data.
5. Earlier Analysis and Design
As programs got more complex, programmers realized that there
might be a better way to write programs than getting a problem
statement and sitting down and writing code.
In other words, a little abstract planning might help.
Along these lines, some abstract modeling techniques were
developed.
Earlier methods included flowcharts and functional
specifications, functional decomposition.
Later techniques involved:
- data flow diagrams (functionality)
- data dictionaries (functionality)
- Decision trees
- process specifications (structured English)
- entity relationship diagrams (data -- Information modeling)
The later techniques arose from the structured programming
languages, and became used in structured analysis and design.
The intention of the models was to create extra levels of
abstraction between the real-world problem/system and the
computer implementation of the system.
They served as a bridge between the real world situation, and the
computer code.
The problem was mapped into a verbal problem statement, then into
a requirements analysis model, which was far removed from the
machine implementation.
The analysis model was then mapped into a design model, which was
one step closer to the machine implementation (the code).
Finally, code was written from the design model.
However, these earlier abstract models all ran on one premise --
there is data, and there are functions that process the data.
The first models attempted only to model the functionality of the
real-world systems.
Later, entity relationship diagrams were added -- these modeled
the data, or the static nature of the system. However, the
functionality models and the data models were distinct and
separate entities.
Coad and Yourdon note in their book Object-Oriented Analysis
(Prentice Hall, 1991) that the fact that there were two basic
sets of models in structured analysis -- functional models and
data models, created a split or rift among analysis teams. This
split caused tremendous headaches.
6. OO The Object Oriented Paradigm
Is it possible to make a computer think the way people naturally
think, rather than in terms of data and instructions that process
data?
The base machine will probably always think in terms of data and
instructions that process data.
However, are there high-level languages that can add another
layer of abstraction on top of the machine, so that the high
level languages can be written in code that mirrors the way
people think?
Yes, of course -- these are the OO languages!
Some OO languages are better at hiding the basic premise of the
machine. Smalltalk is probably the best, while C++ is probably
the worst (more on this later).
1. History of OO languages.
First OO language was Simula -- 1967. Used widely in Europe, it
never caught on in the U.S.
Then came Smalltalk -- 1972.
In 1986, Bjarne Stroustrup invented C++, and Brad Cox created
Objective-C. OO languages began taking off at this time.
But as of 1994, OO languages have been around for 24 years.
Earlier versions, such as Smalltalk, may have been unpopular
because machines weren't powerful enough to handle them.
2. What Makes a Language Object-Oriented?
According to Bertrand Meyer, an OO programming language should
contain the following 7 qualities [7]:
- 1. The support of modules.
- 2. The modules must support data abstraction, which entails:
a.) Information hiding and
b.) Encapsulation.
- 3. Automatic memory management (not necessarily garbage
collection).
- 4. All data are defined by abstract data type (ADT) based
modules. (e.g. classes).
- 5. Ability to extend existing classes (i.e. inheritance).
- 6. Support of polymorphism and dynamic binding.
- 7. Multiple inheritance (the need for this feature in an OO
language is disputed by many in the OO field.)
Objects
Basic building block of OO languages is the object.
Like objects of human thought, OO objects combine both data
(nouns and adjectives) and actions (verbs) into one package.
Data is implemented as data, while actions are implemented as
functions.
The machine still sees a data-functionality split.
But the outside world sees data and functionality merged into one
whole.
Classes
OO Languages promote classification at two levels.
First, there is the class construct.
Classes are like "cookie cutters" from which objects are moulded.
We have a "cat" class from which we create cat objects, each with
different attributes. The class specifies the attributes (and
the functionality) while the objects fill in the attribute
values.
For example, we have a cat class that has weight, color, and name
attributes specified. From the cat class, we create cat objects,
through which the program is actually run.
Inheritance
A looser classification scheme is obtained through inheritance.
We can tie in our cat class into a whole inheritance hierarchy
which includes the entire cat family, the mammals, all animals,
and then all living things.
Polymorphism
Polymorphism is the method by which OO languages mirror the human
thought processes' tendency to group certain actions together.
We can call similar functionality by the same name -- e.g. cats
eat and cows eat, but the way they eat is different. However, we
still call their actions "eating".
OO languages allow for similar functions (actions) to be given
similar names. This allows for conceptual clarity in writing
code.
Note that C and other traditional languages use polymorphism to
some extent. For example, the arithmetic operators act on both
floating point numbers and integers with the same operators (+, -
, /, *, etc) even though the underlying implementation is
radically different.
Abstraction
Software objects mirror the human mind's tendency to abstract out
details.
Software objects hide the internal details of an object behind
the walls of an interface. The "user" or "client" of an object
does not have to worry about the inner details of the object,
only the abstract principles of the object, as defined by the
interface.