【发布时间】:2018-06-22 12:38:58
【问题描述】:
我已经实现了一个几乎可以工作的 Result monad :)
class Result<T> {
private readonly value: T;
private readonly error: Error;
constructor(value?: T, error?: Error) {
if (value) {
this.value = value;
}
if (error) {
this.error = error;
}
}
static ok<T>(ok: T): Result<T> {
return new Result<T>(ok);
}
static error<T>(err: Error): Result<T> {
return new Result<T>(undefined, err);
}
isOk(): boolean {
return this.value !== undefined;
}
isError(): boolean {
return this.error !== undefined;
}
chain<U>(f: (t: T) => Result<U>): Result<U> {
return this.isOk() ?
f(this.value) :
Result.error<U>(this.error);
}
map<U>(f: (t: T) => U): Result<U> {
return this.isOk() ?
Result.ok<U>(f(this.value)) :
Result.error<U>(this.error);
}
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error);
}
getError(): Error {
return this.error;
}
getValue(): T {
return this.value;
}
}
这就是你如何使用它:
const r1 = Result.ok(76)
.concat(Result.ok('ddd'))
.concat(Result.ok(true))
.map((i: (number | string | boolean)[]) => i);
在这里,我连接了三个结果:一个数字、一个字符串和一个布尔值。
之后,我想映射它们并应用转换方法。
.map 方法应该采用类型化的 (number | string | boolean)[] 作为参数。
我知道问题出在哪里,但我现在知道如何解决它。
问题在于 .concat 方法:
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
return this.isOk() ?
Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
Result.error(this.error);
}
每次发生连接时都会调用此方法,如果您愿意,可以“递归地”调用。
所以,就我而言,这就是流程:
第一个 concat - U 是字符串 'ddd' 而 T 是 76 数字.该方法返回一个 (number | string)[]
second concat - U 已经是 (number | string)[] 并且需要一个布尔值。 Typescript 不返回 (number | string | boolean)[],而是返回 (boolean | (number | string)[])[]
我可以让 concat 方法运行并接受 (string | number | boolean)[] 签名吗?
有什么想法吗?
【问题讨论】:
-
这并不完全是一个答案,但我几乎总是将 concat 实现为一个单独的函数而不是实例方法。它有这种类型:(ms: [M, M, M
?, ...]) => M 然后,您可以链接到数据类型并返回将多个事物连接在一起的结果,而不是使用 concat 实例方法。它倾向于很好地推断类型,并允许您在想要映射 concat 的结果时使用数组解构。缺点是您必须硬编码最多 n 个可选类型参数才能在 n 个项目的列表上调用 concat。
标签: typescript generics concatenation monads