View Source Funx.Appendable protocol (funx v0.1.0)

A protocol for combining values in a generic, extensible way.

The Appendable protocol defines how two values of the same type can be combined. It is used throughout Funx in functions like traverse_a/2 and wither_a/2 to accumulate intermediate results without coupling logic to a specific type.

This protocol enables functions to remain flexible and composable when reducing, aggregating, or accumulating values across a wide variety of domains.

Required functions

  • coerce/1 – Normalizes an input value into a form suitable for aggregation.
  • append/2 – Combines two values of the same type into one.

Default – Flat list aggregation

A fallback implementation is provided for all types that do not define a specific Appendable instance. This default uses list concatenation as a universal aggregation strategy: all inputs are coerced into lists (if not already), and combined using ++.

When using the default aggregation strategy, values are collected in a plain list:

validate_positive = fn x ->
  Funx.Monad.Either.lift_predicate(x, &(&1 > 0), fn v -> "Value must be positive: " <> to_string(v) end)
end

validate_even = fn x ->
  Funx.Monad.Either.lift_predicate(x, &(rem(&1, 2) == 0), fn v -> "Value must be even: " <> to_string(v) end)
end

Funx.Monad.Either.validate(4, [validate_positive, validate_even])
#=> %Funx.Monad.Either.Right{right: 4}

Funx.Monad.Either.validate(3, [validate_positive, validate_even])
#=> %Funx.Monad.Either.Left{left: ["Value must be even: 3"]}

Funx.Monad.Either.validate(-3, [validate_positive, validate_even])
#=> %Funx.Monad.Either.Left{left: ["Value must be positive: -3", "Value must be even: -3"]}

Structured aggregation with ValidationError

You can also use a custom struct to hold errors. This example uses ValidationError:

alias Funx.Errors.ValidationError

validate_positive = fn x ->
  Funx.Monad.Either.lift_predicate(x, &(&1 > 0), fn v -> "Value must be positive: " <> to_string(v) end)
  |> Funx.Monad.Either.map_left(&ValidationError.new/1)
end

validate_even = fn x ->
  Funx.Monad.Either.lift_predicate(x, &(rem(&1, 2) == 0), fn v -> "Value must be even: " <> to_string(v) end)
  |> Funx.Monad.Either.map_left(&ValidationError.new/1)
end

Funx.Monad.Either.validate(-3, [validate_positive, validate_even])
#=> %Funx.Monad.Either.Left{
#     left: %ValidationError{
#       errors: ["Value must be positive: -3", "Value must be even: -3"]
#     }
#   }

Summary

Types

t()

All the types that implement this protocol.

Functions

Combines two values into a single result.

Normalizes a single input value into a form suitable for accumulation.

Types

@type t() :: term()

All the types that implement this protocol.

Functions

Link to this function

append(accumulator, coerced)

View Source

Combines two values into a single result.

Implementations must ensure the operation is associative within their type. For types that require disambiguation or structural control, define a custom implementation.

Normalizes a single input value into a form suitable for accumulation.