TYPESCRIPT

Functional Programming Utilities

Collection of functional programming utilities with TypeScript

TypeScriptFunctionalUtilities

Code

// Currying
function curry<T extends any[], R>(
  fn: (...args: T) => R
): (...args: T) => R {
  const curried = (...args: any[]): any => {
    __TOKEN_39__ (args.length >= fn.length) {
      return fn(...args as T);
    }
    __TOKEN_41__ (...nextArgs: any[]) => curried(...args, ...nextArgs);
  };
  return curried as (...args: T) => R;
}

// Partial application
function partial<T extends any[], R>(
  fn: (...args: T) => R,
  ...partialArgs: any[]
): (...args: any[]) => R {
  __TOKEN_45__ (...remainingArgs: any[]) => 
    fn(...partialArgs, ...remainingArgs) as R;
}

// Compose functions
function compose<T>(
  ...fns: ((arg: any) => any)[]
): (initialValue: T) => any {
  __TOKEN_47__ (initialValue: T) => 
    fns.reduceRight((value, fn) => fn(value), initialValue);
}

// Pipe functions (left to right composition)
function pipe<T>(
  ...fns: ((arg: any) => any)[]
): (initialValue: T) => any {
  __TOKEN_49__ (initialValue: T) => 
    fns.reduce((value, fn) => fn(value), initialValue);
}

// Memoization
function memoize<T __TOKEN_51__ (...args: any[]) => any>(
  fn: T,
  resolver?: (...args: Parameters<T>) => string
): T {
  const cache = new Map<string, ReturnType<T>>();
  
  __TOKEN_54__ ((...args: Parameters<T>): ReturnType<T> => {
    const key = resolver ? resolver(...args) : JSON.stringify(args);
    
    __TOKEN_56__ (cache.has(key)) {
      return cache.get(key)!;
    }
    
    const result = fn(...args);
    cache.set(key, result);
    return result;
  }) as T;
}

// Tap (for side effects in pipelines)
function tap<T>(effect: (value: T) => void): (value: T) => T {
  __TOKEN_61__ (value: T) => {
    effect(value);
    return value;
  };
}

// Maybe monad
class Maybe<T> {
  private constructor(private value: T | null) {}
  
  static just<T>(value: T): Maybe<T> {
    return new Maybe(value);
  }
  
  static nothing<T>(): Maybe<T> {
    return new Maybe<T>(null);
  }
  
  static from<T>(value: T | null | undefined): Maybe<T> {
    return value == null ? Maybe.nothing() : Maybe.just(value);
  }
  
  map<U>(fn: (value: T) => U): Maybe<U> {
    return this.value == null ? Maybe.nothing<U>() : Maybe.just(fn(this.value));
  }
  
  flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U> {
    return this.value == null ? Maybe.nothing<U>() : fn(this.value);
  }
  
  getOrElse(defaultValue: T): T {
    return this.value == null ? defaultValue : this.value;
  }
  
  isJust(): boolean {
    return this.value != null;
  }
  
  isNothing(): boolean {
    return this.value == null;
  }
}

// Either monad
type Either<L, R> = Left<L> | Right<R>;

class Left<L> {
  readonly tag = 'Left' as const;
  constructor(readonly value: L) {}
  
  map<R>(_fn: (value: any) => R): Either<L, R> {
    return this;
  }
  
  flatMap<R>(_fn: (value: any) => Either<L, R>): Either<L, R> {
    return this;
  }
}

class Right<R> {
  readonly tag = 'Right' as const;
  constructor(readonly value: R) {}
  
  map<S>(fn: (value: R) => S): Either<any, S> {
    return new Right(fn(this.value));
  }
  
  flatMap<L, S>(fn: (value: R) => Either<L, S>): Either<L, S> {
    return fn(this.value);
  }
}

// Utility functions for Either
const left = <L, R>(value: L): Either<L, R> => new Left(value);
const right = <L, R>(value: R): Either<L, R> => new Right(value);

// Lazy evaluation
class Lazy<T> {
  private fn: () => T;
  private cachedValue?: T;
  private evaluated = false;
  
  constructor(fn: () => T) {
    this.fn = fn;
  }
  
  static of<T>(value: T): Lazy<T> {
    return new Lazy(() => value);
  }
  
  map<U>(fn: (value: T) => U): Lazy<U> {
    return new Lazy(() => fn(this.value));
  }
  
  flatMap<U>(fn: (value: T) => Lazy<U>): Lazy<U> {
    return new Lazy(() => fn(this.value).value);
  }
  
  get value(): T {
    __TOKEN_125__ (!this.evaluated) {
      this.cachedValue = this.fn();
      this.evaluated = true;
    }
    return this.cachedValue!;
  }
}

// Higher-order functions
function not<T>(fn: (value: T) => boolean): (value: T) => boolean {
  __TOKEN_133__ (value: T) => !fn(value);
}

function and<T>(...fns: ((value: T) => boolean)[]): (value: T) => boolean {
  __TOKEN_135__ (value: T) => fns.every(fn => fn(value));
}

function or<T>(...fns: ((value: T) => boolean)[]): (value: T) => boolean {
  __TOKEN_137__ (value: T) => fns.some(fn => fn(value));
}

// Usage examples
// Currying
const add = (a: number, b: number) => a + b;
const curriedAdd = curry(add);
const add5 = curriedAdd(5);
console.log(add5(3)); // 8

// Composition
const double = (x: number) => x * 2;
const square = (x: number) => x * x;
const doubleThenSquare = compose(square, double);
console.log(doubleThenSquare(3)); // 36

const squareThenDouble = pipe(double, square);
console.log(squareThenDouble(3)); // 36

// Memoization
const expensiveCalculation = memoize((a: number, b: number) => {
  console.log('Calculating...');
  return a + b;
});
console.log(expensiveCalculation(1, 2)); // Calculating... 3
console.log(expensiveCalculation(1, 2)); // 3 (cached)

// Maybe monad
const getUser = (id: number): Maybe<{ name: string; age: number }> => {
  return id > 0 ? Maybe.just({ name: 'John', age: 30 }) : Maybe.nothing();
};

const userName = getUser(1)
  .map(user => user.name.toUpperCase())
  .getOrElse('Unknown');

// Either monad
function divideEither(a: number, b: number): Either<string, number> {
  return b === 0 ? left('Division by zero') : right(a / b);
}

const result = divideEither(10, 2)
  .map(x => x * 3)
  .flatMap(x => divideEither(x, 5));

// Lazy evaluation
const lazyValue = Lazy.__TOKEN_154__(() => {
  console.log('Computing...');
  return 42;
});

console.log(lazyValue.value); // Computing... 42
console.log(lazyValue.value); // 42 (cached)

// Pipeline with tap
const processData = pipe(
  (x: number) => x * 2,
  tap(x => console.log('After doubling:', x)),
  (x: number) => x + 10,
  tap(x => console.log('After adding:', x))
);

console.log(processData(5)); // 20