I've been experimenting with TypeScript lately, and I'm trying to implement some basic monads. I've already got a reasonably functioning Maybe (with the same methods of my Either below, at least), but Either is eluding me for type-related reasons I don't quite understand.
My typing strategy for higher kinded types is borrowed from this article: https://medium.com/@gcanti/higher-kinded-types-in-typescript-static-and-fantasy-land-d41c361d0dbe. I know there are libraries that already have all these monads and other FP goodies, but implementing them like this is my way of trying to learn TypeScript more deeply.
declare module './HKT' {
interface TypeIDtoHKT2<E, A> {
Either: Either<E, A>;
}
}
export const TypeID = 'Either';
export type TypeID = typeof TypeID;
export class Left<E> {
readonly _F!: TypeID;
readonly _A!: E;
readonly _tag: 'Left' = 'Left';
constructor(readonly left: E) {}
static of<E, A>(e: E): Either<E, A> {
return new Left(e);
}
map<A, B>(f: (a: A) => B): Either<E, B> {
return this;
}
chain<A, B>(f: (a: A) => Either<A, B>): Either<E, B> {
return this;
}
ap<A, B>(f: Either<A, (a: A) => B>): Either<E, B> {
return this;
}
}
export class Right<A> {
readonly _F!: TypeID;
readonly _A!: A;
readonly _tag: 'Right' = 'Right';
constructor(readonly right: A) {}
static of<A, E>(a: A): Either<E, A> {
return new Right(a);
}
map<E, B>(f: (a: A) => B): Either<E, B> {
return Right.of(f(this.right));
}
chain<E, B>(f: (a: A) => Either<E, B>): Either<E, B> {
return f(this.right);
}
ap<E, B>(f: Either<E, (a: A) => B>): Either<E, B> {
if (f instanceof Left) return f;
return this.map(f.right);
}
}
export type Either<E, A> = Left<E> | Right<A>;
export const left = <E, A>(e: E): Either<E, A> => {
return new Left(e);
};
export const right = <E, A>(a: A): Either<E, A> => {
return new Right(a);
};
When I try to run a test assertion, such as right(4).map(isEven) === true
, I get the following error:
Cannot invoke an expression whose type lacks a call signature. Type '(<E, B>(f: (a: number) => B) => Either<E, B>) | (<A, B>(f: (a: A) => B) => Either<unknown, B>)' has no compatible call signatures.
I don't understand why the E
type is unknown
here or how I can make it known...or if that's even the right thing to be trying to do. Any guidance is appreciated.