Как лучше всего повторять ключи и значения записей в Reasonml?

Я новичок в ReasonML, но прочитал большинство официальных документов. Для этого я мог бы пройти через случайные пробы и ошибки, но поскольку мне нужно писать коды в ReasonML прямо сейчас, я хотел бы знать лучшие практики итерации ключей и значений типов записи причины.


person Sanghyun Kim    schedule 24.09.2020    source источник


Ответы (2)


Я полностью согласен с @Shawn, что вам следует использовать более подходящую структуру данных. Например, список кортежей - это хороший и простой способ передать определенный пользователем набор однородных пар ключ / значение:

fooOnThis([
  ("test1", ["a", "b", "c"]),
  ("test2", ["c"]),
])

Если вам нужны разнородные данные, я бы предложил использовать вариант для указания типа данных:

type data =
  | String(string)
  | KvPairs(list((string, data)));

fooOnThis([
  ("test1", [String("a"), String("b"), String("c")]),
  ("test2", [String("c"), KvPairs([("innerTest", "d")])]),
])

В качестве альтернативы вы можете использовать объекты вместо записей, что похоже на то, что вы действительно хотите.

Для записи: запись требует предварительно определенного типа записи:

type record = {
  foo: int,
  bar: string,
};

и вот как вы их строите:

let value = {
  foo: 42,
  bar: "baz",
};

объекты, с другой стороны, структурно типизированы, что означает, что они не требуют предопределенный тип, и вы создаете их немного иначе:

let value
  : {. "foo": int, "bar": string }
  = {"foo": 42, "bar": "baz"};

Обратите внимание, что ключи - это строки.

С объектами вы можете использовать Js.Obj.keys для получения ключей. :

let keys = Js.Obj.keys(value); // returns [|"foo", "bar"|]

Теперь проблема заключается в получении значений. Не существует Js.Obj API для получения значений или записей, потому что это было бы либо ненадежно, либо очень непрактично. Чтобы продемонстрировать это, попробуем сделать это сами.

Мы легко можем написать собственную привязку к Object.entries:

[@bs.val] external entries: Js.t({..}) => array((string, _)) = "Object.entries";

entries это функция, которая принимает любой объект и возвращает массив кортежей с string ключами и значениями типа, который будет выведен в зависимости от того, как мы их используем. Это небезопасно, потому что мы не знаем, каковы фактические типы значений, и не особенно практично, поскольку они будут однородно типизированы. Например:

let fields = entries({"foo": 42, "bar": "baz"});

// This will infer the value's type as an `int`
switch (fields) {
| [|("foo", value), _|] => value + 2
| _ => 0
};

// This will infer the value's type as an `string`, and yield a type error
// because `fields` can't be typed to hold both `int`s and `string`s
switch (fields) {
| [|("foo", value), _|] => value ++ "2"
| _ => ""
};

Вы можете использовать любое из этих switch выражений (с неожиданными результатами и возможными сбоями во время выполнения), но не оба вместе, поскольку в Reason нет распакованного string | int типа.

Чтобы обойти это, мы можем сделать значение абстрактным типом и использовать Js.Types.classify, чтобы безопасно получить фактический базовый тип данных, аналогично использованию typeof в JavaScript:

type value;

[@bs.val] external entries: Js.t({..}) => array((string, value)) = "Object.entries";

let fields = entries({"foo": 42, "bar": "baz"});

switch (fields) {
| [|("foo", value), _|] =>
  switch (Js.Types.classify(value)) {
  | JSString(str) => str
  | JSNumber(number) => Js.Float.toString(number)
  | _ => "unknown"
  }
| _ => "unknown"
};

Это совершенно безопасно, но, как видите, не очень практично.

Наконец, мы можем немного изменить это, чтобы безопасно использовать и с записями, полагаясь на тот факт, что записи внутренне представлены как объекты JavaScript. Все, что нам нужно сделать, это не ограничивать entries объектами:

[@bs.val] external entries: 'a => array((string, value)) = "Object.entries";

let fields = keys({foo: 42, bar: 24}); // returns [|("foo", 42), ("bar", 24)|]

Это по-прежнему безопасно, потому что все значения являются объектами в JavaScript, и мы не делаем никаких предположений о типах значений. Если мы попытаемся использовать это с примитивным типом, мы просто получим пустой массив, а если мы попытаемся использовать его с массивом, мы получим индексы как ключи.

Но поскольку записи должны быть предварительно определены, это не будет очень полезно. Итак, все это говорит о том, что я все же предлагаю перейти к списку кортежей.

Примечание: здесь используется синтаксис ReasonML, поскольку это то, что вы просили, но относится к документации ReScript, в которой используется немного другой синтаксис ReScript, поскольку документация BuckleScript была удалена (да, сейчас беспорядок, я знаю. Надеюсь, со временем это улучшится.)

person glennsl    schedule 24.09.2020
comment
вау, это было здорово. В итоге я использовал запись и повторил с помощью Belt.map, чтобы преобразовать исходные данные в соответствующий компонент реакции. большая помощь! - person Sanghyun Kim; 25.09.2020

Возможно, я не понимаю вопрос или вариант использования. Но, насколько мне известно, нет возможности перебирать пары ключ / значение в записи. Вы можете использовать другую модель данных:

С помощью записи известны все ключи и типы значений, поэтому вы можете просто написать код для обработки каждого из них, без необходимости повторения.

person Shawn    schedule 24.09.2020
comment
Спасибо за ответ. Что я хотел сделать, так это перебрать тип данных, подобный json, для создания реагирующих компонентов. Я могу использовать сопоставление типов с помощью switch, но хотел разрешить это с помощью функций, подобных параметрам map или obj.keys. Можете ли вы дать еще немного совета по этому поводу? - person Sanghyun Kim; 24.09.2020
comment
Например, учитывая данные {test1: [a, b, c], test2: [c: {innerTest: d}]}, я хотел бы прочитать ключи и их соответствующие значения, которые будут использоваться в каждом из компонент React (дочерние элементы элемента ‹ul›). - person Sanghyun Kim; 24.09.2020
comment
Я не так хорошо знаком с API Bucklescript, но там вам доступны специальные функции javascript. Я думаю, Js.Dict.t (я разместил ссылку на документы API выше) будет делать то, что вы хотите. Он имеет функцию keys, которая перечисляет все ключи, и функцию get, чтобы получить значение определенного ключа. - person Shawn; 24.09.2020