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 maxcState changes (side effects)
count valueprintln adds line to outputExecution in sequence
while causes execution zero or more timescount, maxcState 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
countercount and maxccount 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.hs
Relational (logic) SWI-Prolog programL Counting.pl
Procedural Scala program: CountingProc.scala
Modular Scala programs: CountingMod.scala CountingMod2.scala