lib/monad/either/usage-rules.md

# `Funx.Monad.Either` Usage Rules

## LLM Functional Programming Foundation

**Key Concepts for LLMs:**

**CRITICAL Elixir Implementation**: All monadic operations are under `Funx.Monad` protocol

- **NO separate Functor/Applicative protocols** - Elixir protocols cannot be extended after definition
- Always use `Monad.map/2`, `Monad.bind/2`, `Monad.ap/2` or import `Funx.Monad`
- Different from Haskell's separate Functor, Applicative, Monad typeclasses

**Either**: Represents immediate success/failure with detailed error context

- `left(error)` represents failure with error information
- `right(value)` represents success with the actual value
- **Right-biased**: Operations work on the Right (success) path
- **Immediate/Synchronous**: Values exist right now, no deferred execution
- **No concurrency**: All operations are synchronous - use Effect for async operations

**Right-biased Monad**: Operations transform Right values, preserve Left errors

- `map/2`, `bind/2`, `ap/2` only operate on Right values
- Left values (errors) pass through unchanged
- Similar to Maybe but with error context preserved

**Validation vs Error-handling**: Two distinct patterns

- **Validation**: Use `traverse_a/2` to collect ALL errors
- **Error-handling**: Use `bind/2` chains that stop on first error
- **Critical difference**: validation accumulates, error-handling short-circuits

**Kleisli Functions**: Functions `a -> Either e b` (unwrapped input, wrapped output)

- **Primary use**: `traverse/2`, `traverse_a/2`, and `concat_map/2` for list operations
- **Individual use**: `bind/2` for single Either values
- Example: `validate_email :: String -> Either ValidationError Email`

**Key List Operation Patterns:**

- `concat([Either e a])` → `[a]` (extract all Right values, ignore Left)
- `concat_map([a], kleisli_fn)` → `[b]` (apply Kleisli, collect Right results)
- `traverse([a], kleisli_fn)` → `Either e [b]` (apply Kleisli, all succeed or first Left)
- `traverse_a([a], kleisli_fn)` → `Either [e] [b]` (apply Kleisli, all succeed or collect all Left)
- `sequence([Either e a])` → `Either e [a]` (like traverse with identity, first Left or all Right)

**Sequence (Category Theory)**: Transform type constructor order

- `[Either e a]` → `Either e [a]` (list of Either becomes Either of list)
- Fails fast: first Left value becomes the result
- Success: all Right values collected into Right list

## LLM Decision Guide: When to Use Either

**✅ Use Either when:**

- Need specific error context/details
- Multiple validation steps with different error types  
- Business logic with detailed failure messages
- Error recovery or different handling per error type
- User says: "validate", "check", "ensure", "verify", "error details"

**❌ Use Effect when:**

- Async operations (database calls, HTTP requests, file I/O)
- Need concurrency or deferred execution
- Operations that take significant time
- User says: "async", "concurrent", "fetch", "call API"

**❌ Use Maybe when:**

- Simple presence/absence (no error context needed)
- "Not found" is sufficient error information
- Optional fields where missing is normal

**⚡ Either Strategy Decision:**

- **Single operation error-handling**: Use `bind/2` chains
- **Multi-field validation**: Use `validate/2` to collect all errors
- **Transform success values**: Use `map/2` with regular functions
- **Combine Either values**: Use `ap/2` for applicative patterns
- **Convert from Maybe**: Use `maybe_to_either/2` with error message
- **Pattern match results**: Use `%Left{left: error}` and `%Right{right: value}` struct patterns

**⚙️ Function Choice Guide (Mathematical Purpose):**

- **Chain error-prone operations**: `bind/2` with Kleisli functions
- **Transform success values**: `map/2` with regular functions
- **Validate multiple fields**: `validate/2` for comprehensive error collection
- **Apply functions to multiple Either**: `ap/2` for combining contexts
- **Convert lists**: `sequence/1` to collect successes or first failure
- **Handle specific errors**: Pattern match Left values for recovery

## LLM Context Clues

**User language → Either patterns:**

- "validate user input" → Use Either for validation with specific error messages
- "parse and validate" → Chain with `bind/2` for step-by-step validation
- "check all fields" → Use `validate/2` to collect all validation errors
- "detailed error messages" → Left values contain specific error information
- "stop on first error" → Use `bind/2` chains for fail-fast behavior
- "collect all errors" → Use `validate/2` for comprehensive validation

