Монада 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++ за допомогу з конструюванням цієї монстрятини. Так, з «методами розширення» та більш короткими лямбдами було б ще крутіше, я знаю! ■