Effect 的 provide 和 provideMerge 有什么区别?

同样是把 PoolLive 喂给 Repo.layer,换一个函数名,结果 Layer 的类型就从 Layer<Repo> 变成了 Layer<Repo | Pool>。名字只差一个 Merge,编译器报的错却可能是「找不到 Pool」。

区别只有一点:Layer.provide 喂入后,输出里不再暴露被喂入的服务;Layer.provideMerge 喂入后,输出里保留它。 两者都会消掉下游 Layer 的 RIn。

Layer 是「构建一个服务的配方」,Layer<ROut, E, RIn> 写明产物、构建失败和构建所需(概念见 Layer 与依赖图)。理解这两个组合子,就看你想不想让某个服务成为外部可见接口的一部分。

两种写法的类型差在哪?

先有这组服务(Repo 依赖 Pool,Pool 依赖 Config):

import { Context, Effect, Layer } from "effect"

class Config extends Context.Service<Config, { readonly url: string }>()("app/Config") {
  static readonly layer = Layer.succeed(Config, { url: "db://x" })
}

class Pool extends Context.Service<Pool, {
  readonly query: (sql: string) => Effect.Effect<string>
}>()("app/Pool") {
  static readonly layer: Layer.Layer<Pool, never, Config> = Layer.effect(
    Pool,
    Effect.gen(function* () {
      const cfg = yield* Config
      yield* Effect.log("open pool " + cfg.url)
      yield* Effect.addFinalizer(() => Effect.log("close pool"))
      return { query: (sql) => Effect.succeed("rows:" + sql) }
    })
  )
}

class Repo extends Context.Service<Repo, { readonly all: Effect.Effect<string> }>()("app/Repo") {
  static readonly layer: Layer.Layer<Repo, never, Pool> = Layer.effect(
    Repo,
    Effect.gen(function* () {
      const pool = yield* Pool
      return { all: pool.query("select") }
    })
  )
}

然后分别用两个组合子:

const PoolLive = Pool.layer.pipe(Layer.provide(Config.layer))
const RepoLive: Layer.Layer<Repo> = Repo.layer.pipe(Layer.provide(PoolLive))
const RepoAndPool: Layer.Layer<Repo | Pool> = Repo.layer.pipe(Layer.provideMerge(PoolLive))

RepoLive 的输出只有 Repo:Pool 是它内部的实现细节,外面看不见。RepoAndPool 的输出有 Repo | Pool。

什么时候选 provideMerge?

当入口处还有别的程序要直接用连接池时。用 provide,Pool 被藏在 RepoLive 内部,外面的代码再 yield* Pool 就拿不到;用 provideMerge,Pool 留在输出里,外面可以继续用。

反过来,如果你不希望调用方绕过 Repo 直接碰 Pool,用 provide,让 Pool 保持为内部细节。

为什么编译器说找不到某个服务?

这是搞反的典型症状:用了 provide,但外面还想用被喂入的那个服务。输出里没有它,类型自然不满足。把 provide 换成 provideMerge 就行;如果其实不需要外面用,那就是外面的代码多依赖了。

并列又该用哪个?

两个互不依赖的 Layer 并列,用 Layer.mergeAll(两个时是 Layer.merge),输出是两边输出的并集。它不喂入任何东西,只是并排。

一个类比:Layer 像一张工序卡。provide 是半成品交给下一道工序,之后不再出现在成品清单里;provideMerge 是交给下一道工序的同时也留一份进成品清单;mergeAll 是两条互不相干的流水线并排。

说明:类型与语义按原有章节的示例整理,这次没有重新运行。原文没有记录所用的 effect 版本号,Effect v4 的 API 与旧版差异较大,请以你安装的版本为准。