const input = "..."; const { search } = new URL(input); const { lang = "UK" } = queryStringToMap(search); тепер ми маємо той самий код, що і в прикладі на 21:15 Знову виникає питання: нащо ж нам тоді Either?
@@AleksandrSugak ну, справа не в кількості рядочків, а справа в складності коду. Який сенс писати код складніше, якщо можна -- простіше. За кількістю рядків все може бути один в один.
Головна міць 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`); } Можна посперичатися, що підтримувати складніше, цей код, або той конвейєр.
Типізувати помилки можна і без Either. Головна міць Either (як на мене) це те що код який повертає Either примушує програміста явно оброблювати помилки. В той час як рантайм помилки не можна побачити з сігнатури функції. Будь-який код може кинути помилку, так само як будь-яке значення може бути null або undefined. Тому помилки часто залишаються необроблені, і ми часто маємо помилки cannot read property of undefined. Either та Option дозволяють працювати з помилками та відсутністю значень явно.
@@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); }
const input = "...";
const { search } = new URL(input);
const { lang = "UK" } = queryStringToMap(search);
тепер ми маємо той самий код, що і в прикладі на 21:15
Знову виникає питання: нащо ж нам тоді Either?
якщо ціль - написати якомого менше рядочків коду, то мабудь Either не варто використовувати :)
@@AleksandrSugak ну, справа не в кількості рядочків, а справа в складності коду. Який сенс писати код складніше, якщо можна -- простіше.
За кількістю рядків все може бути один в один.
Головна міць 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`);
}
Можна посперичатися, що підтримувати складніше, цей код, або той конвейєр.
Типізувати помилки можна і без Either. Головна міць Either (як на мене) це те що код який повертає Either примушує програміста явно оброблювати помилки. В той час як рантайм помилки не можна побачити з сігнатури функції. Будь-який код може кинути помилку, так само як будь-яке значення може бути null або undefined. Тому помилки часто залишаються необроблені, і ми часто маємо помилки cannot read property of undefined. Either та Option дозволяють працювати з помилками та відсутністю значень явно.
@@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);
}