lib/monad/maybe/usage-rules.md

# `Funx.Monad.Maybe` 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

**Kleisli Function**: A function `a -> Maybe b` (takes unwrapped value, returns wrapped value)

- **Primary use**: `traverse/2` and `concat_map/2` for list operations
- **Individual use**: `Monad.bind/2` for single Maybe values
- Example: `find_user :: UserId -> Maybe User`

**Key List Operation Patterns:**

- `concat([Maybe a])` → `[a]` (extract all Just values, ignore Nothing)
- `concat_map([a], kleisli_fn)` → `[b]` (apply Kleisli, collect Just results)
- `traverse([a], kleisli_fn)` → `Maybe [b]` (apply Kleisli, all succeed or Nothing)
- `sequence([Maybe a])` → `Maybe [a]` (like traverse with identity function)

**Functor**: Something you can `map` over while preserving structure

- `Monad.map/2 :: (a -> b) -> Maybe a -> Maybe b`
- Transforms the present value, leaves Nothing unchanged

**Applicative**: Allows applying functions inside a context

- `Monad.ap/2 :: Maybe (a -> b) -> Maybe a -> Maybe b`  
- Can combine multiple Maybe values

**Monad**: Supports `bind` for chaining dependent computations

- `Monad.bind/2 :: Maybe a -> (a -> Maybe b) -> Maybe b`
- Flattens nested Maybe values automatically

**Sequence (Category Theory)**: Swap the order of two type constructors

- `[Maybe a]` → `Maybe [a]` (list of Maybe becomes Maybe of list)
- Not about sequential processing - about type transformation

**Maybe**: Represents immediate presence/absence of values

- **Presence**: Value exists and is usable (`just(value)`)
- **Absence**: Value is missing, incomplete, or unavailable (`nothing()`)
- **Immediate/Synchronous**: Values exist right now, no deferred execution
- **No concurrency**: All operations are synchronous - use Effect for async operations

## LLM Decision Guide: When to Use Maybe

**✅ Use Maybe when:**

- Simple presence/absence (user profile, config value)
- No error context needed ("not found" is sufficient)
- Chaining operations that should skip on absence
- Optional fields or nullable database columns
- User says: "optional", "might not exist", "could be missing"

**❌ 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 Either when:**

- Need specific error context ("user not found", "validation failed")
- Multiple error types or recovery strategies
- Business validation with detailed failure messages
- User says: "validate", "check requirements", "ensure valid"

**⚡ Maybe Strategy Decision:**

- **Simple presence check**: Use `just/1` and `nothing/0` constructors
- **Chain operations**: Use `bind/2` for individual Maybe sequencing
- **Transform present values**: Use `map/2` with regular functions
- **Combine multiple Maybe values**: Use `ap/2` for applicative pattern
- **Apply Kleisli to lists**: Use `traverse/2` (all must succeed) or `concat_map/2` (collect successes)
- **Convert lists**: Use `sequence/1` to flip `[Maybe a]` to `Maybe [a]`
- **Pattern match results**: Use `case` with `%Just{value: value}` and `%Nothing{}`

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

- **Chain dependent lookups**: `bind/2` with functions returning Maybe
- **Transform present values**: `map/2` with functions returning plain values  
- **Apply functions to multiple Maybe values**: `ap/2` for combining contexts
- **Handle missing values**: Pattern match or use `from_nil/1`, `to_nil/1`
- **Work with lists**: `sequence/1`, `traverse/2`, `traverse_a/2`

## LLM Context Clues

**User language → Maybe patterns:**

- "optional user profile" → `find_user/1` returning Maybe User
- "might not have email" → Maybe String for optional email field
- "chain lookups" → `bind/2` with multiple Maybe-returning functions
- "transform if present" → `map/2` to modify just the value
- "combine optional values" → `ap/2` to apply function across Maybe values
- "list of optional items" → `sequence/1` to collect all present values

## Quick Reference

- Use `just(value)` for present values, `nothing()` for absence
- Chain operations with `bind/2` - they skip automatically on `nothing`
- Transform values with `map/2` - leaves `nothing` unchanged
- Combine multiple Maybe values with `ap/2`
- Use `bind/2` with identity to flatten nested Maybes: `bind(nested_maybe, fn x -> x end)`
- Convert `[Maybe a]` to `Maybe [a]` with `sequence/1`
- **Prefer `fold_l/3` over pattern matching** for functional case analysis
- **Note**: Maybe values are structs `%Just{value: ...}` or `%Nothing{}`, not tagged tuples
- Import `Funx.Monad` for `map`, `bind`, `ap` and `Funx.Foldable` for `fold_l`

