在打字稿中实现任一 Monad

M-N*_*M-N 5 functional-programming either typescript

我最近一直在试验 TypeScript,我正在尝试实现一些基本的 monad。我已经有了一个功能合理的 Maybe(至少使用与下面的任何一种方法相同的方法),但是由于与类型相关的原因,我不太明白任何一种。

我的更高级类型的打字策略是从这篇文章中借用的:https ://medium.com/@gcanti/higher-kinded-types-in-typescript-static-and-fantasy-land-d41c361d0dbe 。我知道有些库已经拥有所有这些 monad 和其他 FP 好东西,但是像这样实现它们是我尝试更深入地学习 TypeScript 的方式。

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);
};
Run Code Online (Sandbox Code Playgroud)

当我尝试运行测试断言(例如 )时right(4).map(isEven) === true,出现以下错误:

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.

我不明白为什么这个E类型在unknown这里或者我如何让它为人所知……或者这是否是尝试做的正确的事情。任何指导表示赞赏。

Dam*_*ero 2

问题就在这里:

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);
};
Run Code Online (Sandbox Code Playgroud)

这会起作用:

console.log(isEven(3))
console.log((new Right(4)).map(isEven))
Run Code Online (Sandbox Code Playgroud)

这也将起作用:

console.log(right(3))
Run Code Online (Sandbox Code Playgroud)

但是当你尝试这样做时:

right(3).map(isEven)
Run Code Online (Sandbox Code Playgroud)

编译器变得疯狂并且无法正确绑定类型。我认为这是语言的限制。只是不要使用常量并直接使用构造函数。