Монада - инструмент, который позволяет создавать цепочки вызовов функций или методов без лишних проверок данных, которые возвращаются в предыдущем шаге. Монад много, но рассмотрим только популярные -
*
*
*
У каждой из монад есть 3 главных функции,
*
*
В руби нет монад из коробки, но существуют гемы, которые реализуют монады:
* dry-monads
* kleisli
* tomstuart/monads
Советую dry, как единственную поддерживаемую. К тому же, при использовании dry-validation можно легко конвертировать результат валидации в монаду, воспользовавшись экстеншеном:
Это минимум, который нужен, чтобы начать использовать монады в руби приложении. Для закрепления - перепишем изначальный пример с использованием монад:
Кроме использования монад в бизнес логике, попробуйте эту абстракцию для обработки результата, который возвращается из бизнес логики. Как пример - вызов operation из экшена и последующая обработка результата в этом же экшене: cookie_box/show.rb
Что делать с результатом
При использовании
- вызывать на прямую
- использовать `dry-matcher`;
- мой любимый вариант, использовать `case`;
Минусы
1. В отличии от условий (
2. Использование монад может сильно усложнить код. Спасает опыт, а опыт получается в практике;
3. Если хотите начать использовать монады в проекте, придется прорваться через ужас в глазах коллег (причина почему я написал этот текст);
Запомнить
* Монады - абстракция для чейна вызовов функций и следованию railway programming;
* Для использования монад не нужно математическое образование. Главное понять, что монада оборачивает данные в объекты с единым интерфейсом;
* Советую начать с -
*
* Чрезмерное использование монад усложняет код, будьте осторожны и подходите к написанию кода с умом;
Полезное
* Как рефакторить руби код с монадами
* Algebraic Data Types & Monads in Ruby
* Monads and Ruby
* Railway Oriented Programming
Maybe
, Result
и Try
.*
Maybe
- оборачивает значение в Some
или возвращает None
объект без значения. *
Result
- оборачивает значение в Success
или Failure
.*
Try
- оборачивает вызов кода в Result
если не было эксепшенов и в Error
, если код упал с ошибкой (которая ловится)У каждой из монад есть 3 главных функции,
fmap
, bind
и способ получить данные, которые содержит в себе монада.*
fmap
- выполняет блок, если значение монады соответствует Success
варианту, а результат выполнения блока оборачивает в ту же монаду, у которой он вызвался. Например: Some(1).fmap(&:to_s) # => Some('1')
None().fmap(&:to_s) # => Nothing
*
bind
- аналогичен fmap
, только возвращается результат выполнения блока:Some(1).bind(&:to_s) # => '1'
Some(1).bind { |value| Success(value) } # => Success('1')
None().bind(&:to_s) # => Nothing
В руби нет монад из коробки, но существуют гемы, которые реализуют монады:
* dry-monads
* kleisli
* tomstuart/monads
Советую dry, как единственную поддерживаемую. К тому же, при использовании dry-validation можно легко конвертировать результат валидации в монаду, воспользовавшись экстеншеном:
Dry::Validation.load_extensions(:monads)
Это минимум, который нужен, чтобы начать использовать монады в руби приложении. Для закрепления - перепишем изначальный пример с использованием монад:
http.get(url, params) # теперь клиент возвращается Result Monad
# валидация возвращает Result, который используется для следующих вызовов
.bind { |body| validator.call(body).to_result }
# сохраняем в базу, если валидация вернула Success
.bind { |payload| Maybe(user_repository.create(payload)) }
# вызываем воркер, если сохранение вернет Some
.fmap { |user| NotificationWorker.perform_async(user.id) }
Кроме использования монад в бизнес логике, попробуйте эту абстракцию для обработки результата, который возвращается из бизнес логики. Как пример - вызов operation из экшена и последующая обработка результата в этом же экшене: cookie_box/show.rb
Что делать с результатом
При использовании
dry-monads
можно:- вызывать на прямую
success?
, failed?
или value_or
;- использовать `dry-matcher`;
- мой любимый вариант, использовать `case`;
Минусы
1. В отличии от условий (
if
, unless
, etc) нельзя просто взять и использовать монаду. Если не знать в чем смысл абстракции и что значат bind
и fmap
- будет сложно понять код, который написан;2. Использование монад может сильно усложнить код. Спасает опыт, а опыт получается в практике;
3. Если хотите начать использовать монады в проекте, придется прорваться через ужас в глазах коллег (причина почему я написал этот текст);
Запомнить
* Монады - абстракция для чейна вызовов функций и следованию railway programming;
* Для использования монад не нужно математическое образование. Главное понять, что монада оборачивает данные в объекты с единым интерфейсом;
* Советую начать с -
Maybe
, Result
и Try
;*
fmap
и bind
- методы для чейна вызовов функций* Чрезмерное использование монад усложняет код, будьте осторожны и подходите к написанию кода с умом;
Полезное
* Как рефакторить руби код с монадами
* Algebraic Data Types & Monads in Ruby
* Monads and Ruby
* Railway Oriented Programming