## Quick Reference

- Use `right(value)` for success, `left(error)` for failure
- Chain operations with `bind/2` - stops on first Left (error)
- Transform success values with `map/2` - leaves Left unchanged  
- Use `bind/2` with identity to flatten nested Either values
- Validate data comprehensively with `validate/2` - collects all errors
- **Prefer `fold_l/3` over pattern matching** for functional case analysis
- Import `Funx.Monad` for `map`, `bind`, `ap` and `Funx.Foldable` for `fold_l`
- Convert from Maybe with error context using helper functions

## Overview

`Funx.Monad.Either` handles success/failure scenarios with detailed error context.

Use Either for:

- Parsing and validation with specific error messages
- Operations that can fail in multiple ways
- Business logic where error details matter for recovery
- API responses where clients need error specifics

**Key insight**: Either represents "success or failure" with the failure carrying detailed information. Right-biased operations focus on the success path while preserving any errors encountered.

## Constructors

### `right/1` - Wrap a Success Value

Creates an Either representing success:

```elixir
Either.right(42)           # Success: contains 42
Either.right("valid")      # Success: contains "valid"
Either.right([1, 2, 3])    # Success: contains [1, 2, 3]
```

### `left/1` - Wrap an Error Value

Creates an Either representing failure:

```elixir
Either.left("error")                    # Failure: contains error message
Either.left({:validation, "invalid"})   # Failure: structured error
Either.left(%ValidationError{})         # Failure: error struct
```

### `pure/1` - Alias for `right/1`

Alternative constructor for success values:

```elixir
Either.pure(42)    # Same as Either.right(42)
```

## Core Operations

### `map/2` - Transform Success Values

Applies a function to Right values, leaves Left values unchanged:

```elixir
import Funx.Monad
import Funx.Foldable

Either.right("hello")
|> map(&String.upcase/1)        # right("HELLO")

Either.left("error")  
|> map(&String.upcase/1)        # left("error") - function never runs
```

**Use `map` when:**

- You want to transform the success value
- The transformation function returns a plain value (not wrapped in Either)
- You want to preserve the Either structure

### `bind/2` - Chain Error-Prone Operations

Chains operations that return Either values, for fail-fast error handling:

```elixir
import Funx.Monad
import Funx.Foldable

# These functions return Either values
parse_int = fn s -> 
  case Integer.parse(s) do
    {int, ""} -> Either.right(int)
    _ -> Either.left("Invalid integer: #{s}")
  end
end

validate_positive = fn n ->
  if n > 0 do
    Either.right(n)
  else 
    Either.left("Must be positive: #{n}")
  end
end

Either.right("42")
|> bind(parse_int)           # right(42)
|> bind(validate_positive)   # right(42)

Either.right("invalid")
|> bind(parse_int)           # left("Invalid integer: invalid") - chain stops
|> bind(validate_positive)   # left("Invalid integer: invalid") - never runs
```

**Use `bind` when:**

- You're chaining operations that each can fail
- Each step depends on the success of the previous step
- You want fail-fast behavior (stop on first error)

**Common bind pattern:**

```elixir
def process_user_input(input) do
  Either.right(input)
  |> bind(&parse_user_data/1)      # String -> Either Error UserData
  |> bind(&validate_user_data/1)   # UserData -> Either Error ValidUser
  |> bind(&save_user/1)            # ValidUser -> Either Error SavedUser
end
```

### `ap/2` - Apply Functions Across Either Values

Applies a function in an Either to a value in an Either:

```elixir
import Funx.Monad
import Funx.Foldable

# Apply a wrapped function to wrapped values
Either.right(fn x -> x + 10 end)
|> ap(Either.right(5))          # right(15)

# Combine multiple Either values
add = fn x -> fn y -> x + y end end

Either.right(add)
|> ap(Either.right(3))          # right(fn y -> 3 + y end)  
|> ap(Either.right(4))          # right(7)

# If any value is left, result is left
Either.right(add)
|> ap(Either.left("error1"))    # left("error1")
|> ap(Either.right(4))          # left("error1")
```

**Use `ap` when:**

- You want to apply a function to multiple Either values
- You need all values to be Right for the operation to succeed
- You're implementing applicative patterns

### Flattening Nested Either Values with `bind`

Since there's no `join/1` function, use `bind/2` with the identity function to flatten nested Either values:

