在Typescript中实现任一Monad

在Typescript中实现任一Monad,typescript,functional-programming,either,Typescript,Functional Programming,Either,我最近一直在尝试使用TypeScript,我正在尝试实现一些基本的monad。我已经有了一个合理的功能可能(至少与我下面的方法相同),但由于我不太理解的与类型相关的原因,两者都在逃避我 我对更高级类型的键入策略是从本文中借用的:。我知道有些库已经有了所有这些单子和其他FP好东西,但是像这样实现它们是我尝试更深入地学习TypeScript的方式 declare module './HKT' { interface TypeIDtoHKT2<E, A> { Either: E

我最近一直在尝试使用TypeScript,我正在尝试实现一些基本的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);
};
声明模块'/HKT'{
接口类型IDTOHKT2{
要么:要么,;
}
}
export const TypeID='eather';
导出类型TypeID=typeof TypeID;
出口类左{
只读:TypeID;
只读A!:E;
只读标签:“左”=“左”;
构造函数(只读左:E){}
静态的(e:e):或者{
返回新的左(e);
}
映射(f:(a:a)=>B):或者{
归还这个;
}
链(f:(a:a)=>任意一个):任意一个{
归还这个;
}
美联社(f:Orther B>):或者{
归还这个;
}
}
出口类权利{
只读:TypeID;
只读:A;
只读标签:'Right'='Right';
构造函数(只读权限:A){}
(a:a)的静态值:或{
返还新权利(a);
}
映射(f:(a:a)=>B):或者{
(f)(此项权利))的返还权;
}
链(f:(a:a)=>任意一个):任意一个{
返回f(此右);
}
美联社(f:Orther B>):或者{
if(f instanceof Left)返回f;
返回此.map(f.right);
}
}
导出类型或=左|右;
export const left=(e:e):任意一个=>{
返回新的左(e);
};
导出常量右=(a:a):任一=>{
返还新权利(a);
};
当我尝试运行测试断言时,例如
right(4).map(isEven)==true
,我得到以下错误:

无法调用其类型缺少调用签名的表达式。类型“((f:(a:number)=>B)=>other)|((f:(a:a)=>B)=>other)”没有兼容的呼叫签名。

我不明白为什么这里的
E
类型是
unknown
,或者我如何让它被知道……或者这是否是正确的做法。欢迎提供任何指导。

问题在于:

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);
};
这也将有助于:

console.log(right(3))
但当你尝试这样做时:

right(3).map(isEven)

编译器发疯了,无法正确绑定类型。我认为这是语言的局限性。不要使用constans,直接使用构造函数。

我在这里尝试了所有示例,效果很好。您使用的是Node和TS的哪个版本?@Damiánrafaellatenero我使用的是Node 12.6.0和TS 3.5.3。但更重要的是,如果我添加
right(4).map(isEven)
,它不会编译。请看,当我使用Javascript语法时,它并不是故意的,因为它被称为黑客攻击。使用TS接口伪造更高级的类型就是这种误用,我想你所经历的是依赖黑客的后果。@bob这是一个公平的观点。我同意这是一个笨拙的解决方案。你是说TS在这里不是一个好的语言选择,我应该回到JS中使用代数结构吗?或者你对这种结构有更好的TS实现建议吗?如果你真的想应用类型化的纯功能性习惯用法,那么你应该使用purescript,因为它是经过行业验证的。但这是一个艰难的步骤,尤其是当您来自命令式Javascript时。如果您坚持OOP风格,那么Typescript可能是一个不错的选择。
right(3).map(isEven)