-

一、传统手艺:Partial Application

在函数式编程中,Partial Application 是传递依赖的常用方式。例如:

+

一、传统手艺:Partial Application

+

在函数式编程中,Partial Application 是传递依赖的常用方式。例如:

1
2
3
let foo bar baz request = ...
let wired = foo dependency1 dependency2
let response = wired request
- -

优点:

+

优点:

-

缺点:

+

缺点:


-

二、结构化方法:单一环境参数(env)

为解决参数爆炸问题,可将依赖封装为单一环境对象env,并通过接口约束访问权限:

+

二、结构化方法:单一环境参数(env)

+

为解决参数爆炸问题,可将依赖封装为单一环境对象env,并通过接口约束访问权限:

1
2
3
4
5
6
7
8
[<Interface>] type ILog = abstract Logger: ILogger
[<Interface>] type IDb = abstract Database: IDatabase

module Log =
let info (env: #ILog) = env.Logger.Info("Message")

module Db =
let fetchUser (env: #IDb) = env.Database.Query(...)
- -

优点:

+

优点:

-

应用场景:

+

应用场景:

1
2
3
4
5
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,将环境隐式注入计算流程:

+

三、Reader Monad

+

为消除显式的env传递,可引入 Reader Monad,将环境隐式注入计算流程:

1
2
3
4
5
6
7
8
9
10
11
[<Struct>] 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()
-

然后:

1
2
3
4
5
6
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()
}
- -

优点:

+

优点:

-

缺点:

+

缺点:

-

Refs.