This is another wrong, incomplete implementation of Maybe data type in Ruby. This gist is a full-blown gem. In order to use it, just add the following line to your Gemfile.
gem 'maybe', gist: 'da2aaafdfd71e6ff0ffd'
But WHY?
To my knowledge, all existing gems are too fat and do not provide the ability to configure what values should be treated as Just
and what values are Nothing
. Some treat []
as Nothing
, some treat only nil
as Nothing
, some rely on truthiness (this is the most reasonable choice in my opinion). In my particular use case, I needed to treat obj
as Nothing
iff obj.present?
is true
. Obviously, hard-coding this Railsism into the gem is not an option, so I pass the Nothingness predicate into Maybe data constructor (pardon my French).
Enough talking, show me the code!
User = Struct.new(:twitter_account)
TwitterAccount = Struct.new(:number_of_followers)
def has_many_followers?(user)
Maybe(user)
.fmap(&:twitter_account)
.fmap(&:number_of_followers)
.fmap{ |n| n > 1 }
.from_just { false }
end
has_many_followers?(nil)
# => false
has_many_followers?(User.new(nil))
# => false
has_many_followers?(User.new(TwitterAccount.new(nil)))
# => false
has_many_followers?(User.new(TwitterAccount.new(0)))
# => false
has_many_followers?(User.new(TwitterAccount.new(9001)))
# => true
### Ok, cool, but what if we want to use `present?` or `empty?`?
def greet(username)
Maybe(username, &:present?)
.fmap {|x| "Hello, #{x}!"}
.from_just { 'Nothing to see here, move along' }
end
greet('Simon')
# => "Hello, Simon!"
greet('')
# => "Nothing to see here, move along"
greet(false)
# => "Nothing to see here, move along"
Maybe
violates THE FUNCTOR LAWS!
Wait, but your It does indeed.
Maybe(1, &:odd?).fmap(&:succ).fmap(&:pred).from_just{'???'}
# => "???"
Maybe(1, &:odd?).fmap{|x| x.succ.pred}.from_just{'???'}
# => 1
But who cares?
Inspiration
- My own post (in Russian).
- https://www.youtube.com/watch?v=RVHawNWW-JY