我有一些代码可以处理一些功能性的 javascript 概念,例如 Brian Lonsdorf 在 this series 中提出的 Eithers。 .
我一直在尝试输入 Either 表达式。这是我的尝试:
type RightApi<X> = {
map: <C: *>(f: X => C) => RightApi<C>,
fold: <C: *>(f: Function, g: X => C) => C
};
const Right = <X: *>(x: X): RightApi<X> => ({
map: f => Right(f(x)),
fold: (f, g) => g(x),
})
type LeftApi<X> = {
map: (f: Function) => LeftApi<X>,
fold: <C: *>(f: X => C, g: Function) => C,
};
const Left = <X: *>(x: X): LeftApi<X> => ({
map: f => Left(x),
fold: (f, g) => f(x),
});
const fromMayBeNullOrUndefined = <X: *>(x: X) =>
((x === null || x === undefined) ? Left(x) : Right(x));
const test = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
// null | number
不幸的是,这并没有让我达到我想要的程度。在最后一行,我尝试测试打字。从逻辑上讲,我很清楚 test
将是一个 number
。因为提供给fromMayBeNullOrUndefined
的值是3
,即不为null或undefined,所以调用fold
时,应该调用右边的函数.
我是不是弄错了?还是flow无法更精确地推断出结果?
最佳答案
您提到的问题可以通过为 fromMaybeOrUndefined
函数定义不同的类型签名来解决,如下所示:
declare function fromMayBeNullOrUndefined(x: null | void): LeftApi<null | void>;
declare function fromMayBeNullOrUndefined<X>(x: X): RightApi<X>;
function fromMayBeNullOrUndefined<X>(x: X) {
if (x === null || x === undefined) {
return Left(x);
}
return Right(x);
}
现在测试正确的类型检查:
const test: null = fromMayBeNullOrUndefined(null).fold(x => null, x => x);
const test: number = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
但是虽然这是一个有趣的练习,但它完全忽略了Either
类型的要点:
Either
的要点是 Left
和 Right
具有相同的 API,因此在运行时底层值(或通常称为数据)可以是它们的之一。值是 Left
还是 Right
是透明的。这很重要,因为当存在某种可能产生两个结果的外部影响时,您将使用 Either
类型(或任何类型的 Monad)。例如,您想要连接到数据库 - 连接可能被创建或失败。或者在您的情况下,某些 API 的用户可能会提供一个数字或一个空值。
/* @flow */
type Either<L, R> = {
map: <T>(f: R => T) => Either<L, T>,
fold: <T>(f: L => T, g: R => T) => T
};
const Right = <R>(x: R): Either<any, R> => ({
map: <T>(f: R => T): Either<any, T> => Right(f(x)),
fold: <T>(f: any => T, g: R => T): T => g(x),
})
const Left = <L>(x: L): Either<L, any> => ({
map: <T>(f: any => T): Either<any, T> => Left(x),
fold: <T>(f: L => T, g: any => T): T => f(x),
});
const fromMayBeNullOrUndefined = <X>(x: X): Either<null | void, X> =>
((x === null || x === undefined) ? Left(x) : Right(x));
const test = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
(test: null | number)
使用 Either
,您可以继续在“快乐路径”(右侧)中编码,直到您想要将执行路径折叠起来。示例:
function getAccountBalance(name: string): string {
const account: Either<Error, Account> = getAccountByCustomerName(name);
return account
.map(account => account.balance)
.fold(
() => "Seems like you don't have an account at our service",
balance => `Your balance is ${balance}`,
);
}
如下一个视频讲座中所述,当发生多个操作时,此示例将变得更加有趣。
关于javascript - 为什么流无法在这个函数式 Either 示例中更准确地确定类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52191899/