# Option Patterns

## Creating Options

### From Values

```typescript
import { Option } from "better-option";

// Present value
const some = Option.some(42);

// Absent value
const none = Option.none();

// Void/unit value
const unit = Option.some();
```

### From Nullable

Convert `T | null | undefined` to `Option<T>`:

```typescript
// null and undefined become None, everything else becomes Some
const option = Option.from(maybeNull);

// Works with any nullable type
const user = Option.from(users.find((u) => u.id === id));
const header = Option.from(request.headers.get("Authorization"));
```

### From Predicate

Create Option based on condition:

```typescript
// Some if predicate passes, None otherwise
const positive = Option.fromPredicate(value, (x) => x > 0);

// With type guard for narrowing
const isString = (x: unknown): x is string => typeof x === "string";
const str = Option.fromPredicate(input, isString);
// str is Option<string>
```

## Transforming Options

### Map (Transform Value)

```typescript
const option = Option.some(2);

// Transform the inner value
const doubled = option.map((x) => x * 2); // Some(4)

// None passes through unchanged
Option.none().map((x) => x * 2); // None

// Pipeable style
const double = Option.map((x: number) => x * 2);
double(option); // Some(4)
```

### AndThen (Chain Options)

```typescript
// Chain Option-returning functions
const result = Option.some(2)
  .andThen((x) => (x > 0 ? Option.some(x) : Option.none()));

// Commonly used for lookups
const getProfile = (userId: string): Option<Profile> => { ... };
const getSettings = (profileId: string): Option<Settings> => { ... };

const settings = findUser(id)
  .andThen((user) => getProfile(user.profileId))
  .andThen((profile) => getSettings(profile.settingsId));
```

### Filter (Conditional Some to None)

```typescript
// Some becomes None if predicate fails
const positive = Option.some(5).filter((x) => x > 0); // Some(5)
const negative = Option.some(-5).filter((x) => x > 0); // None

// With type guard
const strings = Option.some(value).filter(
  (x): x is string => typeof x === "string"
);
```

## Extracting Values

### Unwrap (Throws on None)

```typescript
// Extract value, throws Panic if None
const value = option.unwrap();

// With custom error message
const value = option.unwrap("User must exist at this point");
```

### UnwrapOr (With Fallback)

```typescript
// Extract value or return fallback
const value = option.unwrapOr("default");

// Type widens to union if fallback differs
const result: number | string = Option.some(42).unwrapOr("fallback");
```

### UnwrapOrElse (Lazy Fallback)

```typescript
// Compute fallback only when needed
const value = Option.unwrapOrElse(option, () => expensiveDefault());

// Pipeable
const withDefault = Option.unwrapOrElse(() => 0);
withDefault(option);
```

### Match (Pattern Match)

```typescript
const message = option.match({
  some: (user) => `Hello, ${user.name}`,
  none: () => "Hello, stranger",
});
```

## Combining Options

### Zip (Combine Two Options)

```typescript
// Some([a, b]) if both are Some, None otherwise
const zipped = Option.zip(optionA, optionB);

// Use with destructuring
Option.zip(getName(), getAge()).match({
  some: ([name, age]) => `${name} is ${age}`,
  none: () => "Missing data",
});
```

### Flatten (Unwrap Nested Option)

```typescript
const nested: Option<Option<number>> = Option.some(Option.some(42));
const flat: Option<number> = Option.flatten(nested); // Some(42)

// Flattens Some(None) to None
Option.flatten(Option.some(Option.none())); // None
```

## Generator Composition

### Basic Generator

```typescript
const option = Option.gen(function* () {
  const user = yield* findUser(id);        // Option<User>
  const profile = yield* getProfile(user); // Option<Profile>
  const name = yield* getName(profile);    // Option<string>
  return Option.some(name);
});
// Option<string> - short-circuits on first None
```

### Async Generator

```typescript
const option = await Option.gen(async function* () {
  const user = yield* Option.await(fetchUser(id));
  const posts = yield* Option.await(fetchPosts(user.id));
  return Option.some({ user, posts });
});
```

### With This Binding

```typescript
const ctx = { multiplier: 10 };

const option = Option.gen(function* (this: typeof ctx) {
  const value = yield* Option.some(5);
  return Option.some(value * this.multiplier);
}, ctx);
// Some(50)
```

## Side Effects

### Tap (Side Effect on Some)

```typescript
// Run side effect, return original option
const option = findUser(id)
  .tap((user) => console.log(`Found: ${user.name}`))
  .map((user) => user.id);

// None skips the tap callback
Option.none().tap(() => console.log("Never called"));
```

### TapAsync

```typescript
await option.tapAsync(async (user) => {
  await logUserAccess(user.id);
});
```

## Serialization

### Serialize/Deserialize

```typescript
// Convert to plain object for RPC/storage
const serialized = Option.serialize(option);
// { status: "some", value: 42 } or { status: "none" }

// Rehydrate back to Option instance
const option = Option.deserialize<User>(rpcResponse);
if (option) {
  option.map(...); // Has all Option methods
}
```

## Type Guards

### IsSome / IsNone

```typescript
if (Option.isSome(option)) {
  console.log(option.value); // TypeScript knows it's Some
}

if (option.isNone()) {
  // TypeScript knows it's None
}
```

## Common Idioms

### Safe Property Access

```typescript
// Instead of: user?.profile?.settings?.theme
const theme = Option.from(user)
  .andThen((u) => Option.from(u.profile))
  .andThen((p) => Option.from(p.settings))
  .map((s) => s.theme)
  .unwrapOr("default");
```

### First Successful Result

```typescript
// Try multiple sources, take first Some
function findConfig(): Option<Config> {
  return Option.gen(function* () {
    // Try each source
    const fromEnv = getEnvConfig();
    if (Option.isSome(fromEnv)) return fromEnv;
    
    const fromFile = getFileConfig();
    if (Option.isSome(fromFile)) return fromFile;
    
    const fromDefault = getDefaultConfig();
    return fromDefault;
  });
}
```

### Collecting Some Values

```typescript
const options = [Option.some(1), Option.none(), Option.some(2)];

// Get all Some values and count Nones
const [values, noneCount] = Option.partition(options);
// values: [1, 2], noneCount: 1
```
