#typescript #functional-programming #fp-ts
#typescript #функциональное программирование #fp-ts
Вопрос:
Допустим, у меня есть следующие типы:
type ValidatedInput = Readonly<{
id: string
}>
type GetStructures = (id: string) => TaskEither<ServiceError, ReadonlyArray<SomeStructure>>
type GetOtherStructures = (ids: ReadonlyArray<string>) => TaskEither<ServiceError, ReadonlyArray<SomeOtherStructure>>
type LookupThings = (initialInput: ReadonlyArray<SomeStructure>, additionalInput: ReadonlyArray<SomeOtherStructure>) => TaskEither<ServiceError, ReadonlyArray<Option<ResponseType>>>
export type Deps = Readonly<{
getStructures: GetStructures
getOtherStructures: GetOtherStructures
lookupThings: LookupThings
}>
export type Ctx<T> = ReaderTaskEither<Deps, Error, T>
И следующий помощник по обработке ошибок:
type Code = 'INVALID_ENTITY' | 'INVALID_API_KEY'
export const fromCode = (code: Code): Error => ({
tag: 'validation',
code
})
Затем я создаю функцию следующим образом:
const constructFullData = (input: ValidatedInput): Ctx<ReadonlyArray<Option<ResponseType>>> => (
(deps: Deps): TE.TaskEither<Error, ReadonlyArray<Option<ResponseType>>> => (
pipe(
deps.getStructures(input.id),
TE.map((structs) => pipe(
deps.getOtherStructures([structs[0].higherOrderId]),
TE.chain(otherStructs => pipe(
deps.lookupThings(structs, otherStructs),
TE.chain(results => {
if (results.filter(isNone).length !== results.length) {
return TE.left(fromCode('INVALID_ENTITY')) // I'm not sure what a better way to filter this is. Ideally the return type of this function wouldn't have an Option in it
} else {
return TE.right(results)
}
})
))
)),
TE.flatten
)
)
)
Все это компилируется просто отлично, но чего я действительно хочу, так это вернуть массив с отфильтрованным none и вызвать соответствующую ошибку, если нет none!
Наивный подход заключается в изменении возврата правильного пути на:
return TE.right(results.map(u => u.value))
Но это не компилируется, выдавая следующую ошибку:
Property 'value' does not exist on type 'None'.
64 return TE.right(usagesWithDrones.map(u => u.value))
Как можно применить такую фильтрацию?
Комментарии:
1. Поправьте меня, если я ошибаюсь, но «что я действительно хочу, так это вернуть массив, в котором ни один не отфильтрован, и вызвать соответствующую ошибку, если есть none» означает, что вы хотите вернуть исходный массив (если они все некоторые) или вернуть ошибку, если естьни одного. Для этого
fp-ts/Array
имеетsequence
: предполагая, что x равноOption<number>[]
, тогдаArray.sequence(option)(x)
будет выданоSome<number[]>
, если все они являются некоторыми илиNone
если даже один из них равен None .
Ответ №1:
Как насчет сворачивания списка параметров в either? Что-то вроде:
import * as TE from "fp-ts/TaskEither";
import * as ROA from "fp-ts/ReadonlyArray";
import * as T from "fp-ts/Task";
import * as E from "fp-ts/Either";
import * as O from "fp-ts/Option";
import { pipe } from "fp-ts/pipeable";
import { constant, flow, Lazy } from "fp-ts/lib/function";
const value = TE.right<"ThisErrorDoesntExist", readonly O.Option<number>[]>(
ROA.of(O.some(42))
);
const arrayOfNumbersMonoid = ROA.getMonoid<number>();
type Result = E.Either<"FoundNoneInList", readonly number[]>;
const rightEmptyListOfNumbers: Result = E.right([]);
const leftFoundNoneInList: Lazy<Result> = constant(E.left("FoundNoneInList"));
const reducer = (acc: typeof rightEmptyListOfNumbers, next: O.Option<number>) =>
pipe(
acc,
E.chain((numbers) =>
pipe(
next,
O.fold(
leftFoundNoneInList,
flow(ROA.of, (n) => arrayOfNumbersMonoid.concat(numbers, n), E.right)
)
)
)
);
const result = pipe(
value,
T.map(E.chainW(ROA.reduce(rightEmptyListOfNumbers, reducer)))
);
Ответ №2:
Самый простой способ удалить параметры из массива — использовать функцию compact (см. Простой пример ниже). Функция compact присутствует в большинстве библиотек fp-ts.
import { Option, some, none } from "fp-ts/lib/Option"
import { compact } from "fp-ts/lib/Array"
const initialArray = [1, 2, 3, 4, 5, 6, 7]
const arrayWithOptions: Option<number>[] = initialArray.map((el) => {
if (el % 2 === 0) return some(el)
else return none
})
const finalArray: number[] = compact(arrayWithOptions)
console.log(finalArray)
В журнале консоли будет напечатано [2,4,6].