From 2e44bbfe2324d7ada41142bcd228d559c65161f4 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 22 Apr 2022 09:25:05 +0200 Subject: [PATCH] optimize intercalate functions --- CHANGELOG.md | 8 +++++ docs/modules/Array.ts.md | 45 ++++++++++++----------- docs/modules/Foldable.ts.md | 10 +++--- docs/modules/NonEmptyArray.ts.md | 44 +++++++++++------------ docs/modules/ReadonlyArray.ts.md | 46 ++++++++++++------------ docs/modules/ReadonlyNonEmptyArray.ts.md | 44 +++++++++++------------ src/Array.ts | 28 +++++++-------- src/Foldable.ts | 19 +++++----- src/NonEmptyArray.ts | 27 +++++++------- src/ReadonlyArray.ts | 34 +++++++++--------- src/ReadonlyNonEmptyArray.ts | 34 +++++++++--------- 11 files changed, 173 insertions(+), 166 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c2d12a0..203e62282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ high state of flux, you're at risk of it changing without notice. - **New Feature** - add `IOOption` module (@gcanti) + - `Array` + - add `intercalate`, #1678 (@thewilkybarkid) - `Bounded` - add `clamp` (@gcanti) - add `reverse` (@gcanti) @@ -39,6 +41,8 @@ high state of flux, you're at risk of it changing without notice. - add `chainFirstEitherKW` (@gcanti) - add `orElseFirstIOK`, #1655 (@thewilkybarkid) - add `bracketW`, #1627 (@thomasvargiu) + - `NonEmptyArray` + - add `intercalate`, #1678 (@thewilkybarkid) - `Option` - add `chainFirstEitherK` (@gcanti) - `Ordering` @@ -60,6 +64,10 @@ high state of flux, you're at risk of it changing without notice. - add `chainNullableK` (@gcanti) - add `fromNullable` (@gcanti) - add `fromNullableK` (@gcanti) + - `ReadonlyArray` + - add `intercalate`, #1678 (@thewilkybarkid) + - `ReadonlyNonEmptyArray` + - add `intercalate`, #1678 (@thewilkybarkid) - `ReadonlyRecord` - add `toEntries`, #1552 (@bravely) - add `fromEntries`, #1552 (@bravely) diff --git a/docs/modules/Array.ts.md b/docs/modules/Array.ts.md index d3d30babe..a2f9ab6a3 100644 --- a/docs/modules/Array.ts.md +++ b/docs/modules/Array.ts.md @@ -87,7 +87,6 @@ Added in v2.0.0 - [flatten](#flatten) - [fromEitherK](#fromeitherk) - [fromOptionK](#fromoptionk) - - [intercalate](#intercalate) - [intersection](#intersection) - [intersperse](#intersperse) - [lefts](#lefts) @@ -198,6 +197,7 @@ Added in v2.0.0 - [findIndex](#findindex) - [findLastIndex](#findlastindex) - [insertAt](#insertat) + - [intercalate](#intercalate) - [isOutOfBound](#isoutofbound) - [lookup](#lookup) - [modifyAt](#modifyat) @@ -1437,28 +1437,6 @@ export declare const fromOptionK: (f: (...a: A) Added in v2.11.0 -## intercalate - -Creates a new `Array` placing an element in between members of the input `Array`, then folds the results using the -provided `Monoid`. - -**Signature** - -```ts -export declare const intercalate: (M: Monoid) => (sep: A) => (as: A[]) => A -``` - -**Example** - -```ts -import * as S from 'fp-ts/string' -import { intercalate } from 'fp-ts/Array' - -assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') -``` - -Added in v2.11.9 - ## intersection Creates an array of unique values that are included in all given arrays using a `Eq` for equality @@ -3454,6 +3432,27 @@ assert.deepStrictEqual(insertAt(2, 5)([1, 2, 3, 4]), some([1, 2, 5, 3, 4])) Added in v2.0.0 +## intercalate + +Places an element in between members of an `Array`, then folds the results using the provided `Monoid`. + +**Signature** + +```ts +export declare const intercalate: (M: Monoid) => (middle: A) => (as: A[]) => A +``` + +**Example** + +```ts +import * as S from 'fp-ts/string' +import { intercalate } from 'fp-ts/Array' + +assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') +``` + +Added in v2.12.0 + ## isOutOfBound Test whether an array contains a particular index diff --git a/docs/modules/Foldable.ts.md b/docs/modules/Foldable.ts.md index 857514f78..680455811 100644 --- a/docs/modules/Foldable.ts.md +++ b/docs/modules/Foldable.ts.md @@ -223,17 +223,17 @@ Fold a data structure, accumulating values in some `Monoid`, combining adjacent export declare function intercalate( M: Monoid, F: Foldable3 -): (sep: M, fm: Kind3) => M +): (middle: M, fm: Kind3) => M export declare function intercalate( M: Monoid, F: Foldable2 -): (sep: M, fm: Kind2) => M +): (middle: M, fm: Kind2) => M export declare function intercalate( M: Monoid, F: Foldable2C -): (sep: M, fm: Kind2) => M -export declare function intercalate(M: Monoid, F: Foldable1): (sep: M, fm: Kind) => M -export declare function intercalate(M: Monoid, F: Foldable): (sep: M, fm: HKT) => M +): (middle: M, fm: Kind2) => M +export declare function intercalate(M: Monoid, F: Foldable1): (middle: M, fm: Kind) => M +export declare function intercalate(M: Monoid, F: Foldable): (middle: M, fm: HKT) => M ``` **Example** diff --git a/docs/modules/NonEmptyArray.ts.md b/docs/modules/NonEmptyArray.ts.md index 0c03172b5..989f53ce6 100644 --- a/docs/modules/NonEmptyArray.ts.md +++ b/docs/modules/NonEmptyArray.ts.md @@ -63,7 +63,6 @@ Added in v2.0.0 - [group](#group) - [groupBy](#groupby) - [insertAt](#insertat) - - [intercalate](#intercalate) - [intersperse](#intersperse) - [modifyAt](#modifyat) - [prependAll](#prependall) @@ -130,6 +129,7 @@ Added in v2.0.0 - [extract](#extract) - [head](#head) - [init](#init) + - [intercalate](#intercalate) - [last](#last) - [max](#max) - [min](#min) @@ -532,27 +532,6 @@ export declare const insertAt: (i: number, a: A) => (as: A[]) => Option(S: Se.Semigroup) => (sep: A) => (as: NonEmptyArray) => A -``` - -**Example** - -```ts -import * as S from 'fp-ts/string' -import { intercalate } from 'fp-ts/NonEmptyArray' - -assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') -``` - -Added in v2.11.9 - ## intersperse Places an element in between members of an array @@ -1403,6 +1382,27 @@ assert.deepStrictEqual(init([1]), []) Added in v2.2.0 +## intercalate + +Places an element in between members of a `NonEmptyArray`, then folds the results using the provided `Semigroup`. + +**Signature** + +```ts +export declare const intercalate: (S: Se.Semigroup) => (middle: A) => (as: NonEmptyArray) => A +``` + +**Example** + +```ts +import * as S from 'fp-ts/string' +import { intercalate } from 'fp-ts/NonEmptyArray' + +assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') +``` + +Added in v2.12.0 + ## last **Signature** diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md index 632271b8a..0a21aefab 100644 --- a/docs/modules/ReadonlyArray.ts.md +++ b/docs/modules/ReadonlyArray.ts.md @@ -82,7 +82,6 @@ Added in v2.5.0 - [flatten](#flatten) - [fromEitherK](#fromeitherk) - [fromOptionK](#fromoptionk) - - [intercalate](#intercalate) - [intersection](#intersection) - [intersperse](#intersperse) - [lefts](#lefts) @@ -195,6 +194,7 @@ Added in v2.5.0 - [head](#head) - [init](#init) - [insertAt](#insertat) + - [intercalate](#intercalate) - [isOutOfBound](#isoutofbound) - [last](#last) - [lookup](#lookup) @@ -971,27 +971,6 @@ export declare const fromOptionK: ( Added in v2.11.0 -## intercalate - -Places an element in between members of an array`, then folds the results using the provided `Monoid`. - -**Signature** - -```ts -export declare const intercalate: (M: Monoid) => (sep: A) => (as: readonly A[]) => A -``` - -**Example** - -```ts -import * as S from 'fp-ts/string' -import { intercalate } from 'fp-ts/Array' - -assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') -``` - -Added in v2.11.9 - ## intersection Creates an array of unique values that are included in all given arrays using a `Eq` for equality @@ -1923,7 +1902,7 @@ Added in v2.7.0 **Signature** ```ts -export declare const Foldable: F.Foldable1<'ReadonlyArray'> +export declare const Foldable: Foldable1<'ReadonlyArray'> ``` Added in v2.7.0 @@ -2762,6 +2741,27 @@ assert.deepStrictEqual(insertAt(2, 5)([1, 2, 3, 4]), some([1, 2, 5, 3, 4])) Added in v2.5.0 +## intercalate + +Places an element in between members of a `ReadonlyArray`, then folds the results using the provided `Monoid`. + +**Signature** + +```ts +export declare const intercalate: (M: Monoid) => (middle: A) => (as: readonly A[]) => A +``` + +**Example** + +```ts +import * as S from 'fp-ts/string' +import { intercalate } from 'fp-ts/Array' + +assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') +``` + +Added in v2.12.0 + ## isOutOfBound Test whether an array contains a particular index diff --git a/docs/modules/ReadonlyNonEmptyArray.ts.md b/docs/modules/ReadonlyNonEmptyArray.ts.md index 14704517a..8e8a64051 100644 --- a/docs/modules/ReadonlyNonEmptyArray.ts.md +++ b/docs/modules/ReadonlyNonEmptyArray.ts.md @@ -68,7 +68,6 @@ Added in v2.5.0 - [getUnionSemigroup](#getunionsemigroup) - [group](#group) - [groupBy](#groupby) - - [intercalate](#intercalate) - [intersperse](#intersperse) - [modifyAt](#modifyat) - [prependAll](#prependall) @@ -136,6 +135,7 @@ Added in v2.5.0 - [concatAll](#concatall) - [head](#head) - [init](#init) + - [intercalate](#intercalate) - [last](#last) - [max](#max) - [min](#min) @@ -608,27 +608,6 @@ assert.deepStrictEqual(groupBy((s: string) => String(s.length))(['a', 'b', 'ab'] Added in v2.5.0 -## intercalate - -**Note**. The constraint is relaxed: a `Semigroup` instead of a `Monoid`. - -**Signature** - -```ts -export declare const intercalate: (S: Se.Semigroup) => (sep: A) => (as: ReadonlyNonEmptyArray) => A -``` - -**Example** - -```ts -import * as S from 'fp-ts/string' -import { intercalate } from 'fp-ts/ReadonlyNonEmptyArray' - -assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') -``` - -Added in v2.11.9 - ## intersperse Places an element in between members of a `ReadonlyNonEmptyArray`. @@ -1509,6 +1488,27 @@ assert.deepStrictEqual(init([1]), []) Added in v2.5.0 +## intercalate + +Places an element in between members of a `ReadonlyNonEmptyArray`, then folds the results using the provided `Semigroup`. + +**Signature** + +```ts +export declare const intercalate: (S: Se.Semigroup) => (middle: A) => (as: ReadonlyNonEmptyArray) => A +``` + +**Example** + +```ts +import * as S from 'fp-ts/string' +import { intercalate } from 'fp-ts/ReadonlyNonEmptyArray' + +assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') +``` + +Added in v2.12.0 + ## last **Signature** diff --git a/src/Array.ts b/src/Array.ts index d1e9c2d45..759026c34 100644 --- a/src/Array.ts +++ b/src/Array.ts @@ -1124,21 +1124,6 @@ export const intersperse = (middle: A): ((as: Array) => Array) => { return (as) => (isNonEmpty(as) ? f(as) : copy(as)) } -/** - * Creates a new `Array` placing an element in between members of the input `Array`, then folds the results using the - * provided `Monoid`. - * - * @example - * import * as S from 'fp-ts/string' - * import { intercalate } from 'fp-ts/Array' - * - * assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') - * - * @category combinators - * @since 2.11.9 - */ -export const intercalate: (M: Monoid) => (sep: A) => (as: Array) => A = RA.intercalate - /** * Creates a new `Array` rotating the input `Array` by `n` steps. * @@ -2903,6 +2888,19 @@ export const some = (predicate: Predicate) => (as: Array): as is NonEmp */ export const exists = some +/** + * Places an element in between members of an `Array`, then folds the results using the provided `Monoid`. + * + * @example + * import * as S from 'fp-ts/string' + * import { intercalate } from 'fp-ts/Array' + * + * assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') + * + * @since 2.12.0 + */ +export const intercalate: (M: Monoid) => (middle: A) => (as: Array) => A = RA.intercalate + // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- diff --git a/src/Foldable.ts b/src/Foldable.ts index f6f1f9bb5..5bbfe7547 100644 --- a/src/Foldable.ts +++ b/src/Foldable.ts @@ -229,19 +229,22 @@ export function reduceM( export function intercalate( M: Monoid, F: Foldable3 -): (sep: M, fm: Kind3) => M -export function intercalate(M: Monoid, F: Foldable2): (sep: M, fm: Kind2) => M -export function intercalate(M: Monoid, F: Foldable2C): (sep: M, fm: Kind2) => M -export function intercalate(M: Monoid, F: Foldable1): (sep: M, fm: Kind) => M -export function intercalate(M: Monoid, F: Foldable): (sep: M, fm: HKT) => M -export function intercalate(M: Monoid, F: Foldable): (sep: M, fm: HKT) => M { +): (middle: M, fm: Kind3) => M +export function intercalate(M: Monoid, F: Foldable2): (middle: M, fm: Kind2) => M +export function intercalate( + M: Monoid, + F: Foldable2C +): (middle: M, fm: Kind2) => M +export function intercalate(M: Monoid, F: Foldable1): (middle: M, fm: Kind) => M +export function intercalate(M: Monoid, F: Foldable): (middle: M, fm: HKT) => M +export function intercalate(M: Monoid, F: Foldable): (middle: M, fm: HKT) => M { interface Acc { readonly init: boolean readonly acc: M } - return (sep, fm) => { + return (middle, fm) => { const go = ({ init, acc }: Acc, x: M): Acc => - init ? { init: false, acc: x } : { init: false, acc: M.concat(M.concat(acc, sep), x) } + init ? { init: false, acc: x } : { init: false, acc: M.concat(M.concat(acc, middle), x) } return F.reduce(fm, { init: true, acc: M.empty }, go).acc } } diff --git a/src/NonEmptyArray.ts b/src/NonEmptyArray.ts index ec5b9d815..d1f19f2e8 100644 --- a/src/NonEmptyArray.ts +++ b/src/NonEmptyArray.ts @@ -551,20 +551,6 @@ export const intersperse = (middle: A) => (as: NonEmptyArray): NonEmptyArr return isNonEmpty(rest) ? pipe(rest, prependAll(middle), prepend(head(as))) : copy(as) } -/** - * **Note**. The constraint is relaxed: a `Semigroup` instead of a `Monoid`. - * - * @example - * import * as S from 'fp-ts/string' - * import { intercalate } from 'fp-ts/NonEmptyArray' - * - * assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') - * - * @category combinators - * @since 2.11.9 - */ -export const intercalate: (S: Semigroup) => (sep: A) => (as: NonEmptyArray) => A = RNEA.intercalate - /** * @category combinators * @since 2.0.0 @@ -1230,6 +1216,19 @@ export const modifyLast = (f: Endomorphism) => (as: NonEmptyArray): Non */ export const updateLast = (a: A): ((as: NonEmptyArray) => NonEmptyArray) => modifyLast(() => a) +/** + * Places an element in between members of a `NonEmptyArray`, then folds the results using the provided `Semigroup`. + * + * @example + * import * as S from 'fp-ts/string' + * import { intercalate } from 'fp-ts/NonEmptyArray' + * + * assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') + * + * @since 2.12.0 + */ +export const intercalate: (S: Semigroup) => (middle: A) => (as: NonEmptyArray) => A = RNEA.intercalate + // ------------------------------------------------------------------------------------- // deprecated // ------------------------------------------------------------------------------------- diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index efd7fb8cb..c03289711 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -13,7 +13,7 @@ import { Eq, fromEquals } from './Eq' import { Extend1 } from './Extend' import { Filterable1 } from './Filterable' import { FilterableWithIndex1, PredicateWithIndex, RefinementWithIndex } from './FilterableWithIndex' -import * as F from './Foldable' +import { Foldable1 } from './Foldable' import { FoldableWithIndex1 } from './FoldableWithIndex' import { FromEither1, fromEitherK as fromEitherK_ } from './FromEither' import { identity, Lazy, pipe } from './function' @@ -49,7 +49,6 @@ import { } from './Witherable' import { Zero1, guard as guard_ } from './Zero' -import Foldable1 = F.Foldable1 import ReadonlyNonEmptyArray = RNEA.ReadonlyNonEmptyArray // ------------------------------------------------------------------------------------- @@ -1013,21 +1012,6 @@ export const intersperse = (middle: A): ((as: ReadonlyArray) => ReadonlyAr return (as) => (isNonEmpty(as) ? f(as) : as) } -/** - * Places an element in between members of an array`, then folds the results using the provided `Monoid`. - * - * @example - * import * as S from 'fp-ts/string' - * import { intercalate } from 'fp-ts/Array' - * - * assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') - * - * @category combinators - * @since 2.11.9 - */ -export const intercalate: (M: Monoid) => (sep: A) => (as: ReadonlyArray) => A = (M) => (sep) => (as) => - F.intercalate(M, Foldable)(sep, as) - /** * Rotate a `ReadonlyArray` by `n` steps. * @@ -2501,6 +2485,22 @@ export const some = (predicate: Predicate) => (as: ReadonlyArray): as i */ export const exists = some +/** + * Places an element in between members of a `ReadonlyArray`, then folds the results using the provided `Monoid`. + * + * @example + * import * as S from 'fp-ts/string' + * import { intercalate } from 'fp-ts/Array' + * + * assert.deepStrictEqual(intercalate(S.Monoid)('-')(['a', 'b', 'c']), 'a-b-c') + * + * @since 2.12.0 + */ +export const intercalate = (M: Monoid): ((middle: A) => (as: ReadonlyArray) => A) => { + const intercalateM = RNEA.intercalate(M) + return (middle) => match(() => M.empty, intercalateM(middle)) +} + // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- diff --git a/src/ReadonlyNonEmptyArray.ts b/src/ReadonlyNonEmptyArray.ts index a2d7d6635..10f6e01b2 100644 --- a/src/ReadonlyNonEmptyArray.ts +++ b/src/ReadonlyNonEmptyArray.ts @@ -22,7 +22,7 @@ import { Eq, fromEquals } from './Eq' import { Extend1 } from './Extend' import { Foldable1 } from './Foldable' import { FoldableWithIndex1 } from './FoldableWithIndex' -import { identity, Lazy, pipe, SK } from './function' +import { flow, identity, Lazy, pipe, SK } from './function' import { bindTo as bindTo_, flap as flap_, Functor1 } from './Functor' import { FunctorWithIndex1 } from './FunctorWithIndex' import { HKT } from './HKT' @@ -566,22 +566,6 @@ export const intersperse = (middle: A) => (as: ReadonlyNonEmptyArray): Rea return isNonEmpty(rest) ? pipe(rest, prependAll(middle), prepend(head(as))) : as } -/** - * **Note**. The constraint is relaxed: a `Semigroup` instead of a `Monoid`. - * - * @example - * import * as S from 'fp-ts/string' - * import { intercalate } from 'fp-ts/ReadonlyNonEmptyArray' - * - * assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') - * - * @category combinators - * @since 2.11.9 - */ -export const intercalate: (S: Semigroup) => (sep: A) => (as: ReadonlyNonEmptyArray) => A = (S) => (sep) => ( - as -) => concatAll(S)(intersperse(sep)(as)) - /** * @category combinators * @since 2.10.0 @@ -1294,6 +1278,22 @@ export const modifyLast = (f: Endomorphism) => (as: ReadonlyNonEmptyArray< */ export const updateLast = (a: A): ((as: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray) => modifyLast(() => a) +/** + * Places an element in between members of a `ReadonlyNonEmptyArray`, then folds the results using the provided `Semigroup`. + * + * @example + * import * as S from 'fp-ts/string' + * import { intercalate } from 'fp-ts/ReadonlyNonEmptyArray' + * + * assert.deepStrictEqual(intercalate(S.Semigroup)('-')(['a', 'b', 'c']), 'a-b-c') + * + * @since 2.12.0 + */ +export const intercalate = (S: Semigroup): ((middle: A) => (as: ReadonlyNonEmptyArray) => A) => { + const concatAllS = concatAll(S) + return (middle) => flow(intersperse(middle), concatAllS) +} + // ------------------------------------------------------------------------------------- // deprecated // -------------------------------------------------------------------------------------