From fc914c9319017abd3da4b11987342fbf56806eca Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 27 Oct 2022 15:18:28 +0200 Subject: [PATCH] NonEmptyTraversable: add sequenceNonEmpty as member --- .changeset/ninety-seas-tan.md | 5 ++++ Overview.md | 3 ++- src/typeclass/NonEmptyTraversable.ts | 34 ++++++++++++++++++++++--- test/test-data/NonEmptyReadonlyArray.ts | 12 +++++++-- test/typeclass/NonEmptyTraversable.ts | 18 ++++++++++++- 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 .changeset/ninety-seas-tan.md diff --git a/.changeset/ninety-seas-tan.md b/.changeset/ninety-seas-tan.md new file mode 100644 index 000000000..f1b8d3dad --- /dev/null +++ b/.changeset/ninety-seas-tan.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +NonEmptyTraversable: add sequenceNonEmpty as member diff --git a/Overview.md b/Overview.md index 8ac1ade70..ada2319df 100644 --- a/Overview.md +++ b/Overview.md @@ -392,8 +392,9 @@ instance instead of `Applicative`. | Name | Given | To | | --------------------------- | ------------------------------------------------ | ------------ | | **traverseNonEmpty** | `NonEmptyApplicative`, `T`, `A => F` | `F>` | +| **sequenceNonEmpty** | `NonEmptyApplicative`, `T>` | `F>` | | traverseNonEmptyComposition | `NonEmptyApplicative`, `T>`, `A => F` | `F>>` | -| sequenceNonEmpty | `NonEmptyApplicative`, `T>` | `F>` | +| sequenceNonEmptyComposition | `NonEmptyApplicative`, `T>>` | `F>>` | ### Of diff --git a/src/typeclass/NonEmptyTraversable.ts b/src/typeclass/NonEmptyTraversable.ts index 45b263062..91d5d0824 100644 --- a/src/typeclass/NonEmptyTraversable.ts +++ b/src/typeclass/NonEmptyTraversable.ts @@ -4,7 +4,8 @@ * @since 1.0.0 */ import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity } from "@fp-ts/core/internal/Function" +import { identity, pipe } from "@fp-ts/core/internal/Function" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" import type { NonEmptyApplicative } from "@fp-ts/core/typeclass/NonEmptyApplicative" /** @@ -19,6 +20,12 @@ export interface NonEmptyTraversable extends TypeClass ) => ( self: Kind ) => Kind> + + readonly sequenceNonEmpty: ( + F: NonEmptyApplicative + ) => ( + self: Kind> + ) => Kind> } /** @@ -34,16 +41,35 @@ export const traverseNonEmptyComposition = ( f: (a: A) => Kind ): (( - tfa: Kind> + self: Kind> ) => Kind>>) => T.traverseNonEmpty(F)(G.traverseNonEmpty(F)(f)) /** + * Returns a default `sequenceNonEmpty` composition. + * + * @since 1.0.0 + */ +export const sequenceNonEmptyComposition = ( + T: NonEmptyTraversable & Covariant, + G: NonEmptyTraversable +) => + (F: NonEmptyApplicative) => + ( + self: Kind>> + ): Kind>> => + T.sequenceNonEmpty(F)(pipe(self, T.map(G.sequenceNonEmpty(F)))) + +/** + * Returns a default `sequenceNonEmpty` implementation. + * * @since 1.0.0 */ -export const sequenceNonEmpty = (T: NonEmptyTraversable) => +export const sequenceNonEmpty = ( + traverseNonEmpty: NonEmptyTraversable["traverseNonEmpty"] +): NonEmptyTraversable["sequenceNonEmpty"] => ( F: NonEmptyApplicative ): (( self: Kind> - ) => Kind>) => T.traverseNonEmpty(F)(identity) + ) => Kind>) => traverseNonEmpty(F)(identity) diff --git a/test/test-data/NonEmptyReadonlyArray.ts b/test/test-data/NonEmptyReadonlyArray.ts index 6acf58e11..0b2c806cb 100644 --- a/test/test-data/NonEmptyReadonlyArray.ts +++ b/test/test-data/NonEmptyReadonlyArray.ts @@ -1,5 +1,6 @@ import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" +import { identity, pipe } from "@fp-ts/core/internal/Function" +import * as covariant from "@fp-ts/core/typeclass/Covariant" import type { NonEmptyApplicative } from "@fp-ts/core/typeclass/NonEmptyApplicative" import type * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" @@ -34,6 +35,10 @@ export const mapWithIndex = ( return out } +export const map = ( + f: (a: A) => B +): (self: NonEmptyReadonlyArray) => NonEmptyReadonlyArray => mapWithIndex(f) + export const traverseWithIndex = ( NonEmptyApplicative: NonEmptyApplicative ) => @@ -54,8 +59,11 @@ export const traverseNonEmpty = ( ): ((self: NonEmptyReadonlyArray) => Kind>) => traverseWithIndex(NonEmptyApplicative)(f) +export const Covariant: covariant.Covariant = covariant.make(map) + export const NonEmptyTraversable: nonEmptyTraversable.NonEmptyTraversable< NonEmptyReadonlyArrayTypeLambda > = { - traverseNonEmpty + traverseNonEmpty, + sequenceNonEmpty: F => self => pipe(self, traverseNonEmpty(F)(identity)) } diff --git a/test/typeclass/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index a3dada07f..c25f022de 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -15,8 +15,24 @@ describe("NonEmptyTraversable", () => { U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, -1]]), O.none) }) + it("traverseNonEmptyComposition", () => { + const sequence = _.sequenceNonEmptyComposition( + { ...NERA.NonEmptyTraversable, ...NERA.Covariant }, + NERA.NonEmptyTraversable + )(O.NonEmptyApplicative) + U.deepStrictEqual(sequence([[O.some(1)]]), O.some([[1]] as const)) + U.deepStrictEqual(sequence([[O.some(1), O.none]]), O.none) + U.deepStrictEqual( + sequence([[O.some(1), O.some(2), O.some(3)], [O.some(4), O.some(5)]]), + O.some([[1, 2, 3], [4, 5]] as const) + ) + U.deepStrictEqual(sequence([[O.some(1), O.some(2), O.some(3)], [O.some(4), O.none]]), O.none) + }) + it("sequenceNonEmpty", () => { - const sequenceNonEmpty = _.sequenceNonEmpty(NERA.NonEmptyTraversable)(O.NonEmptyApplicative) + const sequenceNonEmpty = _.sequenceNonEmpty( + NERA.NonEmptyTraversable.traverseNonEmpty + )(O.NonEmptyApplicative) U.deepStrictEqual(sequenceNonEmpty([O.none]), O.none) U.deepStrictEqual(sequenceNonEmpty([O.some(1)]), O.some([1] as const)) U.deepStrictEqual(sequenceNonEmpty([O.none]), O.none)