Funx Banner

Funx - Functional Programming Patterns for Elixir

Continuous IntegrationHex.pm

⚠️ Beta: Funx is in active development. APIs may change until version 1.0. Feedback and contributions are welcome.

Official website:https://www.funxlib.comCode and API documentation:https://hex.pm/packages/funx

Breaking Changes in 0.6.0

If you're upgrading from 0.6.0 or earlier, be aware of the module reorganization:

Eq changes

# Change protocol implementations
defimpl Funx.Eq, for: MyStruct # Old
defimpl Funx.Eq.Protocol, for: MyStruct # New
# Change imports and aliases
alias Funx.Eq.Utils # Old
use Funx.Eq.Dsl # Old
use Funx.Eq # New (imports eq DSL macro)
alias Funx.Eq # New (for utility functions)
# Example usage
Eq.contramap(&(&1.age))

Ord changes

# Change protocol implementations
defimpl Funx.Ord, for: MyStruct # Old
defimpl Funx.Ord.Protocol, for: MyStruct # New
# Change imports and aliases
alias Funx.Ord.Utils # Old
use Funx.Ord.Dsl # Old
use Funx.Ord # New (imports ord DSL macro)
alias Funx.Ord # New (for utility functions)
# Example usage
Ord.contramap(&(&1.score))

See the CHANGELOG for more details.

Installation

To use Funx, add it to the list of dependencies in mix.exs:

def deps do
[
{:funx, "~> 0.8"}
]
end

Then, run the following command to fetch the dependencies:

mix deps.get

Usage Rules

Funx includes embedded usage rules in addition to API documentation.
They are written for development workflows assisted by LLMs.

Equality

The Eq protocol defines how two values are compared, making equality explicit and adaptable to your domain.

Ordering

The Ord protocol defines ordering relationships in a structured way, without relying on Elixir's built-in comparison operators.

Ord DSL

The Ord module includes a DSL for building custom ordering comparators declaratively:

use Funx.Ord
user_ord = ord do
desc :priority
asc :name
desc :created_at
end
Enum.sort(users, Funx.Ord.comparator(user_ord))

Features:

Eq DSL

The Eq module includes a DSL for building equality comparators with boolean logic:

use Funx.Eq
contact_eq = eq do
on :name
any do
on :email
on :username
end
end
Funx.Eq.eq?(user1, user2, contact_eq)

Features:

Monads

Monads encapsulate computations, allowing operations to be chained while handling concerns like optional values, failures, dependencies, or deferred effects.

Either DSL

The Either monad includes a DSL for writing declarative pipelines that handle errors gracefully:

use Funx.Monad.Either
either user_id do
bind fetch_user()
bind validate_active()
map transform_to_dto()
end

Supported operations:

Formatter Configuration: Funx exports formatter rules for clean DSL formatting. Add :funx to import_deps in your .formatter.exs:

[
import_deps: [:funx],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

See FORMATTER_EXPORT.md for details.

Optics

Optics provide composable, lawful abstractions for focusing on and transforming parts of data structures.

Monoids

Monoids combine values using an associative operation and an identity element. They are useful for accumulation, selection, and combining logic.

Predicates

Predicates are functions that return true or false. Funx provides combinators for composing them cleanly.

Pred DSL

The Predicate module includes a DSL for building boolean predicates declaratively:

use Funx.Predicate
alias Funx.Predicate.{GreaterThanOrEqual, IsTrue, In}
check_eligible = pred do
check :age, {GreaterThanOrEqual, value: 18}
check :verified, IsTrue
check :role, {In, values: [:admin, :moderator]}
end
Enum.filter(users, check_eligible)

Features:

Validation

The Validate module provides declarative data validation with applicative error accumulation—all validators run and all errors are collected.

Validate DSL

use Funx.Validate
alias Funx.Monad.Either
alias Funx.Validator.{Required, Email, MinLength, Positive}
user_validation =
validate do
at :name, [Required, {MinLength, min: 3}]
at :email, [Required, Email]
at :age, Positive
end
Either.validate(%{name: "Alice", email: "alice@example.com", age: 30}, user_validation)
# => %Right{right: %{name: "Alice", email: "alice@example.com", age: 30}}
Either.validate(%{name: "", email: "bad", age: -5}, user_validation)
# => %Left{left: %ValidationError{errors: ["is required", "must be at least 3 characters", ...]}}

Features:

Built-in validators: Required, Email, MinLength, MaxLength, Pattern, String, Integer, Float, Number, Boolean, Atom, List, Map, Positive, Negative, GreaterThan, LessThan, In, NotIn, Range, Each, Confirmation, Not

Folding

The Foldable protocol defines how to reduce a structure to a single result.

Useful for accumulating values, transforming collections, or extracting data.

Filtering

The Filterable protocol defines how to conditionally retain values within a context.

Sequencing

Sequencing runs a series of monadic operations in order, combining the results.

Lifting

Lifting functions promote ordinary logic into a monadic or contextual form.

Interop

Funx integrates with common Elixir patterns like {:ok, value} and {:error, reason}.

Documentation

The authoritative API documentation is published on HexDocs.

Learning Resources

Contributing

  1. Fork the repository.
  2. Create a new branch for the feature or bugfix (git checkout -b feature-branch).
  3. Commit changes (git commit -am 'Add new feature').
  4. Push the branch (git push origin feature-branch).
  5. Create a pull request.

License

This project is licensed under the MIT License.