Primary Paradigms
Historical programming language paradigms
imperative
declarative
Recent programming languages blur distinctions
But concept of programming paradigm meaningful
H. Conrad Cunningham
29 January 2019
Copyright (C) 2017, 2018, 2019, H. Conrad Cunningham
Acknowledgements: I created the original version of 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. In Spring 2019, I created this Scala version.
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
State in above Scala example
count
and
maxc
State changes (side effects)
count
valueprintln
adds line to
outputExecution in sequence
while
causes execution zero or more timescount
, maxc
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 Scala code above can be included in main
method
An implementation using val
and type
inference
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
def counter(count: Int, maxc: Int): String =
if (count <= maxc)
count.toString ++ "\n" ++ counter(count+1,maxc)
else
""
Above Scala example similar in meaning to earlier example
counter
count
and maxc
count
to maxc
one per line def counter(count: Int, maxc: Int): String =
if (count <= maxc)
count.toString ++ "\n" ++ counter(count+1,maxc)
else
""
Function “call” (application) of counter
count
and maxc
def counter(count: Int, maxc: Int): String =
if (count <= maxc)
count.toString ++ "\n" ++ counter(count+1,maxc)
else
""
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 Scala code executes no actions (i.e. no state change)
Execute main
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
object CountingProc {
def counter(count: Int, maxc: Int) {
var kount = count // cannot change parameter value
def has_more(count: Int, maxc: Int) = // Boolean
count <= maxc
def incr {
kount = kount + 1
}
while (has_more(kount: Int, maxc: Int)) {
println(kount.toString)
incr
}
}
def main(args: Array[String]) {
counter(0,10)
}
}
Call counter(0,10)
similar to functional code
counter
is procedure
method, returns special value ()
of type Unit
def counter(count: Int, maxc: Int) {
var kount = count // cannot change parameter value
def has_more(count: Int, maxc: Int) = // Boolean
count <= maxc
def incr {
kount = kount + 1
}
while (has_more(kount: Int, maxc: Int)) {
println(kount.toString)
incr
}
}
Has nested function has_more
and procedure incr
Abstracts out loop test and increment action
def counter(count: Int, maxc: Int) {
var kount = count // cannot change parameter value
def has_more(count: Int, maxc: Int) = // Boolean
count <= maxc
def incr {
kount = kount + 1
}
while (has_more(kount: Int, maxc: Int)) {
println(kount.toString)
incr
}
}
Scope is range of code where name accessible
local methods has_more
and
incr
and local variable kount
scopes within counter
from declaration to end of
block
def counter(count: Int, maxc: Int) {
var kount = count // cannot change parameter value
def has_more(count: Int, maxc: Int) = // Boolean
count <= maxc
def incr {
kount = kount + 1
}
while (has_more(kount: Int, maxc: Int)) {
println(kount.toString)
incr
}
}
counter
parameters count
and maxc
accessible within body
unless hidden, are immutable
hidden in function has_more
by new parameters of same names
accessible in procedure incr
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)
Use Scala object
for
module here
object CountingMod {
var count = 0 // public, using type inference
var maxc = 10 // declaration & setter/getter better practices
private def has_more(count: Int, maxc: Int): Boolean =
count <= maxc
private def incr {
count = count + 1
}
def counter {
while (has_more(count,maxc)) {
println(count.toString)
incr
}
}
}
Defines 3 module-level methods: has_more
, incr
, counter
, first 2 private
Defines 2 module-level (object) variables: count
, maxc
— both part of public interface
here
Initializes variables: count
, maxc
Gives lexical scope in module to variables & functions
object variables count
,
maxc
hidden in has_more
by parameters with same
names
count
, maxc
visible in incr
, counter
object CountingModTest {
def main(args: Array[String]) {
CountingMod.counter
println("[1..20]")
CountingMod.count = 1
CountingMod.maxc = 20
CountingMod.counter
}
}
counter
, public variables count
and maxc
, uses qualified names object CountingMod2 extends CountingIF { // Impl. 1
private var count = 0
private var maxc = 10
def setStart(start: Int) { count = start }
def getStart: Int = count
def setStop(stop: Int) { maxc = stop }
def getStop: Int = maxc
def has_more(c: Int, m: Int) = c <= m
def incr { count = count + 1 }
def counter {
while (has_more(count,maxc)) {
println(count.toString)
incr
}
}
}
object CountingMod2a extends CountingIF { // Impl. 2
private var count = 0
private var maxc = 10
def setStart(start: Int) {
count = start
}
def getStart: Int = count
def setStop(stop: Int) { maxc = stop }
def getStop: Int = maxc
def has_more(c: Int, m: Int) =
c != 0 && Math.abs(c) <= Math.abs(m)
def incr { count = count * 2 }
def counter {
while (has_more(count,maxc)) {
println(count.toString)
incr
}
}
}
Most modern languages support “modules” in some way
Some languages (e.g. Standard ML) provide advanced support for encapsulation and multiple implementations of interfaces
Scala’s object-oriented features offers several possibilities (traits, objects, classes, packages, access controls)
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 Scala programs: CountingImp.scala CountingImp2.scala
Functional Scala program: CountingFun.scala
Relational (logic) SWI-Prolog program: Counting.pl
Procedural Scala program: CountingProc.scala
Modular Scala programs: CountingMod.scala CountingMod2.scala