df_safer_dart 0.13.2 copy "df_safer_dart: ^0.13.2" to clipboard
df_safer_dart: ^0.13.2 copied to clipboard

A package inspired by functional programming, designed to enhance the structure, safety, and debuggability of mission-critical code.

example/example.dart

// ignore_for_file: inference_failure_on_instance_creation, strict_raw_type

import 'package:df_safer_dart/df_safer_dart.dart';

// -----------------------------------------------------------------------------
// THE SCENARIO: A REAL-WORLD PROBLEM
// -----------------------------------------------------------------------------
//
// We need to process a series of user IDs, and for each one, fetch its config,
// parse the JSON, and extract a deeply nested, optional setting:
// `config.notifications.sound`.
//
// This demonstrates how to handle a sequence of fully independent, asynchronous,
// failable pipelines in a purely monadic way, without `await`, `try-catch`,
// `null`, or even `Future`.

// -----------------------------------------------------------------------------
// THE MONADIC PIPELINE
// -----------------------------------------------------------------------------

/// This is the logic pipeline for a single user. It reads like a description of
/// the "happy path," while all potential errors and absent values are handled
/// automatically by the monads.
///
/// It returns an `Async` monad, which encapsulates the entire asynchronous flow.
Async<Option<String>> getUserNotificationSound(int userId) {
  return fetchUserData(userId)
      // 1. `map` chains the next operation. If `fetchUserData` produced an `Err`,
      //    this entire block is skipped, propagating the `Err` state.
      .map(
        // 2. We `unwrap` the synchronous `parseJson` result. If parsing
        //    fails, `unwrap` throws, which the `Async` monad immediately
        //    catches and converts into a failed result for the whole chain.
        (json) => parseJson(json).unwrap(),
      )
      // 3. This is the key monadic pattern for safely accessing nested data.
      .map(
        (data) =>
            // a. Start with the top-level data as `Some`.
            Some(data)
                // b. `flatMap` safely accesses the `config` key.
                //    If `config` is missing or not a Map, `letAsOrNone`
                //    returns `None`, and the rest of the chain is skipped.
                .flatMap((d) => letAsOrNone<Map>(d['config']))
                // c. Chain another `flatMap` to access `notifications`.
                .flatMap((config) => letAsOrNone<Map>(config['notifications']))
                // d. Finally, access `sound`.
                .flatMap(
                  (notifications) =>
                      letAsOrNone<String>(notifications['sound']),
                ),
      );
}

/// Async - Use Case: An operation over time that might fail.
Async<String> fetchUserData(int userId) => Async(() async {
  await Future.delayed(const Duration(milliseconds: 10));
  if (userId == 1) return '{"config":{"notifications":{"sound":"chime.mp3"}}}';
  if (userId == 2) return '{"config":{"notifications":{}}}';
  if (userId == 3) return '{"config":{}}';
  if (userId == 4) return '{"config": "bad_data"}';
  throw Err('User Not Found');
});

/// Sync - Use Case: An immediate operation that might fail (e.g., parsing).
Sync<Map<String, dynamic>> parseJson(String json) =>
    Sync(() => json.decodeJson<Map<String, dynamic>>().unwrap());

// -----------------------------------------------------------------------------
// MAIN EXECUTION (The Pure Monadic Way with `Sequential`)
// -----------------------------------------------------------------------------

void main() {
  print('--- Monadic Pipeline Results using Sequential ---');

  // `Sequential` is the library's tool for managing a series of monadic
  // operations without ever exposing a `Future`.
  final processor = Sequential();
  final userIds = [1, 2, 3, 4, 5];

  // We add each user's processing pipeline to the sequential queue.
  // The `_` in `add((_) => ...)` indicates we don't care about the result
  // of the *previous* operation in the queue, as our tasks are independent.
  for (final id in userIds) {
    processor.add((_) {
      // 1. Run the main pipeline for the current user ID.
      return getUserNotificationSound(id)
          // 2. Chain a final `map` to format the result for display.
          //    This map only runs if the pipeline was successful.
          .map((option) => formatResult(Ok(option)))
          // 3. Use `match` to handle the final `Result` (Ok or Err).
          //    This is the final "exit" from the monad for this one operation.
          .match(
            (successString) {
              print('User $id -> $successString');
              return NONE;
            },
            (err) {
              print('User $id -> ${formatResult(err.transf())}');
              return NONE;
            },
          );
    });
  }
}

/// Helper to safely format the final `Result<Option<String>>`.
String formatResult(Result<Option<String>> result) {
  return result.match(
    (option) => option.match(
      (sound) => '${option.runtimeType}: $sound',
      () => option.runtimeType.toString(),
    ),
    (err) => err.toString(),
  );
}
12
likes
0
points
2.56k
downloads

Publisher

verified publisherdev-cetera.com

Weekly Downloads

A package inspired by functional programming, designed to enhance the structure, safety, and debuggability of mission-critical code.

Homepage
Repository (GitHub)
View/report issues

Topics

#rust #error-handling #functional-programming #debugging #monads

Funding

Consider supporting this project:

www.buymeacoffee.com

License

unknown (license)

Dependencies

equatable, meta, path, stack_trace

More

Packages that depend on df_safer_dart