
Functional Programming Essentials¶
Early History of Programming Languages¶
Note
Interested in computing history? See George’s salon talk at https://ecommons.luc.edu/cs_facpubs/417/ and Mini-History of Computing at https://ecommons.luc.edu/cs_facpubs/103/.
1940s: machine languages → binary code
1950s: assembly languages → mnemonics and symbols
Late 1950s: 3GLs → abstraction and portability (FORTRAN, ALGOL, COBOL, LISP)
Some Paradigm‑Defining Programming Languages¶
1957: FORTRAN → imperative/scientific (Backus)
1958: ALGOL → imperative with recursion (Backus et al)
1964: APL → array programming (Iverson)
1967: Simula → Algol-style OO (Dahl & Nygaard)
1972: C → imperative/system (Kernighan & Ritchie)
1972: Prolog → logic programming (Colmerauer)
1972: Smalltalk → dynamic OO (Kay et al)
1973: Actors → concurrency/task parallelism (Hewitt, Agha, Hoare et al)
1978: ML → statically typed functional programming (Milner et al)
1983: C++ → C-style OO (Stroustrup)
Modern Incarnations, Often Multi-Paradigm¶
1987: Perl → scripting/text processing (Wall)
1987: Erlang → functional/concurrent/reliable systems (Armstrong)
1991: Python → dynamic/multiparadigm/education/scientific (van Rossum)
1995: Java → statically typed OO, portable (Gosling et al)
1995: JavaScript → web scripting (Brendan Eich)
2001: C# → modern OO on .NET (Hejlsberg)
2003: Scala → functional + OO on JVM (Odersky)
2005: F# → functional-first on .NET (Syme)
2007: Clojure → functional Lisp on JVM (Hickey)
2009: Go → compiled, concurrent, systems programming (Pike et al)
2010: Rust → memory-safe systems programming (Mozilla/Graydon Hoare)
2014: Swift → modern safe systems/app language (Apple/Lattner)
Connection to Artificial Intelligence¶
Period |
Dominant AI Language(s) |
Notes |
---|---|---|
1960s–1980s |
Lisp, Prolog |
Symbolic AI, logic programming |
1980s–1990s |
Lisp, Prolog, C/C++, MATLAB |
Expert systems, early ML, robotics |
1990s–2000s |
Java, C++, MATLAB, R |
Statistical AI, teaching, industrial |
2010–Present |
Python |
Deep learning, NLP, open-source boom |
Dominant Programming Paradigms¶
Imperative: explicit mutable state
Functional: pure/recursive functions, immutable values
Object-oriented: public behavior + private state
Actors: concurrent behavior with local/transient state
Logic: queries over facts and rules
So Why Are Things So Imperative?¶
Computer architecture emphasizes stored-program model with shared memory
Theoretical foundations (Turing, von Neumann) reinforce this view
Each of these deserves a closer look.
Turing Machine, 1936¶
Alan Turing’s “abstract machine” that predates modern programmable computers.
A tape which is divided into cells, one next to the other.
A head that can read and write symbols on the tape and move the tape left and right one (and only one) cell at a time.
A table of instructions: (si, symbol at current position, sj, replacement symbol, L or R)
A state register that stores the state of the Turing table.
All programming languages today are said to be Turing-equivalent, because they can all be reduced to the Turing machine as defined here.
Compare these concepts to the ideas of von Neumann.
von Neumann, EDVAC Report, 1945¶
Von Neumann was influential in many endeavors (mathematics, physics, game theory, and computing) and was involved in the Manhattan project and the EDVAC computer (successor to the ENIAC of Eckhert and Mauchly).
Three ideas that have shaped modern computer engineering:
“First: since the device is a computor (sic), it will have to perform the elementary operations of arithmetics….” “Second: the logical control of the device is the proper sequencing of its operations (by…a control organ).” “Third: Any device which is to carry out long and complicated sequences of operations…must have a considerable memory.”
These views of computing are identical and were independently conceived by Babbage in the design of the Analytical Engine, which Ada Lovelace knew how to program!.
How to Choose?!?¶
Match paradigm to the problem
Some solutions use multiple paradigms/languages
Avoid the “hammer/nail” syndrome of the late 1990s
Zoom Out: Fundamental Computational Models¶
Turing machine/von-Neumann architecture: model of the hardware; low-level, unnatural
lambda calculus: model of the computational behavior; simple, mathematical, natural
recursion: direct model of underlying mathematical theory
These three models are equivalent in computational power (Turing complete).
Church-Turing thesis: these three models express anything that is algorithmically computable.
Lambda Calculus¶
<Exp> ::= <ident>
| lambda <ident> . <Exp>
| <Exp> <Exp>
| <constant>
(\f (x, y) -> (f x, f y)) (*2) (2, 3) -> (4, 6)
Powerful enough to express everything using recursion
Make It Safe: Type Systems in Programming Languages¶
A type system is a mechanism to classify program fragments according to the type of value they compute. Can be used to ensure values are used as intended.
A type error is a type-related erroneous program behavior.
Type safety is the use of a type system to prevent type errors.
2 + 3 -> 5
2 + “3” -> error (possible in C)
3[4] -> error (possible in C with cast)
Type System Dimensions¶
Static vs. dynamic
Strong vs. weak
Implicit/inferred vs. explicit
Static Type Safety¶
The use of a static and strong type system to prevent type errors at run time.
Benefits
early detection of logical errors “when things don’t fit together”
do not need to keep type information around at run time -> performance!!
Challenge
keep it from feeling like a straightjacket to the programmer
Timeline of the Language/Type System Pendulum¶
Since mid 1980s: statically typed OO languages: C++, Java, C#
want type safety and performance
language monoculture trap: started to focus on libraries and frameworks instead of languages
Since late 1990s: dynamic languages: Perl, Python, Ruby
wanted productivity (expressiveness, flexibility, extensibility, etc.)
build confidence in correctness through extensive testing (TDD)
performance? avoid bottleneck by using as glue for C/C++ libraries
Back since late 2000s: statically typed functional languages: ML and its descendants F#, Haskell, and OCaml; Scala
want type safety, performance, and agility
want better support for multi-core hardware
ThoughtWorks Technology Radar: Languages Section¶
Main Statically Typed Contenders
Haskell: ML descendant, but lazy and without mutable state, no OO
Scala: Java-based, mutable and immutable state, eager, functional on top of OO
F#: OCaml descendant, mutable and immutable state, eager, OO on top of functional
Swift: Apple language, mutable and immutable state, eager, functional on top of OO
Rust: C/C++ “systems” successor, AoT compilation, mutable and immutable state, eager, functional on top of OO
Note: Others omitted not because we don’t like them but they are not statically typed or lack full FP support (e.g. Python, Go, etc.)
Interesting Dynamically Typed Contenders¶
Clojure: concurrency + performance
Erlang: scalability, soft real-time
JavaScript: front-end ubiquity, CoffeeScript, etc.
Python: itertools, functools
So How to Choose?¶
Foundational soundness
Adherence to design principles (Maclennan 1986)
Readability
Writability/Productivity
Reliability
Cost
Portability
Generality
REPL for exploratory programming
Support for internal and/or external domain-specific languages
Extrinsic Criteria (from Consulting world):¶
Developer expertise and workforce availability
Hiring in local area
Developer productivity cost
Training & migration cost
Tooling cost
Performance/scalability
Offshoring
Community (blogs, meetups, conventions)
Visibility, support, ecosystem
Other Interesting Criteria¶
Performance
Concurrency/parallelism support
Ecosystem (tools, standard libraries, interoperability)
Major projects, community activity