We use cookies to enhance your experience on the site
CodeWorlds

Closures in Functional Programming

In Jurassic Park, each enclosure has its own security system - fence voltage, alert level, incident counter. This data is private and inaccessible from outside, but the functions managing the enclosure have full access to it. This is exactly how closures work - they create a private environment for data, accessible only through specific functions.

What is a Closure?

A closure is a function that "remembers" variables from the scope in which it was created, even after that scope has ceased to exist.

1function createCounter() {
2  let count = 0; // Private variable - inaccessible from outside
3
4  return {
5    increment() { count++; return count; },
6    decrement() { count--; return count; },
7    getCount() { return count; },
8  };
9}
10
11const counter = createCounter();
12counter.increment(); // 1
13counter.increment(); // 2
14counter.getCount();  // 2
15// count is completely private - there is no direct access to it

Data Privacy Through Closures

Closures allow you to create truly private variables in JavaScript - this is a key pattern in functional programming:

1function createSecuritySystem(zoneName) {
2  let fenceVoltage = 10000;
3  let alertLevel = 0;
4  let incidentLog = [];
5
6  // Private helper function
7  function logEvent(event) {
8    incidentLog.push({ event, time: Date.now(), zone: zoneName });
9  }
10
11  return {
12    raiseFence() {
13      fenceVoltage += 2000;
14      logEvent('Fence voltage raised to ' + fenceVoltage);
15      return fenceVoltage;
16    },
17    triggerAlert() {
18      alertLevel++;
19      logEvent('Alert level: ' + alertLevel);
20      return alertLevel;
21    },
22    getStatus() {
23      return {
24        zone: zoneName,
25        voltage: fenceVoltage,
26        alert: alertLevel,
27        incidents: incidentLog.length,
28      };
29    },
30  };
31}
32
33const zoneA = createSecuritySystem('Zone A');
34const zoneB = createSecuritySystem('Zone B');
35
36zoneA.raiseFence();  // 12000
37zoneA.triggerAlert(); // 1
38zoneB.getStatus();   // { zone: 'Zone B', voltage: 10000, alert: 0, incidents: 0 }
39// Each zone has its own isolated data

Function Factories

Closures are ideal for creating factories - functions that produce specialized functions:

1// Validator factory
2function createValidator(rules) {
3  return function validate(data) {
4    const errors = [];
5    for (const [field, rule] of Object.entries(rules)) {
6      if (rule.required && !data[field]) {
7        errors.push(field + ' is required');
8      }
9      if (rule.min && data[field] < rule.min) {
10        errors.push(field + ' must be >= ' + rule.min);
11      }
12      if (rule.max && data[field] > rule.max) {
13        errors.push(field + ' must be <= ' + rule.max);
14      }
15    }
16    return { valid: errors.length === 0, errors };
17  };
18}
19
20const validateDinosaur = createValidator({
21  name: { required: true },
22  weight: { required: true, min: 1, max: 100000 },
23  health: { required: true, min: 0, max: 100 },
24});
25
26validateDinosaur({ name: 'Rex', weight: 8000, health: 95 });
27// { valid: true, errors: [] }
28
29validateDinosaur({ name: '', weight: -5, health: 150 });
30// { valid: false, errors: ['name is required', 'weight must be >= 1', 'health must be <= 100'] }

Memoization with Closures

Memoization is a technique for caching results of expensive computations. Closures provide a private cache:

1function memoize(fn) {
2  const cache = new Map();
3
4  return function(...args) {
5    const key = JSON.stringify(args);
6    if (cache.has(key)) {
7      console.log('Cache hit for:', key);
8      return cache.get(key);
9    }
10    const result = fn(...args);
11    cache.set(key, result);
12    return result;
13  };
14}
15
16// Expensive DNA analysis
17function analyzeDNA(sampleId, depth) {
18  // Simulating a long computation
19  let result = 0;
20  for (let i = 0; i < depth * 1000; i++) {
21    result += Math.random();
22  }
23  return { sampleId, score: result / (depth * 1000) };
24}
25
26const memoizedAnalyze = memoize(analyzeDNA);
27memoizedAnalyze('TREX-001', 100); // computes
28memoizedAnalyze('TREX-001', 100); // from cache - instant!
Go to CodeWorlds