inspect
Logs a value with an optional label and returns it unchanged. A specialized version of tap for debugging that automatically logs to console.log.
Signature
| function inspect<T>(label?: string): (value: T) => T
|
Parameters
label (optional): A string label to prefix the logged value
Returns
A function that:
- Takes a value of any type
- Logs it to console (with label if provided)
- Returns the value unchanged
Usage
Basic Usage with Label
| import { pipe } from 'rambda';
import { just, map } from 'holo-fn/maybe';
import { inspect } from 'holo-fn';
pipe(
just(42),
map(x => x * 2),
inspect('After doubling'), // Logs: "After doubling: Just(84)"
map(x => x + 10)
);
|
Without Label
| import { inspect } from 'holo-fn';
import { map, ok } from 'holo-fn/result';
import { pipe } from 'rambda';
pipe(
ok({ id: 1, name: 'Alice' }),
inspect(), // Logs: Ok({ id: 1, name: 'Alice' })
map(user => user.name)
);
|
Debugging Pipelines
1
2
3
4
5
6
7
8
9
10
11
12 | import { inspect } from 'holo-fn';
import { just, map } from 'holo-fn/maybe';
import { pipe } from 'rambda';
const result = pipe(
just(10),
inspect('Initial value'), // Logs: "Initial value: Just(10)"
map(x => x * 2),
inspect('After doubling'), // Logs: "After doubling: Just(20)"
map(x => x + 5),
inspect('Final result') // Logs: "Final result: Just(25)"
);
|
With Arrays
| import { inspect } from 'holo-fn';
import { pipe } from 'rambda';
pipe(
[1, 2, 3, 4],
inspect('Initial array'), // Logs: "Initial array: [1, 2, 3, 4]"
arr => arr.filter(x => x > 2),
inspect('After filter'), // Logs: "After filter: [3, 4]"
arr => arr.map(x => x * 2),
inspect('Final result') // Logs: "Final result: [6, 8]"
);
|
With Plain Objects
| import { inspect } from 'holo-fn';
import { pipe } from 'rambda';
const user = pipe(
{ id: 1, name: 'Alice', age: 30 },
inspect('User data'), // Logs: "User data: { id: 1, name: 'Alice', age: 30 }"
user => ({ ...user, age: user.age + 1 }),
inspect('After birthday') // Logs: "After birthday: { id: 1, name: 'Alice', age: 31 }"
);
|
Common Use Cases
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import { inspect } from 'holo-fn';
import { chain, fromNullable, map } from 'holo-fn/maybe';
import { pipe } from 'rambda';
const getUserEmail = (userId: number) =>
pipe(
fetchUser(userId),
fromNullable,
inspect('User fetched'),
map(user => user.profile),
inspect('Profile extracted'),
chain(profile => fromNullable(profile.email)),
inspect('Email result')
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13 | import { inspect } from 'holo-fn';
import { fromThrowable, map } from 'holo-fn/result';
import { pipe } from 'rambda';
const parseAndValidate = (input: string) =>
pipe(
input,
inspect('Raw input'),
fromThrowable(JSON.parse),
inspect('After parsing'), // See if parsing succeeded
map(validateSchema),
inspect('After validation') // See validation result
);
|
Monitoring Data Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import { inspect } from 'holo-fn';
import { all, map } from 'holo-fn/maybe';
import { pipe } from 'rambda';
const results = pipe(
[
fetchData(1),
fetchData(2),
fetchData(3)
],
inspect('Individual results'),
all,
inspect('Combined result'),
map(data => processData(data)),
inspect('Processed data')
);
|
Differences from tap
While tap is a generic utility for any side-effect, inspect is specialized for logging:
| // tap - generic, you provide the logging logic
tap(x => console.log('Value:', x))
// inspect - specialized for logging, with optional label
inspect('Value')
|
Both are useful:
- Use tap when you need custom side-effects (metrics, validation, etc.)
- Use inspect for quick debugging with console.log
Key Features
- ✅ Non-intrusive: Doesn't modify the value or pipeline flow
- ✅ Type-safe: Preserves type information through the pipeline
- ✅ Flexible: Works with any type (monads, primitives, objects, arrays)
- ✅ Convenient: Automatic
console.log with optional labeling
- ✅ Composable: Easy to add/remove in pipelines during debugging
Tips
- Add labels for clarity in complex pipelines
- Remove or comment out
inspect calls before production
- Combine with
tap for custom logging behavior
- Use descriptive labels to track transformation stages
- Remember it always uses
console.log - use tap for custom logging
Type Safety
inspect is fully type-safe and preserves types:
| const value: Maybe<number> = pipe(
just(42),
inspect('Number'), // Type: Maybe<number>
map(x => x * 2) // x is correctly inferred as number
);
|