## Overview

`Funx.Monad.Maybe` handles presence and absence without explicit null checks.

Use Maybe for:

- Optional fields and nullable database columns
- Operations that might not return a value
- Chaining computations that should skip on missing data
- Simple presence/absence (no detailed error context needed)

**Key insight**: Maybe represents "optional" - either there's a value (`just`) or there isn't (`nothing`). All operations respect this, automatically skipping work when there's nothing to work with.

## Constructors

### `just/1` - Wrap a Present Value

Creates a Maybe containing a value:

```elixir
Maybe.just(42)        # Present: contains 42
Maybe.just("hello")   # Present: contains "hello"
Maybe.just([1, 2, 3]) # Present: contains [1, 2, 3]
```

### `nothing/0` - Represent Absence

Creates a Maybe representing absence:

```elixir
Maybe.nothing()       # Absent: contains no value
```

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

Alternative constructor for present values:

```elixir
Maybe.pure(42)    # Same as Maybe.just(42)
```

## Core Operations

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

Applies a function to the value inside a `just`, leaves `nothing` unchanged:

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

Maybe.just(5)
|> map(fn x -> x * 2 end)    # just(10)

Maybe.nothing()
|> map(fn x -> x * 2 end)    # nothing() - function never runs
```

**Use `map` when:**

- You want to transform the value if it exists
- The transformation function returns a plain value (not wrapped in Maybe)
- You want to preserve the Maybe structure

### `bind/2` - Chain Dependent Operations

Chains operations that return Maybe values, automatically flattening nested Maybe:

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

# These functions return Maybe values
find_user = fn id -> if id > 0, do: Maybe.just(%{id: id}), else: Maybe.nothing() end
get_email = fn user -> if user.id == 1, do: Maybe.just("user@example.com"), else: Maybe.nothing() end

Maybe.just(1)
|> bind(find_user)    # just(%{id: 1})
|> bind(get_email)    # just("user@example.com")

Maybe.just(-1)
|> bind(find_user)    # nothing() - chain stops here
|> bind(get_email)    # nothing() - this never runs
```

**Use `bind` when:**

- You're chaining operations that each return Maybe
- Each step depends on the result of the previous step
- You want automatic short-circuiting on `nothing`

**Common bind pattern:**

```elixir
def process_user_id(user_id) do
  Maybe.just(user_id)
  |> bind(&find_user/1)         # UserId -> Maybe User
  |> bind(&get_user_profile/1)  # User -> Maybe Profile  
  |> bind(&format_name/1)       # Profile -> Maybe String
end
```

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

Applies a function in a Maybe to a value in a Maybe:

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

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

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

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

# If any value is nothing, result is nothing
Maybe.just(add)
|> ap(Maybe.nothing())        # nothing()
|> ap(Maybe.just(4))          # nothing()
```

**Use `ap` when:**

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

## Folding Maybe Values

**Core Concept**: Both `Just` and `Nothing` 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 Maybe values without pattern matching:

```elixir
import Funx.Foldable

# fold_l(maybe_value, just_function, nothing_function)
result = fold_l(maybe_value, 
  fn value -> "Found: #{value}" end,  # Just case
  fn -> "Not found" end               # Nothing case
)

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

fold_l(Maybe.nothing(), 
  fn value -> value * 2 end,    # Never runs
  fn -> "No value" end          # Runs this: "No value"
)
```

**Use `fold_l` when:**

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

### Folding vs Pattern Matching

```elixir
# ❌ Imperative pattern matching
case maybe_value do
  %Just{value: value} -> "Found: #{value}"
  %Nothing{} -> "Not found"
end

# ✅ Functional folding
fold_l(maybe_value,
  fn value -> "Found: #{value}" end,
  fn -> "Not found" end
)
```

### Advanced Folding Patterns

```elixir
# Extract value with default
get_or_default = fn maybe, default ->
  fold_l(maybe,
    fn value -> value end,
    fn -> default end
  )
end

