Loe
View SourceLoe 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
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
{:error, :bad} <~> fn e -> "Reason: #{e}" end
# => {:error, "Reason: bad"}
unwrap!/1
Unwraps a success value or raises on error.
Loe.unwrap!({:ok, 42}) # => 42
Loe.unwrap!({:error, :fail}) # => ** (RuntimeError) Loe.unwrap!/1 encountered error: :fail
Installation
Add :loe
to your mix.exs
dependencies:
def deps do
[
{:loe, "~> 0.1.2"}
]
end
Then run:
mix deps.get
Usage
Let’s say you have some simple input validation logic:
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:
import Loe
And chain validations like this:
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"}}
Example 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:
4
~>> Validation.integer()
~>> Validation.positive()
~>> Data.double()
<~> Error.transform()
# => {:ok, 8}
Anonymous functions work too
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
{:error, :oops}
<~> (fn e -> "Got: #{e}" end).()
# => {:error, "Got: oops"}
Unwrapping
{:ok, 42} |> Loe.unwrap!()
# => 42
{:error, :fail} |> Loe.unwrap!()
# => ** (RuntimeError) Loe.unwrap!/1 encountered error: :fail
Accepts raw or success values
# Both forms produce the same result:
4
~>> Validation.integer()
~>> Validation.positive()
{:ok, 4}
~>> Validation.integer()
~>> Validation.positive()
Documentation
Full API docs available on HexDocs.
Contributing
Contributions are welcome via issues or pull requests. Created and maintained by Centib.
License
Released under the MIT License.