```elixir
import Funx.Monad
import Funx.Foldable

# Flatten nested Right using bind
nested_right = Either.right(Either.right(42))
bind(nested_right, fn inner -> inner end)    # right(42)

# Left in outer - stays Left
outer_left = Either.left("outer error")
bind(outer_left, fn inner -> inner end)      # left("outer error")

# Left in inner - becomes Left
inner_left = Either.right(Either.left("inner error"))
bind(inner_left, fn inner -> inner end)      # left("inner error")
```

**Use this pattern when:**

- You have nested Either values that need flattening
- You're implementing monadic operations manually
- You're working with higher-order Either computations

## List Operations

### `concat/1` - Extract All Right Values

Removes all Left values and unwraps Right values from a list:

```elixir
Either.concat([
  Either.right(1),
  Either.left("error1"),
  Either.right(3),
  Either.left("error2")
])                              # [1, 3]
```

### `validate/2` - Comprehensive Data Validation

The high-level validation function that collects ALL errors from multiple validators:

```elixir
# Basic validation with error lists
validate_positive = fn n ->
  if n > 0, do: Either.right(n), else: Either.left(["Must be positive"])
end

validate_even = fn n ->
  if rem(n, 2) == 0, do: Either.right(n), else: Either.left(["Must be even"])
end

Either.validate(3, [validate_positive, validate_even])
# left(["Must be even"])

Either.validate(-2, [validate_positive, validate_even])  
# left(["Must be positive"])
```

**Use `validate` when:**

- You need comprehensive validation with ALL error details
- You're validating forms or user input
- You want to show users all validation problems at once
- You need to apply multiple validation rules to a single value

### Validation with ValidationError

For comprehensive domain validation with structured error handling, use `Funx.Errors.ValidationError`:

```elixir
alias Funx.Errors.ValidationError

# Wrap simple errors in ValidationError
validate_age = fn age ->
  Either.lift_predicate(age, &(&1 >= 18), "Must be 18 or older")
  |> Either.map_left(&ValidationError.new/1)
end

Either.validate(user, [validate_age])
# left(ValidationError{errors: ["Must be 18 or older"]})
```

**See `ValidationError` usage rules for advanced patterns:**

- Curried validation functions with `curry_r/1`
- Fallback validation with `Either.or_else/2`
- Error message transformation techniques
- Group validation with `traverse/2` and `traverse_a/2`
- Sequential vs comprehensive validation strategies

### `concat_map/2` - Apply Function and Collect Rights

Applies a function to each element, collecting only Right results:

### `traverse/2` - Apply Kleisli to List (First Error or All Success)

Applies a Kleisli function to each element, stopping at first Left:

```elixir
import Funx.Monad
import Funx.Foldable

# Kleisli function: String -> Either String Integer
parse_number = fn str ->
  case Integer.parse(str) do
    {num, ""} -> Either.right(num)
    _ -> Either.left("Invalid number: #{str}")
  end
end

# All succeed - get Either list
Either.traverse(["1", "2", "3"], parse_number)  # right([1, 2, 3])

# First failure stops processing
Either.traverse(["1", "invalid", "3"], parse_number)  
# left("Invalid number: invalid")
```

**Use `traverse` when:**

- All operations must succeed for meaningful result
- You want fail-fast behavior on lists
- Converting `[a]` to `Either e [b]` with validation

### `traverse_a/2` - Apply Kleisli to List (Collect All Errors)

Applies a Kleisli function to each element, collecting ALL errors:

```elixir
# Same Kleisli function as above, but returns error lists for accumulation
validate_number = fn str ->
  case Integer.parse(str) do
    {num, ""} -> Either.right(num)
    _ -> Either.left(["Invalid number: #{str}"])  # List for accumulation
  end
end

# Collect ALL errors
Either.traverse_a(["1", "invalid", "3", "bad"], validate_number)
# left(["Invalid number: invalid", "Invalid number: bad"])

# All succeed - get Right list
Either.traverse_a(["1", "2", "3"], validate_number)  # right([1, 2, 3])
```

**Use `traverse_a` when:**

- You want to collect ALL errors from validation
- You need comprehensive error reporting
- You're implementing validation that shows all problems at once

### `concat_map/2` - Apply Kleisli to List (Collect Successes)

Applies a Kleisli function to each element, collecting only successful results:

