效应类型 - Effect Type Effect类型描述的是一个延迟执行的工作流或操作。这意味着,创建Effect后,它不会立即运行,而是定义一个程序,该程序可能成功、失败,或者需要一些额外的上下文才能完成。
以下是Effect的一般形式
1 2 3 4 5 ┌─── Represents the success type │ ┌─── Represents the error type │ │ ┌─── Represents required dependencies ▼ ▼ ▼ Effect<Success, Error, Requirements>
这种类型表示存在某种效果:
成功并返回 Success 类型的值。
失败并出现错误类型为 Error
可能需要某些上下文依赖 Requirements 才能执行
不可变性 : Effect值是不可变的,Effect中的每个函数都会产生一个新的Effect值。交互建模 : 这些值本身并不执行任何操作,它们只是对有效的交互进行建模或描述。执行 : Effect可由 Effect Runtime System 执行,该系统会将Effect解释为与外部世界的实际交互。理想情况下,此执行发生在应用程序的单个入口点,例如启动effectful操作的主函数
Effect 只负责定义业务逻辑
服务管理 - Managing Services 1 2 3 ┌─── Represents required dependencies ▼ Effect<Success, Error, Requirements>
依赖项声明 :可以直接在函数的类型中指定函数需要哪些服务,从而将依赖项管理的复杂性推入类型系统。服务提供 :Effect.provideService 用于将服务实现提供给需要它的函数。通过在启动时提供服务,可以确保应用程序的所有部分都能一致地访问所需的服务,从而保持清晰且解耦的架构。
如何在 Effect 中管理服务:
创建服务 :定义一个具有独特功能和接口的服务。
使用服务 :在应用程序的功能中访问和使用该服务。
提供服务实施 :提供服务的实际实施,以满足所声明的要求。
Creating a Service 创建服务 要创建一个新的服务,需要两样东西:
唯一标识符 :字符串 “Logger”
描述操作的类型 :{ log: (msg: string) => Effect.Effect }1 2 3 4 class Logger extends Context.Tag ("Logger" )< Logger , { readonly log : (msg : string ) => Effect .Effect <void > } >() {}
提供服务 使用Effect.provideService提供服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import { Effect , Context } from "effect" class Random extends Context.Tag ("MyRandomService" )< Random , { readonly next : Effect .Effect <number > } >() {} const program = Effect .gen (function * () { const random = yield * Random const randomNumber = yield * random.next console .log (`random number: ${randomNumber} ` ) }) const runnable = Effect .provideService (program, Random , { next : Effect .sync (() => Math .random ()) }) Effect .runPromise (runnable)
Default Services 默认服务 Effect 内置了五项预置服务:
1 type DefaultServices = Clock | ConfigProvider | Console | Random | Tracer
即使程序同时使用了Clock和 Console,代表效果执行所需服务的Requirements参数仍然设置为never会自动为我们无缝处理这些服务。
1 2 3 4 5 6 7 8 9 10 11 import { Effect , Clock , Console } from "effect" const program = Effect .gen (function * () { const now = yield * Clock .currentTimeMillis yield * Console .log (`Application started at ${new Date (now)} ` ) }) Effect .runFork (program)
Managing Layers 管理服务之间的依赖 抽象出一个Layer的概念 Creating Layers 创建图层 Layer类型的结构如下:
1 2 3 4 5 ┌─── The service to be created │ ┌─── The possible error │ │ ┌─── The required dependencies ▼ ▼ ▼ Layer<RequirementsOut, Error, RequirementsIn>
Layer代表构建RequirementsOut(服务)的蓝图。它需要一个RequirementsIn(依赖项)作为输入,并且在构建过程中可能会导致Error类型的错误。
Parameter
Description
RequirementsOut
要创建的服务或资源
Error
服务构建过程中可能出现的错误类型
RequirementsIn
构建该服务所需的依赖项
为特定服务命名Layer时,通常的做法是为“生产”实现添加”Live”后缀,为“测试”实现添加”Test”后缀。例如,对于 Database 服务, DatabaseLive 是您在应用程序中提供的层,而 DatabaseTest 是您在测试中提供的层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import { Effect , Context , Layer } from "effect" class Config extends Context.Tag ("Config" )< Config , { readonly getConfig : Effect .Effect <{ readonly logLevel : string readonly connection : string }> } >() {} const ConfigLive = Layer .succeed (Config , { getConfig : Effect .succeed ({ logLevel : "INFO" , connection : "mysql://username:password@hostname:port/database_name" }) }) class Logger extends Context.Tag ("Logger" )< Logger , { readonly log : (message : string ) => Effect .Effect <void > } >() {} const LoggerLive = Layer .effect ( Logger , Effect .gen (function * () { const config = yield * Config return { log : (message ) => Effect .gen (function * () { const { logLevel } = yield * config.getConfig console .log (`[${logLevel} ] ${message} ` ) }) } }) ) class Database extends Context.Tag ("Database" )< Database , { readonly query : (sql : string ) => Effect .Effect <unknown > } >() {} const DatabaseLive = Layer .effect ( Database , Effect .gen (function * () { const config = yield * Config const logger = yield * Logger return { query : (sql : string ) => Effect .gen (function * () { yield * logger.log (`Executing query: ${sql} ` ) const { connection } = yield * config.getConfig return { result : `Results from ${connection} ` } }) } }) )