# `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`, and `superset?/3`.
* `Ord` is used for: `sort/2` and `strict_sort/2`.
* `strict_sort/2` combines `Ord` (for sorting) and `Eq` (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` or `Ord.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`.
```elixir
Funx.List.uniq([%Car{vin: "A"}, %Car{vin: "A"}])
# => [%Car{vin: "A"}]
```
With custom comparator:
```elixir
eq = Eq.Utils.contramap(& &1.make)
Funx.List.uniq(cars, eq)
```
### `union/3`
Combines two lists, removing duplicates using `Eq`.
```elixir
Funx.List.union([1, 2], [2, 3])
# => [1, 2, 3]
```
### `intersection/3`
Returns the elements common to both lists.
```elixir
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.
```elixir
Funx.List.difference([1, 2, 3], [2])
# => [1, 3]
```
### `symmetric_difference/3`
Returns elements that appear in only one of the two lists.
```elixir
Funx.List.symmetric_difference([1, 2], [2, 3])
# => [1, 3]
```
### `subset?/3` and `superset?/3`
Checks for inclusion using `Eq`.
```elixir
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.
```elixir
Funx.List.sort([3, 1, 2])
# => [1, 2, 3]
```
With ad-hoc ordering:
```elixir
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.
```elixir
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.
```elixir
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 of `Eq`:
```elixir
# BAD
Enum.uniq_by(users, & &1.id) # not composable or testable
```
* Sorting maps or structs without defining `Ord`:
```elixir
# BAD
Enum.sort([%User{}]) # may raise
```
* Mixing raw and protocol-based comparison:
```elixir
# 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` or `difference`.
* You want deterministic, extensible sorting.
* You're working with domain types (e.g., `User`, `Car`, `Ticket`) and want safe behavior.