```elixir
# Collect only successes - get plain list
Either.concat_map(["1", "invalid", "3", "bad"], parse_number)  # [1, 3]

# All succeed - get all results
Either.concat_map(["1", "2", "3"], parse_number)  # [1, 2, 3]

# All fail - get empty list
Either.concat_map(["bad", "invalid", "error"], parse_number)  # []
```

**Use `concat_map` when:**

- Partial success is acceptable
- You want to collect all valid results
- You need resilient processing that continues on failure

### `sequence/1` - Convert List of Either to Either List

Converts `[Either e a]` to `Either e [a]` - equivalent to `traverse` with identity function:

```elixir
# All success - collect values
Either.sequence([
  Either.right(1),
  Either.right(2),
  Either.right(3)
])                              # right([1, 2, 3])

# First failure stops and returns that error
Either.sequence([
  Either.right(1),
  Either.left("error2"),
  Either.left("error3")
])                              # left("error2")

# Relationship to traverse
Either.sequence(either_list) == Either.traverse(either_list, fn x -> x end)
```

**Use `sequence` when:**

- You have a list of Either values from previous computations
- You want all to succeed, or the first failure
- You're collecting results from multiple operations

### Operation Comparison

```elixir
user_data = ["valid@email.com", "invalid-email", "another@valid.com", "bad-format"]

# traverse: Stop at first error
Either.traverse(user_data, &validate_email/1)
# left("Invalid email format: invalid-email")

# traverse_a: Collect all errors  
Either.traverse_a(user_data, &validate_email_with_list_error/1)
# left(["Invalid email format: invalid-email", "Invalid email format: bad-format"])

# concat_map: Collect successes, ignore failures
Either.concat_map(user_data, &validate_email/1)
# ["valid@email.com", "another@valid.com"]
```

## Validation

Validation is a specialized use of Either for comprehensive error collection.

See the `validate/2` function in the List Operations section above.

## Lifting

### `lift_predicate/3` - Convert Predicate to Either

Converts a predicate function into Either-returning validation:

```elixir
validate_positive = Either.lift_predicate(&(&1 > 0), "Must be positive")

validate_positive.(5)   # right(5)
validate_positive.(-1)  # left("Must be positive")
```

### `lift_maybe/2` - Convert Maybe to Either

Converts a Maybe to Either with error context:

```elixir
maybe_user = Maybe.just(%{name: "Alice"})
Either.lift_maybe(maybe_user, "User not found")  # right(%{name: "Alice"})

Maybe.nothing() |> Either.lift_maybe("User not found")  # left("User not found")
```

### `lift_eq/1` and `lift_ord/1` - Lift Comparison Functions

Lifts comparison functions for use in Either context:

```elixir
# Lift equality for Either values
Either.lift_eq(&==/2)

# Lift ordering for Either values  
Either.lift_ord(&compare/2)
```

## Elixir Interoperability

### `from_result/1` - Convert from Result Tuples

```elixir
# Convert from {:ok, value} | {:error, reason} tuples
Either.from_result({:ok, 42})         # right(42)
Either.from_result({:error, "fail"})  # left("fail")
```

### `to_result/1` - Convert to Result Tuples

```elixir
# Convert to {:ok, value} | {:error, reason} tuples
Either.to_result(Either.right(42))        # {:ok, 42}
Either.to_result(Either.left("fail"))     # {:error, "fail"}
```

### `from_try/1` - Safe Function Execution

```elixir
# Run function safely, catching exceptions
Either.from_try(fn -> 42 / 0 end)  # left(%ArithmeticError{})
Either.from_try(fn -> 42 / 2 end)  # right(21.0)
```

### `to_try!/1` - Unwrap or Raise

```elixir
Either.to_try!(Either.right(42))       # 42
Either.to_try!(Either.left("error"))   # raises RuntimeError: "error"
```

## Folding Either Values

**Core Concept**: Both `Left` and `Right` implement the `Funx.Foldable` protocol, providing `fold_l/3` for catamorphism (breaking down data structures).

### `fold_l/3` - Functional Case Analysis

The fundamental operation for handling Either values without pattern matching:

```elixir
import Funx.Foldable

# fold_l(either_value, right_function, left_function)
result = fold_l(either_value, 
  fn success_value -> "Success: #{success_value}" end,  # Right case
  fn error_value -> "Error: #{error_value}" end        # Left case
)

# Examples
fold_l(Either.right(42), 
  fn value -> value * 2 end,     # Runs this: 84
  fn error -> 0 end              # Never runs
)

fold_l(Either.left("failed"), 
  fn value -> value * 2 end,     # Never runs
  fn error -> "Got: #{error}" end # Runs this: "Got: failed"
)
```

