# dry-monads: flaws or intended behaviour?

**URL:** <https://discourse.dry-rb.org/t/dry-monads-flaws-or-intended-behaviour/1524>\
**Category:** Support\
**Created:** [September 22, 2022, 8:38pm UTC](https://discourse.dry-rb.org/t/dry-monads-flaws-or-intended-behaviour/1524 "2022-09-22T20:38:34Z")\
**Posts on this page:** 1\
**Showing post:** 2

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [September 26, 2022, 6:21pm UTC](https://discourse.dry-rb.org/t/dry-monads-flaws-or-intended-behaviour/1524/2 "2022-09-26T18:21:00Z")

</div>

> [@a-chris](#):
>
> using `yield` and `rescue` in the same method breaks the Do Notation because it catches `Dry::Monads::Do::Halt` instead of the real error type.

This is an [intentional design choice](https://github.com/dry-rb/dry-monads/issues/72) to support transaction rollbacks, and altering the behavior would be a breaking change. Exceptions as flow-control is considered an antipattern, and Monads are considered an alternative to that practice.

There still is a need to adapt exception-laden code when wrapping them in monads, though.

You can work around this in a couple ways. The `Try` monad is probably the best approach.

Alternately, you can reraise `Halt`

```ruby
def my_method
  yield Failiure('error code')
rescue Dry::Monads::Do::Halt
  raise
rescue => err
  Failure(err)
end

```

> [@a-chris](#):
>
> Similarly, it’s not possible to use `yield` inside `Try` because it returns `Failure(Dry::Monads::Do::Halt)`

I can’t think of any reason why you would _need_ to use yield inside a `Try` monad. The purpose is to act as a boundary between exceptional code and monadic code. Simply moving the `yield` outside of the `Try` block should work fine

```ruby
yield Try { raise 'something went wrong' }.to_result

```

Some times, using `yield` is inconvenient for various reasons, and in those situations you can use the `bind` interface directly.

```ruby
Dry::Monads::Do.bind Failure('error code')

```

Basically, your code is responsible for transforming what it is doing into a monadic type, _and then_ you hand it off to `yield`. So for example, every Operation object that I use has this helper available:

```ruby
# Utility helper to avoid .to_monad.or {} chaining
#
# @param [#to_monad] result, Dry::Monads::Result or object that responds to `to_monad`
# @param [Dry::Monads::Result::Failure, #to_proc, { :error => Symbol }] error
#
# @example With Yield
# user = yield fetch_user.(id).or { |err| Failure[:not_found, err] }
#
# @example With Either
# user = either fetch_user.(id), Failure(:not_found)
# user = either fetch_user.(id), ->(err) { Failure[:not_found, err] }
# user = either fetch_user.(ud), error: :not_found
#
# @raise [Dry::Monads::Do::Halt]
#
# @return the unwrapped successful value
def either(result, error)
  failure =
    case error
    in { error: error_name }
      proc { |err| Failure[error_name, err] }
    in T::Procable
      error
    else
      proc { error }
    end

  Dry::Monads::Do.bind result.to_monad.or(&failure)
end

```

---

_[View the full topic](https://discourse.dry-rb.org/t/dry-monads-flaws-or-intended-behaviour/1524)._
