veffect
Version:
powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha
391 lines (363 loc) • 10.7 kB
text/typescript
/**
* @since 2.0.0
*/
import * as Equal from "./Equal.js"
import type * as Equivalence from "./Equivalence.js"
import * as Dual from "./Function.js"
import { pipe } from "./Function.js"
import * as Hash from "./Hash.js"
import type { Inspectable } from "./Inspectable.js"
import { format, NodeInspectSymbol, toJSON } from "./Inspectable.js"
import type { Order } from "./Order.js"
import type { Pipeable } from "./Pipeable.js"
import { pipeArguments } from "./Pipeable.js"
import type { Predicate } from "./Predicate.js"
import { hasProperty } from "./Predicate.js"
import * as RBT from "./RedBlackTree.js"
import type { Invariant, NoInfer } from "./Types.js"
const TypeId: unique symbol = Symbol.for("effect/SortedSet")
/**
* @since 2.0.0
* @category symbol
*/
export type TypeId = typeof TypeId
/**
* @since 2.0.0
* @category models
*/
export interface SortedSet<in out A> extends Iterable<A>, Equal.Equal, Pipeable, Inspectable {
readonly [TypeId]: {
readonly _A: Invariant<A>
}
/** @internal */
readonly keyTree: RBT.RedBlackTree<A, boolean>
}
const SortedSetProto: Omit<SortedSet<unknown>, "keyTree"> = {
[TypeId]: {
_A: (_: any) => _
},
[Hash.symbol]<A>(this: SortedSet<A>): number {
return pipe(
Hash.hash(this.keyTree),
Hash.combine(Hash.hash(TypeId)),
Hash.cached(this)
)
},
[Equal.symbol]<A>(this: SortedSet<A>, that: unknown): boolean {
return isSortedSet(that) && Equal.equals(this.keyTree, that.keyTree)
},
[Symbol.iterator]<A>(this: SortedSet<A>): Iterator<A> {
return RBT.keys(this.keyTree)
},
toString<A>(this: SortedSet<A>) {
return format(this.toJSON())
},
toJSON() {
return {
_id: "SortedSet",
values: Array.from(this).map(toJSON)
}
},
[NodeInspectSymbol]() {
return this.toJSON()
},
pipe() {
return pipeArguments(this, arguments)
}
}
const fromTree = <A>(keyTree: RBT.RedBlackTree<A, boolean>): SortedSet<A> => {
const a = Object.create(SortedSetProto)
a.keyTree = keyTree
return a
}
/**
* @since 2.0.0
* @category refinements
*/
export const isSortedSet: {
<A>(u: Iterable<A>): u is SortedSet<A>
(u: unknown): u is SortedSet<unknown>
} = (u: unknown): u is SortedSet<unknown> => hasProperty(u, TypeId)
/**
* @since 2.0.0
* @category constructors
*/
export const empty = <A>(O: Order<A>): SortedSet<A> => fromTree(RBT.empty(O))
/**
* Creates a new `SortedSet` from an iterable collection of values.
*
* @since 2.0.0
* @category constructors
*/
export const fromIterable: {
<B>(ord: Order<B>): <A extends B>(iterable: Iterable<A>) => SortedSet<A>
<A extends B, B>(iterable: Iterable<A>, ord: Order<B>): SortedSet<A>
} = Dual.dual(
2,
<A extends B, B>(iterable: Iterable<A>, ord: Order<B>): SortedSet<A> =>
fromTree(RBT.fromIterable(Array.from(iterable).map((k) => [k, true]), ord))
)
/**
* @since 2.0.0
* @category constructors
*/
export const make =
<K>(ord: Order<K>) => <Entries extends ReadonlyArray<K>>(...entries: Entries): SortedSet<Entries[number]> =>
fromIterable(entries, ord)
/**
* @since 2.0.0
* @category elements
*/
export const add: {
<A>(value: A): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, value: A): SortedSet<A>
} = Dual.dual<
<A>(value: A) => (self: SortedSet<A>) => SortedSet<A>,
<A>(self: SortedSet<A>, value: A) => SortedSet<A>
>(2, (self, value) =>
RBT.has(self.keyTree, value)
? self
: fromTree(RBT.insert(self.keyTree, value, true)))
/**
* @since 2.0.0
*/
export const difference: {
<A, B extends A>(that: Iterable<B>): (self: SortedSet<A>) => SortedSet<A>
<A, B extends A>(self: SortedSet<A>, that: Iterable<B>): SortedSet<A>
} = Dual.dual<
<A, B extends A>(that: Iterable<B>) => (self: SortedSet<A>) => SortedSet<A>,
<A, B extends A>(self: SortedSet<A>, that: Iterable<B>) => SortedSet<A>
>(2, <A, B extends A>(self: SortedSet<A>, that: Iterable<B>) => {
let out = self
for (const value of that) {
out = remove<A | B>(out, value)
}
return out
})
/**
* Check if a predicate holds true for every `SortedSet` element.
*
* @since 2.0.0
* @category elements
*/
export const every: {
<A>(predicate: Predicate<A>): (self: SortedSet<A>) => boolean
<A>(self: SortedSet<A>, predicate: Predicate<A>): boolean
} = Dual.dual(2, <A>(self: SortedSet<A>, predicate: Predicate<A>): boolean => {
for (const value of self) {
if (!predicate(value)) {
return false
}
}
return true
})
/**
* @since 2.0.0
* @category filtering
*/
export const filter: {
<A, B extends A>(predicate: Predicate<B>): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, predicate: Predicate<A>): SortedSet<A>
} = Dual.dual(2, <A>(self: SortedSet<A>, predicate: Predicate<A>): SortedSet<A> => {
const ord = RBT.getOrder(self.keyTree)
let out = empty<A>(ord)
for (const value of self) {
if (predicate(value)) {
out = add(out, value)
}
}
return out
})
/**
* @since 2.0.0
* @category sequencing
*/
export const flatMap: {
<B, A>(O: Order<B>, f: (a: A) => Iterable<B>): (self: SortedSet<A>) => SortedSet<B>
<A, B>(self: SortedSet<A>, O: Order<B>, f: (a: A) => Iterable<B>): SortedSet<B>
} = Dual.dual<
<B, A>(O: Order<B>, f: (a: A) => Iterable<B>) => (self: SortedSet<A>) => SortedSet<B>,
<A, B>(self: SortedSet<A>, O: Order<B>, f: (a: A) => Iterable<B>) => SortedSet<B>
>(3, (self, O, f) => {
let out = empty(O)
forEach(self, (a) => {
for (const b of f(a)) {
out = add(out, b)
}
})
return out
})
/**
* @since 2.0.0
* @category traversing
*/
export const forEach: {
<A>(f: (a: A) => void): (self: SortedSet<A>) => void
<A>(self: SortedSet<A>, f: (a: A) => void): void
} = Dual.dual<
<A>(f: (a: A) => void) => (self: SortedSet<A>) => void,
<A>(self: SortedSet<A>, f: (a: A) => void) => void
>(2, (self, f) => RBT.forEach(self.keyTree, f))
/**
* @since 2.0.0
* @category elements
*/
export const has: {
<A>(value: A): (self: SortedSet<A>) => boolean
<A>(self: SortedSet<A>, value: A): boolean
} = Dual.dual<
<A>(value: A) => (self: SortedSet<A>) => boolean,
<A>(self: SortedSet<A>, value: A) => boolean
>(2, (self, value) => RBT.has(self.keyTree, value))
/**
* @since 2.0.0
*/
export const intersection: {
<A>(that: Iterable<A>): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, that: Iterable<A>): SortedSet<A>
} = Dual.dual<
<A>(that: Iterable<A>) => (self: SortedSet<A>) => SortedSet<A>,
<A>(self: SortedSet<A>, that: Iterable<A>) => SortedSet<A>
>(2, (self, that) => {
const ord = RBT.getOrder(self.keyTree)
let out = empty(ord)
for (const value of that) {
if (has(self, value)) {
out = add(out, value)
}
}
return out
})
/**
* @since 2.0.0
* @category elements
*/
export const isSubset: {
<A>(that: SortedSet<A>): (self: SortedSet<A>) => boolean
<A>(self: SortedSet<A>, that: SortedSet<A>): boolean
} = Dual.dual<
<A>(that: SortedSet<A>) => (self: SortedSet<A>) => boolean,
<A>(self: SortedSet<A>, that: SortedSet<A>) => boolean
>(2, (self, that) => every(self, (a) => has(that, a)))
/**
* @since 2.0.0
* @category mapping
*/
export const map: {
<B, A>(O: Order<B>, f: (a: A) => B): (self: SortedSet<A>) => SortedSet<B>
<B, A>(self: SortedSet<A>, O: Order<B>, f: (a: A) => B): SortedSet<B>
} = Dual.dual<
<B, A>(O: Order<B>, f: (a: A) => B) => (self: SortedSet<A>) => SortedSet<B>,
<B, A>(self: SortedSet<A>, O: Order<B>, f: (a: A) => B) => SortedSet<B>
>(3, (self, O, f) => {
let out = empty(O)
forEach(self, (a) => {
const b = f(a)
if (!has(out, b)) {
out = add(out, b)
}
})
return out
})
/**
* @since 2.0.0
* @category filtering
*/
export const partition: {
<A>(
predicate: (a: NoInfer<A>) => boolean
): (self: SortedSet<A>) => [excluded: SortedSet<A>, satisfying: SortedSet<A>]
<A>(self: SortedSet<A>, predicate: (a: A) => boolean): [excluded: SortedSet<A>, satisfying: SortedSet<A>]
} = Dual.dual(
2,
<A>(self: SortedSet<A>, predicate: (a: A) => boolean): [excluded: SortedSet<A>, satisfying: SortedSet<A>] => {
const ord = RBT.getOrder(self.keyTree)
let right = empty(ord)
let left = empty(ord)
for (const value of self) {
if (predicate(value)) {
right = add(right, value)
} else {
left = add(left, value)
}
}
return [left, right]
}
)
/**
* @since 2.0.0
* @category elements
*/
export const remove: {
<A>(value: A): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, value: A): SortedSet<A>
} = Dual.dual<
<A>(value: A) => (self: SortedSet<A>) => SortedSet<A>,
<A>(self: SortedSet<A>, value: A) => SortedSet<A>
>(2, (self, value) => fromTree(RBT.removeFirst(self.keyTree, value)))
/**
* @since 2.0.0
* @category getters
*/
export const size = <A>(self: SortedSet<A>): number => RBT.size(self.keyTree)
/**
* Check if a predicate holds true for some `SortedSet` element.
*
* @since 2.0.0
* @category elements
*/
export const some: {
<A>(predicate: Predicate<A>): (self: SortedSet<A>) => boolean
<A>(self: SortedSet<A>, predicate: Predicate<A>): boolean
} = Dual.dual<
<A>(predicate: Predicate<A>) => (self: SortedSet<A>) => boolean,
<A>(self: SortedSet<A>, predicate: Predicate<A>) => boolean
>(2, (self, predicate) => {
for (const value of self) {
if (predicate(value)) {
return true
}
}
return false
})
/**
* @since 2.0.0
* @category elements
*/
export const toggle: {
<A>(value: A): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, value: A): SortedSet<A>
} = Dual.dual<
<A>(value: A) => (self: SortedSet<A>) => SortedSet<A>,
<A>(self: SortedSet<A>, value: A) => SortedSet<A>
>(2, (self, value) => has(self, value) ? remove(self, value) : add(self, value))
/**
* @since 2.0.0
*/
export const union: {
<A>(that: Iterable<A>): (self: SortedSet<A>) => SortedSet<A>
<A>(self: SortedSet<A>, that: Iterable<A>): SortedSet<A>
} = Dual.dual<
<A>(that: Iterable<A>) => (self: SortedSet<A>) => SortedSet<A>,
<A>(self: SortedSet<A>, that: Iterable<A>) => SortedSet<A>
>(2, <A>(self: SortedSet<A>, that: Iterable<A>) => {
const ord = RBT.getOrder(self.keyTree)
let out = empty<A>(ord)
for (const value of self) {
out = add(value)(out)
}
for (const value of that) {
out = add(value)(out)
}
return out
})
/**
* @since 2.0.0
* @category getters
*/
export const values = <A>(self: SortedSet<A>): IterableIterator<A> => RBT.keys(self.keyTree)
/**
* @since 2.0.0
* @category equivalence
*/
export const getEquivalence = <A>(): Equivalence.Equivalence<SortedSet<A>> => (a, b) => isSubset(a, b) && isSubset(b, a)