The maybe type represents the possibility of some value or nothing.
It is often used instead of throwing an error or returning an undefined
value like `NA`

or `NULL`

. The advantage of using
a maybe type is that the functions which work with it are both
composable and require the developer to explicitly acknowledge the
potential absence of a value, helping to avoid unexpected behavior.

You can install the released version of maybe from CRAN with:

`install.packages("maybe")`

And the development version from GitHub with:

```
# install.packages("remotes")
::install_github("armcn/maybe") remotes
```

The following example shows how the maybe package can be used to create a safe data processing pipeline.

```
library(maybe)
<- maybe(dplyr::filter, ensure = not_empty)
safe_filter <- maybe(mean, ensure = not_undefined)
safe_mean <- maybe(dplyr::pull)
safe_pull
<- function(.cyl) {
mean_mpg_of_cyl %>%
mtcars safe_filter(cyl == .cyl) %>%
and_then(safe_pull, mpg) %>%
and_then(safe_mean) %>%
with_default(0)
}
mean_mpg_of_cyl(8L)
#> [1] 15.1
mean_mpg_of_cyl(100L)
#> [1] 0
```

Here is an example of working with data stored in JSON format.

```
library(purrr)
<-
parse_numbers function(x) filter_map(x, maybe(as.numeric))
<-
safe_first maybe(function(x) x[[1]], ensure = not_empty)
<- function(json) {
sum_first_numbers ::fromJSON(json) %>%
jsonlitefilter_map(compose(safe_first, parse_numbers)) %>%
perhaps(reduce, default = 0)(`+`)
}
sum_first_numbers('{"a": [], "b": [1, 2.2, "three"], "c": [3]}')
#> [1] 4
sum_first_numbers('{}')
#> [1] 0
sum_first_numbers('1, 2, 3')
#> [1] 0
```

Maybe values can be used to model computations that may fail or have undefined outputs. For example, dividing by zero is mathematically undefined but in many programming languages, including R, infinity is returned. If it is not properly accounted for this may cause unexpected behavior later in the program. The maybe type can be used to improve the safety of the divide function.

```
<- function(a, b) {
divide / b
a
}
<- function(a, b) {
safe_divide if (b == 0) nothing() else just(a / b)
}
divide(10, 2)
#> [1] 5
safe_divide(10, 2)
#> Just
#> [1] 5
divide(10, 0)
#> [1] Inf
safe_divide(10, 0)
#> Nothing
```

`safe_divide(10, 2)`

returns `Just 5`

and
`safe_divide(10, 0)`

returns `Nothing`

. These are
the two possible values of the maybe type. It can be `Just`

the value, or it can be `Nothing`

, the absence of a value.
For the value to be used as an input to another function you need to
specify what will happen if the function returns
`Nothing`

.

This can be done using the `with_default`

function. This
function will return the value contained in the `Just`

, or if
it is `Nothing`

it will return the default. Think of a maybe
value as a container. In this container can be `Just`

the
value or `Nothing`

. To use the contained value in a regular R
function you need to unwrap it first.

```
safe_divide(10, 2)
#> Just
#> [1] 5
safe_divide(10, 2) %>% with_default(0)
#> [1] 5
safe_divide(10, 0)
#> Nothing
safe_divide(10, 0) %>% with_default(0)
#> [1] 0
```

This may seem tedious to rewrite functions to return maybe values and then specify a default value each time. This is where the maybe chaining functions become useful.

`maybe_map`

allows a regular R function to be evaluated on
a maybe value. `maybe_map`

, often called `fmap`

in
other languages, reaches into the maybe value, applies a function to the
value, then re-wraps the result in a maybe. If the input is a
`Just`

value, the return value of `maybe_map`

will
also be a `Just`

. If it is `Nothing`

the return
value will be `Nothing`

.

```
just(9) %>% maybe_map(sqrt)
#> Just
#> [1] 3
nothing() %>% maybe_map(sqrt)
#> Nothing
```

What if we wanted to chain multiple “safe” functions (functions that
return maybe values) together? The function `and_then`

, often
called `bind`

in other languages, works similarly to
`maybe_map`

except the function provided must return a maybe
value.

```
<- function(a) {
safe_max if (length(a) == 0) nothing() else just(max(a))
}
<- function(a) {
safe_sqrt if (a < 0) nothing() else just(sqrt(a))
}
just(1:9) %>%
and_then(safe_max) %>%
and_then(safe_sqrt)
#> Just
#> [1] 3
nothing() %>%
and_then(safe_max) %>%
and_then(safe_sqrt)
#> Nothing
```

The maybe package provides another way to create functions that
return maybe values. Instead of rewriting the function to return maybe
values we can wrap it in the `maybe`

function. This will
modify the function to return `Nothing`

on an error or
warning.

A predicate function (a function that returns `TRUE`

or
`FALSE`

) can be provided as an argument to assert something
about the return value. If the predicate returns `TRUE`

then
a `Just`

value will be returned, otherwise it will be
`Nothing`

.

```
<- maybe(max)
safe_max <- maybe(sqrt, ensure = not_infinite)
safe_sqrt
safe_max(1:9) %>% and_then(safe_sqrt)
#> Just
#> [1] 3
safe_max("hello") %>% and_then(safe_sqrt)
#> Nothing
```

This pattern of modifying a function with the `maybe`

function and then setting a default value is so common that there is a
shortcut, `perhaps`

. The default value is set with the
`default`

parameter. This function will always return a
regular R value, never maybe values.

```
<- perhaps(max, ensure = is.numeric, default = 0)
perhaps_max
perhaps_max(1:9)
#> [1] 9
perhaps_max("hello")
#> [1] 0
```

Multiple predicates can be combined with the
`and`

/`or`

functions.

```
<- maybe(sqrt, ensure = and(not_nan, not_empty))
safe_sqrt
safe_sqrt(9)
#> Just
#> [1] 3
safe_sqrt(-1)
#> Nothing
```

Predefined combinations are also provided such as
`not_undefined`

, which ensures that the output is not any of
`NULL`

, `NA`

, `NaN`

, `-Inf`

,
or `Inf`

.

```
<- maybe(mean, ensure = not_undefined)
safe_mean
safe_mean(c(1, 2, 3))
#> Just
#> [1] 2
safe_mean(c(NA, 2, 3))
#> Nothing
```

The names of functions `maybe_map`

, `and_then`

,
`maybe_flatten`

, and `with_default`

are different
from the traditional names used for these functions in other functional
programming languages. If you would like to use the more traditional
names aliases are provided.

`fmap`

==`maybe_map`

`bind`

==`and_then`

`join`

==`maybe_flatten`

`from_maybe`

==`with_default`