Implementation of kotlin.Triple in JavaScript, TypeScript, and Python
Published on 25 December 2023
Reading time: 2 min Create a complete implementation of the kotlin.Triple class in JavaScript, TypeScript, and Python using functional programming.
Note:The types`Maybe`, Either, `Result`are not part of the base language in JavaScript or TypeScript, so we must implement them ourselves.
JavaScript
//javascript
// Maybe type
const Maybe = (value) => ({
map: (fn) => (value !== null && value !== undefined ? Maybe(fn(value)) : Maybe(null)),
flatMap: (fn) => (value !== null && value !== undefined ? fn(value) : Maybe(null)),
getOrElse: (defaultValue) => (value !== null && value !== undefined ? value : defaultValue),
});
// Either type
const Either = {
Left: (value) => ({
map: (fn) => Either.Left(value),
flatMap: (fn) => Either.Left(value),
getOrElse: (defaultValue) => defaultValue,
isLeft: true,
isRight: false,
}),
Right: (value) => ({
map: (fn) => Either.Right(fn(value)),
flatMap: (fn) => fn(value),
getOrElse: (defaultValue) => value,
isLeft: false,
isRight: true,
}),
};
// Triple class
class Triple {
constructor(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
}
map(fn) {
return new Triple(fn(this.a), fn(this.b), fn(this.c));
}
static of(a, b, c) {
return new Triple(a, b, c);
}
// Example method using Maybe and Either
divideBy(other) {
return Maybe(this.b)
.flatMap((numerator) =>
Maybe(other)
.flatMap((denominator) =>
denominator !== 0
? Either.Right(numerator / denominator)
: Either.Left('Division by zero')
)
)
.getOrElse(Either.Left('Invalid input'));
}
}
// Example usage
const triple = Triple.of(1, 2, 3);
const result = triple.map((x) => x * 2);
console.log(result); // Triple { a: 2, b: 4, c: 6 }
const divisionResult = triple.divideBy(2);
console.log(divisionResult); // Either.Right { value: 1 }
const invalidDivisionResult = triple.divideBy(0);
console.log(invalidDivisionResult); // Either.Left { value: 'Division by zero' }
TypeScript
//Typescript
// Maybe type
// noinspection JSAnnotator
type Maybe<T> = {
map<U>(fn: (value: T) => U): Maybe<U>;
flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U>;
getOrElse(defaultValue: T): T;
};
const just = <T>(value: T): Maybe<T> => ({
map: <U>(fn: (value: T) => U) => just(fn(value)),
flatMap: <U>(fn: (value: T) => Maybe<U>) => fn(value),
getOrElse: (_: T) => value,
});
const nothing = <T>(): Maybe<T> => ({
map: <U>(_: (value: T) => U) => nothing<U>(),
flatMap: <U>(_: (value: T) => Maybe<U>) => nothing<U>(),
getOrElse: (defaultValue: T) => defaultValue,
});
// Either type
type Either<L, R> = {
map<U>(fn: (value: R) => U): Either<L, U>;
flatMap<U>(fn: (value: R) => Either<L, U>): Either<L, U>;
getOrElse(defaultValue: R): R;
isLeft: boolean;
isRight: boolean;
};
const left = <L, R>(value: L): Either<L, R> => ({
map: <U>(_: (value: R) => U) => left<L, U>(value),
flatMap: <U>(_: (value: R) => Either<L, U>) => left<L, U>(value),
getOrElse: (defaultValue: R) => defaultValue,
isLeft: true,
isRight: false,
});
const right = <L, R>(value: R): Either<L, R> => ({
map: <U>(fn: (value: R) => U) => right<L, U>(fn(value)),
flatMap: <U>(fn: (value: R) => Either<L, U>) => fn(value),
getOrElse: (_: R) => value,
isLeft: false,
isRight: true,
});
// Triple class
class Triple<T> {
constructor(public a: T, public b: T, public c: T) {}
map<U>(fn: (value: T) => U): Triple<U> {
return new Triple<U>(fn(this.a), fn(this.b), fn(this.c));
}
static of<T>(a: T, b: T, c: T): Triple<T> {
return new Triple<T>(a, b, c);
}
// Example method using Maybe and Either
divideBy(other: T): Either<string, number> {
return just(this.b)
.flatMap((numerator) =>
just(other)
.flatMap((denominator) =>
denominator !== 0 ? right(numerator / denominator) : left('Division by zero')
)
)
.getOrElse(left('Invalid input'));
}
}
// Example usage
const triple = Triple.of(1, 2, 3);
const result = triple.map((x) => x * 2);
console.log(result); // Triple { a: 2, b: 4, c: 6 }
const divisionResult = triple.divideBy(2);
console.log(divisionResult); // Either.Right { value: 1 }
const invalidDivisionResult = triple.divideBy(0);
console.log(invalidDivisionResult); // Either.Left { value: 'Division by zero' }
Python, pydantic, pymonad
To create a Python implementation using`pydantic`for type validation and`pymonad`for monads:
Install dependencies:
pip install pydantic pymonad
from pymonad.either import Either, Left, Right
from pydantic import BaseModel, ValidationError, validator
# Définition de la classe Triple avec Pydantic
class Triple(BaseModel):
a: int
b: int
c: int
@validator('b')
def validate_b(cls, b, values):
if b == 0:
raise ValueError('Division by zero is not allowed')
return b
# Méthode utilisant Either pour la gestion des erreurs
def divide_by(self, other):
def division(numerator, denominator):
return Right(numerator / denominator)
def handle_error(error):
return Left(str(error))
return Either(lambda: division(self.b, other)).or_else(handle_error)
# Exemple d'utilisation
try:
triple = Triple(a=1, b=2, c=3)
result = triple.divide_by(2)
if result.is_right:
print(f'Division result: {result.value}')
else:
print(f'Error: {result.value}')
except ValidationError as e:
print(f'Validation Error: {e}')
except Exception as e:
print(f'An unexpected error occurred: {e}')
In this example:
-
pydantic`to define the class`Triple`with typed fields. - The method`divide_by`uses the monad`Eitherde `pymonad`to handle potential errors resulting from division by zero. - Custom validation to ensure that the value of`b`is not equal to zero. If it is equal to zero, an exception is thrown, and`pymonad`catch this exception to return a result`Left`indicating the error.