A paradigm is simply a mental model. Human cognition is built around some model that is used to understand the world. The model is usually dictated by the culture or sub-culture and controls the way members of the culture view the world. If all you have is a hammer, then everything looks like a nail.
We can view programmers as a sub-culture divided into sub-sub-cultures, each using a different paradigm to view how computers solve problems.
Treleaven's classification of computer architectures applies to programming paradigms if we view language processors as virtual computers.
According to Treleaven, an architecture is specified by a control mechanism that controls the flow of execution, and a data mechanism that controls the flow of data:
Architecture/Paradigm = Control Mechanism + Data Mechanism
Treleaven identifies four types of control mechanisms and two types of data mechanisms. Combining these gives us eight types of architectures/paradigms:
|
|
Control Mechanisms |
|||
|
Data Mechanisms |
Control Driven |
Data Driven |
Demand Driven |
Pattern Driven |
|
Shared Memory |
COSH |
DASH |
DESH |
PASH |
|
Message Passing |
COME |
DAME |
DEME |
PAME |
Let's expand the definition of instruction to include functions, procedures, actions, events, phrases, etc.
In a control-driven architecture each instruction specifies the next instruction to be executed. A default mechanism is used in case a next instruction isn't specified. For example, if no next, then execute the physically next instruction.
The classical example of this is the goto instruction.
The call/return mechanism is an example of a control-driven mechanism.
Normally an instruction decides which data too process, but this can be reversed. We see this in polymorphism. For example:
employee.print();
The type of employee might determine which print function should be called.
In lazy evaluation algorithms an operand is only evaluated if it is needed. For example:
false && 1 = 2/0
Demand-driven paradigm can also be seen in publisher-subscriber mechanisms, where subscriber instructions are executed each time a publisher object fires an event or changes state.
Pattern matching algorithms are used to determine which instruction should be executed. We see this in XSLT, for example, when the pattern of an XML element determines the function used to convert it into HTML.
Instructions process data. In shared memory architectures all data resides in a centralized repository (memory, storage, database, etc.) and is accessible by all instructions. This is very efficient but can lead to synchronization problems.
In message passing architectures instructions send data to each other. Usually the computer must provide some sort of postal service. Message passing is common in networks. Message passing can be synchronous (TCP) or asynchronous (UDP).
The imperative paradigm views a program as a collection of functions that use complex control structures and the call/return mechanism to update passive data structures.
Examples: C, Pascal, FORTRAN, Assembly
The declarative paradigm views a program as a database of facts and rules
The format of a rule is:
Conclusion if Assumption_1 && Assumption_2 && ... && Assumption_n
A goal is some assertion that the program is trying to verify or refute. Usually there is a stack of goals. The program must verify or refute each one.
The program works backwards from an initial goal or query.
[query]
It uses sophisticated pattern matching algorithms to match the goal to a fact or the conclusion of a rule. In the latter case the goal is replaced in the goal stack by the assumptions and the process repeats.
[Assumption_1 Assumption_2 ... Assumption_n]
The program halts when the stack is empty.
[]
Examples: Prolog, SQL, XSLT
Functional programming avoids shared memory. Instead, functions pass data through parameters. There are no variables or commands!
Some languages, like Haskell, also provide pattern-driven mechanisms.
Examples: Haskell/Hugs, LISP
If we consider objects to be our principle or only form of data, then there are no functions in OO languages. There are methods, but these are things that objects (data) execute. We can regard a method invocations such as:
expression.evaluate()
As passing the print message to the employee object. Of course thanks to polymorhism, the actual code that gets executed might depend on the sub-type of the expression.
Examples: Smalltalk, Java