License: MIT Hex.pm Documentation

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
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.