diff --git a/test/Semigroupal.ts b/test/Semigroupal.ts index 08a25fc05..90e6d3c81 100644 --- a/test/Semigroupal.ts +++ b/test/Semigroupal.ts @@ -1,34 +1,78 @@ import { pipe } from "@fp-ts/core/internal/Function" import * as _ from "@fp-ts/core/Semigroupal" import * as O from "./data/Option" +import * as RA from "./data/ReadonlyArray" import * as string from "./data/string" import * as U from "./util" describe("Semigroupal", () => { - it("zipWithComposition", () => { + describe("zipWithComposition", () => { const sum = (a: number, b: number): number => a + b - const zipWith = _.zipWithComposition(O.Semigroupal, O.Semigroupal) - U.deepStrictEqual(pipe(O.none, zipWith(O.none, sum)), O.none) - U.deepStrictEqual(pipe(O.some(O.none), zipWith(O.none, sum)), O.none) - U.deepStrictEqual(pipe(O.some(O.some(1)), zipWith(O.none, sum)), O.none) - U.deepStrictEqual(pipe(O.some(O.some(1)), zipWith(O.some(O.none), sum)), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.none), zipWith(O.some(O.some(2)), sum)), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.some(1)), zipWith(O.some(O.some(2)), sum)), O.some(O.some(3))) + it("ReadonlyArray", () => { + const zipWith = _.zipWithComposition(RA.Semigroupal, O.Semigroupal) + U.deepStrictEqual(pipe([], zipWith([O.none], sum)), []) + U.deepStrictEqual(pipe([O.none], zipWith([], sum)), []) + U.deepStrictEqual(pipe([O.none], zipWith([O.none], sum)), [O.none]) + U.deepStrictEqual(pipe([O.some(1)], zipWith([O.some(2)], sum)), [O.some(3)]) + U.deepStrictEqual(pipe([O.some(1), O.none], zipWith([O.some(2)], sum)), [O.some(3), O.none]) + U.deepStrictEqual(pipe([O.some(1), O.none], zipWith([O.some(2), O.some(3)], sum)), [ + O.some(3), + O.some(4), + O.none, + O.none + ]) + + it("Option", () => { + const sum = (a: number, b: number): number => a + b + const zipWith = _.zipWithComposition(O.Semigroupal, O.Semigroupal) + U.deepStrictEqual(pipe(O.none, zipWith(O.none, sum)), O.none) + U.deepStrictEqual(pipe(O.some(O.none), zipWith(O.none, sum)), O.none) + U.deepStrictEqual(pipe(O.some(O.some(1)), zipWith(O.none, sum)), O.none) + U.deepStrictEqual(pipe(O.some(O.some(1)), zipWith(O.some(O.none), sum)), O.some(O.none)) + U.deepStrictEqual(pipe(O.some(O.none), zipWith(O.some(O.some(2)), sum)), O.some(O.none)) + U.deepStrictEqual( + pipe(O.some(O.some(1)), zipWith(O.some(O.some(2)), sum)), + O.some(O.some(3)) + ) + }) + }) }) - it("zipManyComposition", () => { - const zipMany = _.zipManyComposition(O.Semigroupal, O.Semigroupal) - U.deepStrictEqual(pipe(O.none, zipMany([])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), zipMany([])), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.some(1)), zipMany([])), O.some(O.some([1] as const))) - U.deepStrictEqual(pipe(O.none, zipMany([O.none])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.none])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.some(O.none)])), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.some(O.some("a"))])), O.some(O.none)) - U.deepStrictEqual( - pipe(O.some(O.some(1)), zipMany([O.some(O.some(2))])), - O.some(O.some([1, 2] as const)) - ) + describe("zipManyComposition", () => { + it("ReadonlyArray", () => { + const zipMany = _.zipManyComposition(RA.Semigroupal, O.Semigroupal) + U.deepStrictEqual(pipe([O.some(1), O.none], zipMany([])), [O.some([1] as const), O.none]) + U.deepStrictEqual(pipe([O.some(1), O.none], zipMany([[O.some(2), O.none]])), [ + O.some([1, 2] as const), + O.none, + O.none, + O.none + ]) + U.deepStrictEqual( + pipe([O.some(1), O.some(2)], zipMany([[O.some(3), O.some(4)], [O.some(5)]])), + [ + O.some([1, 3, 5] as const), + O.some([1, 4, 5] as const), + O.some([2, 3, 5] as const), + O.some([2, 4, 5] as const) + ] + ) + }) + + it("Option", () => { + const zipMany = _.zipManyComposition(O.Semigroupal, O.Semigroupal) + U.deepStrictEqual(pipe(O.none, zipMany([])), O.none) + U.deepStrictEqual(pipe(O.some(O.none), zipMany([])), O.some(O.none)) + U.deepStrictEqual(pipe(O.some(O.some(1)), zipMany([])), O.some(O.some([1] as const))) + U.deepStrictEqual(pipe(O.none, zipMany([O.none])), O.none) + U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.none])), O.none) + U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.some(O.none)])), O.some(O.none)) + U.deepStrictEqual(pipe(O.some(O.none), zipMany([O.some(O.some("a"))])), O.some(O.none)) + U.deepStrictEqual( + pipe(O.some(O.some(1)), zipMany([O.some(O.some(2))])), + O.some(O.some([1, 2] as const)) + ) + }) }) it("ap", () => { diff --git a/test/data/ReadonlyArray.ts b/test/data/ReadonlyArray.ts index cc05cbbbd..414ad807f 100644 --- a/test/data/ReadonlyArray.ts +++ b/test/data/ReadonlyArray.ts @@ -3,7 +3,9 @@ import type * as foldableWithIndex from "@fp-ts/core/FoldableWithIndex" import type * as functor from "@fp-ts/core/Functor" import type * as functorWithIndex from "@fp-ts/core/FunctorWithIndex" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import { pipe } from "@fp-ts/core/internal/Function" import type * as monoidal from "@fp-ts/core/Monoidal" +import type * as semigroupal from "@fp-ts/core/Semigroupal" import type { Sortable } from "@fp-ts/core/Sortable" import type * as traverse_ from "@fp-ts/core/Traversable" import type * as traverseWithIndex_ from "@fp-ts/core/TraversableWithIndex" @@ -14,8 +16,11 @@ export interface ReadonlyArrayTypeLambda extends TypeLambda { readonly type: ReadonlyArray } +const map = (f: (a: A) => B) => + (self: ReadonlyArray): ReadonlyArray => self.map(a => f(a)) + export const Functor: functor.Functor = { - map: (f) => (self) => self.map(a => f(a)) + map } export const FunctorWithIndex: functorWithIndex.FunctorWithIndex = @@ -90,3 +95,35 @@ export const TraverseWithIndex: traverseWithIndex_.TraversableWithIndex< > = { traverseWithIndex } + +export const zipWith = (that: ReadonlyArray, f: (a: A, b: B) => C) => + (self: ReadonlyArray): ReadonlyArray => { + const out: Array = [] + for (const a of self) { + for (const b of that) { + out.push(f(a, b)) + } + } + return out + } + +export const Semigroupal: semigroupal.Semigroupal = { + map, + zipWith, + zipMany: (collection: Iterable>) => + (self: ReadonlyArray): ReadonlyArray]> => { + const fa: ReadonlyArray]> = pipe( + self, + Functor.map(a => [a] as const) + ) + const fas = Array.from(collection) + if (fas.length === 0) { + return fa + } + let out = fa + for (const c of fas) { + out = pipe(out, zipWith(c, (a, b) => [...a, b])) + } + return out + } +}