Перемещение обработки undefined к краям стека

Распространенная проблема со многими языками, но особенно с JavaScript, - undefined. Вы должны постоянно проверять, существует ли что-то, каждый раз, когда вы взаимодействуете с этим, иначе вы получите ошибки. Flow и другие системы набора текста делают это немного проще, напоминая вам, что что-то может быть неопределенным. Но вам все равно придется везде применять проверки. Именно здесь некоторые функциональные языки, такие как Scala или Haskell, используют нечто, называемое Maybe. И в AnyJunk мы применяем ту же философию к интерфейсу.

Сначала я объясню некоторые особенности Maybe и то, как он работает. Если вы сталкивались или использовали Maybe раньше, то этот следующий раздел может быть для вас менее интересным, но я рекомендую вам прочитать следующие части, где я представлю его в React- Redux.

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

A Maybe имеет два подтипа: None и Some. Я считаю, что проще всего думать об этом как о пустом списке и о списке, содержащем один элемент соответственно:

  • None =[]
  • Some(x) = [x]

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

Отправной точкой обычно является некоторый вспомогательный метод, который создает Maybe из другого типа, который может иметь значение NULL. В нашем веб-приложении мы используем monet, у которого есть метод Maybe.fromNull, который принимает значение, которое может быть null или undefined; он возвращает:

  • Нет, если значение не определено или равно null
  • Некоторое (значение), если значение определено.

(В нашем мобильном приложении мы вместо этого используем folktale, у которого есть аналогичный метод: Maybe.fromNullable.)

Чтобы выполнить операцию над значением внутри Maybe, вы можете вызвать для него .map точно так же, как если бы вы изменили значение внутри списка. Если у вас есть значение, оно изменится, если у вас нет значения, все по-прежнему работает, но ничего не меняется.

Если вы хотите фактически отобразить значение, вам нужно будет деконструировать Maybe и получить значение, если оно у вас есть. Если у вас нет ценности, вам нужно будет предложить какую-то альтернативу. В monet для этого используется метод orSome. Чтобы использовать это, вы должны сделать что-нибудь в форме: maybeNumber.orSome(0)

Эти три метода (fromNull, map, orSome) вместе означают, что вы создаете Maybe на одном краю вашего стека и разрешите его на другом крае, передав его как Maybe и используя .map в промежуточных частях. Это может означать принятие значения, которое ваш API может или не может предоставить, и использование Maybe.fromNull, чтобы обернуть его, передать его вашему компоненту и затем вызвать .orSome для решите, что отображать в качестве альтернативы, если у вас ее нет. Это все еще может быть .orSome (null), но, что важно, вам нужно только решить это в самом конце, до этого ничего не нужно знать, какова альтернатива.

Еще одна полезная вещь, которую дают вам Maybe, - это flatMap ( также известная как цепочка в сказке). Это действует как map, но позволяет объединить два объекта Maybe и возвращает None, если любой из них None. и Некоторые, только если они оба Некоторые. Например:

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

Если вы знакомы с нотацией flow для типов, их типы следующие:

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

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

Если вы используете неизменяемый для своего состояния redux, тогда вы знаете, что когда вы используете get или getIn, вы можете не рассчитывайте на получение ожидаемой ценности, даже если вам поможет flow. У нас есть помощник для этого, называемый safeGet, который похож на getIn, за исключением того, что он возвращает Maybe, содержащий значение.

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

Часть getter lens будет использовать safeGet, чтобы дать вам Maybe неизменяемого объекта, а затем вы используете map, чтобы применить маршаллер к этому объекту и получить Maybe вашей модели представления.

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

Конечно, если адрес здесь необязательный, просто используйте map вместо flatMap.

Форма решает, как обрабатывать случай None, в зависимости от варианта использования:

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

Кроме того, есть определенные части вашей модели, которые могут быть необязательными, и Может быть может помочь вам справиться с ними, особенно если у вас есть производное поле, которое зависит от нескольких дополнительных полей, как в приведенном выше примере (полного имя, зависящее от имени и фамилии).

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

Как бы то ни было, вам всегда нужно использовать илиSome в качестве последнего действия, которое вы делаете со своим Maybe. Возможно обычно работают лучше всего, если вы избегаете использования илиSome как можно дольше.

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

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