option.rb is an attempt to duplicate the functionality of Scala's Option class in Ruby
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Because nil stinks and I'm tired of NoMethodError: undefined method '[]' for nil:NilClass


An Option is either the singleton None or an instance of Some:

s = Some(1)
s == Some(1) # true
s != None    # true

Both None and instances of Some have the standard collection methods (including filter, for those of you who agree with me that select is a stupid name):

s.each {|i| i + 1}   # 2
s.map {|i| i + 1}    # Some(2)
s.filter {|i| i > 1} # None

To get the raw values:

s.get # 1
s.each {|i| i} # 1
None.get # RuntimeError: None has no value

Because of this, it's best to give a default:

s.getOrElse(2)    # 1
None.getOrElse(2) # 2

Blocks are supported for lazy loading the default:

s.getOrElse {|| slow_calculation} # 1, slow_calculation is never done
None.getOrElse {|| slow_calculation} # result of slow_calculation

The Option() method is a good way to get an Option. Any value is converted to Some(value), while nil is converted to None:

result      = Option(some_method_that_might_return_nil("something"))
transformed = result.filter{|i| i > 2}.map{|i| i.to_s}
puts filtered.getOrElse("default")

If nil is bad, so are exceptions. Eat them with tryo:

result      = tryo {|| raise "silly error" }
transformed = result.filter{|i| i > 2}.map{|i| i.to_s}
puts filtered.getOrElse("default") # will be "default"

If you start using lots of Options you might want to flatten them with flatMap:

s.map {|id| get_user_from_db(id) }      # Some(Some(User #1 ...))
s.flatMap {|id| get_user_from_db(id) }  # Some(User #1 ...)

The block passed to flatMap must return an Option:

id = Some("unknown id")
id.flatMap {|id| get_user_from_db(id) } # None
id.flatMap {|id| id}                    # RuntimeError: Did not return an Option