DIFFERENT PROGRAMMING PARADIGMS
is bridging the gap between man and machine. Already since the early days of
the computer, people have struggled with the question how to express problem
solving solutions into computer understandable notations. Soon after the
introduction of machine language and associated assembler the need arose to
program in terms of particular application domains rather than in low-level
computer instructions. As a result, in the 1950s and 1960s high
level languages were developed for scientific work (Fortran,
Algol), for business applications (Cobol), for
symbol manipulations (Lisp), and for many other areas. The goal of all those
languages was to bridge the gap between application domains and the
the 1970s and 1980s new ways of programming were developed by the
introduction of different so-called programming paradigms. Each programming
paradigm represented a set of principles and rules for writing programs.
While the languages of the earlier decades were mainly procedural, new
paradigms such as object-oriented, functional, and logic programming
emerged. All these paradigms were developed because the procedural approach
was felt to be inadequate for solving problems in new areas. Bridging
conceptual gaps, which was earlier one of the driving forces for developing
new languages, was now pushing for new paradigms.
developments also started discussions about the differences between imperative
languages and declarative languages.
languages express their programs as sequences of explicit steps. These steps
may be instructions at the assembler level, statements in a high-level
language, or commands for an operating system. They all imply an underlying
machine with memory which is manipulated by individual steps. In the early
days, almost all languages, with the exception of the first version of Lisp,
were imperative languages.
contrast, declarative languages describe what is to be computed
rather than how the computation should proceed. How the computation
is performed depends on the underlying implementation, which embodies
imperative steps not found directly in the declarative language.
distinction between imperative languages and declarative languages is very
coarse and nowadays it is recognized that most languages are lying at a
continuum between both extremes. Still it can be used as a global
categorization of the different programming paradigms.
these two major categories several programming paradigms, representing
different styles of programming, have emerged since the advent of high-level
languages. Most languages are belonging to one of the following classes:
general, functional languages are belonging to the category of declarative
languages, and are based on the principles of mathematical functions.
Functions are the major constructing elements of such languages. The result
of a function is determined solely by the values of its arguments, so a
function called with the same arguments will always yield the same result
and will have no side-effects. One of the main motives for developing
functional languages is that mathematical reasoning about programs is rather
simple, because all functions are mathematical functions.
programming is based on a particular form of mathematical logic known as the
first-order predicate calculus. The term 'first-order' means that the logic
cannot deal directly with statements about statements but only with
statements about single objects. First-order logic allows quantification
over individual objects; second-order logic further allows quantification
programming languages are belonging to the category of declarative
languages. A logic program consists of a number of rules which are used to
derive conclusions from a number of conditions. The logic programing
paradigm introduced a number of new and interesting programming concepts. In
order to derive proper conclusions from a set of rules, the underlying
implementation uses unification to invoke a rule and uses backtracking
in case a rule fails. Furthermore, a rule may produce a set of answers, not
necessarily a single answer. The underlying implementation uses a search
mechanism to obtain the answers.
these languages are using the concepts of objects and classes of objects.
Classes may be organized in hierarchies allowing the use of inheritance
to capture commonalities of sub-classes in super-classes. Most
object-oriented languages are imperative languages. They describe the
behavior of an object by means of methods which are associated with the
class of the object. Interaction between objects is performed by means of
of its roots in simulation, the same principles used in object-oriented
programming can also be used for object-oriented analysis and
object-oriented design. Furthermore, object-oriented
databases is an important research area.
In the following table the most important features of these paradigms are listed:
Because of the major impacts of the object-oriented paradigm we will discuss in more detail some important aspects. Object orientation has become a new major direction in software development in the past few years. It not only changed thoroughly the ideas about programming but it also affected others phases of the software life-cycle. Object-oriented analysis and design became a preferred way of approaching new applications.
Numerous books and articles have been published on the subject. In addition, many object-oriented products have appeared on the market. The suppliers of object-oriented languages, object-oriented user interfaces, object-oriented databases, and object-oriented operating systems are all trying to convince the users that the new approach is solving many problems, if not all.
Why has object orientation become so popular? To answer that question we have to go back into a little bit of history. In the early days of computing, programming was the most important activity in software development. Programming was bridging the gap between man and machine, and between problems and solutions. To reduce the gap, first the assembly language was invented, and then, later on, several high-level languages appeared to diminish the effort to write programs. The history of programming languages shows a progression from low-level computer-oriented constructs towards high-level application-oriented abstractions.
After the introduction of higher level languages it became clear that structured methods were needed to avoid spaghetti code and to write better maintainable programs by using different levels of abstractions. In the same period software engineers became aware of the fact that many more steps are required in software engineering. As a consequence, structured analysis and design were developed in the 1970s as a natural outgrowth of structured programming.
In the 1980s and 1990s the history was repeated by object-oriented programming followed by object-oriented analysis and design.
Object-oriented programming has its roots in the discrete event simulation language Simula 67 which was developed in Norway as early as 1967. Based on concepts found in Simula 67 the language Smalltalk was developed in the 1970s at Xerox Palo Alto Research Center in California. Later on, in the 1980s, other object-oriented languages such as C++ and Eiffel were developed.
All these languages are using the concepts of objects and classes of objects. Classes may be organized in hierarchies allowing the use of inheritance to capture commonalities of sub-classes in super-classes. Most object-oriented languages are imperative languages. They describe the behavior of an object by means of methods which are associated with the class of the object. Interaction between objects is performed by means of messages.
Because of its simulation
background object orientation provides a connection between
objects in the real world and objects in the software. It
also forms the basis for later developments as
object-oriented analysis and object-oriented design. The
ability of object orientation to map real-world objects
directly into corresponding software objects was one of the
driving forces in the acceptance of object orientation.
Object orientation has enriched the software discipline with a different way to view the world. While in conventional software engineering the main emphasis was on actions, object orientation made clear that actions and data are inseparable. This change in direction requires a different mind-set to analyze and to solve problems. Instead of concentrating on the question of what should be the sequence of actions to solve the problem, the new challenge is how to divide the solution space into a set of objects that could solve the problem. This requires a higher level of structuring capabilities of software developers than demanded by conventional methods.
In addition, object orientation introduced a whole new terminology. Objects, classes, methods, messages, instances, encapsulation, inheritance, polymorphism, dynamic binding are new terms which should be mastered by those who want to understand object orientation. Part of the terms are naming new concepts, another part is related to the metaphor of message passing between objects.
Because of the change in
mind-set and the difference in terminology the new technology
of object orientation requires a steep learning curve in
particular for experienced programmers because the new
direction means a break with the past and a considerable
reorientation for the future.
As we have seen, one of the claimed benefits of object orientation is that real world objects can easily be mapped onto corresponding software objects. Real-world objects may be real or abstract things, such as tables, chairs, books, drawings, customers, accounting records. They can be represented in the computer by related objects naming tables, chairs, and so on.
What are Objects?
There are two different forms of relationships which can be recognized in many situations. These are the has-a relationship and the is-a relationship.
The has-a relationship describes object composition. For example, a car has an engine, a customer has a name. In daily life, the description of the relationship may take many forms such as "contains", "holds", "consists-of", and so on.
The is-a relationship describes a generalization relationship. For example, a car is a vehicle, a customer is a person.
Inheritance is a complex
issue. From a conceptual point of view, it seems that
inheritance is a rather simple generalization relationship
which can easily be established. But if you try to use it in
practice, inheritance appears to be a difficult concept.
Take, as an example, the simple question: do squares form a
subclass of rectangles? It appears that finding the answer is
not easy (see Ryant (1997)).
There are also problems with inheritance at the technical side. Implementations of inheritance have been complicated by many factors. In almost all object-oriented languages inheritance, polymorphism, and dynamic binding are closely interconnected. This has the advantage that one mechanism can be used to support all three concepts, but it has the disadvantage that the interdependencies between the different features are causing a number of problems:
Because of the complexities of inheritance many researchers have tried to simplify or to avoid inheritance. From the many alternatives which have been investigated two approaches have appeared to be the most effective. These alternatives, delegation and decomposition, are both based on object composition.
Object composition means that an object is composed of other objects which implies that such an object is able to make use of the other objects. Thus, a child object may use a parent object. One may say that the has-a relationship and the is-a relationship, as discussed earlier are both be implemented by means of a using relationship.
Object composition requires that the objects being composed have well-defined interfaces. No internal details of the referred objects are visible. The differences between inheritance and object composition have been investigated by a number of authors (Budd (1997), Gamma (1995)).
Delegation is one of the alternatives to inheritance. In delegation, two objects are involved in handling a request; a receiving object delegates operations to another object, its delegate. This is comparable with child classes deferring requests to parent classes.
Another approach is to use decomposition. With decomposition, elements of an object are made accessible to the user. That means that a reference to a parent object in a child object can be made available to the user of the child object by means of an accessor operation.
and decomposition are in many cases alternatives of inheritance,
the mechanisms used inheritance are still required in
object-oriented languages for polymorphism and dynamic
One of the main reasons of the problems with object-oriented languages is that there is not a well-defined semantic model for object-orientation. There are only several losely defined implementation models for the different languages. Most problems as we discussed are so interrelated that many attempts to remedy the flaws in one of the implementation models introduces other problems. Some people are therefore stating that objects are pass because further developments are stuck in a blind alley. However, this may be too pessimistic. In the following chapters we will discuss how elements of object-orientation can be used in Domain Orientation.
All these paradigms have proved to be useful in formulating solutions in particular problem domains. Each paradigm favors specific formulations which often result in compact and clear programs.
Many applications contain a number of (sub-)problems which could successfully be solved by a specific paradigm. However, in a single-paradigm language problems can only be worked out within the framework of that paradigm. In many situations the solution to a problem has to be distorted to fit the single paradigm.
If we had a multi-paradigm language then each paradigm could be applied where most appropriate. For example, procedural programming could be used to express sequences of imperative steps, object-oriented programming could be applied to reflect objects in the problem domain, logic programming could be used to describe the relations between those objects, and functional programming could be applied to express mathematical functions applicable to those objects.
Integration of different paradigms may also create new possibilities of solving problems and may open new ways of programming. It may create synergistic effects where the power of an integrated language is larger than the sum of the different paradigms. For example, logic programming applied to objects may open new ways of expressing solutions in terms of the problem domain. One of the most important means to reduce the gap between man and machine, and applications and computers, is to express a solution in terms of its problem domain and that may require the exploitation of different programming paradigms.
In the past, many attempts have been made to integrate two or more paradigms into one single language. Some were successful, such as the integration of object-orientation and Lisp in CLOS (Common Lisp Object System), but many other attempts failed.
There are various obstacles on the way of integrating different paradigms into one single linguistic framework. The main cause is that a number of paradigms are based on mutually conflicting characteristics. For example, assignment statements are essential for the procedural and object-oriented paradigms, but are forbidden in the functional and logic paradigms. Or, as another example, functional and logic are based on a declarative way of programming, while procedural and object-oriented programs have an imperative structure. In general, features needed by one paradigm may conflict with the requirements of another paradigm.
Because incompatible requirements cannot be satisfied by a single and coherent linguistic framework without accepting flaws in the language, one has to find ways to remove those incompatibilities. One way is to extend a given language with a number of features from another paradigm, as has been done in the cases of CLOS, and C++. Another way is to define a new language as exemplified by Eiffel and Java. However, both approaches will require compromises in language design to remove inconsistencies.
One of the main obstacles in designing a coherent linguistic framework for an integrated language are the underlying execution models as dictated by the different paradigms. For example, the object-oriented programming paradigm assumes an imperative, single-value execution model with implied facilities for inheritance and polymorphism while the logic programming paradigm assumes a declarative, multi-value execution model with implied facilities for unification and backtracking.
Because, for several reasons, a programming language should be based on one single execution model, an execution model for an integrated language has to be found which could fulfill the stated requirements and which could support the different language concepts. To define such an execution model, all essential features of the different paradigms have to be analyzed and mapped, where possible, on a single execution model.
An important requirement for an execution model is that it must be simple and easy to understand. Complex execution models are difficult to program because of intricate interactions. Reasoning about programs written for complex execution models is often hard. The same applies to debugging.
Another essential factor in designing an execution model is how efficient the different language elements can be implemented, because the expressive power of a language is one thing, but efficient execution is another important property. Significant performance questions had to be considered, such as: Can a language feature be mapped directly on an underlying machine or are run-time routines necessary? Where possible, for Elisa the choise was made in favor of compilable features with a minimum of run-time support.
Because of the need of a single and efficient execution model, not all properties of a paradigm could be retained in their original form. Some language features had to be dropped or modified in order to be supported by the underlying execution model.
We will give some examples of the kinds of considerations and the decisions we took for Elisa.
In case of the assignment statement some investigations made clear that an assignment statement can be quite useful in the body of a pure function, as long as side-effects are avoided. This brought us to the pragmatic decision to accept assignment statements in the language, although within the functional and logic paradigms they should be used with care.
Regarding the imperative and declarative controversy, it was decided to favor the declarative style of programming without excluding imperative constructs.
Unification and backtracking are important elements of logic programming. Because unification is a complex and expensive mechanism, it was decided to replace it by the simpler mechanism of pattern matching which is sufficient in many circumstances. Backtracking, however, is needed to handle multiple answers and can also be useful in many other situations, thus we decided to include it in the language in such a way that it can be applied selectively.
Inheritance, polymorphism, and dynamic binding are strongly coupled in object-oriented languages. Because of their impact on the execution model and on the language it was decided to decouple them in the language and to replace the inheritance implementation model by facilities for delegation and decomposition.
By using delegation and decomposition instead of inheritance a large number of the problems associated with inheritance are disappearing. Furthermore, polymorphism and dynamic binding can be supported by orthogonal language facilities.
As a result of this approach most of the required features could be incorporated in the language. In addition, some new features have been added. By combining the best concepts of different paradigms and by compensating for the missing features the Elisa language is a programming language with an own and specific character, as will be made clear in the following chapters.
Many references can be found in the bibliography.
About the criticisms of object-orientation interesting references are:
Al Davis (1998a), former editor-in-chief of IEEE Software wrote "We are now witnessing the fall of the Object era" (IEEE Software July/Aug 1998, pp.6-9). In his final interview (1998b) he says, "When I started consulting ten years ago, all my customers wanted to hear the word 'object'. Now, none do, and I find that clients are much happier when they don't. I think 'object' has now gone the way of 'structured'." (IEEE Software Nov/Dec 1998, pp.18-22).
Les Hatton(1998) states in "Does OO sync with how we think" (IEEE Software May/June 1998, pp.46-54), that object orientation is an imperfect paradigm for reliable coding.
Bertrand Meyer(1999) defends Object Technology against the attacks of the opponents in "A Really Good Idea". (IEEE Computer Dec 1999, pp.144-147). One of his conclusions is that most objections are related to C++.
Ivan Ryant(1997) "Why Inheritance means extra trouble", CACM October 1997, pp.118-119.
Taenzer (1989), Ganti, and
Podar are describing the yo-yo problem.
There is a wealth of literature about programming languages and programming paradigms.
For example, Prolog as a representative of logic programming is described by Bratko (1990), Clocksin and Mellish (1984), Sterling and Shapiro (1986), and many others. Kowalsky(1980) showed how logic programming can be used for problem solving.
Object-oriented programming and Eiffel are discussed by Meyer (1988). Smalltalk is described by Goldberg and Robson(1983). C++ is defined by Stroustrup(1993). Many contributions about object-oriented programming can also be found in the yearly OOPSLA proceedings since 1986.
Object-oriented analysis and object-oriented design are discussed by Coad and Yourdon (1990, 1991). Object-oriented design with applications is described by Booch(1991). Object-oriented modeling and design are discussed by Rumbaugh(1991) and others.
Functional programming is discussed by Backus (1978), by Henderson(1980), by Darlington, Henderson, and Turner (1982), and by Bird and Wadler (1988).
Lisp, is described by McCarthy(1960), Winston and Horn(1984), Steel (1984), and many others. Applications of Lisp are also described by Abelson and Sussman(1985), and by Charniak and McDermott(1985).
DeGroot and Lindstrom (1986) contains a number of interesting papers about the unification of logic and functional languages.
Bobrow and others (1986) are describing how to merge Lisp and object-oriented programming. Moon (1989) describes the CLOS system. Gabriel, White, and Bobrow (1991) are discussing how object-oriented and functional programming are integrated in CLOS.
Petre and Winder (1990) classified different programming languages according to their ratio of imperative and declarative aspects.
Graham (1991) and Wegner(1990) are discussing the integration of different paradigms.
Placer (1988) described a multi-paradigm language and discussed the advantages of a multi-paradigm approach (1991).
Moss (1994) describes Prolog ++, which combines object-oriented programming and logic programming.
Budd (1995) describes Leda, which is one of the first languages designed for multiparadigm programming.
Budd(1997) describes and compares various features of different object-oriented languages.
Of the many papers about the problems of inheritance, the papers of Snyder(1986,1987) are worthwhile to study. The differences between inheritance and object composition have been described by Gamma (1995) and Budd (1997). Budd (1997) distinguishes 8 forms of inheritance. Meyer (1996) found 12 kinds of inheritance grouped into 3 broad categories.
Delegation was first described by Lieberman (1986). Later on, the term delegation was redefined by Wegner (1987).
Wilde(1993), Matthews, and Huitt are discussing maintenance problems of object-oriented software.
Many contributions about object orientation can also be found in the yearly OOPSLA proceedings (since 1986), and in the Journal of Object-Oriented Programming (since 1988).
This page was last modified on 27-09-2012 12:11:26