import * as RA from "../../Array.js" import * as Chunk from "../../Chunk.js" import { dual, pipe } from "../../Function.js" import * as HashSet from "../../HashSet.js" import type * as Option from "../../Option.js" import { hasProperty, type Predicate } from "../../Predicate.js" import * as STM from "../../STM.js" import type * as TMap from "../../TMap.js" import type * as TSet from "../../TSet.js" import * as core from "./core.js" import * as tMap from "./tMap.js" /** @internal */ const TSetSymbolKey = "effect/TSet" /** @internal */ export const TSetTypeId: TSet.TSetTypeId = Symbol.for( TSetSymbolKey ) as TSet.TSetTypeId const tSetVariance = { /* c8 ignore next */ _A: (_: any) => _ } /** @internal */ class TSetImpl implements TSet.TSet { readonly [TSetTypeId] = tSetVariance constructor(readonly tMap: TMap.TMap) {} } const isTSet = (u: unknown) => hasProperty(u, TSetTypeId) /** @internal */ export const add = dual< (value: A) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, value: A) => STM.STM >(2, (self, value) => tMap.set(self.tMap, value, void 0 as void)) /** @internal */ export const difference = dual< (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, other: TSet.TSet) => STM.STM >(2, (self, other) => core.flatMap( toHashSet(other), (values) => removeIf(self, (value) => HashSet.has(values, value), { discard: true }) )) /** @internal */ export const empty = (): STM.STM> => fromIterable([]) /** @internal */ export const forEach = dual< (f: (value: A) => STM.STM) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, f: (value: A) => STM.STM) => STM.STM >(2, (self, f) => reduceSTM(self, void 0 as void, (_, value) => f(value))) /** @internal */ export const fromIterable = (iterable: Iterable): STM.STM> => core.map( tMap.fromIterable(Array.from(iterable).map((a): [A, void] => [a, void 0])), (tMap) => new TSetImpl(tMap) ) /** @internal */ export const has = dual< (value: A) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, value: A) => STM.STM >(2, (self, value) => tMap.has(self.tMap, value)) /** @internal */ export const intersection = dual< (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, other: TSet.TSet) => STM.STM >(2, (self, other) => core.flatMap( toHashSet(other), (values) => pipe(self, retainIf((value) => pipe(values, HashSet.has(value)), { discard: true })) )) /** @internal */ export const isEmpty = (self: TSet.TSet): STM.STM => tMap.isEmpty(self.tMap) /** @internal */ export const make = >( ...elements: Elements ): STM.STM> => fromIterable(elements) /** @internal */ export const reduce = dual< (zero: Z, f: (accumulator: Z, value: A) => Z) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, zero: Z, f: (accumulator: Z, value: A) => Z) => STM.STM >(3, (self, zero, f) => tMap.reduce( self.tMap, zero, (acc, _, key) => f(acc, key) )) /** @internal */ export const reduceSTM = dual< (zero: Z, f: (accumulator: Z, value: A) => STM.STM) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, zero: Z, f: (accumulator: Z, value: A) => STM.STM) => STM.STM >(3, (self, zero, f) => tMap.reduceSTM( self.tMap, zero, (acc, _, key) => f(acc, key) )) /** @internal */ export const remove = dual< (value: A) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, value: A) => STM.STM >(2, (self, value) => tMap.remove(self.tMap, value)) /** @internal */ export const removeAll = dual< (iterable: Iterable) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, iterable: Iterable) => STM.STM >(2, (self, iterable) => tMap.removeAll(self.tMap, iterable)) /** @internal */ export const removeIf: { (predicate: Predicate, options: { readonly discard: true }): (self: TSet.TSet) => STM.STM ( predicate: Predicate, options?: { readonly discard: false } ): (self: TSet.TSet) => STM.STM> (self: TSet.TSet, predicate: Predicate, options: { readonly discard: true }): STM.STM ( self: TSet.TSet, predicate: Predicate, options?: { readonly discard: false } ): STM.STM> } = dual( (args) => isTSet(args[0]), (self, predicate, options) => options?.discard === true ? tMap.removeIf(self.tMap, (key) => predicate(key), { discard: true }) : pipe( tMap.removeIf(self.tMap, (key) => predicate(key)), core.map(RA.map((entry) => entry[0])) ) ) /** @internal */ export const retainIf: { (predicate: Predicate, options: { readonly discard: true }): (self: TSet.TSet) => STM.STM ( predicate: Predicate, options?: { readonly discard: false } ): (self: TSet.TSet) => STM.STM> (self: TSet.TSet, predicate: Predicate, options: { readonly discard: true }): STM.STM ( self: TSet.TSet, predicate: Predicate, options?: { readonly discard: false } ): STM.STM> } = dual((args) => isTSet(args[0]), (self, predicate, options) => options?.discard === true ? tMap.retainIf(self.tMap, (key) => predicate(key), { discard: true }) : pipe( tMap.retainIf(self.tMap, (key) => predicate(key)), core.map(RA.map((entry) => entry[0])) )) /** @internal */ export const size = (self: TSet.TSet): STM.STM => core.map(toChunk(self), (chunk) => chunk.length) /** @internal */ export const takeFirst = dual< (pf: (a: A) => Option.Option) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, pf: (a: A) => Option.Option) => STM.STM >(2, (self, pf) => tMap.takeFirst(self.tMap, (key) => pf(key))) /** @internal */ export const takeFirstSTM = dual< (pf: (a: A) => STM.STM, R>) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, pf: (a: A) => STM.STM, R>) => STM.STM >(2, (self, pf) => tMap.takeFirstSTM(self.tMap, (key) => pf(key))) /** @internal */ export const takeSome = dual< (pf: (a: A) => Option.Option) => (self: TSet.TSet) => STM.STM>, (self: TSet.TSet, pf: (a: A) => Option.Option) => STM.STM> >(2, (self, pf) => tMap.takeSome(self.tMap, (key) => pf(key))) /** @internal */ export const takeSomeSTM = dual< ( pf: (a: A) => STM.STM, R> ) => (self: TSet.TSet) => STM.STM, E, R>, ( self: TSet.TSet, pf: (a: A) => STM.STM, R> ) => STM.STM, E, R> >(2, (self, pf) => tMap.takeSomeSTM(self.tMap, (key) => pf(key))) /** @internal */ export const toChunk = (self: TSet.TSet): STM.STM> => tMap.keys(self.tMap).pipe(STM.map(Chunk.unsafeFromArray)) /** @internal */ export const toHashSet = (self: TSet.TSet): STM.STM> => reduce( self, HashSet.empty(), (acc, value) => pipe(acc, HashSet.add(value)) ) /** @internal */ export const toArray = (self: TSet.TSet): STM.STM> => reduce, A>( self, [], (acc, value) => [...acc, value] ) /** @internal */ export const toReadonlySet = (self: TSet.TSet): STM.STM> => core.map(toArray(self), (values) => new Set(values)) /** @internal */ export const transform = dual< (f: (a: A) => A) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, f: (a: A) => A) => STM.STM >(2, (self, f) => tMap.transform(self.tMap, (key, value) => [f(key), value])) /** @internal */ export const transformSTM = dual< (f: (a: A) => STM.STM) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, f: (a: A) => STM.STM) => STM.STM >(2, (self, f) => tMap.transformSTM( self.tMap, (key, value) => core.map(f(key), (a) => [a, value]) )) /** @internal */ export const union = dual< (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, (self: TSet.TSet, other: TSet.TSet) => STM.STM >(2, (self, other) => forEach(other, (value) => add(self, value)))