Монада Maybe мовою C, Дмитро Hестерук
Блог про програмування — C#, F#, C++, архітектура та багато іншого
Монада Maybe мовою C++
Монада – це просто патерн, але з функціонального програмування. А оскільки в C++ є лямбди і всякі функціональні об'єкти, чому б не побудувати монаду? Не обіцяю, що буде красиво, але все ж таки.
Тут і вище використання raw pointer'ів – воно навмисне. Можна було б замість них використовувати shared_ptr або boost::optional, про це поговоримо пізніше.
Отже, скажімо ми хочемо написати функцію яка, якщо їй згодувати покажчик на Person , друкує ім'я будинку якщо таке є. Проста реалізація виглядатиме якось так:
Думаю проблема ясна - всі ці перевірки на nullptr стомлюючі і хотілося б простіше. Ну як сказати простіше ... розумніша чи що.
Реалізуємо Maybe
Отже, ми будуватимемо екскаватор, тобто. клас який вміє копати вглиб об'єктів і навіть перевірки на nullptr у нього вбудовані, тобто. їх не потрібно робити самому. Почнемо з того, що зробимо просто клас із покажчиком:
Ось, просто клас який тримає покажчик якийсь контекст, тобто. на те місце, де ми зараз копаємо. Все добре, але в C++ немає виведення типів для конструкторів, т.к. ось так не можна:
Це якось похмуро, у зв'язку з чим ми реалізуємо helper function котрий займеться власне висновком типів:
Тут правила такі:
Смуток у тому, що не можна просто повернути поточний контекст, т.к. Maybe
Отже, як повернення значення якщо «все ОК» йде через функцію, яка тут шаблонним параметром представлена (були спроби використовувати std::function , але воно в контексті шаблонів з лямбдами не дружить). Так от, тут все ОК, але що щодо повернення контексту якщо унас вже nullptr? По ідеї потрібно повернути лише Maybe де T – це тип контесту, який використовує скормлена нам лямбда. Проблема в тому що
- У нас немає T у явному вигляді
- Зате ми знаємо, що функція, яку нам згодували, повертає T*
- Отже, видаляємо покажчик, і вперед:
Як бачите, вийшло трохи пекло, але воно працює. Тепер можна писати
і поглиблення в структуру відбудеться лише якщо поточний контекст не nullptr.
Icing on the Cake
Зазвичай у монаду Maybe додають ще всякого сміття для просто обробки, if-ів і всякого такого. Наприклад, додамо в нашу монаду функцію Do() , яка просто виконуватиме якусь лямбду:
Вище, як бачите, ми можемо повертати *this тому що контекст ми в цій функції не змінюємо. Тепер ми можемо дописати нашу оригінальну функцію:
Я тут дуже синтетично використовував nullable конструкти, що в реальному житті використовують хіба що в мові С, в якій однаково не збудувати подібну штуку. У реальному житті рядки дуже часто просто тримаються by value, тобто ну буде порожній рядок, але ніяк не null, і відповідно доведеться вже розбирати той факт, що він порожній.
Те саме щодо просто об'єктів, які часто не T* а shared_ptr /unique_ptr або навіть boost::optional і відповідно перевіряти їх вже потрібно по-іншому.
Вообщем ось якось так. Дякую колегам з R++ за допомогу з конструюванням цієї монстрятини. Так, з «методами розширення» та більш короткими лямбдами було б ще крутіше, я знаю! ■