0

我正在尝试使用 fp-ts 验证将命令应用于表示 svg 路径数据的数组的操作。

type CommandValidation = (commands: CommandArray, nextCommand: Command) => option.Option<string>;

const newCommandValidations: Array<CommandValidation> = [
  validateFirstCommandIsMove,
  validateSymmetricCommandFollowsBezier,
  validateNoMoveAfterMove,
];

export const safelyPushCommand = (command: Command) => either.map((commands: CommandArray) => {
  // the errors don't overlap yet but in future errors may co-exist
  const validationErrors = fpFunction.pipe(newCommandValidations,
    fpArray.map((validation) => validation(commands, command)),
    fpArray.filter(option.isSome),
    fpArray.map(option.fold(() => undefined, (some) => some)));

  if (validationErrors.length > 0) {
    return either.left(validationErrors);
  }
  return either.right(pushCommands([command])(commands));
});

不幸的是,validationErrors 仍然被视为一个项目列表,其中可能没有。

如何获取表示此操作的任何验证错误的字符串数组(按此类输入)。

我应该使用“应用”函数将参数放入验证函数吗?

如何获得验证器的应用功能?

当第二个函数只是恒等函数时,有没有更好的方法来进行折叠操作?编辑:我在这个视频https://youtu.be/1LCqHnaJJtY?t=2470中回答了这个问题:option.fold(() => undefined, (some) => some) === option.getOrElse(() => undefined) === option.toUndefined

很多问题来自迷失在 fp-ts 丛林中的人,希望您能提供一些见解来帮助我的项目变得有用。示例代码包含在我在这里剪切的一个分支中:https ://github.com/justin-hackin/fp-ts-svg-path-d/tree/validators

4

1 回答 1

2

以下是对您提供的代码的一些改进,如果有必要,我很乐意在我收到对我关于一些不清楚的细节的评论的回复后进行更新。

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
    ),
  )
}
于 2021-07-01T14:20:49.667 回答