Maybe
The dry-monads gem provides an approach to handling optional values by returning a Maybe object from operations that can return nil.
dry-types has an extension that can return Maybes from calls to types. That is, it wraps the return result in either:
- a
Someobject (with the resulting value) - a
Noneobject (when the value would benil)
NOTE: You must
require 'dry-monads', and includeDry::Monads[:maybe]
- Load
dry-monadswithMaybeand thedry-types:maybeextension in your application.
require 'dry-monads'
include Dry::Monads[:maybe] # This should be inside your class
require 'dry-types'
Dry::Types.load_extensions(:maybe)
Types = Dry.Types()
- Append
.maybeto aTypeto return aMaybeobject
Types::Strict::Integer.maybe[nil] # => None
Types::Strict::Integer.maybe[123] # => Some(123)
Types::Coercible::String.maybe[nil] # => None
Types::Coercible::String.maybe[123] # => Some("123")
Types::Coercible::Float.maybe[nil] # => None
Types::Coercible::Float.maybe['12.3'] # => Some(12.3)
Types::Strict::String.maybe[123] # => raises Dry::Types::ConstraintError
Types::Strict::Integer.maybe["foo"] # => raises Dry::Types::ConstraintError
If you want to capture the errors (e.g. to return messages) instead of raising them, you may want to use the :monads extension instead, which returns a Result.
Or, if you prefer, instead of calling .maybe you can use the Maybe:: namespaced types instead.
The following examples are identical to the ones above:
Types::Maybe::Strict::Integer[nil] # => None
Types::Maybe::Strict::Integer[123] # => Some(123)
Types::Maybe::Coercible::String[nil] # => None
Types::Maybe::Coercible::String[123] # => Some("123")
Types::Maybe::Coercible::Float[nil] # => None
Types::Maybe::Coercible::Float['12.3'] # => Some(12.3)
Types::Maybe::Strict::String[123] # => raises Dry::Types::ConstraintError
Types::Maybe::Strict::Integer["foo"] # => raises Dry::Types::ConstraintError
Mapping methods on Maybe
Since these are dry-monads Maybe objects, you can #fmap methods to them: applying the method to the value when the value is Some (and keeping the None when the value is None).
You can #fmap these, and then use #value_or to return the underlying value out of a Some or return a default when the value is None.
maybe_string = Types::Strict::String.maybe
maybe_string[nil] # => None
maybe_string[nil].fmap(&:upcase) # => None
maybe_string['something'] # => Some('something')
maybe_string['something'].fmap(&:upcase) # => Some('SOMETHING')
maybe_string['something'].fmap(&:upcase).value_or('NOTHING') # => "SOMETHING"