【发布时间】:2022-12-10 12:19:44
【问题描述】:
在进行函数式编程时,我经常遇到这样的情况:我知道一些语言的类型系统不知道的东西。考虑以下解析 UUID 并向用户显示嵌入字段的 TypeScript 示例。该程序首先使用 io-ts 验证它的输入,以确保输入遵循 UUID 规范。后来,在拆分输入后,程序无法验证拆分后的 UUID 是否包含五个部分,这给我留下了fp-tsOption。它从getOrElse 中抛出一个assert false 来摆脱Option。函数式编程是否有一些更惯用的方法来处理断言?向最终用户报告错误并没有帮助,因为这种情况是程序员基本假设的错误,而不是最终用户可以解决的问题。
#!/usr/bin/env ts-node
import { append, intersperse, map, prepend } from 'fp-ts/lib/Array';
import { isRight } from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { IO } from 'fp-ts/lib/IO';
import { fromPredicate, getOrElse } from 'fp-ts/lib/Option';
import { empty } from 'fp-ts/lib/string';
import * as t from 'io-ts';
type Tuple5<A, B, C, D, E> = [A, B, C, D, E];
const length = 5;
const fromArray = fromPredicate(
<A>(as: Array<A>): as is Tuple5<A, A, A, A, A> => as.length === length,
);
const Tuple5_ = {
length,
fromArray,
};
const separator = '-';
const hex = (n: number): string => `[A-Fa-f0-9]{${n}}`;
const fields: Tuple5<number, number, number, number, number> = [8, 4, 4, 4, 12];
const regexp = pipe(
fields,
map(hex),
intersperse(separator),
prepend('^'),
append('$'),
).join(empty);
export type Uuid = t.Branded<string, UuidBrand>;
export type UuidC = t.BrandC<t.StringC, UuidBrand>;
export const Uuid: UuidC = t.brand(
t.string,
(x): x is t.Branded<string, UuidBrand> => x.match(RegExp(regexp)) !== null,
'Uuid',
);
export type UuidBrand = {
readonly Uuid: unique symbol;
};
export type TimeLow = string;
export type TimeMid = string;
export type TimeHiAndVersion = string;
export type ClockSeq = string;
export type Node = string;
export type Groups = Tuple5<TimeLow, TimeMid, TimeHiAndVersion, ClockSeq, Node>;
export const groups = (uuid: Uuid): Groups =>
pipe(
uuid.split(separator),
Tuple5_.fromArray,
getOrElse((): Groups => {
// eslint-disable-next-line
throw new Error('Assert false! Uuid invalid despite validation.');
}),
);
const main: IO<void> = () => {
const [_node, _script, input] = process.argv;
const result = Uuid.decode(input);
if (isRight(result)) {
const uuid: Uuid = result.right;
const [timeLow, timeMid, timeHiAndVersion, clockSeq, node] = groups(uuid);
console.log({ timeLow, timeMid, timeHiAndVersion, clockSeq, node });
} else {
console.error('Invalid input!');
}
};
main();
【问题讨论】:
-
“拆分输入后,程序无法验证拆分后的 UUID 是否包含五个部分”这是为什么?我不会试图去理解 TypeScript 的那堵墙(一种我只了解基本知识的语言),但是基于那句话,五元组不能完成这项工作吗?
-
您可能会发现 Alexis King 的 Parse, don't validate 很有启发性。
-
您可以编写自己的函数来拆分 UUID,该 UUID 具有您需要的内置假设。类似于
(uuid: Uuid) => Tuple5。如果出现问题,您可能会在函数中抛出错误,但您甚至不需要这样做,因为 Uuid 类型基本上可以保证您拥有正确的格式。虽然它会要求你使用我想象的类型断言
标签: typescript functional-programming assert fp-ts