From ad8f9f55ea79a4d587aecc69698808da82de3d62 Mon Sep 17 00:00:00 2001 From: somhairle Date: Tue, 21 Apr 2026 20:40:27 +0800 Subject: 二〇二六年四月二十一日 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...256\236\350\267\265\346\214\207\345\215\227.md" | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 "source/_posts/\345\234\250F-\344\270\255\345\244\204\347\220\206\345\244\215\346\235\202\344\276\235\350\265\226\346\263\250\345\205\245\347\232\204\345\256\236\350\267\265\346\214\207\345\215\227.md" (limited to 'source/_posts/在F-中处理复杂依赖注入的实践指南.md') diff --git "a/source/_posts/\345\234\250F-\344\270\255\345\244\204\347\220\206\345\244\215\346\235\202\344\276\235\350\265\226\346\263\250\345\205\245\347\232\204\345\256\236\350\267\265\346\214\207\345\215\227.md" "b/source/_posts/\345\234\250F-\344\270\255\345\244\204\347\220\206\345\244\215\346\235\202\344\276\235\350\265\226\346\263\250\345\205\245\347\232\204\345\256\236\350\267\265\346\214\207\345\215\227.md" new file mode 100644 index 00000000..7af1a6f5 --- /dev/null +++ "b/source/_posts/\345\234\250F-\344\270\255\345\244\204\347\220\206\345\244\215\346\235\202\344\276\235\350\265\226\346\263\250\345\205\245\347\232\204\345\256\236\350\267\265\346\214\207\345\215\227.md" @@ -0,0 +1,99 @@ +--- +title: 在F#中处理复杂依赖注入的实践指南 +date: 2025-03-31 09:51:08 +tags: [Technique] +--- + + +## 一、传统手艺:Partial Application + +在函数式编程中,Partial Application 是传递依赖的常用方式。例如: + +```fsharp +let foo bar baz request = ... +let wired = foo dependency1 dependency2 +let response = wired request +``` + +优点: +- 无需框架或反射,直接通过函数参数传递依赖。 +- 符合函数式编程的纯函数理念。 + +缺点: +- 参数爆炸:当功能扩展时,参数数量激增(如日志、数据库、加密等)。 +- 维护困难:新增依赖需修改所有调用点的参数传递。 +- 隐式依赖:难以从函数签名直接区分核心参数与辅助依赖。 + +--- + +## 二、结构化方法:单一环境参数(`env`) + +为解决参数爆炸问题,可将依赖封装为单一环境对象`env`,并通过接口约束访问权限: + +```fsharp +[] type ILog = abstract Logger: ILogger +[] type IDb = abstract Database: IDatabase + +module Log = + let info (env: #ILog) = env.Logger.Info("Message") + +module Db = + let fetchUser (env: #IDb) = env.Database.Query(...) +``` + +优点: +- 显式依赖声明:函数签名仅需`env`参数,编译器验证接口实现。 +- 模块化隔离:各模块仅声明所需接口(如`ILog`、`IDb`),避免全局依赖。 +- 易于测试:通过模拟`env`实现单元测试,无需依赖具体实现。 + +应用场景: +```fsharp +let changePass env req = task { + let! user = Db.fetchUser env req.UserId + Log.info env "Processing user: %i" user.Id + ... +} +``` + +--- + +## 三、Reader Monad + +为消除显式的`env`传递,可引入 Reader Monad,将环境隐式注入计算流程: + +```fsharp +[] type Effect<'env, 'out> = Effect of ('env -> 'out) + +module Effect = + let run env (Effect fn) = fn env + let bind f effect = Effect (fun env -> run env (f (run env effect))) + +type EffectBuilder() = + member __.Bind(e, f) = Effect.bind f e + member __.Return(x) = Effect (fun _ -> x) + +let effect = EffectBuilder() +``` + +然后: +```fsharp +let changePass req = effect { + let! user = Db.fetchUser req.UserId + let! salt = Random.bytes 32 + do! Log.info "Password updated for user %i" user.Id + return Ok() +} +``` + +优点: +- 隐式依赖管理:通过`effect`计算表达式自动传递`env`,减少样板代码。 +- 组合性:支持与其他计算表达式(如`async`/`task`)结合,处理异步操作。 + +缺点: +- 性能开销:频繁的闭包创建和间接调用可能导致性能下降。 +- 生态兼容性:需自定义计算表达式,与现有异步框架集成复杂。 + + +## Refs. +- Spring的构造器注入 +- Blazor的DI实现 \ No newline at end of file -- cgit v1.2.3