Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 1: The Functional Paradigm & Getting Started with Haskell

Lesson 2 of 17 in the free Functional & Logic Programming notes on Siksha Sarovar, written by Rohit Jangra.

1.1 What is functional programming?

In a functional language, a program is built entirely from expressions and function definitions. Computation means evaluating an expression to a value — never executing commands that overwrite memory.

Imperative thinkingFunctional thinking
Variables are boxes you can overwriteNames are bound to values once, forever
Loops repeat commandsRecursion repeats computation
x = x + 1 is normalx = x + 1 is a contradiction (no solution!)
Order of statements mattersOrder of definitions does not matter
Functions may have side effectsPure functions: output depends only on input

1.2 Key properties of functional languages

  • Purity / no side effects. Calling f 5 today, tomorrow, or twice in a row always gives the same answer. This property is called referential transparency — any expression can be replaced by its value without changing the program's meaning.
  • First-class functions. Functions are values: they can be passed as arguments, returned as results and stored in lists. A function that takes or returns functions is called a higher-order function.
  • Lazy evaluation (Haskell-specific). An expression is evaluated only when its value is actually needed. This allows infinite data structures like [1..] (the list of all positive integers).
  • Strong static typing. Every expression has a type known at compile time; type errors are caught before the program runs.

1.3 Getting started with GHC and GHCi

GHC (Glasgow Haskell Compiler) is the standard Haskell compiler. GHCi is its interactive shell (REPL) — you type expressions, it evaluates them.

-- Start the REPL by typing:  ghci
ghci> 2 + 3 * 4
14
ghci> (2 + 3) * 4
20
ghci> not True
False
ghci> "hello " ++ "world"
"hello world"

Useful GHCi commands (all start with a colon):

CommandWhat it does
:load myfile.hs (or :l)Load a script file
:reload (or :r)Reload after editing the file
:type expr (or :t)Show the type of an expression
:info name (or :i)Show information about a name or type class
:quit (or :q)Exit GHCi

1.4 Your first script

Save this as First.hs and load it with :l First.hs:

-- First.hs : our first Haskell definitions
square :: Int -> Int          -- type signature: Int to Int
square x = x * x              -- definition: an EQUATION, not a command

areaOfCircle :: Double -> Double
areaOfCircle r = pi * r * r

greet :: String -> String
greet name = "Hello, " ++ name ++ "!"
ghci> square 7
49
ghci> areaOfCircle 2.0
12.566370614359172
ghci> greet "Asha"
"Hello, Asha!"

Notice what is missing: no return, no braces, no semicolons, no parentheses around arguments. square 7 — function application is written by juxtaposition (a space) and binds tighter than any operator, so square 3 + 1 means (square 3) + 1 = 10.

1.5 Function application vs operators

  • f x y applies f to two arguments.
  • Operators like + are just functions with symbolic names; (+) 2 3 is the same as 2 + 3.
  • A two-argument function can be used as an infix operator by wrapping its name in backquotes:
7 `div` 2      -- infix form: exactly the same as  div 7 2  →  3

1.6 The evaluation model: rewriting

Haskell evaluates by rewriting expressions using the defining equations, just like simplifying in algebra:

square (3 + 1)
= square 4            -- arithmetic (or later, if lazy)
= 4 * 4               -- by the equation  square x = x * x
= 16

This "calculation" view is the heart of the whole subject — the lambda-calculus lesson makes it formal, and the graph-reduction lesson shows how the machine actually does it.

1.7 Referential transparency — the exam definition

Definition. An expression is referentially transparent if it can be replaced by its value (or by any other expression equal to it) anywhere in the program without changing the program's meaning. A language is referentially transparent when every expression in it has this property.

Why imperative code breaks it — a side-by-side you can reproduce in the exam:

// C-like, with hidden state:          -- Haskell, pure:
int c = 0;                             f :: Int -> Int
int f(int x) {                         f x = x + 1
    c = c + 1;
    return x + c;                      -- f 5 is ALWAYS 6, so
}                                      -- f 5 + f 5  ==  2 * f 5   ✓ holds
// f(5) + f(5) = 6 + 7 = 13
// 2 * f(5)    = 2 * 6 = 12   → NOT equal! equational reasoning fails

Consequences worth listing in an answer: (1) the compiler may evaluate sub-expressions in any order; (2) repeated expressions can be computed once and shared; (3) expressions can safely run in parallel; (4) programs can be verified by substituting equals for equals, exactly like algebra.

1.8 Eager vs lazy evaluation — first taste

The same expression can be reduced in two different orders. Both reach the same answer here (the Church–Rosser theorem of the lambda-calculus lesson guarantees answers never disagree), but the work done differs:

Applicative order (eager — C, Java, Python):     Normal order + sharing (lazy — Haskell):
square (3 + 1)                                    square (3 + 1)
= square 4          -- argument first             = x * x  where x = (3+1)   -- ONE shared node
= 4 * 4                                           = 4 * 4                     -- forced once
= 16                                              = 16

Where laziness visibly changes behaviour:

ghci> fst (1, undefined)     -- lazy: undefined never touched
1
ghci> take 3 [1..]           -- works on an INFINITE list
[1,2,3]

An eager language would crash on the first and loop forever on the second. Remember the slogan: lazy evaluation computes a value at most once, and only if it is needed.

1.9 Advantages and limitations of functional programming (classic 5-mark question)

Advantages

  1. Easier reasoning and testing — referential transparency means a function is fully specified by its input/output table; no hidden state to set up.
  2. Conciseness — higher-order functions and recursion replace boilerplate loop code (quicksort in ~3 lines, Unit 2).
  3. Fewer bugs by construction — no aliasing, no race conditions on shared mutable state; strong static types catch errors at compile time.
  4. Free parallelism opportunities — independent pure expressions can be evaluated concurrently.
  5. Mathematical foundation — programs can be proved correct by induction (Unit 2) because programs are equations.

Limitations

  1. I/O and state need special machinery (IO actions, Unit 1's data & I/O lesson) — less direct than imperative assignment.
  2. Performance can be harder to predict — laziness may build up unevaluated expressions (space leaks, discussed with graph reduction).
  3. Algorithms that are naturally in-place (e.g. array sorting) need different formulations or extra copying.
  4. A genuinely different mental model — the learning curve is the price of the benefits.

Exam pointers

  • "Define referential transparency / pure function / higher-order function" — quote the boxed definition and give the C-vs-Haskell counter-example above.
  • "Differentiate imperative and functional programming" — use the table in §1.1; add x = x + 1 as the punchline.
  • Hand-evaluation of a small definition (like §1.6) is a near-certain question — write every rewrite step, one per line, with the rule used.

Self-check

  1. Why is x = x + 1 meaningless as a Haskell equation but normal in C?
  2. State two compiler optimisations that referential transparency makes safe.
  3. Evaluate square (square 2) by hand, showing every step.
  4. Which GHCi command shows the type of an expression? Which reloads a file?
  5. Give one expression that terminates lazily but would fail under eager evaluation.