# Convert Maybe to result tuple
to_result = fn maybe ->
  fold_l(maybe,
    fn value -> {:ok, value} end,
    fn -> {:error, :not_found} end
  )
end

# Conditional processing
process_if_present = fn maybe ->
  fold_l(maybe,
    fn value -> expensive_operation(value) end,
    fn -> :skipped end
  )
end
```

### Flattening Nested Maybe Values with `bind`

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

```elixir
import Funx.Monad

# Flatten nested Maybe using bind
nested = Maybe.just(Maybe.just(42))
bind(nested, fn inner -> inner end)    # just(42)

# Nothing in outer - stays nothing
outer_nothing = Maybe.nothing()
bind(outer_nothing, fn inner -> inner end)    # nothing()

# Nothing in inner - becomes nothing  
inner_nothing = Maybe.just(Maybe.nothing())
bind(inner_nothing, fn inner -> inner end)    # nothing()
```

**Use this pattern when:**

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

## Functional Error Handling

**Important**: Maybe values are structs `%Just{value: ...}` or `%Nothing{}`, not tagged tuples. Pattern matching must respect this shape.

Maybe values are best handled with functional folding:

```elixir
fold_l(maybe_value,
  fn value -> "Found: #{value}" end,
  fn -> "Not found" end
)
```

**Common patterns:**

```elixir
# Extract with default
value = fold_l(maybe_user,
  fn user -> user.name end,
  fn -> "Guest" end
)

# Process only if present
fold_l(maybe_config,
  fn config -> apply_config(config) end,
  fn -> :ok end
)
```

## Refinement

### `just?/1` and `nothing?/1` - Type Checks

```elixir
Maybe.just?(Maybe.just(42))      # true
Maybe.just?(Maybe.nothing())     # false

Maybe.nothing?(Maybe.nothing())  # true
Maybe.nothing?(Maybe.just(42))   # false
```

## Fallback and Extraction

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

```elixir
Maybe.just(42) |> Maybe.get_or_else(0)        # 42
Maybe.nothing() |> Maybe.get_or_else(0)       # 0
```

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

```elixir
Maybe.just(42) |> Maybe.or_else(fn -> Maybe.just(0) end)     # just(42)
Maybe.nothing() |> Maybe.or_else(fn -> Maybe.just(0) end)    # just(0)
```

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

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

```elixir
import Funx.Monad

# Combine two Maybe values using ap
add_fn = Maybe.just(&+/2)
ap(add_fn, Maybe.just(3)) |> ap(Maybe.just(4))     # just(7)
ap(add_fn, Maybe.just(3)) |> ap(Maybe.nothing())   # nothing()
ap(add_fn, Maybe.nothing()) |> ap(Maybe.just(4))   # nothing()

# More concise with helper function
combine_maybe = fn ma, mb, f ->
  Maybe.just(f) |> ap(ma) |> ap(mb)
end

combine_maybe.(Maybe.just(3), Maybe.just(4), &+/2)         # just(7)
combine_maybe.(Maybe.just(3), Maybe.nothing(), &+/2)       # nothing()

# String concatenation
combine_maybe.(Maybe.just("Hello, "), Maybe.just("World!"), &<>/2)  # just("Hello, World!")

# Working with structs
combine_maybe.(
  Maybe.just(%{name: "Alice"}),
  Maybe.just(%{age: 30}),
  fn user, age_info -> Map.merge(user, age_info) end
)  # just(%{name: "Alice", age: 30})
```

**Use this pattern when:**

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

## Common Patterns

### Safe Navigation

Instead of nested null checks:

```elixir
# Imperative style with null checks
def get_user_city(user_id) do
  fold_l(find_user(user_id),
    fn user ->
      fold_l(get_address(user),
        fn address -> address.city end,
        fn -> nil end
      )
    end,
    fn -> nil end
  )
end

# Functional style with Maybe
def get_user_city(user_id) do
  Maybe.just(user_id)
  |> bind(&find_user_maybe/1)     # Returns Maybe User
  |> bind(&get_address_maybe/1)   # Returns Maybe Address  
  |> map(& &1.city)               # Extract city if present
