How different languages handle the processing of data through projection and aggregation.
Data Processing
Python Language
TypeScript Language
Map
Data projection from a list or array-like data structure.
numbers = [1, 2, 3, 4, 5] squares = list(map(lambda x: x * x, numbers)) print(squares) # [1, 4, 9, 16, 25] evens = list(filter(lambda x: x % 2 == 0, numbers)) print(evens) # [2, 4]
Better implemented using a list comprehension:
numbers = [1, 2, 3, 4, 5] squares = [x * x for x in numbers]
const numbers = [1, 2, 3, 4, 5]; const squares = numbers.map((n) => n * n); console.log(squares); // [1, 4, 9, 16, 25] const evens = numbers.filter((n) => n % 2 === 0); console.log(evens); // [2, 4]
Reduce
Reduction of a list or array-like data structure into a new aggregate object.
from functools import reduce sum_numbers = reduce(lambda acc, x: acc + x, numbers) print(sum_numbers) # 15
Better implemented using a list comprehension:
numbers = [1, 2, 3, 4, 5] squares = [x * x for x in numbers] sum_of_squares = sum(x * x for x in numbers)
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15
Deferred Execution
Execution of operations is delayed until necessary.
To defer execution of the call graph in Python, you can use lambda functions, generators, or callables to delay evaluation until explicitly invoked.
Using a lambda function:
def compute(): print("Running compute") return 42 lazy_result = lambda: compute() # Deferred print("Before calling") result = lazy_result() # Triggers execution print("Result:", result)
Using a generator:
def lazy_range(n): for i in range(n): print(f"Yielding {i}") yield i for num in lazy_range(3): print("Got:", num)
Support via libraries for deferred execution. RxJs and lodash/fp support pipelines, but these don't allow expression analysis for deferred execution.
import { of } from "rxjs"; import { map, filter } from "rxjs/operators"; const numbers$ = of(1, 2, 3, 4, 5).pipe( filter((n) => n % 2 === 0), // Deferred filter map((n) => n * n), // Deferred map ); // Execution only happens when subscribed numbers$.subscribe((result) => console.log(result)); // Output: 4, 16
import fp from "lodash/fp"; const numbers = [1, 2, 3, 4, 5]; const processNumbers = fp.pipe( fp.filter((n) => n % 2 === 0), // Deferred filter fp.map((n) => n * n), // Deferred map ); console.log(processNumbers(numbers)); // [4, 16]
Deferred Execution with AST
Execution is deferred and represented as an Abstract Syntax Tree (AST).
To defer execution and be able to analyze the AST of the expression passed in, use the ast
and inspect
modules to inspect a function or lambda expression.
import ast import inspect f = lambda x: x * x + 1 source = inspect.getsource(f) tree = ast.parse(source) print(ast.dump(tree, indent=4))
This gives you full access to the internal structure of the function before execution, enabling you to:
- Perform static analysis
- Rewrite or transform code safely
- Enforce constraints or validations
For advanced manipulation, consider using libraries like astor
, libcst
, or RedBaron
for AST round-tripping and source regeneration.
Deferred Execution With AST
JavaScript proxy support does allow for some analysis:
const handler = { get: (target, prop) => { return (...args) => { target.operations.push({ prop, args }); return target; }; }, }; function createExpression() { return new Proxy({ operations: [] }, handler); } const expr = createExpression(); expr.filter((x) => x > 2).map((x) => x * 2); console.log(expr.operations); // Output: [{ prop: 'filter', args: [Function] }, { prop: 'map', args: [Function] }]