https://github.com/soumyaray/result
result package for R
https://github.com/soumyaray/result
Last synced: 5 months ago
JSON representation
result package for R
- Host: GitHub
- URL: https://github.com/soumyaray/result
- Owner: soumyaray
- License: other
- Created: 2023-11-20T16:32:30.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-12-02T16:50:56.000Z (over 1 year ago)
- Last Synced: 2024-12-01T12:51:08.873Z (5 months ago)
- Language: R
- Size: 54.7 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.Rmd
- Changelog: NEWS.md
- License: LICENSE
Awesome Lists containing this project
- jimsghstars - soumyaray/result - result package for R (R)
README
---
output: github_document
---```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```# result
[](https://github.com/soumyaray/result/actions/workflows/R-CMD-check.yaml)
The `result` type captures expressions and functions that might cause error or
otherwise fail to produce a value. Using `result` can make code more readable
and less error-prone by abstracting away the need for nested `if` statements and
`tryCatch` blocks. At its most powerful, `result` can be used to build a
composable pipeline of functions that will either end with a `success` object,
or stop gracefully with a `failure` object as soon as an error is detected.## Example
### Safe functions
Make your own functions fault-tolerant by wrapping your results in `success`
or `failure` for downstream users to safely use:``` {r, eval = TRUE}
library(result)# A function that might fail
risky_fn <- function(good = TRUE) {
if (good) success(42)
else failure("cannot find good answer")
}# Downstream users are now encouraged to inspect the result for success
risky_result <- risky_fn(good = TRUE)
if (is_success(risky_result)) {
print(paste0("The answer: ", value(risky_result)))
}risky_result <- risky_fn(good = FALSE)
if (is_failure(risky_result)) {
cat(paste0("Failed because ", value(risky_result)))
}
```### Smart Results
If you are using others' functions, wrap their results safely for immediate
inspection, or wrap the whole function for safe use later:``` {r, eval = TRUE}
# A third-party function that might crash with error
external_api <- function(good = TRUE) {
if (good) 42
else stop("Cannot connect to API")
}# We can either safely inspect the results:
api_result <- as_result(external_api(good = TRUE))
is_success(api_result)
value(api_result)# Or we can wrap the risky function for safe use later!
safely_call_api <- result(external_api)
safely_call_api(good = FALSE) |> is_failure()
```### Pipelines
We can chain together functions to create a pipeline that will either complete
successfully or else stop gracefully at the first sign of failure.Operations for our pipeline:
``` {r, eval = TRUE}
# Start by wrapping the first operation in a result to evaluate later
safely_call_api <- result(external_api)# Other (risky) operations we want to safely use
add_some <- function(x, y) x + y
times_some <- function(x, y) x * y
times_too_much <- function(x, y) {
z <- x * y
if (z > 100) stop("Result has become too big") else z
}
```A successful pipeline binding `result` operations together:
``` {r, eval = TRUE}
process <-
safely_call_api(good = TRUE) |>
then_try(times_some, 2) |>
then_try(add_some, 10)if (is_success(process)) {
print(paste0("Processed: ", value(process)))
} else {
cat(paste0("Could not process: ", value(process)))
}
```A failing pipeline that should fail gracefully at the second step (`times_too_much`):
``` {r, eval = TRUE}
process <-
safely_call_api(good = TRUE) |>
then_try(times_too_much, 50) |>
then_try(add_some, 10)if (is_success(process)) {
print(paste0("Processed: ", value(process)))
} else {
cat(paste0("Could not process: ", value(process)))
}
```## Installation
You can install the released version of result from CRAN:
``` r
install.packages("result")
```You can install the development version of result from
[GitHub](https://github.com/soumyaray/result) with:``` r
# install.packages("devtools")
devtools::install_github("soumyaray/result")
```## Why not Either or Maybe?
`Result` is a generalization of the **`Maybe`** type that is available to R devs from
the [`maybe`](https://github.com/armcn/maybe) package. Conversely, the `Maybe` type
is a special case of the `Result` type where the error type is `Nothing`. `Maybe`
shines in situations when an operation might return something or nothing.
But `Result` can convey more information than `Maybe` in error situations
(e.g., a status code or message explaining the error). In this R implementation,
the `result()` and `as_result()` functions also capture errors automatically,
sparing developers from having to wrap expressions in their own `tryCatch` blocks.The `Result` type is a special case of the **`Either`** type.
Whereas `Either` resolves to `Left` or `Right`, `Result` usually resolves to
notions of "Ok' or "Error". Use of `Left` and `Right` can be confusing to
newcomers to functional concepts, and do not express the relevance of outcomes.
In this R implementation of `Result`, the `result()` and `as_result()` functions
resolve to `success` and `failure` respectively, which should be intuitive to
many developers. Implementations of `Result` in other languages (such as Rust)
sometimes use `ok` and `error` (or `err`), but these can conflict with
variable names and keywords in other languages.