end
```

### Optional Field Processing

```elixir
# Process optional email field
def send_welcome_email(user) do
  user.email
  |> Maybe.from_nil()
  |> map(&normalize_email/1)
  |> bind(&validate_email/1)      # Returns Maybe valid_email
  |> map(&send_email/1)           # Send if valid
  |> Either.lift_maybe("No valid email")
end
```

### Collecting Optional Values

```elixir
# Gather optional settings
def load_user_preferences(user_id) do
  preferences = [
    get_theme_preference(user_id),      # Maybe String
    get_language_preference(user_id),   # Maybe String  
    get_timezone_preference(user_id)    # Maybe String
  ]
  
  Maybe.sequence(preferences)
  |> map(fn [theme, lang, tz] -> 
    %{theme: theme, language: lang, timezone: tz}
  end)
end
```

## Integration with Other Modules

### With Funx.Utils

```elixir
# Curry functions for Maybe operations
find_by_id = Utils.curry(&find_user/1)
user_finder = find_by_id.(42)

Maybe.just(database)
|> bind(user_finder)

# Compose Maybe-returning functions
compose_maybe = Utils.compose([
  &Maybe.from_nil/1,
  &get_user_profile/1,  # Returns Maybe
  &format_display_name/1
])
```

### With Predicate Logic

```elixir
# Convert predicates to Maybe values
def predicate_to_maybe(predicate, value) do
  if predicate.(value) do
    Maybe.just(value)
  else
    Maybe.nothing()
  end
end

# Use with validation
is_adult = fn user -> user.age >= 18 end

Maybe.just(user)
|> bind(fn u -> predicate_to_maybe(is_adult, u) end)
|> map(&process_adult_user/1)
```

## Conversions Between Types

### Conversion to Either

```elixir
# Convert Maybe to Either with error context using the built-in function
Either.lift_maybe(maybe_value, "Error message")

# Usage in validation pipeline
Maybe.just(user_input)
|> bind(&parse_user_id/1)
|> maybe_to_either("Invalid user ID")
|> Either.bind(&detailed_validation/1)
```

## List Operations

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

Removes all Nothing values and unwraps Just values from a list:

```elixir
Maybe.concat([
  Maybe.just(1),
  Maybe.nothing(),
  Maybe.just(3),
  Maybe.nothing()
])                              # [1, 3]
```

### `concat_map/2` - Apply Function and Collect Just Results

### `traverse/2` - Apply Kleisli to List (All Must Succeed)

Applies a Kleisli function to each element, requiring all operations to succeed:

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

# Kleisli function: String -> Maybe Integer
parse_number = fn str ->
  case Integer.parse(str) do
    {num, ""} -> Maybe.just(num)
    _ -> Maybe.nothing()
  end
end

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

# Any fail - get Nothing
Maybe.traverse(["1", "invalid", "3"], parse_number)  # nothing()
```

**Use `traverse` when:**

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

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

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

```elixir
# Same Kleisli function as above
parse_number = fn str ->
  case Integer.parse(str) do
    {num, ""} -> Maybe.just(num)
    _ -> Maybe.nothing()
  end
end

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

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

# All fail - get empty list
Maybe.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 Maybe to Maybe List

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

```elixir
# All present - success
Maybe.sequence([
  Maybe.just(1),
  Maybe.just(2), 
  Maybe.just(3)
])                            # just([1, 2, 3])

# Any absent - failure  
Maybe.sequence([
  Maybe.just(1),
  Maybe.nothing(),
  Maybe.just(3)  
])                            # nothing()

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

**Use `sequence` when:**

- You have a list of Maybe values from previous computations
- You want all values to be present, or nothing at all
- You're collecting results from multiple optional operations

### List Operations Comparison

```elixir
user_ids = [1, 2, 999, 4]  # 999 is invalid ID

# traverse: All must succeed or nothing
Maybe.traverse(user_ids, &find_user/1)
# nothing() - because user 999 doesn't exist

# concat_map: Collect successes, ignore failures  
Maybe.concat_map(user_ids, &find_user/1)
# [user1, user2, user4] - got the valid users

# sequence: All existing values must be present
existing_maybes = [Maybe.just(user1), Maybe.nothing(), Maybe.just(user3)]
Maybe.sequence(existing_maybes)  # nothing() - because one is Nothing
```

## Lifting

### `lift_predicate/2` - Convert Value Based on Predicate

Converts a value to Just if it meets a predicate, otherwise Nothing:

