Пташки тут наспівали, що не всім очевидно навіщо і як використовувати Optional в програмуванні і чому «чисті null це зло»... Давай же розберемось та ще й порівняємо підходи в різних мовах – #java, #typescript, #csharp, #python – аби зрозуміти основоположні принципи та механізми 😉
Почнемо з Java, в якій підхід – якраз самий класичний, простий і надійний.
Припустимо ми робимо якусь операцію - «зберегти файл» і хочемо запам'ятати результат (власне збережений файл), але ж результату може і не бути, бо наприклад полетить помилка:
Код скомпілюється, все ймовірно запрацює, а потім – одного дня, коли по якійсь причині, наприклад не буде права на запис - код грохнеться з чимось типу
Ну... ми поколупаємось, розберемось і підправимо код:
Так от, давні коДаóси помітили :), що коли ми працюємо з нулами то постійно вилазять такого роду проблеми, і краще щось робити для того, щоб «не забувати обробляти нули одразу».
Саме просте, що іноді (наприклад у версіях джави до 9-тої) – я роблю коли під рукою немає спеціального засобу типу Optional (про який буде мова далі) – це просто міняю назву змінної в якій може бути
Але все ще, це такий собі лайфхак – все залежить від моєї уважності 🙂
Ідеальний же варіант, це коли сам компілятор мені допомагатиме не забути перевірити на відсутність результату... Класичне вирішення проблеми - це використання типу
Зверни увагу чим останній код відрізняються від варіанту з
Виходить з Optional – та ж сама логіка, тільки більше задроства – суть якого в тому, щоб ми не забули що треба перевірити, і саме щоб нагадував нам про це – сам компілятор ще на стадії коли ми пишемо код;)
Що там в інших мовах? – гортай далі 😉 ⬇️
Почнемо з Java, в якій підхід – якраз самий класичний, простий і надійний.
Припустимо ми робимо якусь операцію - «зберегти файл» і хочемо запам'ятати результат (власне збережений файл), але ж результату може і не бути, бо наприклад полетить помилка:
File storedFile = null;
try {
storedFile = storeFile("~/tmp/temp.txt");
} catch (Exception error) {
// що завгодно, можемо залогувати помилку чи ще що...
}
// далі щось може відбуватись ...
// і в кінці, ми повертаємось до нашого файлу...
// наприклад, щоб запам'ятати його ім'я...
// чи просто надрукувати:
System.out.printLine(storedFile.getName());
Код скомпілюється, все ймовірно запрацює, а потім – одного дня, коли по якійсь причині, наприклад не буде права на запис - код грохнеться з чимось типу
NullPointerException
і з повідомлення про помилку – ми ох як довго не зрозуміємо у чому справа 🙂Ну... ми поколупаємось, розберемось і підправимо код:
File storedFile = null;
try {
storedFile = storeFile("~/tmp/temp.txt");
} catch (Exception error) {
// ...
}
// ...
System.out.printLine(storedFile != null ? storedFile.getName() : "file was not stored!");
Так от, давні коДаóси помітили :), що коли ми працюємо з нулами то постійно вилазять такого роду проблеми, і краще щось робити для того, щоб «не забувати обробляти нули одразу».
Саме просте, що іноді (наприклад у версіях джави до 9-тої) – я роблю коли під рукою немає спеціального засобу типу Optional (про який буде мова далі) – це просто міняю назву змінної в якій може бути
null
(себто відсутність результату, пустота):
File maybeStoredFile = null;
try {
maybeStoredFile = storeFile("~/tmp/temp.txt");
} catch (Exception error) {
// ...
}
// ...
/*
// тепер нижче у мене руки не повернуться написати:
System.out.printLine(maybeStoredFile.getName());
// тому що прямим же текстом написано MAYBE stored file!
// тобто може він і є, а може і нема,
// значить перед тим як на ньому викликати якийсь get* треба спочатку перевірити на нулл:
*/
System.out.printLine(maybeStoredFile != null ? storedFile.getName() : "file was not stored!");
Але все ще, це такий собі лайфхак – все залежить від моєї уважності 🙂
Ідеальний же варіант, це коли сам компілятор мені допомагатиме не забути перевірити на відсутність результату... Класичне вирішення проблеми - це використання типу
Optional
(в деяких мовах програмування його називають Maybe по аналогії з моїм префіксом вище, але не суть):
Optional<File> storedFile = Optional.empty();
try {
storedFile = Optional.of(storeFile("~/tmp/temp.txt"));
} catch (Exception error) {
// ...
}
// ...
/*
// тепер нижче якщо ми забудемо,
// що вище не обов'язково файл був збережений,
// то сам компілятор не дозволить нам написати:
System.out.printLine(storedFile.getName());
// тепер єдиний спосіб отримати що ми хочемо це витягнути його з "коробки optional"
// а раз будемо витягувати то ясно що треба спочатку перевірити чи в коробкі щось є:
*/
System.out.printLine(storedFile.isPresent() ? storedFile.get().getName() : "file was not stored!");
Зверни увагу чим останній код відрізняються від варіанту з
null
:
System.out.printLine(maybeStoredFile != null ? storedFile.getName() : "file was not stored!");
Виходить з Optional – та ж сама логіка, тільки більше задроства – суть якого в тому, щоб ми не забули що треба перевірити, і саме щоб нагадував нам про це – сам компілятор ще на стадії коли ми пишемо код;)
Що там в інших мовах? – гортай далі 😉 ⬇️
❤4🔥2✍1👍1🎉1
Отже, ми встигли розібрати ⬆️ що null-и (в Python – None) можуть бути болючими бо приводять до незрозумілих помилок. В #java та і в принципі у більшості мов програмування існує простий механізм – тип Optional (іноді називають його Maybe, іноді ще й обзивають особливим словом Монада...). Це універсальний, масимально простий і надійний механізм, тому якщо ти не джавіст, все одно проскроль вище і почитай;)
При цьому у інших мовах програмування є більш легкі механізми, і зараз ми поглянемо їх на прикладі #typescript, #csharp та #python, і подумаємо чи кращі вони за Optional у Java.
У мовах типу TypeScript чи C# за допомогою спеціальних опцій компілятора, можна включити режим "строгості нуллів" (`--strictNullChecks` чи
В C# працюватиме все точно так само:
В Python та ж історія що і в TypeScript чи C#, тільки треба додатково встановлювати mypy та плагін до нього:
Тепер питання - чи пощастило TS, C# та частково Python більше ніж Java з його Optional замість спеціального синтаксису? А ось цієї відповіді почекаємо в наступному пості, стей тюнд 😉
P.S.
Діліться в коментарях доповненнями до прикладів вище (вони доволі чорнові), і є повно цікавих нюансів, які не хотілось поки впихувати в сам пост. Також цікаво хто ще що використовував, і в джава і в інших мовах є куча альтернативних способів ;)
При цьому у інших мовах програмування є більш легкі механізми, і зараз ми поглянемо їх на прикладі #typescript, #csharp та #python, і подумаємо чи кращі вони за Optional у Java.
У мовах типу TypeScript чи C# за допомогою спеціальних опцій компілятора, можна включити режим "строгості нуллів" (`--strictNullChecks` чи
"strictNullChecks": true
для tsconfig.json в TS; <Nullable>enable</Nullable>
в C#), і тоді тої ж самої цілі можна досягти навіть з нулами:
// код на TypeScript
let storedFile: File | null = null;
try {
storedFile = storeFile("~/tmp/temp.txt");
} catch (error) {
// ...
}
// ...
/*
// тепер нижче компілятор не дасть написати:
console.log(storedFile.name);
// ми будемо змушені або свідомо звернутись до name через синтаксис !.
console.log(storedFile!.name);
// або ж спочатку перевірити на null:
*/
console.log(storedFile != null ? storedFile.name : "file was not stored!");
В C# працюватиме все точно так само:
// тільки оголошуємо тип як nullable через синтаксис знаку питання
FileStream? storedFile = null;
try {
storedFile = storeFile("~/tmp/temp.txt");
} catch (Exception error) {
// ...
}
/*
// тепер нижче компілятор не дасть написати:
System.Console.WriteLine(storedFile.Name);
// ми будемо змушені або свідомо звернутись до Name через синтаксис !.
System.Console.WriteLine(storedFile!.Name);
// або ж спочатку перевірити на null:
*/
System.Console.WriteLine(storedFile != null ? storedFile.Name : "file was not stored!");
В Python та ж історія що і в TypeScript чи C#, тільки треба додатково встановлювати mypy та плагін до нього:
stored_file: Optional[TextIO] = None
try:
stored_file = store_file("~/tmp/temp.txt")
except Exception as e:
...
# ...
'''
# тепер якщо встановлений додатковий пакет mypy
# та опційно налаштований плагін для нього в редакторі
# то тайп чекер mypy не дасть написати:
print(stored_file.name)
# ми будемо змушені або свідомо через коментар ігнорувати помилку:
print(stored_file.name) # type: ignore)
# або ж спочатку перевірити на None:
'''
print(stored_file.name if stored_file is not None else "file was not stored!")
Тепер питання - чи пощастило TS, C# та частково Python більше ніж Java з його Optional замість спеціального синтаксису? А ось цієї відповіді почекаємо в наступному пості, стей тюнд 😉
P.S.
Діліться в коментарях доповненнями до прикладів вище (вони доволі чорнові), і є повно цікавих нюансів, які не хотілось поки впихувати в сам пост. Також цікаво хто ще що використовував, і в джава і в інших мовах є куча альтернативних способів ;)
Telegram
AutotestЯк
Пташки тут наспівали, що не всім очевидно навіщо і як використовувати Optional в програмуванні і чому «чисті null це зло»... Давай же розберемось та ще й порівняємо підходи в різних мовах – #java, #typescript, #csharp, #python – аби зрозуміти основоположні…
❤7👍7