Skip to content

ImNotAVirus/std_result

Repository files navigation

StdResult

Hex.pm version Hex.pm license Build Status Coverage Status

Table of Contents

Description

StdResult is a library heavily inspired by Rust's Result type for handling function results in a consistent manner in Elixir.

Handling function results in Elixir can sometimes be inconsistent, with some functions returning :ok or :error, while others return tuples like {:ok, term} or {:error, reason}.

StdResult provides macros and functions to create and manipulate results, promoting a unified approach to error handling.

Detailed documentation can be found at https://hexdocs.pm/std_result.

Installation

Add std_result to your list of dependencies in mix.exs:

def deps do
  [
    {:std_result, "~> 0.1"}
  ]
end

And that's all.

The original issue

Let's take a simple example.
Let's say we need to retrieve an environment variable, convert it to an integer and check that it's positive. Our function should return {:ok, value} or {:error, reason}.

One of the most popular solutions is to use with which would give something like:

with {:ok, port_str} <- System.fetch_env("PORT"),
     port when port > 0 <- String.to_integer(port_str) do
  {:ok, port}
else
  # Return by System.fetch_env/1
  :error -> {:error, "PORT env required"}
  # Returned by `port when port > 0`
  value when is_integer(value) -> {:error, "PORT must be a positive number, got: #{value}"}
end

I think you're beginning to understand what I'm getting at.
Here our error handling is very complicated to reread because the returns of the functions used in the with are not normalized.

This is a simple example, but the more conditions there are, the more confusing the else block becomes.

The simplest solution would be to wrap each function in another, normalizing the returns. But you'd soon find yourself with lots of functions that just normalize each other's returns.

StdResult overcomes this problem.

Usage

Here is the same problem as above, but with StdResult :

import StdResult

System.fetch_env("PORT")
# This will transform `:error` into a `:error` tuple
|> normalize_result()
# If there is an error, explicit the message
|> or_result(err("PORT env required"))
# If no error, parse the string as an integer
# We could also have used `Integer.parse/1` but for simplicity's sake we won't.
|> map(&String.to_integer/1)
# Test if the number is positive
|> and_then(&(if &1 >= 0, do: ok(&1), else: err("PORT must be a positive number, got: #{&1}")))

# The result will be either:
# - `{:ok, port}`
# - `{:error, "PORT env required"}`
# - `{:error, "PORT must be a positive number, got: <value>"}`

NOTE: This lib is not intended to replace with, but rather to complement it in certain cases.

Contributing

Contributions are welcome and appreciated. If you have any ideas, suggestions, or bugs to report, please open an issue or a pull request on GitHub.

About

Yet another way to standardize function returns # Rust inspiration

Topics

Resources

License

Stars

Watchers

Forks

Languages