Knowledge / Computer Science and Engineering / Functional programming basics

Functional programming basics

Computer Science and Engineering (B.E./B.Tech) · Programming · free study notes for exams, placements and interviews

In short

Functional programming (FP) builds a program out of functions that compute values instead of statements that change state: no reassignment, no hidden side effects, and functions themselves are ordinary data that can be passed and returned. Its theoretical base is lambda calculus, which is also a model of computation equivalent in power to a Turing machine. In Indian university syllabi it appears in *Principles of Programming Languages / Programming Paradigms* papers (lambda calculus, evaluation order, higher-order functions) and inside Python, Java and JavaScript courses as lambdas, streams and map/filter/reduce; the exact coverage of lambda calculus varies by university and GATE syllabus revision, so check your own scheme. In placements it is examined constantly: TCS, Infosys, Wipro, Accenture, Cognizant and Capgemini interviews ask pure vs impure functions, Stream pipelines and recursion, while product and data companies (Flipkart, Swiggy, Zomato, Jio, PhonePe, Freshworks, Standard Chartered GBS) use Scala/Spark, Kotlin and React reducers where immutability is the working rule.

Explained

What makes a program "functional"

AspectImperative / proceduralFunctional
Unit of workStatement changing stateExpression producing a value
VariablesMutable cellsImmutable bindings
Repetitionfor, while loopsRecursion, folds, comprehensions
StateShared and updatedThreaded through arguments/returns
ReasoningTrace execution orderSubstitute equals for equals
ConcurrencyNeeds locksSafe by default (no shared mutation)
Typical costLower memory churnMore allocation, GC pressure

Functions as first-class values

Recursion as the loop of FP

map, filter, fold and the combinator toolkit

Lambda calculus - the formal core

Evaluation strategies and laziness

Handling effects, errors and state functionally

Key points

Formulas and examples

β-reduction: (λx.M) N → M[x := N] (substitute N for every free x in M, renaming to avoid capture)
α-conversion: λx.M ≡ λy.M[x := y], y not free in M; η-reduction: λx.(f x) → f, x not free in f
Currying: f : A × B → C ⟺ curry f : A → (B → C); uncurry is the inverse
Composition: (f ∘ g)(x) = f(g(x)); associative: f ∘ (g ∘ h) = (f ∘ g) ∘ h; identity: f ∘ id = f
foldr ⊕ z [x₁,…,xₙ] = x₁ ⊕ (x₂ ⊕ ( … (xₙ ⊕ z)))
foldl ⊕ z [x₁,…,xₙ] = ((( z ⊕ x₁) ⊕ x₂) ⊕ … ⊕ xₙ)
Church numeral n = λf.λx. fⁿ x; SUCC = λn.λf.λx. f (n f x); PLUS = λm.λn.λf.λx. m f (n f x)
Y = λf.(λx. f (x x))(λx. f (x x)) so that Y g = g (Y g)
Naive two-call Fibonacci: number of calls C(n) = 2·F(n+1) − 1, where F is the Fibonacci number (F(1)=1, F(2)=1)
Stack space: non-tail recursion O(n) frames; tail recursion with TCO O(1); map/filter/fold over n items O(n) applications

Example 1 - β-reduction, step by step. Reduce (λx. λy. x + 2*y) 3 4.

  1. Application is left-associative: ((λx. λy. x + 2*y) 3) 4.
  2. β-reduce the inner application, x := 3 → (λy. 3 + 2*y) 4.
  3. β-reduce again, y := 4 → 3 + 2*4 = 3 + 8 = **11**.
  4. Note the intermediate λy. 3 + 2*y - a *closure* holding x = 3. This is currying in action: a 2-argument function applied one argument at a time.

Example 2 - Church numerals: PLUS 2 3 = 5.

Example 3 - foldl vs foldr with a non-associative operator. List [1, 2, 3], operator −, seed 0.

Example 4 - map / filter / reduce on a placement data set. Six offers in ₹ lakh CTC: [3.6, 12.0, 4.8, 18.0, 7.2, 9.6]. Keep offers ≥ ₹6 lakh, apply a 10% revision, then find the total and mean.

  1. filter (c -> c >= 6) → [12.0, 18.0, 7.2, 9.6] (4 of 6 retained).
  2. map (c -> c * 1.10) → [13.20, 19.80, 7.92, 10.56].
  3. reduce (+) 0 → 13.20 + 19.80 = 33.00; 33.00 + 7.92 = 40.92; 40.92 + 10.56 = ₹51.48 lakh.
  4. Mean = 51.48 / 4 = ₹12.87 lakh.
double total = offers.stream()                 // Java 8+
        .filter(c -> c >= 6.0)
        .map(c -> c * 1.10)
        .reduce(0.0, Double::sum);             // 51.48
from functools import reduce
total = reduce(lambda a, b: a + b,
               map(lambda c: c * 1.10,
                   filter(lambda c: c >= 6.0, offers)), 0.0)   # 51.48

Example 5 - tail vs non-tail recursion. factorial(5).

Example 6 - memoisation and referential transparency. Naive fib(30) with fib(n)=fib(n−1)+fib(n−2):

StrategyArgument evaluatedRe-evaluated per useExample language
Call by valueBefore the callNo (already a value)C, Java, Python, Scala (default)
Call by nameOnly when usedYes, every useScala => T parameters, macros
Call by need (lazy)Only when first usedNo (thunk cached)Haskell, Scala lazy val
OperationHaskellJava 8+ StreamPythonJavaScript
mapmap f xs.map(f)map(f, xs) / comprehensionxs.map(f)
filterfilter p xs.filter(p)filter(p, xs)xs.filter(p)
reducefoldl'/foldr f z xs.reduce(z, f)functools.reduce(f, xs, z)xs.reduce(f, z)
flatten-mapconcatMap f xs.flatMap(f)itertools.chain.from_iterablexs.flatMap(f)
lambda\x -> x+1x -> x+1lambda x: x+1x => x+1

Where it is used

Interview questions

Common mistakes

Related topics

Recursion and divide-and-conquer algorithms · Lambda calculus and theory of computation · Python essentials · Java essentials · JavaScript essentials · Object-oriented programming concepts

Open in the Knowledge reader →Find jobs →