Skip to content

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

1
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

1
2
3
4
5
6
7
8
9
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

1
2
3
4
5
6
7
8
9
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

Debugging Complex Transformations

 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')
  );

Finding Where Transformations Fail

 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:

1
2
3
4
5
// 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

  1. Add labels for clarity in complex pipelines
  2. Remove or comment out inspect calls before production
  3. Combine with tap for custom logging behavior
  4. Use descriptive labels to track transformation stages
  5. Remember it always uses console.log - use tap for custom logging

Type Safety

inspect is fully type-safe and preserves types:

1
2
3
4
5
const value: Maybe<number> = pipe(
  just(42),
  inspect('Number'),  // Type: Maybe<number>
  map(x => x * 2)     // x is correctly inferred as number
);