Functional Patterns — Currying, Composition, Immutability

JS অর্ধেক functional — সেই অর্ধেকটি শক্তিশালী

~40 min Advanced 14 practice problems Live runner

1. Pure Functions

A pure function (a) returns the same output for the same input and (b) has no side effects (no mutation, no I/O, no random). Pure functions are easy to test, reason about, cache, and parallelize.

pure.js
// Pure
const add = (a, b) => a + b;

// Impure — depends on hidden state
let count = 0;
const tick = () => ++count;

// Impure — mutates input
const push = (arr, x) => { arr.push(x); return arr; };

// Pure refactor — return a new array
const append = (arr, x) => [...arr, x];

const a = [1, 2];
console.log(append(a, 3), a);   // [1,2,3] [1,2]
Pure function = same input → same output, কোনো side effect নেই। এই সরল নিয়ম bug প্রায় শূন্যে নামিয়ে আনে।

2. Immutability

immut.js
const user = { name: "Arif", age: 22 };

// Update — return new object
const older = { ...user, age: 23 };

const nums = [1, 2, 3];
const next = [...nums, 4];
const noTwo = nums.filter(n => n !== 2);

console.log(user, older);
console.log(nums, next, noTwo);

// Object.freeze — shallow lock
const CFG = Object.freeze({ debug: false });

Libraries like Immer give you "mutable-looking" code that produces immutable updates under the hood — easier to write, same correctness.

3. Currying & Partial Application

curry.js
// Manual curry
const add = a => b => c => a + b + c;
console.log(add(1)(2)(3));

// Partial application — fix some args, leave the rest
const partial = (fn, ...preset) => (...rest) => fn(...preset, ...rest);
const log = (level, msg) => console.log(`[${level}] ${msg}`);
const info = partial(log, "INFO");
info("server up");
info("port 3000");

4. Composition: compose & pipe

compose.js
// pipe — left to right (more readable)
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

// compose — right to left (math style)
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

const trim = s => s.trim();
const lower = s => s.toLowerCase();
const dasherize = s => s.replace(/\s+/g, "-");

const slug = pipe(trim, lower, dasherize);
console.log(slug("  Hello Bangla World "));

5. Map / Filter / Reduce as Building Blocks

mfr.js
const users = [
    { name: "Arif",   age: 22, active: true },
    { name: "Karim",  age: 35, active: false },
    { name: "Nusrat", age: 28, active: true },
];

const avgAgeOfActive = users
    .filter(u => u.active)
    .map(u => u.age)
    .reduce((a, b, _, arr) => a + b / arr.length, 0);

console.log(avgAgeOfActive);

6. Persistent Data — Quick Mention

Real production FP uses structural sharing — when you "update" an object, you reuse all unchanged subtrees. Libraries like Immer, Immutable.js, or Zustand patches do this for you. The benefit: cheap equality checks via reference, and ergonomic React/Redux state.

7. When NOT to Go Full FP in JS

  • Performance-critical inner loops — direct mutation can be 10× faster
  • DOM code where mutation is the model the browser exposes
  • Stream processing with native callback APIs

Use FP where it shines (data transformation, state updates, validation), and pragmatic OOP/imperative everywhere else.

8. Glossary (শব্দকোষ)

TermMeaningবাংলায়
Pure functionSame input → same output, no side effects.একই input → একই output, কোনো side effect নেই।
Side effectMutation, I/O, randomness, time.Mutation, I/O, random, time।
ImmutabilityDon't mutate; return new value instead.Mutate না করে নতুন value ফেরত দেওয়া।
CurryingTransform f(a, b) into f(a)(b).f(a, b)-কে f(a)(b)-তে রূপান্তর।
Partial applicationPre-fix some args of a function.কিছু argument আগেই দেওয়া।
Compositioncompose(f, g)(x) = f(g(x)) — right to left.f(g(x)) — ডান থেকে বামে।
Pipepipe(f, g)(x) = g(f(x)) — left to right.বাম থেকে ডানে — পড়তে সহজ।
Referential transparencyAn expression can be replaced by its value without changing behaviour.Expression-কে তার value দিয়ে replace করা যায়।
Structural sharing"Update" reuses unchanged subtrees — cheap immutable updates.অপরিবর্তিত অংশ পুনঃব্যবহার — সস্তা immutable update।
মূল কথা: Pure function + immutable update দিয়ে predictable code লিখুন। React/Redux-এর state এই pattern-এই কাজ করে। pipe/compose দিয়ে chain লিখুন। বড় state-এ Immer-এর মতো library mutation-এর ergonomics + immutable result দুটোই দেয়।

