Contextを使って実体を流し込む流れ
登場人物
Serviceの
Interface
識別子
②実体
③Context
①と②のペア
④program
Serviceのinterface(①)のみに依存したもの
具体的な②のことは知らない
⑤runnable
③と④を結びつけたもの
実行できる
code:ts
import { Effect, Context } from "effect"
classでも関数でも書ける
code:ts
// ①Logger,
class Random extends Context.Tag("MyRandomService")<
Random,
{
readonly next: Effect.Effect<number>;
}
() {}
// ①
type Log = (message: string) => Effect.Effect<void>;
const Logger = Context.GenericTag<Log>("MyLoggerService");
② Serviceの具体的な実装・実体
code:ts
// ② Serviceの具体的な実装・実体
const log: Log = (message: string) => Effect.sync(() => console.log(message));
例えば、実装用とテスト用、とか
例えば、Node用、Bun用、とか
④ program
具体的な②のことは知らない
code:ts
// ④ program
const program = Effect.gen(function* () {
const random = yield* Random
const logger = yield* Logger
const randomNumber = yield* random.next
return yield* logger.log(String(randomNumber))
})
型に全てのcontextが乗る
「この①には、この②を結びつける」というペア
code:ts
// ④ context
const context = Context.empty().pipe(
Context.add(Random, { next: Effect.sync(() => Math.random()) }),
Context.add(Logger, log)
);
説明のため、Loggerの方は③を別で書いたが、
実際はRandomみたいに、Contextの宣言時に書くことも多い
⑤runnable
構築した④programに、③Contextを結びつけて、実体を流し込む
code:ts
// ⑤runnable
const runnable = Effect.provide(program, context)
ちなみに、④と⑤が密結合した書き方もある
code:ts
const runnable = program.pipe(
Effect.provideService(Random, {
next: Effect.sync(() => Math.random())
}),
Effect.provideService(Logger, {
log: (message) => Effect.sync(() => console.log(message))
})
)