Effect v4 里 Effect.Service 用什么替代?
在 effect 4.0.5 里,Effect.Service、Context.Tag、Context.GenericTag、Layer.scoped 全部不存在,tsc 会直接报属性不存在。
替代是两个名字:声明服务只用 Context.Service,构造 Layer 只用 Layer.effect。
这是同一个变化的两面:服务只有一种声明方式,Layer 构造也只有一个入口,Scope 的处理由 Layer.effect 的类型自动扣掉。下面用一个会连接、发送、断开的 Mailer 示例,把这几件事的写法和行为讲清楚。需要先了解服务与 Layer 是什么,可以看 Effect 服务与 Layer:把依赖写进类型里。
旧写法分别换成什么?
Effect.Service与Context.Tag、Context.GenericTag:统一用Context.Service。Layer.scoped:用Layer.effect。它的类型会把Scope从需求里扣掉,构建 Layer 时用acquireRelease注册的资源,会在 Layer 的作用域关闭时释放。- v3 的
Effect.Tag式静态访问器没有了,取而代之的是Service.use(f)。 Layer.succeed两种写法都能用:Layer.succeed(Tag, impl)和Layer.succeed(Tag)(impl)。
没有 Layer.scoped,资源还会被释放吗?
会。下面的例子在 Layer 里 acquireRelease 一个「连接」,然后用 ManagedRuntime 运行一次:
// mailer.ts
import { Context, Effect, Layer, ManagedRuntime } from "effect"
export const events: Array<string> = []
export class Mailer extends Context.Service<Mailer, {
readonly send: (to: string) => Effect.Effect<void>
}>()("app/Mailer") {
static readonly layer = Layer.effect(
Mailer,
Effect.gen(function* () {
yield* Effect.acquireRelease(
Effect.sync(() => events.push("connect")),
() => Effect.sync(() => events.push("disconnect"))
)
return Mailer.of({ send: (to) => Effect.sync(() => events.push(`send ${to}`)) })
})
)
}
export const notify = Mailer.use((mailer) => mailer.send("a@example.com"))
export const runOnce = async () => {
const runtime = ManagedRuntime.make(Mailer.layer)
await runtime.runPromise(notify)
await runtime.dispose()
}跑完之后 events 是 connect、send a@example.com、disconnect:资源在 dispose 时按顺序释放,没有 Layer.scoped 也没有手动的 Effect.scoped。
Mailer.use(f) 就是上面说的静态访问器的替代:拿到服务实例,交给你的函数。
构造逻辑能放在服务类上吗?
能。Context.Service 接受一个 make 选项,把构造逻辑放在服务类上,之后用 Layer.effect(Self, Self.make) 组装:
// counter.ts
import { Context, Effect, Layer, Ref } from "effect"
export class Counter extends Context.Service<Counter, {
readonly next: Effect.Effect<number>
}>()("app/Counter", {
make: Effect.gen(function* () {
const ref = yield* Ref.make(0)
return { next: Ref.updateAndGet(ref, (n) => n + 1) }
})
}) {
static readonly layer = Layer.effect(Counter, Counter.make)
}v3 的 dependencies 和 accessors 选项呢?
v3 的 Effect.Service 里的 dependencies 与 accessors 选项也一并没有了:依赖用 Layer.provide 接,访问用 use。
运行时那一侧要改什么?
Runtime.runPromise 之类的函数在 4.0.5 里不存在,长驻宿主用 ManagedRuntime.make,写法见上面的 runOnce。运行器该放在哪、测试怎么写,见 Effect 运行时边界与测试:只在入口 run,用 TestClock 测时间。整体迁移顺序见 Effect 从 v3 升级到 v4 要改哪些地方?。
版本是 effect 4.0.5,被移除的 API 用 tsc 确认过不存在;上面的 Mailer 示例在该版本上编译并运行过,事件顺序是实际结果。v3 一侧的行为用 effect 3.22.3 对照确认过。dependencies与accessors选项的结论来自同一轮核对。