sweet-monads
sweet-monads copied to clipboard
Possible library improuvements
Hello, while @JSMonk was away this spring i created my fork of this library. Here it is https://github.com/AlexXanderGrib/monads-io
And here some good ideas you can grab from it:
1. Use inheritance
class EitherConstructor<L, R>
implements AsyncMonad<R>, Alternative<R>, Container<R> { ... }
class Right<L, R> extends EitherConstructor<L, R> {
get [Symbol.toStringTag](): "Right" { ... }
get name(): "Either" { ... }
get type(): EitherType.Right { ... }
getRight(): R { ... }
getLeft(): undefined { ... }
private constructor(public readonly right: R) {
super();
Object.freeze(this);
}
}
class Left<L, R> extends EitherConstructor<L, R> {
...
get type(): EitherType.Left { ... }
}
-
It's simpler. Types can now be defined as:
type Either<L, R> = Left<L, R> | Right<L, R> type Maybe<T> = Just<T> | None<T>; // <T> may be obsolete, cause of T = never by default
-
It's more efficient. Now left and right are different on class level, so difference is expressed through prototype without specific property, so overhead is minimal
2. Make None
a singleton
Continuing with classes, it is possible to create one and only one None for all program
class None<T = unknown> extends MaybeConstructor<T> implements SerializedNone {
static readonly instance = new None<never>();
static create<T>(): None<T> {
return None.instance;
}
get [Symbol.toStringTag]() {
return "None";
}
}
3. Create rust-like iterator helpers
export function* iterator<T>(
callback: () => Maybe<T>
): Generator<T, void, void> {
let result: Maybe<T>;
while ((result = callback()).isJust()) {
yield result.unwrap();
}
}
export async function* asyncIterator<T>(
callback: () => MaybePromiseLike<Maybe<MaybePromiseLike<T>>>
): AsyncGenerator<T, void, void> {
let result: Maybe<MaybePromiseLike<T>>;
while ((result = await callback()).isJust()) {
yield await result.unwrap();
}
}
export function* filterMap<T, X>(
iterable: Iterable<T>,
filterMap: (value: T, index: number) => Maybe<X>
): Generator<X, void, void> {
let index = 0;
for (const value of iterable) {
const processed = filterMap(value, index++);
if (processed.isJust()) {
yield processed.unwrap();
}
}
}
4. Add await
method for async monads
export interface AsyncMonad<A> extends Monad<A> {
await<A>(this: AsyncMonad<MaybePromiseLike<A>>): Promise<AsyncMonad<A>>;
}
5. Add zip
method
Can be used to refactor .apply()
class EitherConstructor {
zip<A, B>(either: Either<A, B>): Either<L | A, [R, B]> {
return this.chain((value) => either.map((right) => [value, right]));
}
}
Hi @AlexXanderGrib.
Thank you for the proposal, it looks great.
I want to know a little bit more about the use cases of each bullet because it's hard to understand, why people should have an iterator for Maybe
or Either
.
Could you describe a few use cases inside each paragraph?
Hello @JSMonk, haven't used @sweet-monads/iterator
, all proposed iterator features implemented there