Primary Paradigms
Historical programming language paradigms
imperative
declarative
Recent programming languages blur distinctions
But concept of programming paradigm meaningful
H. Conrad Cunningham
31 January 2019
Copyright (C) 2017, 2018, H. Conrad Cunningham
Acknowledgements: I created these slides in Fall 2017 and revised them in Summer 2018 to accompany what is now Chapter 2, Programming Paradigms, of the book Exploring Languages with Interpreters and Functional Programming.
Browser Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of January 2019 is a recent version of Firefox from Mozilla.
Introduce concepts of abstraction: procedural and data
Describe what is meant by term “programming paradigm”
Examine natures of the two primary programming paradigms: imperative and declarative (functional and logic)
Examine natures of other important programming paradigms: procedural, modular, etc.
Simplicity is good; complexity is bad.
concentrating on the essentials and ignoring the details
remembering the “what” and ignoring the “how”
Make large complex problems understandable by decomposing into subproblems
Procedural abstraction: separate properties of action from implementation details
Break complex actions into subprograms; develop program as sequence of calls
Data abstraction: separate properties of data from representation details
Identify key data representations; develop program around these and associated operations
Procedure (pure) takes zero or more arguments but does not return value; executed for effects
Function (pure) takes zero or more arguments and returns a value; does not have effects
Method is procedure or function associated with object in object-oriented program; object may be implicit parameter
Good practice to maintain distinction between procedure and function even if language does not
Timothy A. Budd. Multiparadigm Programming in Leda, Addison-Wesley, 1995, page 3:
“a way of conceptualizing what it means to perform computation, of structuring and organizing how tasks are to be carried out on a computer.”
Historical programming language paradigms
imperative
declarative
Recent programming languages blur distinctions
But concept of programming paradigm meaningful
Imperative program
Imperative language
Imperative program
int count = 0;
int maxc = 10;
while (count <= maxc) {
System.out.println(count) ;
count = count + 1
}
State in above Java example
count
and maxc
int count = 0;
int maxc = 10;
while (count <= maxc) {
System.out.println(count) ;
count = count + 1
}
State changes (side effects)
count
valueprintln
adds line to output int count = 0;
int maxc = 10;
while (count <= maxc) {
System.out.println(count) ;
count = count + 1
}
Execution in sequence
while
causes execution zero or more timescount
, maxc
int count = 0;
int maxc = 10;
while (count <= maxc) {
System.out.println(count) ;
count = count + 1
}
State implicit because state assumed from context
Variable count
mutable because value can change—yield different values at different times
Variable maxc
also mutable, but does not change here
To allow execution, the Java code above can be included in main
method
Imperative languages well suited for traditional computer architectures
Examples: Fortran, C, C++, Java, C#, Python, JavaScript, Lua
Object-oriented languages?
Declarative program
Declarative program
Declarative language
Declarative program
Declarative paradigm subdivided into
Underlying computational model is mathematical function
counter :: Int -> Int -> String
counter count maxc
| count <= maxc = show count ++ "\n" ++ counter (count+1) maxc
| otherwise = ""
Above Haskell example similar in meaning to earlier Java example
counter
count
and maxc
count
to maxc
one per line counter :: Int -> Int -> String
counter count maxc
| count <= maxc = show count ++ "\n" ++ counter (count+1) maxc
| otherwise = ""
Function “call” (application) of counter
count
and maxc
counter :: Int -> Int -> String
counter count maxc
| count <= maxc = show count ++ "\n" ++ counter (count+1) maxc
| otherwise = ""
Repetition of counter
by recursive call
State of counter
explicit because passed in arguments
Values of arguments immutable within body
Names like count
and maxc
(in pure functional language)
Previous Haskell code executes no actions
Must execute IO
program to do input/output
Referential transparency very important! It allows:
In functional languages, functions often are:
Higher-order functions very important
Pure examples: Haskell, Idris, Elm, Miranda, Hope, Backus’s FP
Hybrid examples: Scala, F#, OCaml, SML, Erlang, Elixir, Lisp, Clojure, Scheme
Mainstream imperative language with functional subsets: Java 8+, C#, Python, Ruby, Groovy, Rust, Swift
Underlying computational model is mathematical relation (or predicate)
counter(X,Y,S) :- count(X,Y,R), atomics_to_string(R,'\n',S).
count(X,X,[X]).
count(X,Y,[]) :- X > Y.
count(X,Y,[X|Rs]) :- X < Y, NX is X+1, count(NX,Y,Rs).
Above SWI-Prolog example similar in meaning to earlier Java and Haskell examples
X
to Y
, one per line counter(X,Y,S) :- count(X,Y,R), atomics_to_string(R,'\n',S).
count(X,X,[X]).
count(X,Y,[]) :- X > Y.
count(X,Y,[X|Rs]) :- X < Y, NX is X+1, count(NX,Y,Rs).
Above defines database consisting of clauses
count(X,X,[X]).
defines fact
X
, list [X]
with single value X
, count(X,X,[X])
asserted truecount(X,Y,[]) :- X > Y.
defines rule
:-
true if right side also truecount(X,Y,[])
true when X > Y
counter(X,Y,S) :- count(X,Y,R), atomics_to_string(R,'\n',S).
count(X,X,[X]).
count(X,Y,[]) :- X > Y.
count(X,Y,[X|Rs]) :- X < Y, NX is X+1, count(NX,Y,Rs).
Can query database for missing components
count(1,1,Z).
yields value Z = [1]
count(X,1,[1]).
yields value X = 1.
counter(0,10,S)
, print value of S
with following ruleImperative and functional languages run computation in one direction yielding one answer
Prolog can run computation in multiple directions yielding multiple answers
Example relational languages: Prolog, Parlog, miniKanren
Most Prolog implementations not pure, have imperative features (cut, assert and retract clauses)
Subcategory of imperative paradigm
organizes programs primarily using procedural abstractions
may limit variable/subprogram scope by nesting
def counter(count,maxc):
def has_more(count,maxc): # new variables
return count <= maxc
def incr():
nonlocal count # from counter
count = count + 1
while has_more(count,maxc):
print(f'{count}') # Py 3.6 string interpolation
incr()
Call counter(0,10)
similar to Java fragment
counter
procedure called “function” in Python
counter
returns special value None
def counter(count,maxc):
def has_more(count,maxc): # new variables
return count <= maxc
def incr():
nonlocal count # from counter
count = count + 1
while has_more(count,maxc):
print(f'{count}') # Py 3.6+ string interpolation
incr()
Has nested function has_more
and procedure incr
Abstracts out loop test and increment action
def counter(count,maxc):
def has_more(count,maxc): # new variables
return count <= maxc
def incr():
nonlocal count # from counter
count = count + 1
while has_more(count,maxc):
print(f'{count}') # Py 3.6 string interpolation
incr()
Scope is range of code where name accessible
Lexical scope begins at definition, ends at end of block
has_more
and incr
scopes begin with definitions, end with counter
body
def counter(count,maxc):
def has_more(count,maxc): # new variables
return count <= maxc
def incr():
nonlocal count # from counter
count = count + 1
while has_more(count,maxc):
print(f'{count}') # Py 3.6 string interpolation
incr()
counter
parameters count
and maxc
accessible within body unless hidden
hidden in function has_more
by new parameters of same names
accessible in procedure incr
, but count
declared nonlocal
to allow assignment
Python, C, Fortran, Pascal, Lua, etc.
Refers more to design method than to language
Decomposes program into units (i.e. modules) that can be developed separately
Encapsulates key design and implementation details inside module (i.e. exhibits information hiding)
Each Python module is in separate file, creates separate name space
Defines 3 module-level functions: has_more
, incr
, counter
Defines 2 module-level global variables: count
, maxc
Initializes global variables: count
, maxc
Gives lexical scope in module to global variables & functions
globals count
, maxc
hidden in has_more
by parameters with same names
globals count
, maxc
visible in incr
, counter
Declares count
as global
in incr
for assignment
Imports feature counter
from module CountingMod.py
Does not make other features accessible
Imports all features of module, but requires qualified names
Allows modification of imported module’s variables
Most modern languages support “modules” in some way
Some languages (e.g. Standard ML) provide advanced support for encapsulation and multiple implementations of interfaces
Object-based paradigms (object-based, class-bassed, object-oriented, prototype-based) — Chapter 3
Concurrent paradigms — future
etc.
Procedural and data abstraction
Imperative paradigm expresses how something computed
Declarative paradigm expresses what is computed
Imperative Java program Counting.java
Functional Haskell program Counting.hs
Relational (logic) SWI-Prolog program Counting.pl
Procedural Python 3 program CountingProc.py
Modular Python 3 program CountingMod.py