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: :failInstallation
Add :loe to your mix.exs dependencies:
def deps do
[
{:loe, "~> 0.1.2"}
]
endThen 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}"}
endNow import Loe to enable its macros:
import LoeAnd 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: :failAccepts 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.