## Introduction & Usage

## Introduction

`dry-monads`

is a set of common monads for Ruby.

Monads provide an elegant way of handling errors, exceptions and chaining functions so that the code is much more understandable and has all the error handling, without all the `if`

’s and `else`

’s. The gem was inspired by Kleisli gem.

See dry-matcher for an example of how to use monads for controlling the flow of code with a result.

## Usage

### Maybe monad

The `Maybe`

monad is used when a series of computations could return `nil`

at any point.

`bind`

Applies a block to a monadic value. If the value is `Some`

then calls the block passing unwrapped value as an argument. Returns itself if the value is `None`

.

```
require 'dry-monads'
M = Dry::Monads
maybe_user = M.Maybe(user).bind do |u|
M.Maybe(u.address).bind do |a|
M.Maybe(a.street)
end
end
# If user with address exists
# => Some("Street Address")
# If user or address is nil
# => None()
# You also can pass a proc to #bind
add_two = -> (x) { M.Maybe(x + 2) }
M.Maybe(5).bind(add_two).bind(add_two) # => Some(9)
M.Maybe(nil).bind(add_two).bind(add_two) # => None()
```

`fmap`

Similar to `bind`

but lifts the result for you.

```
require 'dry-monads'
Dry::Monads::Maybe(user).fmap(&:address).fmap(&:street)
# If user with address exists
# => Some("Street Address")
# If user or address is nil
# => None()
```

`value`

You always can unlift the result by calling `value`

.

```
require 'dry-monads'
Dry::Monads::Some(5).fmap(&:succ).value # => 6
Dry::Monads::None().fmap(&:succ).value # => nil
```

`or`

The opposite of `bind`

.

```
require 'dry-monads'
M = Dry::Monads
add_two = -> (x) { M.Maybe(x + 2) }
M.Maybe(5).bind(add_two).bind(add_two).or(M.Some(1)) # => Some(9)
M.Maybe(nil).bind(add_two).bind(add_two).or(M.Some(1)) # => Some(1)
```

### Either monad

The `Either`

monad is useful to express a series of computations that might

return an error object with additional information.

The `Either`

mixin has two type constructors: `Right`

and `Left`

. The `Right`

can be thought of as “everything went right” and the `Left`

is used when

“something has gone wrong”.

`Either::Mixin`

```
require 'dry-monads'
class EitherCalculator
include Dry::Monads::Either::Mixin
attr_accessor :input
def calculate
i = Integer(input)
Right(i).bind do |value|
if value > 1
Right(value + 3)
else
Left("value was less than 1")
end
end.bind do |value|
if value % 2 == 0
Right(value * 2)
else
Left("value was not even")
end
end
end
end
# EitherCalculator instance
c = EitherCalculator.new
# If everything went right
c.input = 3
result = c.calculate
result # => Right(12)
result.value # => 12
# If it failed in the first block
c.input = 0
result = c.calculate
result # => Left("value was less than 1")
result.value # => "value was less than 1"
# if it failed in the second block
c.input = 2
result = c.calculate
result # => Left("value was not even")
result.value # => "value was not even"
```

`fmap`

An example of using `fmap`

with `Right`

and `Left`

.

```
require 'dry-monads'
M = Dry::Monads
result = if foo > bar
M.Right(10)
else
M.Left("wrong")
end.fmap { |x| x * 2 }
# If everything went right
result # => Right(20)
# If it did not
result # => Left("wrong")
# #fmap accepts a proc, just like #bind
upcase = :upcase.to_proc
M.Right('hello').fmap(upcase) # => Right("HELLO")
```

`value`

Unlift the result by calling `value`

.

```
M = Dry::Monads
M.Right(10).value # => 10
M.Left('Error').value # => 'Error'
```

`or`

An example of using `or`

with `Right`

and `Left`

.

```
M = Dry::Monads
M.Right(10).or(M.Right(99)) # => Right(10)
M.Left("error").or(M.Left("new error")) # => Left("new error")
M.Left("error").or { |err| M.Left("new #{err}") } # => Left("new error")
```

`to_maybe`

Sometimes it’s useful to turn an `Either`

into a `Maybe`

.