**Use `fold_l` when:**

- You need to convert Either to a different type
- You want functional case analysis without pattern matching
- You're implementing higher-level combinators
- You need to handle both success and error cases

### Folding vs Pattern Matching

```elixir
# ❌ Imperative pattern matching
case either_result do
  %Right{right: value} -> "Success: #{value}"
  %Left{left: error} -> "Error: #{error}"
end

# ✅ Functional folding
fold_l(either_result,
  fn value -> "Success: #{value}" end,
  fn error -> "Error: #{error}" end
)
```

### Advanced Folding Patterns

```elixir
# Convert Either to Result tuple
to_result = fn either ->
  fold_l(either,
    fn value -> {:ok, value} end,
    fn error -> {:error, error} end
  )
end

# Extract value with default
get_or_default = fn either, default ->
  fold_l(either,
    fn value -> value end,
    fn _error -> default end
  )
end

# Conditional processing based on Either state
process_conditionally = fn either ->
  fold_l(either,
    fn value -> expensive_success_operation(value) end,
    fn error -> log_error_and_return_default(error) end
  )
end
```

**When pattern matching is still appropriate:**

```elixir
# Complex data destructuring that fold_l can't handle elegantly
case either_result do
  %Right{right: %User{name: name, role: :admin, permissions: perms}} -> 
    handle_admin(name, perms)
  %Right{right: %User{role: :user} = user} -> 
    handle_regular_user(user)
  %Left{left: %ValidationError{field: field, message: msg}} -> 
    handle_validation_error(field, msg)
  %Left{left: error} -> 
    handle_generic_error(error)
end
```

## Validation Patterns

### Error-handling (Fail Fast)

Use `bind/2` for operations that should stop on the first error:

```elixir
def process_payment(payment_data) do
  Either.right(payment_data)
  |> bind(&validate_card_number/1)     # Stop if card invalid
  |> bind(&validate_expiry_date/1)     # Stop if expiry invalid  
  |> bind(&validate_cvv/1)             # Stop if CVV invalid
  |> bind(&charge_card/1)              # Stop if charge fails
end
```

### Validation (Collect All Errors)

Use `traverse_a/2` to collect all validation errors:

```elixir
def validate_user_registration(data) do
  fields = [data.name, data.email, data.password, data.age]
  validators = [
    &validate_name/1,
    &validate_email/1, 
    &validate_password/1,
    &validate_age/1
  ]
  
  Either.traverse_a(fields, validators)
  |> fold_l(
    fn [name, email, password, age] -> 
      {:ok, %User{name: name, email: email, password: password, age: age}}
    end,
    fn errors -> {:error, List.flatten(errors)} end
  )
end
```

## Refinement

### `right?/1` and `left?/1` - Type Checks

```elixir
Either.right?(Either.right(42))      # true
Either.right?(Either.left("err"))    # false

Either.left?(Either.left("err"))     # true
Either.left?(Either.right(42))       # false
```

## Fallback and Extraction

### `get_or_else/2` - Extract Value with Default

```elixir
Either.right(42) |> Either.get_or_else(0)        # 42
Either.left("error") |> Either.get_or_else(0)    # 0
```

### `or_else/2` - Fallback on Left

```elixir
Either.right(42) |> Either.or_else(fn -> Either.right(0) end)     # right(42)
Either.left("error") |> Either.or_else(fn -> Either.right(0) end) # right(0)
```

### `map_left/2` - Transform Left Values

```elixir
# Transform error without affecting success
Either.right(42) |> Either.map_left(&String.upcase/1)     # right(42)
Either.left("error") |> Either.map_left(&String.upcase/1) # left("ERROR")
```

### `flip/1` - Swap Left and Right

```elixir
Either.flip(Either.right(42))           # left(42)
Either.flip(Either.left("error"))       # right("error")
```

### `filter_or_else/3` - Conditional Left Conversion

```elixir
# Convert Right to Left if predicate fails
Either.right(42) |> Either.filter_or_else(&(&1 > 50), "too small")  # left("too small")
Either.right(100) |> Either.filter_or_else(&(&1 > 50), "too small") # right(100)
```

### Combining Two Either Values with `ap/2`

Use the applicative pattern with `ap/2` to combine two Either values with a binary function:

```elixir
import Funx.Monad
import Funx.Foldable

# Combine two Either values using ap
add_fn = Either.right(&+/2)
ap(add_fn, Either.right(3)) |> ap(Either.right(4))     # right(7)
ap(add_fn, Either.right(3)) |> ap(Either.left("error"))   # left("error")
ap(add_fn, Either.left("error")) |> ap(Either.right(4))   # left("error")

# More concise with helper function
combine_either = fn ma, mb, f ->
  Either.right(f) |> ap(ma) |> ap(mb)
end

combine_either.(Either.right(3), Either.right(4), &+/2)         # right(7)
combine_either.(Either.right(3), Either.left("error"), &+/2)    # left("error")

# String concatenation
combine_either.(Either.right("Hello, "), Either.right("World!"), &<>/2)  # right("Hello, World!")

# Validation combining
combine_either.(
  validate_name("Alice"),
  validate_age(30),
  fn name, age -> %{name: name, age: age} end
)  # right(%{name: "Alice", age: 30}) or left(error)
```

**Use this pattern when:**

- You need to combine exactly two Either values with a binary function
- You want applicative-style combination that fails fast on first Left
- You're implementing patterns similar to liftA2 from other functional languages

## Common Patterns

### API Response Handling

```elixir
def fetch_user_profile(user_id) do
  Either.right(user_id)
  |> bind(&validate_user_id/1)        # Validate ID format
  |> bind(&fetch_from_database/1)     # Database lookup
  |> bind(&check_permissions/1)       # Authorization check
  |> bind(&format_profile/1)          # Format response
  |> fold_l(
    fn profile -> {:ok, profile} end,
    fn error -> {:error, error} end
  )
end
```

### Form Validation with Comprehensive Error Collection

```elixir
# Create individual field validators that work on the whole form
validate_name_field = fn form_data ->
  if String.length(form_data.name) > 0 do
    Either.right(form_data.name)
  else
    Either.left(["Name is required"])
  end
end

validate_email_field = fn form_data ->
  if String.contains?(form_data.email, "@") and String.length(form_data.email) > 5 do
    Either.right(form_data.email)
  else
    Either.left(["Email must be valid"])
  end
end

validate_password_field = fn form_data ->
  if String.length(form_data.password) >= 8 do
    Either.right(form_data.password)
  else
    Either.left(["Password must be at least 8 characters"])
  end
end

# Validate the entire form - collects ALL validation errors
def validate_registration_form(form_data) do
  validators = [
    validate_name_field,
    validate_email_field,
    validate_password_field
  ]
  
  Either.validate(form_data, validators)
  |> fold_l(
    fn validated_form ->
      {:ok, %{
        name: validated_form.name,
        email: validated_form.email,
        password: validated_form.password
      }}
    end,
    fn all_errors -> 
      {:error, "Registration failed: #{Enum.join(List.flatten(all_errors), ", ")}"}
    end
  )
end

# Example usage
form_data = %{name: "", email: "invalid", password: "123"}

validate_registration_form(form_data)
# {:error, "Registration failed: Name is required, Email must be valid, Password must be at least 8 characters"}

valid_form = %{name: "Alice", email: "alice@example.com", password: "securepass123"}
validate_registration_form(valid_form)
# {:ok, %{name: "Alice", email: "alice@example.com", password: "securepass123"}}
```

### Configuration Loading

```elixir
def load_config(config_path) do
  Either.right(config_path)
  |> bind(&read_config_file/1)         # File -> Either Error String
  |> bind(&parse_json/1)               # String -> Either Error Map
  |> bind(&validate_schema/1)          # Map -> Either Error ValidConfig
  |> bind(&apply_defaults/1)           # ValidConfig -> Either Error FinalConfig
end

defp read_config_file(path) do
  File.read(path)
  |> Either.from_result()
  |> Either.map_left(fn reason -> "Failed to read #{path}: #{reason}" end)
end

defp parse_json(content) do
  Jason.decode(content)
  |> Either.from_result()
  |> Either.map_left(fn %Jason.DecodeError{data: data} -> "Invalid JSON: #{data}" end)
end
```

## Integration with Other Modules

### With Funx.Utils