```elixir
validate_positive = Maybe.lift_predicate(&(&1 > 0))

validate_positive.(5)   # just(5)
validate_positive.(-1)  # nothing()
```

### `lift_either/1` - Convert Either to Maybe

Converts an Either to Maybe, discarding Left error information:

```elixir
Maybe.lift_either(Either.right(42))    # just(42)
Maybe.lift_either(Either.left("error")) # nothing()
```

### `lift_identity/1` - Convert Identity to Maybe

Converts an Identity monad to Maybe:

```elixir
Maybe.lift_identity(Identity.pure(42))  # just(42)
```

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

Lifts comparison functions for use in Maybe context:

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

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

## Elixir Interoperability

### `from_nil/1` - Convert Nil to Maybe

```elixir
Maybe.from_nil(42)      # just(42)
Maybe.from_nil(nil)     # nothing()
```

### `to_nil/1` - Convert Maybe to Nil

```elixir
Maybe.to_nil(Maybe.just(42))    # 42
Maybe.to_nil(Maybe.nothing())   # nil
```

### `from_result/1` - Convert Result Tuple to Maybe

```elixir
Maybe.from_result({:ok, 42})        # just(42)
Maybe.from_result({:error, "fail"}) # nothing()
```

### `to_result/1` - Convert Maybe to Result Tuple

```elixir
Maybe.to_result(Maybe.just(42))    # {:ok, 42}
Maybe.to_result(Maybe.nothing())   # {:error, nil}
```

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

```elixir
# Run function safely, returning Nothing on exception
Maybe.from_try(fn -> 42 / 0 end)  # nothing()
Maybe.from_try(fn -> 42 / 2 end)  # just(21.0)
```

### `to_try!/2` - Unwrap or Raise with Custom Error

```elixir
Maybe.to_try!(Maybe.just(42), "No value")     # 42
Maybe.to_try!(Maybe.nothing(), "No value")   # raises RuntimeError: "No value"
```

## Testing Strategies

### Property-Based Testing

```elixir
defmodule MaybePropertyTest do
  use ExUnit.Case
  use StreamData

  property "map preserves just structure" do
    check all value <- term(),
              f <- StreamData.constant(fn x -> x + 1 end) do
      result = Maybe.just(value) |> Monad.map(f)
      assert Maybe.just?(result)
    end
  end

  property "map on nothing returns nothing" do
    check all f <- StreamData.constant(fn x -> x + 1 end) do
      result = Maybe.nothing() |> Monad.map(f)
      assert result == Maybe.nothing()
    end
  end

  property "bind with just applies function" do
    check all value <- integer(),
              result_value <- integer() do
      f = fn _x -> Maybe.just(result_value) end
      result = Maybe.just(value) |> Monad.bind(f)
      assert result == Maybe.just(result_value)
    end
  end
end
```

### Unit Testing Common Patterns

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

  test "chaining operations with bind" do
    # Successful chain
    result = Maybe.just(5)
    |> bind(fn x -> Maybe.just(x * 2) end)
    |> bind(fn x -> Maybe.just(x + 1) end)
    
    assert result == Maybe.just(11)
    
    # Chain breaks on nothing
    result = Maybe.just(5)
    |> bind(fn _x -> Maybe.nothing() end)
    |> bind(fn x -> Maybe.just(x + 1) end)  # Never executed
    
    assert result == Maybe.nothing()
  end

  test "combining values with ap" do
    add = fn x -> fn y -> x + y end end
    
    result = Maybe.just(add)
    |> ap(Maybe.just(10))
    |> ap(Maybe.just(5))
    
    assert result == Maybe.just(15)
    
    # Fails if any value is nothing
    result = Maybe.just(add)
    |> ap(Maybe.nothing())
    |> ap(Maybe.just(5))
    
    assert result == Maybe.nothing()
  end

  test "sequence converts list of Maybe to Maybe list" do
    # All present
    result = Maybe.sequence([
      Maybe.just(1),
      Maybe.just(2),
      Maybe.just(3)
    ])
    assert result == Maybe.just([1, 2, 3])
    
    # Any absent
    result = Maybe.sequence([
      Maybe.just(1),
      Maybe.nothing(),
      Maybe.just(3)
    ])
    assert result == Maybe.nothing()
  end