```
require 'dry-monads'
result = if foo > bar
Dry::Monads.Right(10)
else
Dry::Monads.Left("wrong")
end.to_maybe
# If everything went right
result # => Some(10)
# If it did not
result # => None()
```

`left?`

and `right?`

You can explicitly check the type by calling `left?`

or `right?`

on a monadic value. Also `left?`

has `failure?`

alias and `right?`

has `success?`

.

### Try monad

Rescues a block from an exception. `Try`

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

```
require 'dry-monads'
module ExceptionalLand
extend Dry::Monads::Try::Mixin
res = Try() { 10 / 2 }
res.value if res.success?
# => 5
res = Try() { 10 / 0 }
res.exception if res.failure?
# => #<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
```

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.

`Try`

monad consists of two types: `Success`

and `Failure`

. The first is returned when code did not raise an error and the second is returned when the error was captured.

`bind`

Works exactly the same as `Either#bind`

does.

```
require 'dry-monads'
module ExceptionalLand
extend Dry::Monads::Try::Mixin
Try() { 10 / 2 }.bind { |x| x * 3 }
# => 15
Try(ZeroDivisionError) { 10 / 0 }.bind { |x| x * 3 }
# => Failure(ZeroDivisionError: divided by 0)
end
```

`fmap`

Allows you to chain blocks that can raise exceptions.

```
Try(NetworkError, DBError) { grap_user_by_making_request }.fmap { |user| user_repo.save(user) }
# Possible outcomes:
# => Success(persisted_user)
# => Failure(NetworkError: request timeout)
# => Failure(DBError: unique constraint violated)
```

`value`

and `exception`

Use `value`

for unlifting a `Success`

and `exception`

for getting error object from a `Failure`

.

`to_either`

and `to_maybe`

`Try`

’s `Success`

and `Failure`

can be transformed to `Right`

and `Left`

correspondingly by calling `to_either`

and to `Some`

and `None`

by calling `to_maybe`

. Keep in mind that by transforming `Try`

to `Maybe`

you loose information about an exception so be sure that you’ve processed the error before doing so.

### List monad

`bind`

Lifts a block/proc and runs it against each member of the list. The block must return a value coercible to a list. As in other monads if no block given the first argument will be treated as callable and used instead.

```
require 'dry-monads'
M = Dry::Monads
M::List[1, 2].bind { |x| [x + 1] } # => List[2, 3]
M::List[1, 2].bind(-> x { [x, x + 1] }) # => List[1, 2, 2, 3]
M::List[1, nil].bind { |x| [x + 1] } # => error
```

`fmap`

Maps a block over the list. Acts as `Array#map`

. As in other monads if no block given the first argument will be treated as callable and used instead.

```
require 'dry-monads'
M = Dry::Monads
M::List[1, 2].fmap { |x| x + 1 } # => List[2, 3]
```

`value`

You always can unlift the result by calling `value`

.

```
require 'dry-monads'
M = Dry::Monads
M::List[1, 2].value # => [1, 2]
```

#### Concatenates

```
require 'dry-monads'
M = Dry::Monads
M::List[1, 2] + M::List[3, 4] # => List[1, 2, 3, 4]
```

`head`

and `tail`

`head`

returns the first element wrapped with a `Maybe`

.

```
require 'dry-monads'
M = Dry::Monads
M::List[1, 2, 3, 4].head # => Some(1)
M::List[1, 2, 3, 4].tail # => List[2, 3, 4]
```

`traverse`

Traverses the list with a block (or without it). This methods “flips” List structure with the given monad (obtained from the type).

**Note that traversing requires the list to be typed.**

```
require 'dry-monads'
M = Dry::Monads
M::List[M::Right(1), M::Right(2)].typed(M::Either).traverse # => Right([1, 2])
M::List[M::Maybe(1), M::Maybe(nil), M::Maybe(3)].typed(M::Maybe).traverse # => None
# also, you can use fmap with #traverse
M::List[1, 2].fmap { |x| M::Right(x) }.typed(M::Either).traverse # => Right([1, 2])
M::List[1, nil, 3].fmap { |x| M::Maybe(x) }.typed(M::Maybe).traverse # => None
```

## Credits

`dry-monads`

is inspired by Josep M. Bachâ€™s Kleisli gem and its usage by `dry-transactions`

and `dry-types`

.