The functional programming elements are increasingly appearing in C# programming language every year. However, there are still elements for which there is no native support in the language. It is not always appropriate or possible to change the language to another, where there are such elements. Based on Language-Ext library as an example, which is widely used in the development by Uklon, we will analyse the most useful and popular elements of functional programming that have been already implemented in this library.
2. 1. Процес моделювання маршруту за допомогою
функціональних елементів
2. Керування функціональними елементами
3. Типові питання
4. Висновки
Зміст
3. Дисклеймер – яку мову програмування
ми використовуємо?
Ми використовуємо C#
Чому не F#?
Це буде зрозуміло наприкінці презентації
4. Guard – для валідування НЕ
бізнес-логіки
Language-Ext — бібліотека
скарбничка функціональних
елементів, про яку саме йде річ
Дисклеймер — які бібліотеки
ми використовуємо?
5. Специфіка домену MapService
1. Практично немає внутрішньої поведінки класів
2. Дуже багато інваріантів
3. Все залежить від зовнішніх сервісів (налаштувань, БД)
4. Контроль типів як наслідок
5. Контроль валідності стану системи
6. Де це все використовувати?
HERE!
Мова йде тільки про домен
Гарне питання про те,
як використовувати функціональні
елементи в інфраструктурі:
https:/
/github.com/louthy/language-ext/
discussions/1102
7. Процес моделювання маршруту
за допомогою функціональних елементів
● Звичайний клас
● Record
● Засилля примітивних типів
● Вічний Null
● Discriminated Union
● Опціональні значення
● Створення нового об’єкту
10. Record
З переваг – компілятор
за нас написав Equals
& Compare & ToString
Елегантно (ні) вирішуємо
всі наявні проблеми одним
ключовим словом
Все ще маршрут
відповідає за інваріанти
своїх дистанції та довжини
З таким записом змінювати
рекорд за допомогою with
не можна
11. На жаль конструктор не генерує,
тому створення лише через init
Насправді, саме так
правильно дотримуватись
інваріантів у випадку рекордів
Record
12. Суттєвий мінус —
використовує
застарілий кодген
Виглядає практично так само
як і нативний рекорд, але
просто з’явився раніше
Практично всі ті самі фічі
як і в нативному.
Але є Лінзи, що зручно
Record
13. Недоліки:
Висновок: не є зараз актуальним, так як з’явився нативний рекорд. Буде більш
актуальним тоді, коли в ньому з’являться перевірки на Null, та буде
використовувати Source Generators.
● Допускає запис Null https:/
/github.com/louthy/language-ext/issues/1105
● Використовує застарілий кодген
Record — Language Ext
14. Засилля примітивних типів
Кожен раз де є дистанція
так перевіряти?
Можемо помилятися
в порядку при
створенні маршруту
Це метри, кілометри,
сантиметри, чи що?
18. NewType — зручне створення нових типів обгорток
NumType & FloatType — все те ж саме, але для цілих чисел,
або чисел з рухомою комою
Сигнатура:
а. NewType<NEWTYPE, A, PRED>
i. NEWTYPE — реалізуємий тип
ii. A – внутрішній тип
iii. PRED — предикат, який валідує внутрішній тип
За замовчуванням викидає виняток, якщо предикат повертає false
Є статичний метод NewOption з результатом у вигляді типу Option
Засилля примітивних типів
21. Переваги та недоліки типів обгорток
проти засилля примітивних типів
Переваги
● Зникає проблема подвійної
валідації
● Зникає проблема “X-ing”
● Неможливо переплутати
багато однакових аргументів
місцями
Недоліки
● Необхідно постійно створювати ці
типи обгортки
● Більше навантаження на GC?
(треба вимірювати)
25. А ось тут кодген вже корисний, бо
нативної підтримки типів сум (юніонів)
в C# ще (сподіваюсь) немає
Тут можливі два випадки (кейси):
● АБО загальна дистанція
● АБО розділена дистанція
Чи потрібно тут створювати
нові класи обгортки
DistanceInsideCIty & DistanceOutsideCity?
Залежить від вимог
Discriminated Union
29. Недоліки:
Висновок: є зараз актуальним, бо нативних аналогів немає. Можна емулювати,
але емулювання не дає compile-time safe гарантію обробки всіх станів.
Елегантно вирішує проблему опису кількох одночасних станів.
● Допускає запис Null https:/
/github.com/louthy/language-ext/issues/1105
● Використовує застарілий кодген
Discriminated Union — Language Ext
30. Аксіома - Null означає
відсутність об'єкта в
застосунку,
а не в бізнес-логіці!
31. З C# 8 можна навіть
класи помітити як ті,
які опціонально
містять null
Але так моделювати опціональні
значення незручно
Опціональні значення
33. Семантично означає опціональне
значення
Один з найвикористовуваніших типів
для моделювання домену
Не потрібно писати власноруч,
вже є в бібліотеці, разом з низкою
корисних методів
Опціональні значення
34. Самописний Option
з бібліотеки Uklon.Nul
Справжній Option повинен:
● Бути структурою
● Реалізовувати всі інтерфейси
порівняння та серіалізації
● Мати такі методи:
- Option<A>.Some(...)
- Option<A>.None
- Map
- Bind
Option з бібліотеки
Language-Ext
Опціональні значення
35. Переваги та недоліки моделювання опціональних
значень за допомогою Option чи null
Option
● Зникає проблема подвійної валідації
● Коректна семантика опціональності
● Ідеально для бізнес-логіки
● Захаращує код
Null
● Помилка часу виконання
● Семантика існування посилання
● Ідеально для інфраструктури на
кордонах застосунку (адаптери,
контролери)
● Захаращує пам’ять та час розробника
36. Як маючи ці два примітивні
значення створити маршрут?
Створення нового об’єкту
37. Оптимістичний підхід
Ціле виключення на таку незначну
дрібницю як невалідна
дистанція/тривалість?
Багато честі
Але який тип тоді потрібен, щоб
показати, що створення не вдалось?
Створення нового об’єкту
38. Виглядає практично як
імперативний код
Як тільки зустрічаємо перший None
● наступні кроки НЕ обробляються
● значення всього виразу стає None
Монадична обробка
Порядок обробки:
● повертає валідну дистанцію або None
● повертає валідну тривалість або None
● повертає валідний маршрут або None
Створення нового об’єкту
39. Але я хочу знати, яка саме
помилка при створенні
об'єкта!
40. Дуже зручно помилки
моделювати юніонами
Практично те саме, що й з Option, але
замість None буде CreateRouteError
Створення нового об’єкту
45. Переваги та недоліки функціонального рішення
створення нового об’єкту
Переваги
● Ніяких раптових винятків
● Даний підхід ЗМУШУЄ подумати
про обробку помилок
Недоліки
● Неясно
● Незрозуміло
● Не прийнято суспільством
● Достатньо високий поріг входження
● Через брак підтримки в мові,
потребує використання самописних
бібліотек
46. Висновки по створенню об’єктів
● Зовнішня простота приховує за собою внутрішню складність
● Винятки – лише для виняткових ситуацій в програмі!
● Якщо бізнес-логіка об’єкта передбачає можливість невдалого створення
об’єкта (наприклад, під час валідації) – це не виняткова ситуація!
● Можливі три різні підходи для створення об’єктів в функціональному стилі:
а. використовуючи тип Option – якщо нам важливо лише те, чи був створений об’єкт, чи ні
b. використовуючи тип Either – якщо важлива лише перша помилка при створенні об'єкта
с. використовуючи тип Validation – якщо важливо зібрати всі помилки при створенні об'єкта
47. Головні функції для керування
елементами
Так робити некоректно, Option не стикаються!
Якщо потрібно перетворити Option
і в результаті перетворення
отримуємо новий Option
Якщо внутрішнє значення Option
потрібно в щось перетворити
Єдиний вірний варіант отримати
внутрішнє значення (крім Match)
Всі ці функції стандартні,
також є в Either & Validation
48. Стандартна функція, яка
також є в Either & Validation
Оброблюємо всі
можливі стани
Головні функції для керування
елементами
50. ● Це інша мова зі своїм специфічним синтаксисом та особливостями
● Вибір мови – це не твій персональний вибір
● Простіше вивчити нову бібліотеку ніж мову?
● C# вже дуже гарний та майже функціональний (але зрозуміло ООП
підхід перш за все)
● Вчити елементи та концепції функціонального програмування на
початку простіше в знайомому оточенні
Чому б просто не використовувати F#?
51. Чому саме ця бібліотека?
● Або писати самому – або брати вже готове
● На прикладі бібліотеки Uklon.Null ми побачили що буде, якщо
писати це все самому
● До того ж, є неймовірно багато речей, які можуть бути корисними, але про
них треба знати. Якщо писати самому — про них не дізнаєшся
52. Навіщо це взагалі потрібно?
● Дані методи дають змогу безпечно моделювати складний
домен, де багато зовнішніх залежностей
● Ці методи НЕ замінюють тести, сонар та інше, а лише
ДОПОВНЮЮТЬ!
53. Висновки
● Спробуйте на практиці змоделювати свою бізнес-логику за допомогою
описаних методів
● Насправді порядок моделювання не дуже важливий, до кінцевого результату ви
врешті решт прийдете використавши всі потрібні вам та описані методи
● Так в чому ж основна перевага? Ми ЗАВЖДИ знаємо, як комбінувати наші типи
декларативним способом, що автоматично робить наш код більш простим та
підтримуваним