Implementing the Either Monad in TypeScript
The Either monad is a powerful tool in functional programming for representing computations that can result in one of two possible outcomes: a success or a failure. Implementing an Either type in TypeScript allows you to handle potential errors gracefully and chain operations in a more readable and maintainable way, avoiding deeply nested if/else statements or try/catch blocks. This challenge asks you to create a robust Either type with associated utility functions.
Problem Description
You are tasked with implementing an Either type in TypeScript. This type should represent a value that can be either a Left (representing a failure or error) or a Right (representing a success). Your implementation should include the following:
Either<L, R>Type: A generic type parameterized by two types,L(Left, representing the error type) andR(Right, representing the success type).Left<L>Constructor: A constructor to create anEitherinstance representing a failure, holding a value of typeL.Right<R>Constructor: A constructor to create anEitherinstance representing a success, holding a value of typeR.isLeft()Method: A method that returnstrueif theEitherinstance represents a failure (Left), andfalseotherwise.isRight()Method: A method that returnstrueif theEitherinstance represents a success (Right), andfalseotherwise.getLeft()Method: A method that returns the value held in aLeftinstance. This method should throw an error if called on aRightinstance.getRight()Method: A method that returns the value held in aRightinstance. This method should throw an error if called on aLeftinstance.map<U>(f: (r: R) => U): Either<L, U>Method: A method that applies a functionfto the value inside aRightinstance and returns a newEitherinstance with the result. If theEitherinstance is aLeft, it should return the originalLeftinstance unchanged.flatMap<U>(f: (r: R) => Either<L, U>): Either<L, U>Method: A method that applies a functionfto the value inside aRightinstance. The functionfshould return anotherEitherinstance. This method chains theseEitherinstances together, effectively flattening them. If the originalEitherinstance is aLeft, it should return the originalLeftinstance unchanged.
Examples
Example 1:
Input: new Right<number, string>("Success!")
Output: { isLeft: false, isRight: true, getRight: () => "Success!", getLeft: () => { throw new Error("Called getLeft on Right") } }
Explanation: Creates a Right Either with the value "Success!".
Example 2:
Input: new Left<string, number>("An error occurred!")
Output: { isLeft: true, isRight: false, getLeft: () => "An error occurred!", getRight: () => { throw new Error("Called getRight on Left") } }
Explanation: Creates a Left Either with the value "An error occurred!".
Example 3:
const either1 = new Right<string, number>(10);
const either2 = either1.map(x => x * 2);
console.log(either2.getRight()); // Output: 20
const either3 = new Left<string, number>("Error!");
const either4 = either3.map(x => x * 2);
console.log(either4.getLeft()); // Output: "Error!"
const either5 = new Right<string, Either<string, number>>(new Left<string, number>("Error"));
const either6 = either5.flatMap(x => x);
console.log(either6.getLeft()); // Output: "Error"
Explanation: Demonstrates map and flatMap functionality, including handling of Left cases.
Constraints
- The
getLeft()andgetRight()methods must throw an error if called on the incorrect type ofEitherinstance. - The
mapandflatMapmethods should be immutable; they should return newEitherinstances instead of modifying the original. - The code should be well-typed and adhere to TypeScript best practices.
- No external libraries are allowed.
Notes
- Consider using discriminated unions to distinguish between
LeftandRightinstances. - Think about how to handle the error cases gracefully and consistently.
- The
flatMapmethod is crucial for chaining operations that themselves returnEitherinstances. It's a key part of the monad's power. - Focus on creating a type-safe and robust implementation.