Try

Rescues a block from an exception. The Try monad is useful when you want to wrap some code that can raise exceptions of certain types. A common example is making an HTTP request or querying a database.

require 'dry/monads'

class ExceptionalLand
  include Dry::Monads[:try]

  def call
    res = Try { 10 / 2 }
    res.value! if res.value?
    # => 5

    res = Try { 10 / 0 }
    res.exception if res.error?
    # => #<ZeroDivisionError: divided by 0>

    # By default Try catches all exceptions inherited from StandardError.
    # However you can catch only certain exceptions like this
    Try[NoMethodError, NotImplementedError] { 10 / 0 }
    # => raised ZeroDivisionError: divided by 0 exception
  end
end

It is better if you pass a list of expected exceptions which you are sure you can process. Catching exceptions of all types is considered bad practice.

The Try monad consists of two types: Value and Error. The first is returned when code did not raise an error and the second is returned when the error was captured.

bind

Allows you to chain blocks that can raise exceptions.

Try[NetworkError, DBError] { grap_user_by_making_request }.bind { |user| user_repo.save(user) }

# Possible outcomes:
# => Value(persisted_user)
# => Error(NetworkError: request timeout)
# => Error(DBError: unique constraint violated)

fmap

Works exactly the same way as Result#fmap does.

require 'dry/monads'

class ExceptionalLand
  include Dry::Monads[:try]

  def call
    Try { 10 / 2 }.fmap { |x| x * 3 }
    # => Try::Value(15)

    Try[ZeroDivisionError] { 10 / 0 }.fmap { |x| x * 3 }
    # => Try::Error(ZeroDivisionError: divided by 0)
  end
end

value! and exception

Use value! for unwrapping a Success and exception for getting error object from a Failure.

to_result and to_maybe

Try's Value and Error can be transformed to Success and Failure correspondingly by calling to_result and to Some and None by calling to_maybe. Keep in mind that by transforming Try to Maybe you lose the information about an exception so be sure that you've processed the error before doing so.

recover

Recovers from an error:

extend Dry::Monads[:try]

Try { 10 / 0 }.recover(ZeroDivisionError) { 1 } # => Try::Value(1)

No explicit list of exceptions required, StandardError will be the default:

extend Dry::Monads[:try]
Try { Hash.new.fetch(:missing) }.recover { :found } # => Try::Value(:found)

Of course, it's a no-op on values:

Try { 10 }.recover { 1 } # => Try::Value(10)

Multiple exception types are allowed:

extend Dry::Monads[:try]

Try { bang! }.recover(KeyError, ArgumentError) { :failsafe }

octocatEdit on GitHub