View Source
Funx.List
Usage Rules
Quick Reference
- All functions operate on Elixir lists (
[term()]
). Eq
is used for:uniq/2
,union/3
,intersection/3
,difference/3
,symmetric_difference/3
,subset?/3
, andsuperset?/3
.Ord
is used for:sort/2
andstrict_sort/2
.strict_sort/2
combinesOrd
(for sorting) andEq
(for deduplication).- All functions default to protocol dispatch; no wiring needed if instances exist.
- Ad-hoc comparators can be passed using
Eq.Utils.contramap/1
orOrd.Utils.contramap/1
.
Overview
The Funx.List
module provides equality- and ordering-aware utilities for working with lists.
It supports deduplication, sorting, and set-like behavior using Eq
and Ord
instances.
This allows logic like "unique cars by VIN" or "sort by price, then mileage" to be clean, composable, and domain-aware.
Eq-Based Operations
uniq/2
Removes duplicates using Eq
.
Funx.List.uniq([%Car{vin: "A"}, %Car{vin: "A"}])
# => [%Car{vin: "A"}]
With custom comparator:
eq = Eq.Utils.contramap(& &1.make)
Funx.List.uniq(cars, eq)
union/3
Combines two lists, removing duplicates using Eq
.
Funx.List.union([1, 2], [2, 3])
# => [1, 2, 3]
intersection/3
Returns the elements common to both lists.
Funx.List.intersection([1, 2, 3], [2, 3, 4])
# => [2, 3]
difference/3
Returns elements from the first list that are not in the second.
Funx.List.difference([1, 2, 3], [2])
# => [1, 3]
symmetric_difference/3
Returns elements that appear in only one of the two lists.
Funx.List.symmetric_difference([1, 2], [2, 3])
# => [1, 3]
subset?/3
and superset?/3
Checks for inclusion using Eq
.
Funx.List.subset?([1, 2], [1, 2, 3])
# => true
Funx.List.superset?([1, 2, 3], [1, 2])
# => true
Ord-Based Operations
sort/2
Sorts the list using Ord
. Defaults to protocol dispatch.
Funx.List.sort([3, 1, 2])
# => [1, 2, 3]
With ad-hoc ordering:
ord = Ord.Utils.contramap(& &1.price)
Funx.List.sort(cars, ord)
strict_sort/2
Sorts the list and removes duplicates. Uses Ord
for sorting and derives Eq
from ordering.
Funx.List.strict_sort([3, 1, 3, 2])
# => [1, 2, 3]
Concatenation
concat/1
Concatenates a list of lists left-to-right using the ListConcat
monoid.
Funx.List.concat([[1], [2, 3], [4]])
# => [1, 2, 3, 4]
Good Patterns
- Use
uniq/2
,intersection/3
, and related functions instead of manual deduplication. - Use
contramap/1
to lift equality or ordering by projecting key fields. - Use
strict_sort/2
when you need sorted unique results. - Define protocol instances for domain types to remove the need for custom comparator logic.
Anti-Patterns
Using
==
in list operations instead ofEq
:# BAD Enum.uniq_by(users, & &1.id) # not composable or testable
Sorting maps or structs without defining
Ord
:# BAD Enum.sort([%User{}]) # may raise
Mixing raw and protocol-based comparison:
# BAD if user1.id == user2.id and Eq.eq?(user1, user2), do: ...
When to Use
Use Funx.List
when:
- You want list operations that follow your domain's equality or ordering rules.
- You need composable set logic like
union
ordifference
. - You want deterministic, extensible sorting.
- You're working with domain types (e.g.,
User
,Car
,Ticket
) and want safe behavior.