```elixir
# Curry validation functions
validate_range = Utils.curry(fn min, max, value ->
  cond do
    value < min -> Either.left("Value #{value} below minimum #{min}")
    value > max -> Either.left("Value #{value} above maximum #{max}")  
    true -> Either.right(value)
  end
end)

validate_age = validate_range.(0, 150)
validate_percentage = validate_range.(0, 100)

Either.right(25) |> bind(validate_age)        # right(25)
Either.right(-5) |> bind(validate_age)        # left("Value -5 below minimum 0")
```

### Conversion from Maybe

```elixir
# Convert Maybe to Either with error context
def maybe_to_either(maybe_value, error_message) do
  Maybe.fold_l(maybe_value, 
    fn value -> Either.right(value) end,
    fn -> Either.left(error_message) end
  )
end

# Usage in pipeline
def find_and_validate_user(user_id) do
  user_id
  |> find_user()                    # Returns Maybe User
  |> maybe_to_either("User not found")
  |> bind(&validate_user_active/1)  # Continue with Either validation
end
```

### With Predicate Logic

```elixir
# Convert predicates to Either validators
def predicate_to_either(predicate, error_message) do
  fn value ->
    if predicate.(value) do
      Either.right(value)
    else
      Either.left(error_message)
    end
  end
end

# Use with validation
is_adult = fn user -> user.age >= 18 end
validate_adult = predicate_to_either(is_adult, "Must be 18 or older")

Either.right(%{age: 25})
|> bind(validate_adult)             # right(%{age: 25})

Either.right(%{age: 16})
|> bind(validate_adult)             # left("Must be 18 or older")
```

## Advanced Patterns

### Error Recovery

```elixir
def process_with_fallback(data) do
  data
  |> process_primary_method()
  |> fold_l(
    fn result -> Either.right(result) end,
    fn _error -> data |> process_fallback_method() end
  )
end

# Or using a helper function
def either_or_else(either_result, fallback_fn) do
  fold_l(either_result, &Either.right/1, fn _error -> fallback_fn.() end)
end

data
|> process_primary_method()
|> either_or_else(fn -> process_fallback_method(data) end)
```

### Error Mapping

```elixir
def map_error(either_value, error_mapper) do
  fold_l(either_value, 
    &Either.right/1,
    fn error -> Either.left(error_mapper.(error)) end
  )
end

# Usage: Convert database errors to user-friendly messages
def friendly_database_error(db_error) do
  case db_error do
    {:constraint, _} -> "Data validation failed"
    {:connection, _} -> "Database temporarily unavailable"
    _ -> "An unexpected error occurred"
  end
end

database_operation()
|> map_error(&friendly_database_error/1)
```

## Testing Strategies

### Unit Testing Validation Logic

```elixir
defmodule ValidationTest do
  use ExUnit.Case
  import Funx.Monad

  test "email validation with detailed errors" do
    # Valid email
    assert validate_email("user@example.com") == Either.right("user@example.com")
    
    # Invalid formats
    assert validate_email("") == Either.left("Email cannot be empty")
    assert validate_email("invalid") == Either.left("Email must contain @")
    assert validate_email("user@") == Either.left("Invalid domain")
  end

  test "chaining validations with bind" do
    # Successful chain
    result = Either.right("123")
    |> bind(&parse_integer/1)
    |> bind(&validate_positive/1)
    
    assert result == Either.right(123)
    
    # Chain breaks on first error
    result = Either.right("invalid")
    |> bind(&parse_integer/1)        # Fails here
    |> bind(&validate_positive/1)    # Never runs
    
    assert {:left, _error} = result
  end

  test "collecting validation errors with traverse_a" do
    invalid_data = ["", "not-email", "invalid-age"]
    validators = [&validate_name/1, &validate_email/1, &validate_age/1]
    
    case Either.traverse_a(invalid_data, validators) do
      {:left, errors} ->
        assert length(errors) == 3  # All three validations failed
        assert "Name cannot be empty" in errors
      {:right, _} ->
        flunk("Expected validation errors")
    end
  end
end
```

## Performance Considerations

### Short-Circuiting

```elixir
# bind chains short-circuit on first Left
# This makes error-handling very efficient

expensive_validation = fn data ->
  # This never runs if earlier validation failed
  Process.sleep(1000)
  Either.right(data)
end

Either.left("early error")
|> bind(&some_validation/1)
|> bind(expensive_validation)      # Never executes
|> bind(&another_validation/1)
# Result: left("early error"), computed instantly
```

### Memory Usage