end
```

## Performance Considerations

### Lazy Evaluation

```elixir
# Operations on nothing are essentially no-ops
# This makes Maybe chains very efficient when they short-circuit early

expensive_computation = fn x ->
  # This never runs if we start with nothing
  Process.sleep(1000)
  x * 2
end

Maybe.nothing()
|> map(expensive_computation)    # Returns immediately
|> bind(fn x -> Maybe.just(x + 1) end)
# Result: nothing(), computed instantly
```

### Memory Usage

```elixir
# Maybe uses minimal memory overhead
# just(value) stores the value plus a small wrapper
# nothing() is a singleton, shared across all nothing instances

# Efficient for optional fields
user = %{
  id: 1,
  name: "Alice",
  email: Maybe.just("alice@example.com"),  # Small overhead
  phone: Maybe.nothing()                   # Shared singleton
}
```

## Troubleshooting Common Issues

### Issue: Nested Maybe Values

```elixir
# ❌ Problem: Manual nesting creates Maybe Maybe a
result = Maybe.just(user_id)
|> map(&find_user/1)  # find_user returns Maybe User
# Result: Maybe (Maybe User) - nested!

# ✅ Solution: Use bind for functions that return Maybe
result = Maybe.just(user_id) 
|> bind(&find_user/1)  # Automatically flattens to Maybe User
```

### Issue: Mixing Maybe with Nil

```elixir
# ❌ Problem: Inconsistent nil/Maybe usage
def process_data(data) do
  fold_l(get_user(data),
    fn user -> Maybe.just(user) end,
    fn -> Maybe.nothing() end
  )
  |> map(&transform_user/1)
end

# ✅ Solution: Convert early, stay in Maybe context
def process_data(data) do
  get_user(data)
  |> Maybe.from_nil()      # Convert nil -> Maybe early
  |> map(&transform_user/1)
end
```

### Issue: Pattern Matching Confusion

```elixir
# ❌ Problem: Imperative pattern matching instead of functional folding
case maybe_user do
  %Just{value: user} -> process_user(user)
  %Nothing{} -> handle_missing()
end

# ✅ Solution: Use functional folding
fold_l(maybe_user,
  fn user -> process_user(user) end,
  fn -> handle_missing() end
)
```

### Issue: Over-using Pattern Matching

```elixir
# ❌ Problem: Manual unwrapping defeats the purpose
fold_l(maybe_value,
  fn value -> 
    new_value = transform(value)
    Maybe.just(new_value)
  end,
  fn -> Maybe.nothing() end
)

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

## When Not to Use Maybe

### Use Either Instead When

```elixir
# ❌ Maybe loses error context
def validate_email(email) do
  if valid_email_format?(email) do
    Maybe.just(email)
  else
    Maybe.nothing()  # Lost: why did validation fail?
  end
end

# ✅ Either preserves error context  
def validate_email(email) do
  cond do
    String.length(email) == 0 -> Either.left("Email cannot be empty")
    not String.contains?(email, "@") -> Either.left("Email must contain @")
    not valid_domain?(email) -> Either.left("Invalid email domain")
    true -> Either.right(email)
  end
end
```

### Use Plain Values When

```elixir
# ❌ Maybe overhead for always-present values
def calculate_tax(amount) do
  # Tax rate is always known, no need for Maybe
  Maybe.just(amount)
  |> map(fn amt -> amt * 0.1 end)
end

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

## Summary

Maybe provides null-safe computation for optional values:

**Core Operations:**

- `just/1`: Wrap present values
- `nothing/0`: Represent absence  
- `map/2`: Transform present values, skip absent
- `bind/2`: Chain Maybe-returning operations with automatic flattening
- `ap/2`: Apply functions across multiple Maybe values
- `sequence/1`: Convert `[Maybe a]` to `Maybe [a]`

**Key Patterns:**

- Chain dependent lookups with `bind/2`
- Transform values with `map/2`
- Combine multiple optional values with `ap/2`
- Collect all-or-nothing results with `sequence/1`
- Pattern match for final handling

**Mathematical Properties:**

- **Functor**: `map` preserves structure
- **Applicative**: `ap` applies functions in context
- **Monad**: `bind` enables dependent sequencing with flattening

Remember: Maybe represents "optional" - use it when absence is a valid state that should be handled gracefully, without needing specific error information.