以下是对您提供的代码的一些改进,如果有必要,我很乐意在我收到对我关于一些不清楚的细节的评论的回复后进行更新。
import * as either from 'fp-ts/lib/Either'
import * as fpFunction from 'fp-ts/lib/function'
import * as option from 'fp-ts/lib/Option'
import * as fpArray from 'fp-ts/lib/Array'
// Didn't know what these types should be so I kept them as basic as possible
// to allow me to compile the code
type CommandArray = unknown[];
type Command = unknown;
type CommandValidation = (commands: CommandArray, nextCommand: Command) => option.Option<string>;
// Declaring these consts here just to allow me to write the code below
declare const validateFirstCommandIsMove: CommandValidation
declare const validateSymmetricCommandFollowsBezier: CommandValidation
declare const validateNoMoveAfterMove: CommandValidation
const newCommandValidations: Array<CommandValidation> = [
validateFirstCommandIsMove,
validateSymmetricCommandFollowsBezier,
validateNoMoveAfterMove,
];
export const safelyPushCommand = (command: Command) => (commands: CommandArray) => {
return fpFunction.pipe(
newCommandValidations,
fpArray.map((validation) => validation(commands, command)),
option.sequenceArray, // Converts Array<Option<T>> -> Option<Array<T>>
option.fold(
// if we have a None, it means no validations returns an validation error message, so we succeed
() => either.right(pushCommands([command])(commands)),
// if we have a Some, we fail with the validation errors
(validationErrors) => either.left(validationErrors)
)
);
};
以上只是我试图调整您的代码以使其或多或少地工作。但是,代码不是很地道。
Option
首先, is的约定用于None
指示某种失败或缺少值。在您的代码中,您使用None
表示验证通过但 aSome
表示验证失败,这感觉有点违反直觉。我建议改用一个Either
,其中左侧用于保存验证错误。
由于Either
设计为在第一次故障后短路但您想收集所有验证错误,因此我们需要另一种技术。关于这个主题有一篇很好的文章-> https://dev.to/gcanti/getting-started-with-fp-ts-either-vs-validation-5eja
下面的解决方案是一个小示例,使用either.getApplicativeValidation
(either.getValidation 现在已弃用,因此上面链接的文章有点过时)允许我们运行所有 3 个验证函数并收集NonEmptyArray<string>
.
import { sequenceT } from 'fp-ts/lib/Apply'
import { getSemigroup, NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'
declare const validateFirstCommandIsMove1: (command: Command) => either.Either<NonEmptyArray<string>, Command>;
declare const validateSymmetricCommandFollowsBezier1: (command: Command) => either.Either<NonEmptyArray<string>, Command>;
declare const validateNoMoveAfterMove1: (command: Command) => either.Either<NonEmptyArray<string>, Command>;
const runValidations = sequenceT(either.getApplicativeValidation(getSemigroup<string>()))
const validateCommand = (command: Command): either.Either<NonEmptyArray<string>, string> => {
return fpFunction.pipe(
command,
runValidations(
validateFirstCommandIsMove1,
validateSymmetricCommandFollowsBezier1,
validateNoMoveAfterMove1
),
)
}