1import * as Effect from "effect/Effect"
2import { pipe } from "effect/Function"
3import * as Ref from "effect/Ref"
4import type * as Scope from "effect/Scope"
5
6interface Counter {
7 acquire(): Effect.Effect<number, never, Scope.Scope>
8 incrementAcquire(): Effect.Effect<number>
9 incrementRelease(): Effect.Effect<number>
10 acquired(): Effect.Effect<number>
11 released(): Effect.Effect<number>
12}
13
14class CounterImpl implements Counter {
15 constructor(readonly ref: Ref.Ref<readonly [number, number]>) {}
16
17 acquire(): Effect.Effect<number, never, Scope.Scope> {
18 return pipe(
19 this.incrementAcquire(),
20 Effect.andThen(Effect.addFinalizer(() => this.incrementRelease())),
21 Effect.andThen(this.acquired()),
22 Effect.uninterruptible
23 )
24 }
25
26 incrementAcquire(): Effect.Effect<number> {
27 return Ref.modify(this.ref, ([acquire, release]) => [acquire + 1, [acquire + 1, release] as const] as const)
28 }
29
30 incrementRelease(): Effect.Effect<number> {
31 return Ref.modify(this.ref, ([acquire, release]) => [release + 1, [acquire, release + 1] as const] as const)
32 }
33
34 acquired(): Effect.Effect<number> {
35 return pipe(
36 Ref.get(this.ref),
37 Effect.map((tuple) => tuple[0])
38 )
39 }
40
41 released(): Effect.Effect<number> {
42 return pipe(
43 Ref.get(this.ref),
44 Effect.map((tuple) => tuple[1])
45 )
46 }
47}
48
49export const make = (): Effect.Effect<Counter> => {
50 return pipe(
51 Ref.make([0, 0]),
52 Effect.map((ref) => new CounterImpl(ref as any))
53 )
54}