Выбор полей из объекта с определенным RecordType с Sanctuary

У меня есть объект с параметрами, который соответствует следующему типу записи:

const AwsRegionsEnum = $.EnumType(
  'AWS/Regions',
  'http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html',
  [
    'us-east-1',
    'us-east-2',
    'us-west-1',
    'us-west-2',
    'ca-central-1',
    'eu-west-1',
    'eu-central-1',
    'eu-west-2',
    'ap-northeast-1',
    'ap-northeast-2',
    'ap-southeast-1',
    'ap-southeast-2',
    'ap-south-1',
    'sa-east-1',
  ]
);
const Credentials = $.RecordType({
  accessKeyId: $.String,
  secretAccessKey: $.String,
  region: AwsRegionsEnum,
});

const PullOpts = $.RecordType({
  waitTimeSeconds: S.MaybeType($.Number),
  maxNumberOfMessages: S.MaybeType($.Number),
  credentials: Credentials,
});

И я хочу создать функцию, которая выбирает параметры из таких записей, как R.pick из рамды библиотеки. Но я хочу напечатать список полей для выбора. Этот список может содержать только поля, которые действительны для записи типа PullOpts,

Ожидаемое поведение для функции:

// pickOpts :: List(<some_type_for_validate_options>) -> PullOpts -> <constructed_type_for_return>
const pickOpts = (pickingOpts, allOpts) => {};

Резюме:

  1. Как я могу написать введите мои аргументы функции правильно (<some_type_for_validate_options> а также<constructed_type_for_return>)?

  2. Как я могу написать тело функции, используя композиции функций Sanctuary?

Спасибо за любую помощь:)

1 ответ

Решение

В Sanctuary обычно нет необходимости выбирать определенные поля из записи. Рассмотрим этот модуль:

const $ = require('sanctuary-def');
const types = require('./types');

const def = $.create({checkTypes: true, env: $.env});

//    User :: Type
const User = $.RecordType({
  id: types.UUID,
  username: $.NonEmpty($.String),
  email: types.Email,
});

//    MinimalUser :: Type
const MinimalUser = $.RecordType({
  id: types.UUID,
  username: $.NonEmpty($.String),
});

//    toMinimalUser :: User -> MinimalUser
const toMinimalUser = def(
  'toMinimalUser',
  {},
  [User, MinimalUser],
  user => ???
);

Какова реализация, которая должна появиться вместо ???, Ответ может быть удивительным: мы просто возвращаемся user, Это потому, что каждый член User тип также является членом MinimalUser тип. Запись может содержать дополнительные поля. Это позволяет определять функции с такими типами, как id :: { id :: UUID } -> UUID которые не ограничены определенным типом (User, Invoiceили что угодно).

Так, toMinimalUser может быть ненужным. Так как каждый член User тип также является членом MinimalUser типа, мы можем передать User/MinimalUser к функции, которая ожидает MinimalUser,

Хотя система типов не потребует от нас "убирать" поля, мы можем захотеть сделать это по соображениям конфиденциальности. Рассматривать toJson :: User -> String, Если мы создаем ответ API от User значение, которое мы можем, чтобы исключить email поле по соображениям конфиденциальности. В этом случае я бы предложил эту реализацию:

//    toJson :: User -> String
const toJson = def(
  'toJson',
  {},
  [User, $.String],
  ({id, username}) => JSON.stringify({id, username})
);

Это более многословно, чем R.pick эквивалентно, но это цена, которую мы должны заплатить, чтобы иметь дело с записями более дисциплинированным образом, чем того требует JavaScript.

Я могу представить, что однажды мы добавим такую ​​функцию в Sanctuary:

S.pick :: Set String -> StrMap a -> StrMap a

Я не могу представить, однако, подобную функцию, работающую с произвольными записями, так как мы были бы вынуждены использовать свободный тип, такой как Set String -> Object -> Object, При работе с записями в Sanctuary я предпочитаю писать код, который немного более многословен, но дает более сильные гарантии.

Другие вопросы по тегам