# Loe
[](LICENSE.md)
[](https://hex.pm/packages/loe)
[](https://hexdocs.pm/loe/)
**Loe** is a tiny Elixir library for working with values that may be raw (`val`), wrapped as `{:ok, val}`, or wrapped as `{:error, reason}`.
It provides simple, composable tools to normalize, transform, and chain values using functions or infix macros — making it ideal for expressive, railway-style flows.
## Terminology
Throughout this library and documentation:
- **Raw value** — A plain value not wrapped in a tuple (e.g., `42`)
- **Success value** — A two-element tuple in the form `{:ok, value}`
- **Error value** — A two-element tuple in the form `{:error, reason}`
Tuples like `{:ok, a, b}` or `{:error, a, b}` are not supported — they’ll be treated as raw values.
## Core Functions & Macros
### `lift/2` (or `~>>`)
Transforms a raw or success value using a function.
- Automatically wraps raw inputs as `{:ok, val}`
- Skips transformation if the input is an error
- Wraps raw results as `{:ok, val}` if the function returns a plain value
```elixir
4 ~>> (fn x -> x * 2 end).() # => {:ok, 8}
{:ok, 3} ~>> (fn x -> {:ok, x + 1} end).() # => {:ok, 4}
{:error, :fail} ~>> (fn _ -> :skip end).() # => {:error, :fail}
```
### `tfil/2` (or `<~>`)
Transforms an error value using a function.
- Only applies when the input is an error
- Returns a new `{:error, transformed}` tuple
- Passes through success and raw values unchanged
```elixir
{:error, :bad} <~> fn e -> "Reason: #{e}" end
# => {:error, "Reason: bad"}
```
### `unwrap!/1`
Unwraps a success value or raises on error.
```elixir
Loe.unwrap!({:ok, 42}) # => 42
Loe.unwrap!({:error, :fail}) # => ** (RuntimeError) Loe.unwrap!/1 encountered error: :fail
```
## Installation
Add `:loe` to your `mix.exs` dependencies:
```elixir
def deps do
[
{:loe, "~> 0.1.0"}
]
end
```
Then run:
```bash
mix deps.get
```
## Usage
Let’s say you have some simple input validation logic:
```elixir
defmodule Validation do
def integer(v) when is_integer(v), do: {:ok, v}
def integer(_), do: {:error, :not_integer}
def positive(v) when v > 0, do: {:ok, v}
def positive(_), do: {:error, :not_positive}
end
defmodule Data do
def double(v), do: 2 * v
end
defmodule Error do
def transform(reason), do: %{message: "Validation failed: #{reason}"}
end
```
Now import Loe to enable its macros:
```elixir
import Loe
```
And chain validations like this:
```elixir
4
~>> Validation.integer()
~>> Validation.positive()
# => {:ok, 4}
-4
~>> Validation.integer()
~>> Validation.positive()
# => {:error, :not_positive}
3.4
~>> Validation.integer()
~>> Validation.positive()
<~> Error.transform()
# => {:error, %{message: "Validation failed: not_integer"}}
```
`Validation` functions return wrapped results, while `Data.double/1` returns a raw value.
Thanks to `lift/2`, Loe handles both seamlessly — so you can chain functions without worrying about return formats:
```elixir
4
~>> Validation.integer()
~>> Validation.positive()
~>> Data.double()
<~> Error.transform()
# => {:ok, 8}
```
### Anonymous functions work too
```elixir
2
~>> (fn x -> x * 3 end).()
~>> (fn x -> {:ok, x + 1} end).()
<~> (fn e -> {:error, {:wrapped, e}} end).()
# => {:ok, 7}
```
### Works with error values directly
```elixir
{:error, :oops}
<~> (fn e -> "Got: #{e}" end).()
# => {:error, "Got: oops"}
```
### Unwrapping
```elixir
{:ok, 42} |> Loe.unwrap!()
# => 42
{:error, :fail} |> Loe.unwrap!()
# => ** (RuntimeError) Loe.unwrap!/1 encountered error: :fail
```
### Accepts raw or success values
```elixir
# Both forms produce the same result:
4
~>> Validation.integer()
~>> Validation.positive()
{:ok, 4}
~>> Validation.integer()
~>> Validation.positive()
```
## Documentation
Full API docs available on [HexDocs](https://hexdocs.pm/loe).
## Contributing
Contributions are welcome via issues or pull requests.
Created and maintained by [Centib](https://github.com/Centib).
## License
Released under the [MIT License](LICENSE.md).