type Either<L, R> = Left<L, R> | Right<L, R>; class Left<L, R> { constructor(public value: L) {} isLeft(): this is Left<L, R> { return true; } isRight(): this is Right<L, R> { return false; } } class Right<L, R> { constructor(public value: R) {} isLeft(): this is Left<L, R> { return false; } isRight(): this is Right<L, R> { return true; } }
function map<L, R, U>(either: Either<L, R>, fn: (value: R) => U): Either<L, U> { return either.isRight() ? new Right<L, U>(fn(either.value)) : new Left<L, U>(either.value); } function flatMap<L, R, U>(either: Either<L, R>, fn: (value: R) => Either<L, U>): Either<L, U> { return either.isRight() ? fn(either.value) : new Left<L, U>(either.value); }
function composeM<L, R, U, V>( f: (x: R) => Either<L, U>, g: (x: U) => Either<L, V> ): (x: R) => Either<L, V> { return (x: R) => flatMap(f(x), g); }
const parseNumber = (str: string): Either<string, number> => { const num = Number(str); return isNaN(num) ? new Left("Invalid number") : new Right(num); }; const increment = (num: number): Either<string, number> => { return new Right(num + 1); }; const composedFunction = composeM(parseNumber, increment); const result1 = composedFunction("42"); // Right(43) const result2 = composedFunction("abc"); // Left("Invalid number") console.log(result1); // Right { value: 43 } console.log(result2); // Left { value: 'Invalid number' }
export function composeMultiple<L, R>(...fns: ((x: any) => Either<L, any>)[]): (x: R) => Either<L, R> { return (x: R) => fns.reduce((acc, fn) => flatMap(acc, fn), new Right<L, R>(x)); }