```elixir
# Either uses minimal memory overhead
# right(value) stores value plus small wrapper
# left(error) stores error plus small wrapper

# Efficient for error handling
validation_result = %{
  user: Either.right(%User{id: 1}),    # Small overhead
  error: Either.left("Validation failed")  # Small overhead
}
```

## Troubleshooting Common Issues

### Issue: Nested Either Values

```elixir
# ❌ Problem: Manual nesting creates Either (Either a)
result = Either.right(user_data)
|> map(&validate_user/1)  # validate_user returns Either
# Result: Either (Either User) - nested!

# ✅ Solution: Use bind for functions that return Either
result = Either.right(user_data)
|> bind(&validate_user/1)  # Automatically flattens to Either User
```

### Issue: Mixing Validation Strategies

```elixir
# ❌ Problem: Inconsistent error handling approach
def mixed_validation(data) do
  Either.right(data)
  |> bind(&validate_required_field/1)    # Stops on first error
  |> Either.validate([&validate_format/1])  # But this tries to collect all
end

# ✅ Solution: Pick one strategy consistently
def fail_fast_validation(data) do
  Either.right(data)
  |> bind(&validate_required_field/1)
  |> bind(&validate_format/1)
  |> bind(&validate_business_rules/1)
end

def collect_all_errors_validation(data) do
  fields = [data.field1, data.field2, data.field3]
  validators = [&validate_field1/1, &validate_field2/1, &validate_field3/1]
  Either.traverse_a(fields, validators)
end
```

### Issue: Pattern Matching Confusion

```elixir
# ❌ Problem: Imperative pattern matching instead of functional folding
case either_value do
  %Right{right: value} -> process_success(value)
  %Left{left: error} -> handle_error(error)
end

# ✅ Solution: Use functional folding instead  
either_value
|> fold_l(
  fn value -> process_success(value) end,
  fn error -> handle_error(error) end
)
```

### Issue: Over-using Pattern Matching

```elixir
# ❌ Problem: Manual unwrapping defeats the purpose
case either_value do
  %Right{right: value} ->
    new_value = transform(value)
    Either.right(new_value)
  %Left{left: error} -> Either.left(error)
end

# ✅ Solution: Use map to stay in Either context
either_value |> map(&transform/1)
```

## When Not to Use Either

### Use Maybe Instead When

```elixir
# ❌ Either with generic errors loses its advantage
def find_user(id) do
  case get_user(id) do
    nil -> Either.left("not found")  # Generic error
    user -> Either.right(user)
  end
end

# ✅ Maybe is simpler for basic presence/absence
def find_user(id) do
  case get_user(id) do
    nil -> Maybe.nothing()
    user -> Maybe.just(user)
  end
end
```

### Use Plain Values When

```elixir
# ❌ Either overhead for operations that can't fail
def calculate_tax(amount) do
  Either.right(amount)
  |> map(fn amt -> amt * 0.1 end)
end

# ✅ Plain calculation for guaranteed operations
def calculate_tax(amount) do
  amount * 0.1
end
```

### Use Exceptions When

```elixir
# ❌ Either for truly exceptional conditions
def divide(a, b) do
  if b == 0 do
    Either.left("Division by zero")
  else
    Either.right(a / b)
  end
end

# ✅ Exception for programmer errors
def divide(a, b) when b != 0 do
  a / b
end
# Let it crash on division by zero - it's a programming error
```

## Summary

Either provides error-safe computation with detailed failure context:

**Core Operations:**

- `right/1`: Wrap success values
- `left/1`: Wrap error values with context
- `map/2`: Transform success values, preserve errors
- `bind/2`: Chain Either-returning operations with fail-fast behavior
- `ap/2`: Apply functions across multiple Either values
- `traverse_a/2`: Validate with error accumulation
- `sequence/1`: Convert `[Either e a]` to `Either e [a]` with fail-fast

**Key Patterns:**

- Chain error-prone operations with `bind/2` for fail-fast
- Validate multiple fields with `traverse_a/2` for error collection
- Transform success values with `map/2`
- Pattern match for specific error handling and recovery
- Convert from {:ok, value} | {:error, reason} tuples

**Mathematical Properties:**

- **Functor**: `map` preserves structure (Right-biased)
- **Applicative**: `ap` applies functions in context (fails if any Left)
- **Monad**: `bind` enables dependent sequencing with error propagation

Remember: Either represents "success or detailed failure" - use it when error context matters for debugging, user feedback, or recovery strategies.