Skip to content

SynchronizedRef

SynchronizedRef<A>は、型Aの値への可変参照として機能します。これを使用することで、不変データを保存し、原子的かつ効果的に更新を行うことができます。

SynchronizedRefのほとんどの操作は、Refの操作に似ています。 もしRefにまだ慣れていない場合は、最初に Ref の概念を読むことをお勧めします。

SynchronizedRefの独特な機能はupdateEffectです。この関数は効果的な操作を受け取り、それを実行して共有状態を変更します。これがSynchronizedRefRefと区別する重要な機能です。

import { Effect, SynchronizedRef } from "effect";
const program = Effect.gen(function* () {
const ref = yield* SynchronizedRef.make("current");
// 効果的な更新操作をシミュレート
const updateEffect = Effect.succeed("update");
yield* SynchronizedRef.updateEffect(ref, () => updateEffect);
const value = yield* SynchronizedRef.get(ref);
return value;
});
Effect.runPromise(program).then(console.log);
/*
出力:
update
*/

実世界のアプリケーションでは、エフェクト(例えば、データベースのクエリなど)を実行し、その後に共有状態を更新する必要があるシナリオが存在します。これがSynchronizedRefの強みで、アクターモデルの方式で共有状態を更新することを可能にします。私たちは共有の可変状態を持っていますが、各異なるコマンドやメッセージに対して、エフェクトを実行し状態を更新したいのです。

すべての更新に対して効果的なプログラムを渡すことができます。全ての更新は並行して行われますが、その結果は異なる時間に状態に影響を与えるように順序付けられ、最終的には一貫した状態となります。

以下の例では、各ユーザーのgetAgeリクエストを送信し、それに応じて状態を更新します:

import { Effect, SynchronizedRef } from "effect";
// APIをシミュレート
const getAge = (userId: number) => Effect.succeed({ userId, age: userId * 10 });
const users = [1, 2, 3, 4];
const meanAge = Effect.gen(function* () {
const ref = yield* SynchronizedRef.make(0);
const log = <R, E, A>(label: string, effect: Effect.Effect<A, E, R>) =>
Effect.gen(function* () {
const value = yield* SynchronizedRef.get(ref);
yield* Effect.log(`${label} get: ${value}`);
return yield* effect;
});
const task = (id: number) =>
log(
`task ${id}`,
SynchronizedRef.updateEffect(ref, (sumOfAges) =>
Effect.gen(function* () {
const user = yield* getAge(id);
return sumOfAges + user.age;
})
)
);
yield* task(1).pipe(
Effect.zip(task(2), { concurrent: true }),
Effect.zip(task(3), { concurrent: true }),
Effect.zip(task(4), { concurrent: true })
);
const value = yield* SynchronizedRef.get(ref);
return value / users.length;
});
Effect.runPromise(meanAge).then(console.log);
/*
出力:
... fiber=#1 message="task 4 get: 0"
... fiber=#2 message="task 3 get: 40"
... fiber=#3 message="task 1 get: 70"
... fiber=#4 message="task 2 get: 80"
25
*/