The Optics Fluent API provides seamless integration between Algebraic Data Types (ADTs) and the optics system, allowing you to use .view, .set, .over, .preview, and .review methods directly on ADT instances without requiring .pipe() or manual function calls.
The .preview method in ObservableLiteOptics has been enhanced to work with any optic kind (Lens, Prism, or Optional) while preserving type safety and purity guarantees.
The unified .preview method automatically detects the optic type and applies the appropriate extraction logic:
// For lenses, wraps the result in Maybe.Just
// If the lens fails to extract (throws), returns Maybe.Nothing
const nameLens = lens(
person => person.name,
(person, name) => ({ ...person, name })
);
const observable = ObservableLiteOptics.of(maybePerson);
const nameObservable = observable.preview(nameLens);
// Returns: ObservableLite<Maybe<string>>// For prisms, uses the native match method
const justPrism = prism(
m => m.isJust ? Maybe.Just(m.value) : Maybe.Nothing(),
value => Maybe.Just(value)
);
const observable = ObservableLiteOptics.of(maybePerson);
const valueObservable = observable.preview(justPrism);
// Returns: ObservableLite<Maybe<Person>>// For optionals, uses the native getOption method
const valueOptional = {
getOption: (m) => m.isJust ? Maybe.Just(m.value) : Maybe.Nothing(),
set: (m, value) => new Maybe(value, m.isJust),
over: (f, m) => m.isJust ? new Maybe(f(m.value), true) : m
};
const observable = ObservableLiteOptics.of(maybePerson);
const valueObservable = observable.preview(valueOptional);
// Returns: ObservableLite<Maybe<Person>>The unified preview method works seamlessly with cross-kind optic composition:
// Lens → Prism composition
const nameLens = lens(
person => person.name,
(person, name) => ({ ...person, name })
);
const firstLetterPrism = prism(
name => name.length > 0 ? Maybe.Just(name[0]) : Maybe.Nothing(),
letter => letter
);
// Composed optic: lens.then(prism) = optional
const composedOptic = {
get: (m) => m.isJust ? nameLens.get(m.value) : null,
set: (m, name) => m.isJust ? new Maybe(nameLens.set(m.value, name), true) : m,
over: (f, m) => m.isJust ? new Maybe(nameLens.over(f, m.value), true) : m
};
const observable = ObservableLiteOptics.of(maybePerson);
const firstLetterObservable = observable.preview(composedOptic);
// Returns: ObservableLite<Maybe<string>>The unified preview method uses runtime type detection to determine the appropriate extraction strategy:
enhanced.preview = function(optic) {
return this.map(value => {
// Check if it's a lens (has get method)
if (optic.get && optic.set) {
try {
const result = optic.get(value);
return Maybe.Just(result);
} catch (error) {
return Maybe.Nothing();
}
}
// Check if it's a prism (has match method)
else if (optic.match && optic.build) {
return optic.match(value);
}
// Check if it's an optional (has getOption method)
else if (optic.getOption) {
return optic.getOption(value);
}
// Unknown optic type
else {
throw new Error(`Unknown optic type: ${typeof optic}`);
}
});
};- Lens: Always returns
Maybe<A>(wraps result inJustor handles errors withNothing) - Prism: Returns
Maybe<A>(uses native prism matching) - Optional: Returns
Maybe<A>(uses native optional extraction) - Composed: Returns
Maybe<A>(follows composition rules)
- All preview operations are pure and preserve the
'Async'effect tag forObservableLite - No side effects are introduced by the preview method
- Type inference is preserved across all optic kinds
- Lens errors: Caught and converted to
Maybe.Nothing() - Unknown optic types: Throws descriptive error at runtime
- Null/undefined values: Handled gracefully by each optic type
// Lens on ObservableLite
const personObservable = ObservableLiteOptics.of(maybePerson);
const nameObservable = personObservable.preview(nameLens);
// Prism on ObservableLite
const valueObservable = personObservable.preview(justPrism);
// Optional on ObservableLite
const valueObservable = personObservable.preview(valueOptional);// Chain preview with other operations
const result = ObservableLiteOptics.of(maybePerson)
.preview(nameLens)
.map(maybeName => maybeName.map(name => name.toUpperCase()))
.filter(maybeName => maybeName.isJust);// Working with form validation
const formObservable = ObservableLiteOptics.of(maybeForm);
const emailErrorObservable = formObservable.preview(fieldErrorLens('email'));
emailErrorObservable.subscribe({
next: maybeError => {
if (maybeError.isJust) {
console.log('Email error:', maybeError.value);
}
}
});The unified preview method integrates seamlessly with the broader ADT optics system:
- Works with
Maybe,Either,Result, andObservableLiteinstances - Preserves HKT type parameters and purity markers
- Supports cross-kind composition rules
- Maintains law compliance for all optic types
- Unified API: Single method works with all optic kinds
- Type Safety: Preserves TypeScript type inference
- Purity: Maintains functional programming principles
- Composability: Works with cross-kind optic composition
- Error Handling: Graceful handling of failures and edge cases
- Performance: Efficient runtime type detection
This unified approach eliminates the need for separate preview methods for each optic kind while maintaining the full power and type safety of the optics system.