Функціональний TypeScript: fp-ts Either

Поделиться
HTML-код
  • Опубликовано: 25 янв 2025

Комментарии • 6

  • @dimitro.cardellini
    @dimitro.cardellini Год назад

    const input = "...";
    const { search } = new URL(input);
    const { lang = "UK" } = queryStringToMap(search);
    тепер ми маємо той самий код, що і в прикладі на 21:15
    Знову виникає питання: нащо ж нам тоді Either?

    • @AleksandrSugak
      @AleksandrSugak  Год назад

      якщо ціль - написати якомого менше рядочків коду, то мабудь Either не варто використовувати :)

    • @dimitro.cardellini
      @dimitro.cardellini Год назад

      ​@@AleksandrSugak ну, справа не в кількості рядочків, а справа в складності коду. Який сенс писати код складніше, якщо можна -- простіше.
      За кількістю рядків все може бути один в один.

  • @dimitro.cardellini
    @dimitro.cardellini Год назад

    Головна міць Either в тому, що вона дозволяє типізувати помилки.
    Беремо той самий приклад, але тепер уявімо, що ми маємо відповисти не Error, якщо у нас невалідний урл, або мова не в переліку дозволених, а треба повернути щось типу такого:
    { errorCode: "EINVALID_INPUT" | "EINVALID_LANG" }
    тоді використання Either стає цілком виправданним:
    Бо зараз фінальний приклад виглядатиме простіше:
    const validLangueges = ["EN", "DE", "UK"];
    type Lang = typeof validLanguages[number];
    const isValidLang = (value: any): value is Lang => validLangueges.includes(value);
    const input = "...";
    const { search } = new URL(input);
    const { lang = "UK" } = queryStringToMap(search);
    if (!isValidLang(lang)) {
    throw new Error(`${lang} is not a valid language`);
    }
    Можна посперичатися, що підтримувати складніше, цей код, або той конвейєр.

    • @AleksandrSugak
      @AleksandrSugak  Год назад

      Типізувати помилки можна і без Either. Головна міць Either (як на мене) це те що код який повертає Either примушує програміста явно оброблювати помилки. В той час як рантайм помилки не можна побачити з сігнатури функції. Будь-який код може кинути помилку, так само як будь-яке значення може бути null або undefined. Тому помилки часто залишаються необроблені, і ми часто маємо помилки cannot read property of undefined. Either та Option дозволяють працювати з помилками та відсутністю значень явно.

    • @dimitro.cardellini
      @dimitro.cardellini Год назад

      ​@@AleksandrSugak так, можна типізувати помилки за допомогою Discriminated Union. Так Either -- це і є Discriminated Union.
      Що таке Either? Either -- це функтор, який накладає на тип ефект альтернативи, при чому обидві альтернативи чітко типізовані, в наслідок чого, ми можемо включити помилки до контракту, написавши ось такий код:
      type LanguageProvider = (url: string) => Either;
      А в другу чергу, це монада, що дозволяє нам з'єднувати стрілки типу: a -> Either, тобто поєднувати обчислення абстрагувавшись від ефекту.
      Монада -- це Засіб, а не Мета. А ось ефект альтернативи -- це і є Мета)
      До речі, механізм Exception -- цілком можна розглядати, як реалізацію монади Either в імперативній мові програмування. Але типізацію для цього механізма в TS не завезли, а потім і в Проміси так само не завезли. То ж, ми і терпимо ці "борошна".
      Щодо опрацювання помилок... -- та ніхто нікого ні до чого не примушує. І приклад у відео -- яскравий тому приклад: помилка Error -- майже те саме, що any (хоч message читай), і ми її викадємо на прикинці, так само, як і Exception.
      Я теж довго вважав, що цей пайпінг/чейнінг -- то є норм. Поки не дійшов в хаскелі до Do-нотації. Це банальний синтаксичний цукор, що той весь чейнінг перетворює у звизчний імперативний код. Так, там лишається іммутабільність, реферальна чистота, але програму з поінтфрі переписали в імперативному стилі -- бо зручно читати і писати і немає того шуму від bind (>>=). Власне await ми отримали з цієї ж самої прчини.
      Може колись ми отримаємо в ECMAScript аналог растівського Result з його оператором "?", що, або розпаковує Ok, або виходить з функції з результатом Err (без розпаковки). І тоді всі наші прогами будуть виглядати ось так:
      const validLangueges = ["EN", "DE", "UK"] as const;
      type Lang = typeof validLanguages[number];
      const isValidLang = (value: any): value is Lang => validLangueges.includes(value);
      const parseUrl = (input: string) => {
      try { return Ok(new URL(input)); } catch { return Err("EINVALID_INPIT" as const); }
      }
      const input = "...";
      const { search } = unpack parseUrl(input);
      const { lang = "UK" } = queryStringToMap(search);
      if (!isValidLang(lang)) {
      return Err("EINVALID_LANG" as const);
      }