Disclaimer: Many monad is still work in progress and still haven’t yet being added to Rails 6.
DHH proposed to bring the Many monad to Active Support and Active Record Relation. Here is the link to the issue where you track all the conversation: #37875
The basic idea is to reduce the deep chaining happening on the Active Record Relation collection. Let me try to explain this with an example
class Blog < ApplicationRecord
has_many :categories
end
class Category < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
has_many :comments
end
Let say, we want to retrieve all the comments for all the blogs.
# Before addition of Many Monad
blogs = Blog.where(author: "DHH")
blogs.flat_map(:categories).flat_map(:posts).flat_map(:comments)
# After addition of Many Monad
blogs.categories.posts.comments
This issue got me curious to look into What is Monads?
and as DHH has added in the issue description, a talk on monads in Ruby by Tom Stuart is the best way to start on learning Monads. After learning about Monad, I raised a PR: #38788 to add Many Monad. Let’s see if the PR get merged and Monad become the part of Rails 6.1
Let discuss a bit about what I learned in the entire process about Monads.
What is Monad?
Monads are abstract data types, which means that on certain values(eg: collection or object) the kind of operations we can perform with certain rules applied to it.
Handle nil
Monad should be able to handle nil
because if any of the association in the chain return nil all the future chain will start to break. Handling nil
without monad on long chaining could be difficult. It involves a lot of conditional checks on each operation.
Rails already have a try
method which does the similar things conditional logic for us. As Tom has mentioned in his talk, using monkey patching on each object is a code smell.
Monad handles nils very gracefully by adding a try
or and_then
method to each monad. This try
operation could be different for different monads but has a couple of rules. The first rule is try
should always return a Monad.
Method missing
Each monad can be of any type. In the context of Many monad, it could be Array, Hash or an ActiveRecord::Relation collection. The set of methods, that can be performed on each type of collection values can be different. That is why method_missing
handles the invoked property on each monad.
method_missing
uses try
to handle nils. This is where the second rule comes into the picture. try
should always call the block.
The third rule of the monads is that they do not mutate the value. In our scenario, method_missing
or try
both do not mutate the value but just iterates over each value and calls the block passed.
In conclusion, Monads helps in making our code simpler and more reusable. I fell in love with Monad. I would love to use such simple monad in my day to day work.
Happy Coding!!