9. Practice Problems

  1. Write a pure increment that doesn't touch its argument.
    ✨ Show Answer
    a1.js
    const increment = ({ count }) => ({ count: count + 1 });
    const s = { count: 5 };
    console.log(increment(s), s);
  2. Curry add so add(2)(3)(4) returns 9.
    ✨ Show Answer
    a2.js
    const add = a => b => c => a + b + c;
    console.log(add(2)(3)(4));
  3. Build pipe and chain three transformations.
    ✨ Show Answer
    a3.js
    const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
    console.log(pipe(n => n + 1, n => n * 2, n => n - 3)(5));
  4. Update the age field of an object immutably.
    ✨ Show Answer
    a4.js
    const u = { name: "Arif", age: 22 };
    console.log({ ...u, age: 23 }, u);
  5. Remove an element from an array immutably.
    ✨ Show Answer
    a5.js
    const remove = (arr, x) => arr.filter(v => v !== x);
    console.log(remove([1, 2, 3, 4], 3));
  6. Why do React-style apps prefer immutable updates?
    ✨ Show Answer

    Answer: Reference equality becomes a cheap "did anything change?" check. Components only re-render when their props or state objects change identity, so { ...state, x: 1 } triggers exactly the right work — much faster than deep comparison and easier to reason about than mutation-tracking proxies.

  7. Build partial and use it to create info, warn, error from a generic log.
    ✨ Show Answer
    a7.js
    const partial = (fn, ...p) => (...r) => fn(...p, ...r);
    const log = (level, msg) => console.log(`[${level}] ${msg}`);
    ["INFO", "WARN", "ERROR"].forEach(l => partial(log, l)("hello"));
  8. Compose three string transforms to make a slug.
    ✨ Show Answer
    a8.js
    const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
    const slug = pipe(s => s.trim(), s => s.toLowerCase(), s => s.replace(/\s+/g, "-"));
    console.log(slug("  Hello Bangla World  "));
  9. Demonstrate that Object.freeze is shallow.
    ✨ Show Answer
    a9.js
    const o = Object.freeze({ inner: { x: 1 } });
    o.inner.x = 99;
    console.log(o.inner);   // { x: 99 }
  10. Implement deepFreeze recursively.
    ✨ Show Answer
    a10.js
    const deepFreeze = o => {
        if (o && typeof o === "object") {
            Object.values(o).forEach(deepFreeze);
            Object.freeze(o);
        }
        return o;
    };
    const c = deepFreeze({ a: { b: 1 } });
    try { c.a.b = 2; } catch {}
    console.log(c.a.b);
  11. Map an array of strings to their lengths using map.
    ✨ Show Answer
    a11.js
    console.log(["hi", "there", "hello"].map(s => s.length));
  12. Build a curried filter that filters by predicate.
    ✨ Show Answer
    a12.js
    const filter = pred => arr => arr.filter(pred);
    const evens = filter(n => n % 2 === 0);
    console.log(evens([1, 2, 3, 4]));
  13. In one paragraph, explain when FP shines vs when imperative is better.
    ✨ Show Answer

    Answer: FP shines for data transformation pipelines, validation, state-update reducers, and anywhere correctness over speed matters. Imperative code wins for tight inner loops, DOM updates, parser/state-machine work, and any case where a single in-place mutation expresses intent more directly than a stack of pure transformers. Most modern apps mix both — pure for app state, imperative for hot paths.

  14. Show why arr.push + return arr is impure.
    ✨ Show Answer

    The function modifies the caller's array — same input now produces different output across calls. Anyone holding a reference observes the change, leading to "spooky action at a distance" bugs. Use [...arr, x] for the immutable equivalent.

Summary — Module 33

Pure functions + immutable updates eliminate whole classes of bugs and play perfectly with React-style frameworks. Currying, partial application, and composition are the toolset. Use pipe over deeply nested calls. Object.freeze is shallow — write your own deep freeze if you need it.

Pure function এবং immutable update দিয়ে predictable code লিখুন। pipe/compose দিয়ে transformation chain করুন। বড় state-এর জন্য Immer-এর মতো library ব্যবহার করুন।

Next Module → Tooling — npm, Vite, ESLint, Prettier।