В программировании много абстракций, которые кажутся сложными на первый взгляд, но после практики становятся понятнее. Например - монады.



Я видел разработчиков, которые бояться использовать монады, а код с использованием монад - сжигают на месте. Основная причина - убеждение, что для этого необходимо знать теорию категорий и хаскель, да и зачем лишние абстракции в проекте. Другие не понимают зачем эту абстракцию тащить в руби. При этом, люди используют монады каждый день и не подозревают этого (ссылки). Поэтому сегодня поговорим о практическом использовании монад в руби и зачем это нужно.



Не значит, что монады - серебряная пуля, которая решит любую проблему. Так же, это не значит, что использование монад - единственно верный способ написания кода.



Поэтому, цель текста - не объяснить что такое монада, а показать, как и где начать ее использовать . Не ждите функторов, аппликативных функторов, математических выкладок и подробного объяснения зачем каждая монада нужна. Только практика и императивное объяснение. Статьи для любопытных:



* Объяснение функторов и монад в картинках

* Объяснение в HaskellWiki

* A Fistful of Monads - Learn You a Haskell for Great Good!





Код



Встречаются места когда приходится работать с данными и состояниями, например валидны данные или нет, вернула бд данные и если да - какие это данные, etc. Например:



response = http.get(url, params)

if response[:status] == :success

user_repository.create(response[:body])

end





Пример не выглядит сложным, но с бизнес логикой - вложенность выходит из под контроля:



response = http.get(url, params)

if response[:status] == :success

validation_result = validator.call(response[:body])



if validation_result.valid?

if user = user_repository.create(response[:body])

NotificationWorker.perform_async(user.id)

end

else

validation_result.errors

end

else

response

end





Вариант с гардами мне показался сложнее для восприятия



Вспоминая railway programming, было бы здорово переписать наш пример с использованием последовательных шагов:



step :get_response # returns response or Failed result

step :validate # returns payload or error message

step :persist # returns user or nothing

step :notify # calls worker with user id





В случаях, когда данные из прошлого шага влияют на последовательность логики, приходят на помощь монады. Важно запомнить, что монада - объект с общим интерфейсом, в котором лежит значение. Ближайшая абстракция - коробка. В коробке лежит все, что поместиться. При этом, не открыв коробку - значение не получить. Коробки - разные, большие, маленькие, цветные, но каждая коробка открывается одинаково - просто подними крышку и посмотри что там.