昨天,我和几位同事试图在fp-ts中获得一个应用验证的玩具示例。我们最终通过手动将每个临时步骤存储在一个变量中并调用下一个步骤来实现它。但使用fp-t的管道函数会更优雅。使用"任一"直接执行此操作,但不会将多个Left值合并为一个值(例如,将具有字符串错误的数组连接在一起(。但是对于pipe((,ap((-调用需要两个参数,但只能得到一个。我们如何在这里正确使用管道:
import * as E from "fp-ts/lib/Either";
import * as RA from "fp-ts/lib/ReadonlyArray";
import { pipe } from "fp-ts/lib/function";
export class CreditCard {
constructor(
public readonly number: string,
public readonly expiry: string,
public readonly cvv: string
) { }
}
export const validate = (
card: CreditCard
): E.Either<ReadonlyArray<string>, CreditCard> => {
const createCreditCard = (a: string) => (b: string) => (c: string) =>
new CreditCard(a, b, c);
const v1 = (s: string): E.Either<ReadonlyArray<string>, string> => {
return s !== "invalid" ? E.right(s) : E.left(RA.of("invalid number"));
};
const v2 = (s: string): E.Either<ReadonlyArray<string>, string> => {
return s !== "invalid" ? E.right(s) : E.left(RA.of("invalid expiry"));
};
const v3 = (s: string): E.Either<ReadonlyArray<string>, string> => {
return s !== "invalid" ? E.right(s) : E.left(RA.of("invalid cvv"));
};
const V = E.getApplicativeValidation((RA.getSemigroup<string>()));
// this does not work, because V.ap wants 2 arguments but only has 1?
// const fromPipe = pipe(
// V.of(createCreditCard),
// V.ap(v1(card.number)),
// V.ap(v2(card.expiry)),
// V.ap(v3(card.cvv))
// );
// return fromPipe;
// this works, but is ugly
const liftedFunction = V.of(createCreditCard);
const afterFirstValidation = V.ap(liftedFunction, v1(card.number));
const afterSecondValidation = V.ap(afterFirstValidation, v2(card.expiry));
const afterThirdValidation = V.ap(afterSecondValidation, v3(card.cvv));
return afterThirdValidation;
};
Either.getApplicativeValidation
返回Applicative2C
的一个实例,该实例具有类方法的不可管道版本。目前,获得使用组合子(如getApplicativeValidation
(计算的实例的可管道版本的方法是将实例从pipeable.ts
模块传递给pipeable
组合子。
因此,将您的代码更改为:
const validation = E.getApplicativeValidation(RA.getSemigroup<string>())
const V = pipeable(validation)
const fromPipe = pipe(
validation.of(createCreditCard),
V.ap(v1(card.number)),
V.ap(v2(card.expiry)),
V.ap(v3(card.cvv))
)
你应该发现它能按你的意愿工作。
然而,我相信在fp-ts
v3.x.x中,类型类接口将在默认情况下更改为全部可管道化,因此对pipeable
的需求将消失。