From 8fb2d08ecad9de097d7cb725c3480d8e671b6654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Espiau?= <7319147+FredericEspiau@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:23:31 +0200 Subject: [PATCH] Add getSemigroupUnion, getMonoidUnion and getSemigroupIntersection for Record --- .changeset/large-laws-drum.md | 5 ++ packages/typeclass/src/data/Record.ts | 58 +++++++++++++++++++++ packages/typeclass/test/data/Record.test.ts | 41 +++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 .changeset/large-laws-drum.md diff --git a/.changeset/large-laws-drum.md b/.changeset/large-laws-drum.md new file mode 100644 index 0000000000..1daa5f9023 --- /dev/null +++ b/.changeset/large-laws-drum.md @@ -0,0 +1,5 @@ +--- +"@effect/typeclass": patch +--- + +Add getSemigroupUnion, getMonoidUnion and getSemigroupIntersection for Record diff --git a/packages/typeclass/src/data/Record.ts b/packages/typeclass/src/data/Record.ts index 3478b9c584..26a477bc49 100644 --- a/packages/typeclass/src/data/Record.ts +++ b/packages/typeclass/src/data/Record.ts @@ -10,6 +10,8 @@ import type * as applicative from "../Applicative.js" import * as covariant from "../Covariant.js" import type * as filterable from "../Filterable.js" import type * as invariant from "../Invariant.js" +import * as monoid from "../Monoid.js" +import * as semigroup from "../Semigroup.js" import type * as traversable from "../Traversable.js" import type * as traversableFilterable from "../TraversableFilterable.js" @@ -201,3 +203,59 @@ export const getTraversableFilterable = (): traversableFiltera * @since 0.24.0 */ export const TraversableFilterable = getTraversableFilterable() + +/** + * A `Semigroup` that creates an union of two records. + * + * @example + * import * as NumberInstances from "@effect/typeclass/data/Number" + * import { getSemigroupUnion } from "@effect/typeclass/data/Record" + * + * assert.deepStrictEqual(getSemigroupUnion(NumberInstances.MonoidSum).combine({ a: 1 }, { a: 1, b: 3 }), { a: 2, b: 3 }) + * + * @category instances + * @since 0.29.3 + */ +export const getSemigroupUnion: ( + value: semigroup.Semigroup +) => semigroup.Semigroup> = (value: semigroup.Semigroup) => + semigroup.make>((self, that) => Record.union(self, that, value.combine)) + +/** + * A `Monoid` that creates an union of two records. + * + * The `empty` value is `{}`. + * + * @example + * import * as NumberInstances from "@effect/typeclass/data/Number" + * import { getMonoidUnion } from "@effect/typeclass/data/Record" + * + * const monoid = getMonoidUnion(NumberInstances.MonoidSum) + * + * assert.deepStrictEqual(monoid.combine({ a: 1 }, { a: 1, b: 3 }), { a: 2, b: 3 }) + * assert.deepStrictEqual(monoid.combine({ a: 1 }, monoid.empty), { a: 1 }) + * + * @category instances + * @since 0.29.3 + */ +export const getMonoidUnion: ( + value: monoid.Monoid +) => monoid.Monoid> = (value: monoid.Monoid) => + monoid.fromSemigroup(getSemigroupUnion(value), Record.empty()) + +/** + * A `Semigroup` that creates an intersection of two records. + * + * @example + * import * as NumberInstances from "@effect/typeclass/data/Number" + * import { getSemigroupIntersection } from "@effect/typeclass/data/Record" + * + * assert.deepStrictEqual(getSemigroupIntersection(NumberInstances.MonoidSum).combine({ a: 1 }, { a: 1, b: 3 }), { a: 2 }) + * + * @category instances + * @since 0.29.3 + */ +export const getSemigroupIntersection: ( + value: semigroup.Semigroup +) => semigroup.Semigroup> = (value: semigroup.Semigroup) => + semigroup.make>((self, that) => Record.intersection(self, that, value.combine)) diff --git a/packages/typeclass/test/data/Record.test.ts b/packages/typeclass/test/data/Record.test.ts index fe5196fa54..448bc008ca 100644 --- a/packages/typeclass/test/data/Record.test.ts +++ b/packages/typeclass/test/data/Record.test.ts @@ -1,5 +1,8 @@ +import * as NumberInstances from "@effect/typeclass/data/Number" import * as OptionInstances from "@effect/typeclass/data/Option" import * as RecordInstances from "@effect/typeclass/data/Record" +import * as monoid from "@effect/typeclass/Monoid" +import * as semigroup from "@effect/typeclass/Semigroup" import * as Option from "effect/Option" import { describe, expect, it } from "vitest" @@ -40,4 +43,42 @@ describe.concurrent("Record", () => { } expect(traverse(symbolRecord, (a) => Option.some(a))).toStrictEqual(Option.some({})) }) + + it("SemigroupUnion", () => { + const semigroupUnion = RecordInstances.getSemigroupUnion(semigroup.struct({ + inner: NumberInstances.SemigroupSum + })) + + const a = { a: { inner: 1 } } + const b = { a: { inner: 3 }, b: { inner: 2 } } + const c = { b: { inner: 7 } } + expect(semigroupUnion.combine(a, b)).toStrictEqual({ a: { inner: 4 }, b: { inner: 2 } }) + expect(semigroupUnion.combineMany(a, [b, c])).toStrictEqual({ a: { inner: 4 }, b: { inner: 9 } }) + }) + + it("MonoidUnion", () => { + const monoidUnion = RecordInstances.getMonoidUnion(monoid.struct({ + inner: NumberInstances.MonoidMax + })) + + const a = { a: { inner: 1 } } + const b = { a: { inner: 3 }, b: { inner: 2 } } + const c = { b: { inner: 7 } } + expect(monoidUnion.combine(a, b)).toStrictEqual({ a: { inner: 3 }, b: { inner: 2 } }) + expect(monoidUnion.combine(a, monoidUnion.empty)).toStrictEqual(a) + expect(monoidUnion.combineMany(a, [b, c])).toStrictEqual({ a: { inner: 3 }, b: { inner: 7 } }) + expect(monoidUnion.combineAll([a, b, c])).toStrictEqual({ a: { inner: 3 }, b: { inner: 7 } }) + }) + + it("SemigroupIntersection", () => { + const semigroupIntersection = RecordInstances.getSemigroupIntersection(semigroup.struct({ + inner: NumberInstances.SemigroupSum + })) + + const a = { a: { inner: 1 } } + const b = { a: { inner: 3 }, b: { inner: 2 } } + const c = { b: { inner: 7 } } + expect(semigroupIntersection.combine(a, b)).toStrictEqual({ a: { inner: 4 } }) + expect(semigroupIntersection.combineMany(a, [b, c])).toStrictEqual({}) + }) })