Theory of
Programming
Language
Table of contents
Functional
01 Programming 02 Lambda Calculus
03 Reduction
04 Recursion
01
Functional
Programming
“In computer science, functional
programming is a programming
paradigm where programs are constructed
by applying and composing functions”
Functional programming languages are
specially designed to handle symbolic
computation and list processing applications.
It is based on mathematical functions.
Functional programming languages
Lisp Erlang Elixir
Python Haskell Clojure
Types of functional
Programming Language
Functional programming languages are categorized
into two groups, i.e. −
• Pure Functional Languages −
These types of functional languages support only the
functional paradigms.
For example − Haskell.
• Impure Functional Languages −
These types of functional languages support the
functional paradigms and imperative style
programming.
For example − LISP.
Characteristics
● These languages are designed on the concept of
mathematical functions that use conditional
expressions and recursion to perform computation.
● Functional programming supports higher-order
functions and lazy evaluation features.
● It don’t support flow Controls like loop statements
and conditional statements like If-Else and Switch
Statements. They directly use the functions and
functional calls.
● Like OOP, functional programming languages
support popular concepts such as Abstraction,
Encapsulation, Inheritance, and Polymorphism.
Advantages
Bugs-Free Code
Functional programming does not
support state, so there are no side-effect
results and we can write error-free codes.
Efficient Parallel Programming
Functional programming languages have NO
Mutable state, so there are no state-change
issues. One can program "Functions" to work
parallel as "instructions". Such codes support
easy reusability and testability.
Advantages
Efficiency
Functional programs consist of independent
units that can run concurrently. As a result, such
programs are more efficient.
Supports Nested Functions
Functional programming supports Nested
Functions.
Lazy Evaluation
Functional programming supports Lazy
Functional Constructs like Lazy Lists, Lazy
Maps, etc.
Syntax
returnType functionName (type1 argument1,
type2 argument2, . . . )
{
// function body
}
Example
sum(x, y) // sum is function
taking x and y as
arguments return
x + y // sum is returning sum
of x and y without changing
them
02
Lambda
Calculus
Lambda calculus (also written as λ-calculus) is
a formal system in mathematical logic for
expressing computation based on function
abstraction and application using variable
binding and substitution.
It is a universal model of computation that can
be used to simulate any Turing machine.
It was introduced by the mathematician
Alonzo Church in the 1930s as part of his
research into the foundations of mathematics.
• Function creation − Church introduced the
notation λx.E to denote a function in which ‘x’ is a
formal argument and ‘E’ is the functional body. These
functions can be of without names and single
arguments.
• Function application − Church used the
notation E1.E2 to denote the application of
function E1 to actual argument E2. And all the functions
are on single argument.
Syntax
Lamdba calculus includes three different
types of expressions, i.e.,
• E :: = x (variables)
• | E1 E2 (function application)
• | λx.E (function creation)
Where λx.E is called Lambda abstraction
and E is known as λ-expressions.
Example
Pure lambda calculus has no built-in functions. Let us
evaluate the following expression −
(+ (* 5 6) (* 8 3))
Here, we can’t start with '+' because it only operates on
numbers.
There are two reducible expressions: (* 5 6) and (* 8 3).
We can reduce either one first.
For example −
(+ (* 5 6) (* 8 3))
(+ 30 (* 8 3))
(+ 30 24)
= 54
03
Reduction
The meaning of lambda
expressions is defined by
how expressions can be
reduced.
Types of Reduction
α-conversion
changing bound variables
η-reduction
which captures a notion of
extensionality
β-reduction
applying functions to their
arguments
Reduction strategies
• Normal order
The leftmost, outermost redex is always reduced first. That is,
whenever possible the arguments are substituted into the body of
an abstraction before the arguments are reduced.
• Applicative order
The leftmost, innermost redex is always reduced first. Intuitively
this means a function's arguments are always reduced before the
function itself. Applicative order always attempts to apply
functions to normal forms, even when this is not possible.
• Full β-reductions
Any redex can be reduced at any time. This means essentially the
lack of any particular reduction strategy—with regard to
reducibility, "all bets are off".
04
Recursion
Recursive functions repeatedly
call themselves, until it reaches
the base case.
There are no “for” or “while” loop
in functional languages.
Iteration in functional languages is
implemented through recursion.
Example
fib(n)
if (n <= 1)
return 1;
Else
return fib(n - 1) + fib(n - 2);
Consider the factorial function F(n) recursively defined by
F(n) = 1, if n = 0; else n × F(n − 1)
In the lambda expression which is to represent this function, a parameter (typically the first
one) will be assumed to receive the lambda expression itself as its value, so that
calling it – applying it to an argument – will amount to recursion. Thus to achieve
recursion, the intended-as-self-referencing argument (called r here) must always be
passed to itself within the function body, at a call point:
G := λr. λn.(1, if n = 0; else n × (r r (n−1)))
with r r x = F x = G r x to hold, so r = G and
F := G G = (λx.x x) G
Y := λg.(λx.g (x x)) (λx.g (x x))
In the lambda calculus, Y g is a fixed-point of g, as it expands to:
Y g
(λh.(λx.h (x x)) (λx.h (x x))) g
(λx.g (x x)) (λx.g (x x))
g ((λx.g (x x)) (λx.g (x x)))
g (Y g)
Now, to perform our recursive call to the factorial function, we would simply call (Y G) n, where n is the
number we are calculating the factorial of. Given n = 4, for example, this gives:
(Y G) 4
G (Y G) 4
(λr.λn.(1, if n = 0; else n × (r (n−1)))) (Y G) 4
(λn.(1, if n = 0; else n × ((Y G) (n−1)))) 4
1, if 4 = 0; else 4 × ((Y G) (4−1))
4 × (G (Y G) (4−1))
4 × ((λn.(1, if n = 0; else n × ((Y G) (n−1)))) (4−1))
4 × (1, if 3 = 0; else 3 × ((Y G) (3−1)))
4 × (3 × (G (Y G) (3−1)))
4 × (3 × ((λn.(1, if n = 0; else n × ((Y G) (n−1)))) (3−1)))
4 × (3 × (1, if 2 = 0; else 2 × ((Y G) (2−1))))
4 × (3 × (2 × (G (Y G) (2−1))))
4 × (3 × (2 × ((λn.(1, if n = 0; else n × ((Y G) (n−1)))) (2−1))))
4 × (3 × (2 × (1, if 1 = 0; else 1 × ((Y G) (1−1)))))
4 × (3 × (2 × (1 × (G (Y G) (1−1)))))
4 × (3 × (2 × (1 × ((λn.(1, if n = 0; else n × ((Y G) (n−1)))) (1−1)))))
4 × (3 × (2 × (1 × (1, if 0 = 0; else 0 × ((Y G) (0−1))))))
4 × (3 × (2 × (1 × (1))))
24
Thank You