欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里插入图片描述

前言

Dart 是一门面向对象(OOP)语言,但它也混入了很多函数式编程(FP)的特性(如一等函数、map/reduce)。
然而,在处理错误处理空安全副作用管理时,传统的 OOP 做法(try-catch, if-null)往往会导致代码嵌套过深,逻辑分散。

fpdart 是目前 Dart 生态中最完善的函数式编程库。它引入了 Haskell/Scala/Rust 等语言中经过实战检验的概念:

  • Option: 优雅处理可能为空的值(告别 null)。
  • Either: 优雅处理错误(告别 try-catch)。
  • Task: 优雅处理异步(即 Lazy Future)。

对于 OpenHarmony 应用,使用 fpdart 能让你的业务逻辑像数学公式一样严谨、可组合,极大提升代码的健壮性。

一、核心概念:Monad 三剑客

1.1 Option (vs Null)

Option<T> 表示一个值可能存在 (Some) 也可能不存在 (None)。它逼迫你在编译时就处理空情况,而不是等到运行时报 NPE。

1.2 Either (vs Exception)

Either<L, R> 表示结果是两种可能之一。通常 L (Left) 代表错误,R (Right) 代表成功。这让错误处理变成了类型签名的一部分。

1.3 Task (vs Future)

Task<T> 是一个不想立即执行的 Future。它让异步操作变为了纯函数,直到你调用 run()

返回

折叠 (Fold)

Left

Right

业务逻辑

Either<错误, 数据>

Flutter 组件

错误页面

内容页面

二、集成与用法详解

2.1 添加依赖

dependencies:
  fpdart: ^1.2.0

2.2 告别 Try-Catch:Either 实战

传统写法

int parse(String s) {
  try {
    return int.parse(s);
  } catch (e) {
    throw Exception('不是数字');
  }
}
// 调用处必须记得 try-catch,否则 crash

fpdart 写法

import 'package:fpdart/fpdart.dart';

Either<String, int> parse(String s) {
  try {
    return Right(int.parse(s)); // 成功返回 Right
  } catch (e) {
    return Left('格式错误'); // 失败返回 Left
  }
}

void main() {
  final result = parse('123');
  
  // 强制处理两种情况
  result.fold(
    (left) => print('错误: $left'), 
    (right) => print('成功: $right'),
  );
}

在这里插入图片描述

2.3 链式调用:Pipe 与 Map

FP 的精髓在于组合。假设我们要:

  1. 解析字符串。
  2. 如果是偶数,除以 2。
  3. 转换为字符串输出。
String process(String input) {
  return parse(input)
      .flatMap((i) => i % 2 == 0 ? Right(i) : Left('奇数'))
      .map((i) => i ~/ 2)
      .map((i) => '结果是 $i')
      .getOrElse((err) => '失败: $err');
}

print(process('10')); // Result is 5
print(process('11')); // Failed: Odd number
print(process('abc')); // Failed: Format Error

多么线性的逻辑!没有缩进地狱。

在这里插入图片描述

三、OpenHarmony 适配与实战:健壮的网络层

在鸿蒙 APP 的网络层,我们通常会遇到各种异常(网络断开、JSON 解析错、服务器错误)。使用 EitherTaskEither 来封装 Dio 请求是最佳实践。

3.1 封装 Repository

import 'package:fpdart/fpdart.dart';
import 'package:dio/dio.dart';

// 定义特定领域的错误类型
class Failure {
  final String message;
  Failure(this.message);
}

class UserRepository {
  final Dio _dio;
  UserRepository(this._dio);

  // 返回 TaskEither<Failure, User>
  // 意味着:这是一个异步任务,可能失败(Failure),可能成功(User)
  TaskEither<Failure, User> getUser(int id) {
    return TaskEither.tryCatch(
      () async {
        final response = await _dio.get('/users/$id');
        return User.fromJson(response.data);
      },
      (error, stack) {
        if (error is DioException) {
          return Failure('网络错误: ${error.type}');
        }
        return Failure('未知错误');
      },
    );
  }
}

在这里插入图片描述

3.2 在 Bloc/Provider 中使用

Future<void> loadUser() async {
  // 执行任务
  final result = await repository.getUser(1).run();
  
  // 处理结果
  result.match(
    (failure) => emit(ErrorState(failure.message)),
    (user) => emit(LoadedState(user)),
  );
}

这种模式确保了你永远不会忘记处理错误,不管是鸿蒙系统底层的网络错误还是业务逻辑错误,都被统一在 Failure 类型中处理。

四、高级进阶:Do Notation (模拟)

有些语言(如 Haskell)有 do 语法糖来简化 monad 嵌套。Dart 没有,但 fpdart 提供了类似的能力。

虽然 Dart 的 async/await 已经很好用了,但它只针对 Futurefpdart 让你能以类似的方式组合 OptionEither

五、总结

fpdart 不是一个简单的工具库,它是一种编程范式的转变。一旦你习惯了“所有可能的错误都在类型签名里”的安全感,你就很难回到“满屏 try-catch”的时代了。

对于 OpenHarmony 开发者:

  • 高可靠性:鸿蒙系统作为新生态,底层 API 可能还在快速迭代。使用 FP 范式能帮你构建出更具防御性的代码,从容应对各种不确定性。
  • 易测试:纯函数(Pure Functions)是最好测试的,而 FP 鼓励写纯函数。

最佳实践

  1. 渐进式采用:不要试图重写整个 App。从最核心的业务逻辑(Domain Layer)或最复杂的工具类开始引入 OptionEither
  2. 团队共识:FP 有一定的学习曲线(Functor, Monad…),确保团队成员都理解基本概念。
  3. 结合 freezedfpdart 配合 freezed 的 Union Types (密封类),简直是 Dart 开发的终极形态。

六、完整实战示例

import 'package:fpdart/fpdart.dart';

// 1. 定义统一的错误模型
class Failure {
  final String message;
  Failure(this.message);
  
  String toString() => 'Failure: $message';
}

// 2. 模拟可能会失败的操作 (返回 TaskEither)
// TaskEither = Future<Either<L, R>>,用于处理异步且可能失败的任务
TaskEither<Failure, int> parseInput(String input) {
  return TaskEither.tryCatch(
    () async {
      final value = int.parse(input);
      if (value < 0) throw FormatException('Negative number');
      return value;
    },
    (error, stack) => Failure(error.toString()),
  );
}

void main() async {
  final inputs = ['123', '-5', 'abc'];

  print('=== 函数式处理流程 ===');
  for (var input in inputs) {
    // 3. 链式调用 (Pipeline)
    final task = parseInput(input)
        .map((i) => i * 2) // 只有解析成功,才会执行乘法
        .map((i) => 'Result: $i'); // 转换为字符串

    // 执行任务并运行
    final result = await task.run();

    // 4. 模式匹配处理最终结果
    result.match(
      (failure) => print('处理 "$input" 失败: $failure'),
      (success) => print('处理 "$input" 成功: $success'),
    );
  }
}

在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