import * as Equal from "../../Equal.js" import { dual, pipe } from "../../Function.js" import * as Option from "../../Option.js" import * as Order from "../../Order.js" import type { Predicate } from "../../Predicate.js" import type * as STM from "../../STM.js" import type * as TArray from "../../TArray.js" import type * as TRef from "../../TRef.js" import * as core from "./core.js" import * as stm from "./stm.js" import * as tRef from "./tRef.js" /** @internal */ const TArraySymbolKey = "effect/TArray" /** @internal */ export const TArrayTypeId: TArray.TArrayTypeId = Symbol.for(TArraySymbolKey) as TArray.TArrayTypeId const tArrayVariance = { /* c8 ignore next */ _A: (_: any) => _ } /** @internal */ export class TArrayImpl implements TArray.TArray { readonly [TArrayTypeId] = tArrayVariance constructor(readonly chunk: Array>) {} } /** @internal */ export const collectFirst = dual< (pf: (a: A) => Option.Option) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, pf: (a: A) => Option.Option) => STM.STM> >(2, (self, pf) => collectFirstSTM( self, (a) => pipe(pf(a), Option.map(core.succeed)) )) /** @internal */ export const collectFirstSTM = dual< ( pf: (a: A) => Option.Option> ) => ( self: TArray.TArray ) => STM.STM, E, R>, ( self: TArray.TArray, pf: (a: A) => Option.Option> ) => STM.STM, E, R> >( 2, (self: TArray.TArray, pf: (a: A) => Option.Option>) => core.withSTMRuntime((runtime) => { let index = 0 let result: Option.Option> = Option.none() while (Option.isNone(result) && index < self.chunk.length) { const element = pipe(self.chunk[index], tRef.unsafeGet(runtime.journal)) const option = pf(element) if (Option.isSome(option)) { result = option } index = index + 1 } return pipe( result, Option.match({ onNone: () => stm.succeedNone, onSome: core.map(Option.some) }) ) }) ) /** @internal */ export const contains = dual< (value: A) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, value: A) => STM.STM >(2, (self, value) => some(self, (a) => Equal.equals(a)(value))) /** @internal */ export const count = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: Predicate) => STM.STM >(2, (self, predicate) => reduce( self, 0, (n, a) => predicate(a) ? n + 1 : n )) /** @internal */ export const countSTM = dual< (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM >(2, (self, predicate) => reduceSTM( self, 0, (n, a) => core.map(predicate(a), (bool) => bool ? n + 1 : n) )) /** @internal */ export const empty = (): STM.STM> => fromIterable([]) /** @internal */ export const every = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: Predicate) => STM.STM >(2, (self, predicate) => stm.negate(some(self, (a) => !predicate(a)))) /** @internal */ export const everySTM = dual< (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM >(2, (self, predicate) => core.map( countSTM(self, predicate), (count) => count === self.chunk.length )) /** @internal */ export const findFirst = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, predicate: Predicate) => STM.STM> >(2, (self, predicate) => collectFirst(self, (a) => predicate(a) ? Option.some(a) : Option.none())) /** @internal */ export const findFirstIndex = dual< (value: A) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, value: A) => STM.STM> >(2, (self, value) => findFirstIndexFrom(self, value, 0)) /** @internal */ export const findFirstIndexFrom = dual< (value: A, from: number) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, value: A, from: number) => STM.STM> >(3, (self, value, from) => findFirstIndexWhereFrom( self, (a) => Equal.equals(a)(value), from )) /** @internal */ export const findFirstIndexWhere = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, predicate: Predicate) => STM.STM> >(2, (self, predicate) => findFirstIndexWhereFrom(self, predicate, 0)) /** @internal */ export const findFirstIndexWhereFrom = dual< ( predicate: Predicate, from: number ) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, predicate: Predicate, from: number) => STM.STM> >(3, (self, predicate, from) => { if (from < 0) { return stm.succeedNone } return core.effect>((journal) => { let index: number = from let found = false while (!found && index < self.chunk.length) { const element = tRef.unsafeGet(self.chunk[index], journal) found = predicate(element) index = index + 1 } if (found) { return Option.some(index - 1) } return Option.none() }) }) /** @internal */ export const findFirstIndexWhereSTM = dual< ( predicate: (value: A) => STM.STM ) => (self: TArray.TArray) => STM.STM, E, R>, ( self: TArray.TArray, predicate: (value: A) => STM.STM ) => STM.STM, E, R> >(2, (self, predicate) => findFirstIndexWhereFromSTM(self, predicate, 0)) /** @internal */ export const findFirstIndexWhereFromSTM = dual< ( predicate: (value: A) => STM.STM, from: number ) => (self: TArray.TArray) => STM.STM, E, R>, ( self: TArray.TArray, predicate: (value: A) => STM.STM, from: number ) => STM.STM, E, R> >(3, ( self: TArray.TArray, predicate: (value: A) => STM.STM, from: number ) => { const forIndex = (index: number): STM.STM, E, R> => index < self.chunk.length ? pipe( tRef.get(self.chunk[index]), core.flatMap(predicate), core.flatMap((bool) => bool ? core.succeed(Option.some(index)) : forIndex(index + 1) ) ) : stm.succeedNone return from < 0 ? stm.succeedNone : forIndex(from) }) /** @internal */ export const findFirstSTM = dual< ( predicate: (value: A) => STM.STM ) => ( self: TArray.TArray ) => STM.STM, E, R>, ( self: TArray.TArray, predicate: (value: A) => STM.STM ) => STM.STM, E, R> >(2, (self: TArray.TArray, predicate: (value: A) => STM.STM) => { const init = [Option.none() as Option.Option, 0 as number] as const const cont = (state: readonly [Option.Option, number]) => Option.isNone(state[0]) && state[1] < self.chunk.length - 1 return core.map( stm.iterate(init, { while: cont, body: (state) => { const index = state[1] return pipe( tRef.get(self.chunk[index]), core.flatMap((value) => core.map( predicate(value), (bool) => [bool ? Option.some(value) : Option.none(), index + 1] as const ) ) ) } }), (state) => state[0] ) }) /** @internal */ export const findLast = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, predicate: Predicate) => STM.STM> >(2, (self: TArray.TArray, predicate: Predicate) => core.effect>((journal) => { let index = self.chunk.length - 1 let result: Option.Option = Option.none() while (Option.isNone(result) && index >= 0) { const element = tRef.unsafeGet(self.chunk[index], journal) if (predicate(element)) { result = Option.some(element) } index = index - 1 } return result })) /** @internal */ export const findLastIndex = dual< (value: A) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, value: A) => STM.STM> >(2, (self, value) => findLastIndexFrom(self, value, self.chunk.length - 1)) /** @internal */ export const findLastIndexFrom = dual< (value: A, end: number) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, value: A, end: number) => STM.STM> >(3, (self, value, end) => { if (end >= self.chunk.length) { return stm.succeedNone } return core.effect>((journal) => { let index: number = end let found = false while (!found && index >= 0) { const element = tRef.unsafeGet(self.chunk[index], journal) found = Equal.equals(element)(value) index = index - 1 } if (found) { return Option.some(index + 1) } return Option.none() }) }) /** @internal */ export const findLastSTM = dual< ( predicate: (value: A) => STM.STM ) => (self: TArray.TArray) => STM.STM, E, R>, ( self: TArray.TArray, predicate: (value: A) => STM.STM ) => STM.STM, E, R> >(2, (self: TArray.TArray, predicate: (value: A) => STM.STM) => { const init = [Option.none() as Option.Option, self.chunk.length - 1] as const const cont = (state: readonly [Option.Option, number]) => Option.isNone(state[0]) && state[1] >= 0 return core.map( stm.iterate(init, { while: cont, body: (state) => { const index = state[1] return pipe( tRef.get(self.chunk[index]), core.flatMap((value) => core.map( predicate(value), (bool) => [bool ? Option.some(value) : Option.none(), index - 1] as const ) ) ) } }), (state) => state[0] ) }) /** @internal */ export const forEach = dual< (f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, f: (value: A) => STM.STM) => STM.STM >(2, (self, f) => reduceSTM(self, void 0 as void, (_, a) => f(a))) /** @internal */ export const fromIterable = (iterable: Iterable): STM.STM> => core.map( stm.forEach(iterable, tRef.make), (chunk) => new TArrayImpl(chunk) ) /** @internal */ export const get = dual< (index: number) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, index: number) => STM.STM >(2, (self, index) => { if (index < 0 || index >= self.chunk.length) { return core.dieMessage("Index out of bounds") } return tRef.get(self.chunk[index]) }) /** @internal */ export const headOption = (self: TArray.TArray): STM.STM> => self.chunk.length === 0 ? core.succeed(Option.none()) : core.map(tRef.get(self.chunk[0]), Option.some) /** @internal */ export const lastOption = (self: TArray.TArray): STM.STM> => self.chunk.length === 0 ? stm.succeedNone : core.map(tRef.get(self.chunk[self.chunk.length - 1]), Option.some) /** @internal */ export const make = ]>( ...elements: Elements ): STM.STM> => fromIterable(elements) /** @internal */ export const maxOption = dual< (order: Order.Order) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, order: Order.Order) => STM.STM> >(2, (self, order) => { const greaterThan = Order.greaterThan(order) return reduceOption(self, (acc, curr) => greaterThan(acc)(curr) ? curr : acc) }) /** @internal */ export const minOption = dual< (order: Order.Order) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, order: Order.Order) => STM.STM> >(2, (self, order) => { const lessThan = Order.lessThan(order) return reduceOption(self, (acc, curr) => lessThan(acc)(curr) ? curr : acc) }) /** @internal */ export const reduce = dual< (zero: Z, f: (accumulator: Z, current: A) => Z) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, zero: Z, f: (accumulator: Z, current: A) => Z) => STM.STM >( 3, (self: TArray.TArray, zero: Z, f: (accumulator: Z, current: A) => Z) => core.effect((journal) => { let index = 0 let result = zero while (index < self.chunk.length) { const element = tRef.unsafeGet(self.chunk[index], journal) result = f(result, element) index = index + 1 } return result }) ) /** @internal */ export const reduceOption = dual< (f: (x: A, y: A) => A) => (self: TArray.TArray) => STM.STM>, (self: TArray.TArray, f: (x: A, y: A) => A) => STM.STM> >( 2, (self: TArray.TArray, f: (x: A, y: A) => A) => core.effect>((journal) => { let index = 0 let result: A | undefined = undefined while (index < self.chunk.length) { const element = tRef.unsafeGet(self.chunk[index], journal) result = result === undefined ? element : f(result, element) index = index + 1 } return Option.fromNullable(result) }) ) /** @internal */ export const reduceOptionSTM = dual< (f: (x: A, y: A) => STM.STM) => (self: TArray.TArray) => STM.STM, E, R>, (self: TArray.TArray, f: (x: A, y: A) => STM.STM) => STM.STM, E, R> >( 2, (self: TArray.TArray, f: (x: A, y: A) => STM.STM) => reduceSTM(self, Option.none(), (acc, curr) => Option.isSome(acc) ? core.map(f(acc.value, curr), Option.some) : stm.succeedSome(curr)) ) /** @internal */ export const reduceSTM = dual< ( zero: Z, f: (accumulator: Z, current: A) => STM.STM ) => (self: TArray.TArray) => STM.STM, ( self: TArray.TArray, zero: Z, f: (accumulator: Z, current: A) => STM.STM ) => STM.STM >(3, (self, zero, f) => core.flatMap( toArray(self), stm.reduce(zero, f) )) /** @internal */ export const size = (self: TArray.TArray): number => self.chunk.length /** @internal */ export const some = dual< (predicate: Predicate) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: Predicate) => STM.STM >(2, (self, predicate) => core.map( findFirst(self, predicate), Option.isSome )) /** @internal */ export const someSTM = dual< (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM >(2, (self, predicate) => core.map(countSTM(self, predicate), (n) => n > 0)) /** @internal */ export const toArray = (self: TArray.TArray): STM.STM> => stm.forEach(self.chunk, tRef.get) /** @internal */ export const transform = dual< (f: (value: A) => A) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, f: (value: A) => A) => STM.STM >(2, (self, f) => core.effect((journal) => { let index = 0 while (index < self.chunk.length) { const ref = self.chunk[index] tRef.unsafeSet(ref, f(tRef.unsafeGet(ref, journal)), journal) index = index + 1 } return void 0 })) /** @internal */ export const transformSTM = dual< (f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, f: (value: A) => STM.STM) => STM.STM >(2, (self: TArray.TArray, f: (value: A) => STM.STM) => core.flatMap( stm.forEach( self.chunk, (ref) => core.flatMap(tRef.get(ref), f) ), (chunk) => core.effect((journal) => { const iterator = chunk[Symbol.iterator]() let index = 0 let next: IteratorResult while ((next = iterator.next()) && !next.done) { tRef.unsafeSet(self.chunk[index], next.value, journal) index = index + 1 } return void 0 }) )) /** @internal */ export const update = dual< (index: number, f: (value: A) => A) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, index: number, f: (value: A) => A) => STM.STM >(3, (self, index, f) => { if (index < 0 || index >= self.chunk.length) { return core.dieMessage("Index out of bounds") } return tRef.update(self.chunk[index], f) }) /** @internal */ export const updateSTM = dual< (index: number, f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, (self: TArray.TArray, index: number, f: (value: A) => STM.STM) => STM.STM >(3, (self, index, f) => { if (index < 0 || index >= self.chunk.length) { return core.dieMessage("Index out of bounds") } return pipe( tRef.get(self.chunk[index]), core.flatMap(f), core.flatMap((updated) => tRef.set(self.chunk[index], updated)) ) })