From 15a1a4a2982ea3103a77381a0eda076b15b026ab Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 14:32:47 +0100 Subject: [PATCH 01/80] add missing readonly modifiers --- .changeset/famous-parrots-shake.md | 5 +++++ pnpm-lock.yaml | 6 +++--- src/typeclass/Order.ts | 2 +- src/typeclass/Product.ts | 2 +- src/typeclass/SemiProduct.ts | 4 ++-- src/typeclass/Semigroup.ts | 4 ++-- 6 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 .changeset/famous-parrots-shake.md diff --git a/.changeset/famous-parrots-shake.md b/.changeset/famous-parrots-shake.md new file mode 100644 index 000000000..b73d5bcf9 --- /dev/null +++ b/.changeset/famous-parrots-shake.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add missing readonly modifiers diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8350642ea..7ac1e5a4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,7 +75,7 @@ importers: concurrently: 7.6.0 cpx: 1.5.0 docs-ts: 0.7.0_brpckf4sz23pco3jyty2eys3iq - dtslint: github.com/gcanti/dtslint/f361dc93d6a195f530df28779082548e01cecd5e + dtslint: github.com/gcanti/dtslint/4552d162099399c4e14f8486ced673411e5b3659 eslint: 8.28.0 eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee eslint-plugin-codegen: 0.16.1 @@ -7055,8 +7055,8 @@ packages: engines: {node: '>=10'} dev: true - github.com/gcanti/dtslint/f361dc93d6a195f530df28779082548e01cecd5e: - resolution: {tarball: https://codeload.github.com/gcanti/dtslint/tar.gz/f361dc93d6a195f530df28779082548e01cecd5e} + github.com/gcanti/dtslint/4552d162099399c4e14f8486ced673411e5b3659: + resolution: {tarball: https://codeload.github.com/gcanti/dtslint/tar.gz/4552d162099399c4e14f8486ced673411e5b3659} name: dtslint version: 0.4.4 engines: {node: '>=6.10.0'} diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index e6a17b131..571a872f6 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -42,7 +42,7 @@ export const fromCompare = (compare: Order["compare"]): Order => ({ * @since 1.0.0 */ export const tuple = >( - ...orders: { [K in keyof A]: Order } + ...orders: { readonly [K in keyof A]: Order } ): Order> => fromCompare(that => self => { diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index 3ad68f9a5..bf0f108d6 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -32,7 +32,7 @@ export const tuple = (F: Product) => * @since 1.0.0 */ export const struct = (F: Product) => - >>(fields: R): Kind< + }>(fields: R): Kind< F, ([R[keyof R]] extends [Kind] ? R : never), ([R[keyof R]] extends [Kind] ? O : never), diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 230c95a2e..3b3af21d9 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -156,8 +156,8 @@ type EnforceNonEmptyRecord = keyof R extends never ? never : R * @since 1.0.0 */ export const nonEmptyStruct = (F: SemiProduct) => - >>>( - fields: EnforceNonEmptyRecord & Record> + }>( + fields: EnforceNonEmptyRecord & { readonly [x: PropertyKey]: Kind } ): Kind< F, ([R[keyof R]] extends [Kind] ? R : never), diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index fe0b3a8f1..a91c90319 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -110,7 +110,7 @@ export const reverse = (S: Semigroup): Semigroup => ({ * * @since 1.0.0 */ -export const struct = (semigroups: { [K in keyof A]: Semigroup }): Semigroup< +export const struct = (semigroups: { readonly [K in keyof A]: Semigroup }): Semigroup< { readonly [K in keyof A]: A[K] } @@ -133,7 +133,7 @@ export const struct = (semigroups: { [K in keyof A]: Semigroup }): Semi * @since 1.0.0 */ export const tuple = >( - ...semigroups: { [K in keyof A]: Semigroup } + ...semigroups: { readonly [K in keyof A]: Semigroup } ): Semigroup> => fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) From 3d5aebd1979025fb8e0b6e19b7b66c905be5ce05 Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 15:54:00 +0100 Subject: [PATCH 02/80] add Function module --- .changeset/unlucky-chicken-try.md | 5 + package.json | 1 + pnpm-lock.yaml | 154 ++++++ src/Function.ts | 648 +++++++++++++++++++++++++ src/index.ts | 5 + src/internal/Function.ts | 268 ---------- src/typeclass/Bicovariant.ts | 2 +- src/typeclass/Chainable.ts | 2 +- src/typeclass/FlatMap.ts | 2 +- src/typeclass/Foldable.ts | 2 +- src/typeclass/NonEmptyTraversable.ts | 2 +- src/typeclass/Product.ts | 2 +- src/typeclass/SemiApplicative.ts | 2 +- src/typeclass/SemiProduct.ts | 2 +- src/typeclass/Semigroup.ts | 5 +- src/typeclass/Traversable.ts | 2 +- test/Function.ts | 154 ++++++ test/data/NonEmptyReadonlyArray.ts | 2 +- test/data/Option.ts | 2 +- test/data/ReadonlyArray.ts | 2 +- test/internal/Function.ts | 33 -- test/limbo/CovariantWithIndex.ts | 2 +- test/limbo/FoldableWithIndex.ts | 2 +- test/typeclass/Applicative.ts | 2 +- test/typeclass/Bicovariant.ts | 2 +- test/typeclass/Chainable.ts | 2 +- test/typeclass/Contravariant.ts | 2 +- test/typeclass/Coproduct.ts | 2 +- test/typeclass/Covariant.ts | 2 +- test/typeclass/CovariantWithIndex.ts | 2 +- test/typeclass/FlatMap.ts | 2 +- test/typeclass/Foldable.ts | 2 +- test/typeclass/FoldableWithIndex.ts | 2 +- test/typeclass/Invariant.ts | 2 +- test/typeclass/Monoid.ts | 2 +- test/typeclass/Order.ts | 2 +- test/typeclass/Product.ts | 2 +- test/typeclass/SemiApplicative.ts | 2 +- test/typeclass/SemiCoproduct.ts | 2 +- test/typeclass/SemiProduct.ts | 2 +- test/typeclass/Semigroup.ts | 2 +- test/typeclass/Traversable.ts | 2 +- test/typeclass/TraversableWithIndex.ts | 2 +- 43 files changed, 1003 insertions(+), 338 deletions(-) create mode 100644 .changeset/unlucky-chicken-try.md create mode 100644 src/Function.ts delete mode 100644 src/internal/Function.ts create mode 100644 test/Function.ts delete mode 100644 test/internal/Function.ts diff --git a/.changeset/unlucky-chicken-try.md b/.changeset/unlucky-chicken-try.md new file mode 100644 index 000000000..930cdd2ae --- /dev/null +++ b/.changeset/unlucky-chicken-try.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Function module diff --git a/package.json b/package.json index 41e116dcd..c8f97183b 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@types/rimraf": "^3.0.2", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", + "@vitest/coverage-c8": "^0.27.1", "babel-plugin-annotate-pure-calls": "^0.4.0", "c8": "^7.11.3", "concurrently": "^7.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ac1e5a4a..2b1ddf22e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,7 @@ importers: '@types/rimraf': ^3.0.2 '@typescript-eslint/eslint-plugin': ^5.44.0 '@typescript-eslint/parser': ^5.44.0 + '@vitest/coverage-c8': ^0.27.1 babel-plugin-annotate-pure-calls: ^0.4.0 c8: ^7.11.3 concurrently: ^7.6.0 @@ -70,6 +71,7 @@ importers: '@types/rimraf': 3.0.2 '@typescript-eslint/eslint-plugin': 5.44.0_fnsv2sbzcckq65bwfk7a5xwslu '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + '@vitest/coverage-c8': 0.27.1 babel-plugin-annotate-pure-calls: 0.4.0_@babel+core@7.20.5 c8: 7.12.0 concurrently: 7.6.0 @@ -1287,6 +1289,25 @@ packages: eslint-visitor-keys: 3.3.0 dev: true + /@vitest/coverage-c8/0.27.1: + resolution: {integrity: sha512-/9VTGDIAp4hv8PBawfyijxhkiyucfOxFRRP+7kzy3Dj0wONy1Mc2MBoPmiH4aZVc0LViQqecrQLs8JVGt42keA==} + dependencies: + c8: 7.12.0 + vitest: 0.27.1 + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@vitest/browser' + - '@vitest/ui' + - happy-dom + - jsdom + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /acorn-jsx/5.3.2_acorn@8.8.1: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1693,6 +1714,11 @@ packages: yargs-parser: 20.2.9 dev: true + /cac/6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /cache-base/1.0.1: resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} engines: {node: '>=0.10.0'} @@ -4493,6 +4519,10 @@ packages: hasBin: true dev: true + /jsonc-parser/3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + /jsonfile/4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: @@ -4901,6 +4931,15 @@ packages: hasBin: true dev: true + /mlly/1.1.0: + resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} + dependencies: + acorn: 8.8.1 + pathe: 1.0.0 + pkg-types: 1.0.1 + ufo: 1.0.1 + dev: true + /module-definition/3.4.0: resolution: {integrity: sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA==} engines: {node: '>=6.0'} @@ -5273,6 +5312,14 @@ packages: engines: {node: '>=8'} dev: true + /pathe/0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + dev: true + + /pathe/1.0.0: + resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} + dev: true + /pathval/1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true @@ -5302,6 +5349,14 @@ packages: find-up: 4.1.0 dev: true + /pkg-types/1.0.1: + resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.1.0 + pathe: 1.0.0 + dev: true + /pluralize/8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -5868,6 +5923,10 @@ packages: object-inspect: 1.12.2 dev: true + /siginfo/2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -6022,6 +6081,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback/0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + /static-extend/0.1.2: resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} engines: {node: '>=0.10.0'} @@ -6151,6 +6214,12 @@ packages: acorn: 8.8.1 dev: true + /strip-literal/1.0.0: + resolution: {integrity: sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==} + dependencies: + acorn: 8.8.1 + dev: true + /stylus-lookup/3.0.2: resolution: {integrity: sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg==} engines: {node: '>=6.0.0'} @@ -6613,6 +6682,10 @@ packages: hasBin: true dev: true + /ufo/1.0.1: + resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==} + dev: true + /ultra-runner/3.10.5: resolution: {integrity: sha512-0U2OPII7sbvtbu9rhDlUUkP4Au/DPz2Tzbnawd/XwDuUruDqd+t/Bmel3cLJxl3yMLHf0OY0TMcIx9zzxdlAZw==} engines: {node: '>=10.0.0'} @@ -6730,6 +6803,29 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vite-node/0.27.1_@types+node@18.11.9: + resolution: {integrity: sha512-d6+ue/3NzsfndWaPbYh/bFkHbmAWfDXI4B874zRx+WREnG6CUHUbBC8lKaRYZjeR6gCPN5m1aVNNRXBYICA9XA==} + engines: {node: '>=v14.16.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.1.0 + pathe: 0.2.0 + picocolors: 1.0.0 + source-map: 0.6.1 + source-map-support: 0.5.21 + vite: 3.2.4_@types+node@18.11.9 + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite/2.9.15: resolution: {integrity: sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ==} engines: {node: '>=12.2.0'} @@ -6868,6 +6964,55 @@ packages: - terser dev: true + /vitest/0.27.1: + resolution: {integrity: sha512-1sIpQ1DVFTEn7c1ici1XHcVfdU4nKiBmPtPAtGKJJJLuJjojTv/OHGgcf69P57alM4ty8V4NMv+7Yoi5Cxqx9g==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.4 + '@types/chai-subset': 1.3.3 + '@types/node': 18.11.9 + acorn: 8.8.1 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4 + local-pkg: 0.4.2 + picocolors: 1.0.0 + source-map: 0.6.1 + strip-literal: 1.0.0 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 3.2.4_@types+node@18.11.9 + vite-node: 0.27.1_@types+node@18.11.9 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /walkdir/0.4.1: resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} engines: {node: '>=6.0.0'} @@ -6927,6 +7072,15 @@ packages: isexe: 2.0.0 dev: true + /why-is-node-running/2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} diff --git a/src/Function.ts b/src/Function.ts new file mode 100644 index 000000000..d93985a8a --- /dev/null +++ b/src/Function.ts @@ -0,0 +1,648 @@ +/** + * @since 1.0.0 + */ +import type { TypeLambda } from "@fp-ts/core/HKT" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +// ------------------------------------------------------------------------------------- +// type lambdas +// ------------------------------------------------------------------------------------- + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface FunctionTypeLambda extends TypeLambda { + readonly type: (a: this["In"]) => this["Target"] +} + +/** + * @since 1.0.0 + */ +export const compose: (bc: (b: B) => C) => (ab: (a: A) => B) => (a: A) => C = (bc) => + (ab) => flow(ab, bc) + +/** + * Unary functions form a semigroup as long as you can provide a semigroup for the codomain. + * + * @example + * import { Predicate } from '@fp-ts/data/Predicate' + * import { pipe, getSemigroup } from '@fp-ts/data/Function' + * import * as B from '@fp-ts/data/Boolean' + * + * const f: Predicate = (n) => n <= 2 + * const g: Predicate = (n) => n >= 0 + * + * const S1 = getSemigroup(B.SemigroupAll)() + * + * assert.deepStrictEqual(pipe(f, S1.combine(g))(1), true) + * assert.deepStrictEqual(pipe(f, S1.combine(g))(3), false) + * + * const S2 = getSemigroup(B.SemigroupAny)() + * + * assert.deepStrictEqual(pipe(f, S2.combine(g))(1), true) + * assert.deepStrictEqual(pipe(f, S2.combine(g))(3), true) + * + * @category instances + * @since 1.0.0 + */ +export const getSemigroup = (Semigroup: semigroup.Semigroup) => + (): semigroup.Semigroup<(a: A) => S> => + semigroup.fromCombine((that) => (self) => (a) => Semigroup.combine(that(a))(self(a))) + +/** + * Unary functions form a monoid as long as you can provide a monoid for the codomain. + * + * @example + * import { Predicate } from '@fp-ts/data/Predicate' + * import { getMonoid, pipe } from '@fp-ts/data/Function' + * import * as B from '@fp-ts/data/Boolean' + * + * const f: Predicate = (n) => n <= 2 + * const g: Predicate = (n) => n >= 0 + * + * const M1 = getMonoid(B.MonoidAll)() + * + * assert.deepStrictEqual(pipe(f, M1.combine(g))(1), true) + * assert.deepStrictEqual(pipe(f, M1.combine(g))(3), false) + * + * const M2 = getMonoid(B.MonoidAny)() + * + * assert.deepStrictEqual(pipe(f, M2.combine(g))(1), true) + * assert.deepStrictEqual(pipe(f, M2.combine(g))(3), true) + * + * @category instances + * @since 1.0.0 + */ +export const getMonoid = (Monoid: monoid.Monoid) => + (): monoid.Monoid<(a: A) => M> => { + const S = getSemigroup(Monoid)() + const empty = () => Monoid.empty + return ({ + ...S, + combineAll: (collection) => S.combineMany(collection)(empty), + empty + }) + } + +/** + * @since 1.0.0 + */ +export const apply = (a: A) => (self: (a: A) => B): B => self(a) + +/** + * A lazy argument + * + * @since 1.0.0 + */ +export interface LazyArg { + (): A +} + +/** + * @example + * import { FunctionN } from '@fp-ts/data/Function' + * + * export const sum: FunctionN<[number, number], number> = (a, b) => a + b + * + * @since 1.0.0 + */ +export interface FunctionN, B> { + (...args: A): B +} + +/** + * @since 1.0.0 + */ +export const identity = (a: A): A => a + +/** + * @since 1.0.0 + */ +export const unsafeCoerce: (a: A) => B = identity as any + +/** + * @since 1.0.0 + */ +export const constant = (a: A): LazyArg => () => a + +/** + * A thunk that returns always `true`. + * + * @since 1.0.0 + */ +export const constTrue: LazyArg = constant(true) + +/** + * A thunk that returns always `false`. + * + * @since 1.0.0 + */ +export const constFalse: LazyArg = constant(false) + +/** + * A thunk that returns always `null`. + * + * @since 1.0.0 + */ +export const constNull: LazyArg = constant(null) + +/** + * A thunk that returns always `undefined`. + * + * @since 1.0.0 + */ +export const constUndefined: LazyArg = constant(undefined) + +/** + * A thunk that returns always `void`. + * + * @since 1.0.0 + */ +export const constVoid: LazyArg = constUndefined + +/** + * Flips the arguments of a curried function. + * + * @example + * import { flip } from '@fp-ts/data/Function' + * + * const f = (a: number) => (b: string) => a - b.length + * + * assert.strictEqual(flip(f)('aaa')(2), -1) + * + * @since 1.0.0 + */ +export const flip = (f: (a: A) => (b: B) => C): ((b: B) => (a: A) => C) => + (b) => (a) => f(a)(b) + +/** + * Performs left-to-right function composition. The first argument may have any arity, the remaining arguments must be unary. + * + * @example + * import { flow } from '@fp-ts/data/Function' + * + * const len = (s: string): number => s.length + * const double = (n: number): number => n * 2 + * + * const f = flow(len, double) + * + * assert.strictEqual(f('aaa'), 6) + * + * @see {@link pipe} + * @since 1.0.0 + */ +export function flow, B>(ab: (...a: A) => B): (...a: A) => B +export function flow, B, C>( + ab: (...a: A) => B, + bc: (b: B) => C +): (...a: A) => C +export function flow, B, C, D>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D +): (...a: A) => D +export function flow, B, C, D, E>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E +): (...a: A) => E +export function flow, B, C, D, E, F>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F +): (...a: A) => F +export function flow, B, C, D, E, F, G>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G +): (...a: A) => G +export function flow, B, C, D, E, F, G, H>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H +): (...a: A) => H +export function flow, B, C, D, E, F, G, H, I>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I +): (...a: A) => I +export function flow, B, C, D, E, F, G, H, I, J>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J +): (...a: A) => J +export function flow( + ab: Function, + bc?: Function, + cd?: Function, + de?: Function, + ef?: Function, + fg?: Function, + gh?: Function, + hi?: Function, + ij?: Function +): unknown { + switch (arguments.length) { + case 1: + return ab + case 2: + return function(this: unknown) { + return bc!(ab.apply(this, arguments)) + } + case 3: + return function(this: unknown) { + return cd!(bc!(ab.apply(this, arguments))) + } + case 4: + return function(this: unknown) { + return de!(cd!(bc!(ab.apply(this, arguments)))) + } + case 5: + return function(this: unknown) { + return ef!(de!(cd!(bc!(ab.apply(this, arguments))))) + } + case 6: + return function(this: unknown) { + return fg!(ef!(de!(cd!(bc!(ab.apply(this, arguments)))))) + } + case 7: + return function(this: unknown) { + return gh!(fg!(ef!(de!(cd!(bc!(ab.apply(this, arguments))))))) + } + case 8: + return function(this: unknown) { + return hi!(gh!(fg!(ef!(de!(cd!(bc!(ab.apply(this, arguments)))))))) + } + case 9: + return function(this: unknown) { + return ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab.apply(this, arguments))))))))) + } + } + return +} + +/** + * @since 1.0.0 + */ +export const absurd = (_: never): A => { + throw new Error("Called `absurd` function which should be uncallable") +} + +/** + * Creates a tupled version of this function: instead of `n` arguments, it accepts a single tuple argument. + * + * @example + * import { tupled } from '@fp-ts/data/Function' + * + * const add = tupled((x: number, y: number): number => x + y) + * + * assert.strictEqual(add([1, 2]), 3) + * + * @since 1.0.0 + */ +export const tupled = , B>(f: (...a: A) => B): ((a: A) => B) => + (a) => f(...a) + +/** + * Inverse function of `tupled` + * + * @since 1.0.0 + */ +export const untupled = , B>(f: (a: A) => B): ((...a: A) => B) => + (...a) => f(a) + +/** + * Pipes the value of an expression into a pipeline of functions. + * + * @example + * import { pipe } from '@fp-ts/data/Function' + * + * const len = (s: string): number => s.length + * const double = (n: number): number => n * 2 + * + * // without pipe + * assert.strictEqual(double(len('aaa')), 6) + * + * // with pipe + * assert.strictEqual(pipe('aaa', len, double), 6) + * + * @see {@link flow} + * @since 1.0.0 + */ +export function pipe(a: A): A +export function pipe(a: A, ab: (a: A) => B): B +export function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C +export function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E +): E +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F +): F +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G +): G +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H +): H +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I +): I +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J +): J +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K +): K +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L +): L +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M +): M +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N +): N +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O +): O + +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P +): P + +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q +): Q + +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R +): R + +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S +): S + +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S, + st: (s: S) => T +): T +export function pipe( + a: unknown, + ab?: Function, + bc?: Function, + cd?: Function, + de?: Function, + ef?: Function, + fg?: Function, + gh?: Function, + hi?: Function +): unknown { + switch (arguments.length) { + case 1: + return a + case 2: + return ab!(a) + case 3: + return bc!(ab!(a)) + case 4: + return cd!(bc!(ab!(a))) + case 5: + return de!(cd!(bc!(ab!(a)))) + case 6: + return ef!(de!(cd!(bc!(ab!(a))))) + case 7: + return fg!(ef!(de!(cd!(bc!(ab!(a)))))) + case 8: + return gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))) + case 9: + return hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))) + default: { + let ret = arguments[0] + for (let i = 1; i < arguments.length; i++) { + ret = arguments[i](ret) + } + return ret + } + } +} + +/** + * Type hole simulation + * + * @since 1.0.0 + */ +export const hole: () => T = absurd as any + +/** + * `SK` function (SKI combinator calculus). + * + * @since 1.0.0 + */ +export const SK = (_: A, b: B): B => b diff --git a/src/index.ts b/src/index.ts index cd437d1bb..386a6f144 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import * as hkt from "@fp-ts/core/HKT" // typeclasses // ------------------------------------------------------------------------------------- +import * as _function from "@fp-ts/core/Function" import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -38,6 +39,10 @@ import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" export { + /** + * @since 1.0.0 + */ + _function as function, /** * @category typeclass * @since 1.0.0 diff --git a/src/internal/Function.ts b/src/internal/Function.ts deleted file mode 100644 index 2b02a2d18..000000000 --- a/src/internal/Function.ts +++ /dev/null @@ -1,268 +0,0 @@ -/** @internal */ - -/** @internal */ -export const identity = (a: A): A => a - -/** @internal */ -export function pipe(a: A): A -/** @internal */ -export function pipe(a: A, ab: (a: A) => B): B -/** @internal */ -export function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C -/** @internal */ -export function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E -): E -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F -): F -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G -): G -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H -): H -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I -): I -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J -): J -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K -): K -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L -): L -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M -): M -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N -): N -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O -): O -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O, - op: (o: O) => P -): P -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O, - op: (o: O) => P, - pq: (p: P) => Q -): Q -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O, - op: (o: O) => P, - pq: (p: P) => Q, - qr: (q: Q) => R -): R -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O, - op: (o: O) => P, - pq: (p: P) => Q, - qr: (q: Q) => R, - rs: (r: R) => S -): S -/** @internal */ -export function pipe( - a: A, - ab: (a: A) => B, - bc: (b: B) => C, - cd: (c: C) => D, - de: (d: D) => E, - ef: (e: E) => F, - fg: (f: F) => G, - gh: (g: G) => H, - hi: (h: H) => I, - ij: (i: I) => J, - jk: (j: J) => K, - kl: (k: K) => L, - lm: (l: L) => M, - mn: (m: M) => N, - no: (n: N) => O, - op: (o: O) => P, - pq: (p: P) => Q, - qr: (q: Q) => R, - rs: (r: R) => S, - st: (s: S) => T -): T -export function pipe() { - let out = arguments[0] - for (let i = 1; i < arguments.length; i++) { - out = arguments[i](out) - } - return out -} diff --git a/src/typeclass/Bicovariant.ts b/src/typeclass/Bicovariant.ts index 7de7e2576..dda5d076a 100644 --- a/src/typeclass/Bicovariant.ts +++ b/src/typeclass/Bicovariant.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { identity } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" /** diff --git a/src/typeclass/Chainable.ts b/src/typeclass/Chainable.ts index ff67fcdc6..697a8fa7a 100644 --- a/src/typeclass/Chainable.ts +++ b/src/typeclass/Chainable.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" import type { FlatMap } from "@fp-ts/core/typeclass/FlatMap" diff --git a/src/typeclass/FlatMap.ts b/src/typeclass/FlatMap.ts index 6830e5946..13bb5809f 100644 --- a/src/typeclass/FlatMap.ts +++ b/src/typeclass/FlatMap.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" /** * @category type class diff --git a/src/typeclass/Foldable.ts b/src/typeclass/Foldable.ts index 6b2744c7d..408c7406b 100644 --- a/src/typeclass/Foldable.ts +++ b/src/typeclass/Foldable.ts @@ -2,8 +2,8 @@ * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" import type { Monad } from "@fp-ts/core/typeclass/Monad" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" diff --git a/src/typeclass/NonEmptyTraversable.ts b/src/typeclass/NonEmptyTraversable.ts index 7ec2cdd34..7f9aaf96d 100644 --- a/src/typeclass/NonEmptyTraversable.ts +++ b/src/typeclass/NonEmptyTraversable.ts @@ -3,8 +3,8 @@ * * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index bf0f108d6..675d7c733 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import type { Of } from "@fp-ts/core/typeclass/Of" import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index 9bbf4ffb2..064dc041b 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 3b3af21d9..9bc244c14 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" import type { Invariant } from "@fp-ts/core/typeclass/Invariant" import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index a91c90319..b49e76b28 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -21,7 +21,6 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" -import { identity } from "@fp-ts/core/internal/Function" import type * as invariant from "@fp-ts/core/typeclass/Invariant" import type { Order } from "@fp-ts/core/typeclass/Order" import type * as product from "@fp-ts/core/typeclass/Product" @@ -153,8 +152,8 @@ export const intercalate = (separator: A) => * @since 1.0.0 */ export const first = (): Semigroup => ({ - combine: () => identity, - combineMany: () => identity + combine: () => a => a, + combineMany: () => a => a }) /** diff --git a/src/typeclass/Traversable.ts b/src/typeclass/Traversable.ts index 91b386a98..f99b23096 100644 --- a/src/typeclass/Traversable.ts +++ b/src/typeclass/Traversable.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type { Applicative } from "@fp-ts/core/typeclass/Applicative" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" diff --git a/test/Function.ts b/test/Function.ts new file mode 100644 index 000000000..d893e4d05 --- /dev/null +++ b/test/Function.ts @@ -0,0 +1,154 @@ +import * as Function from "@fp-ts/core/Function" +import { deepStrictEqual, double } from "@fp-ts/core/test/util" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import * as assert from "assert" + +const f = (n: number): number => n + 1 +const g = double +const size = (s: string): number => s.length + +// TODO: replace with "@fp-ts/core/Number" +const sum = (that: number) => (self: number): number => self + that +const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(sum) +const MonoidSum: monoid.Monoid = { + ...SemigroupSum, + combineAll: (collection) => SemigroupSum.combineMany(collection)(0), + empty: 0 +} + +describe.concurrent("Function", () => { + it("getSemigroup", () => { + const S = Function.getSemigroup(SemigroupSum)() + const f = (s: string) => s === "a" ? 0 : 1 + const g = Function.pipe(size, S.combine(f)) + deepStrictEqual(g(""), 1) + deepStrictEqual(g("a"), 1) + deepStrictEqual(g("b"), 2) + deepStrictEqual(S.combineMany([size, size])(size)("a"), 3) + }) + + it("getMonoid", () => { + const M = Function.getMonoid(MonoidSum)() + const f = (s: string) => s === "a" ? 0 : 1 + const g = Function.pipe(size, M.combine(f)) + deepStrictEqual(g(""), 1) + deepStrictEqual(g("a"), 1) + deepStrictEqual(g("b"), 2) + deepStrictEqual(Function.pipe(size, M.combine(M.empty))("a"), 1) + deepStrictEqual(Function.pipe(M.empty, M.combine(size))("a"), 1) + deepStrictEqual(M.combineAll([size, size])("a"), 2) + }) + + it("apply", () => { + deepStrictEqual(Function.apply("a")(size), 1) + }) + + it("flip", () => { + const f = (a: number) => (b: string) => a - b.length + deepStrictEqual(Function.flip(f)("aaa")(2), -1) + }) + + it("compose", () => { + deepStrictEqual(Function.pipe(size, Function.compose(double))("aaa"), 6) + }) + + it("unsafeCoerce", () => { + deepStrictEqual(Function.unsafeCoerce, Function.identity) + }) + + it("constant", () => { + deepStrictEqual(Function.constant("a")(), "a") + }) + + it("constTrue", () => { + deepStrictEqual(Function.constTrue(), true) + }) + + it("constFalse", () => { + deepStrictEqual(Function.constFalse(), false) + }) + + it("constNull", () => { + deepStrictEqual(Function.constNull(), null) + }) + + it("constUndefined", () => { + deepStrictEqual(Function.constUndefined(), undefined) + }) + + it("constVoid", () => { + deepStrictEqual(Function.constVoid(), undefined) + }) + + it("absurd", () => { + assert.throws(() => Function.absurd(null as any as never)) + }) + + it("hole", () => { + assert.throws(() => Function.hole()) + }) + + it("SK", () => { + expect(Function.SK(1, 2)).toEqual(2) + }) + + it("flow", () => { + deepStrictEqual(Function.flow(f)(2), 3) + deepStrictEqual(Function.flow(f, g)(2), 6) + deepStrictEqual(Function.flow(f, g, f)(2), 7) + deepStrictEqual(Function.flow(f, g, f, g)(2), 14) + deepStrictEqual(Function.flow(f, g, f, g, f)(2), 15) + deepStrictEqual(Function.flow(f, g, f, g, f, g)(2), 30) + deepStrictEqual(Function.flow(f, g, f, g, f, g, f)(2), 31) + deepStrictEqual(Function.flow(f, g, f, g, f, g, f, g)(2), 62) + deepStrictEqual(Function.flow(f, g, f, g, f, g, f, g, f)(2), 63) + // this is just to satisfy noImplicitReturns and 100% coverage + deepStrictEqual((Function.flow as any)(...[f, g, f, g, f, g, f, g, f, g]), undefined) + }) + + it("tupled", () => { + const f1 = (a: number): number => a * 2 + const f2 = (a: number, b: number): number => a + b + const u1 = Function.tupled(f1) + const u2 = Function.tupled(f2) + deepStrictEqual(u1([1]), 2) + deepStrictEqual(u2([1, 2]), 3) + }) + + it("untupled", () => { + const f1 = (a: readonly [number]): number => a[0] * 2 + const f2 = (a: readonly [number, number]): number => a[0] + a[1] + const u1 = Function.untupled(f1) + const u2 = Function.untupled(f2) + deepStrictEqual(u1(1), 2) + deepStrictEqual(u2(1, 2), 3) + }) + + it("pipe", () => { + deepStrictEqual(Function.pipe(2), 2) + deepStrictEqual(Function.pipe(2, f), 3) + deepStrictEqual(Function.pipe(2, f, g), 6) + deepStrictEqual(Function.pipe(2, f, g, f), 7) + deepStrictEqual(Function.pipe(2, f, g, f, g), 14) + deepStrictEqual(Function.pipe(2, f, g, f, g, f), 15) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g), 30) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f), 31) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g), 62) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f), 63) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g), 126) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f), 127) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g), 254) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f), 255) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 510) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 511) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 1022) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 1023) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 2046) + deepStrictEqual(Function.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 2047) + deepStrictEqual( + (Function.pipe as any)(...[2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g]), + 4094 + ) + }) +}) diff --git a/test/data/NonEmptyReadonlyArray.ts b/test/data/NonEmptyReadonlyArray.ts index 775a2c355..0a53f4b2f 100644 --- a/test/data/NonEmptyReadonlyArray.ts +++ b/test/data/NonEmptyReadonlyArray.ts @@ -1,5 +1,5 @@ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import * as covariant from "@fp-ts/core/typeclass/Covariant" import type * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" diff --git a/test/data/Option.ts b/test/data/Option.ts index c3890a22f..e88852b59 100644 --- a/test/data/Option.ts +++ b/test/data/Option.ts @@ -12,8 +12,8 @@ * * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type * as extendable from "@fp-ts/core/test/limbo/Extendable" import type * as alternative from "@fp-ts/core/typeclass/Alternative" import type * as applicative from "@fp-ts/core/typeclass/Applicative" diff --git a/test/data/ReadonlyArray.ts b/test/data/ReadonlyArray.ts index a11bea077..9c3e7cd63 100644 --- a/test/data/ReadonlyArray.ts +++ b/test/data/ReadonlyArray.ts @@ -1,6 +1,6 @@ import { semiProduct } from "@fp-ts/core" +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type * as applicative from "@fp-ts/core/typeclass/Applicative" import * as covariant from "@fp-ts/core/typeclass/Covariant" import type * as foldable from "@fp-ts/core/typeclass/Foldable" diff --git a/test/internal/Function.ts b/test/internal/Function.ts deleted file mode 100644 index b6df7944d..000000000 --- a/test/internal/Function.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as _ from "@fp-ts/core/internal/Function" -import * as U from "../util" - -describe("Function", () => { - it("pipe", () => { - const f = (n: number): number => n + 1 - const g = U.double - U.deepStrictEqual(_.pipe(2), 2) - U.deepStrictEqual(_.pipe(2, f), 3) - U.deepStrictEqual(_.pipe(2, f, g), 6) - U.deepStrictEqual(_.pipe(2, f, g, f), 7) - U.deepStrictEqual(_.pipe(2, f, g, f, g), 14) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f), 15) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g), 30) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f), 31) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g), 62) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f), 63) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g), 126) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f), 127) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g), 254) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f), 255) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 510) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 511) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 1022) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 1023) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 2046) - U.deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 2047) - U.deepStrictEqual( - (_.pipe as any)(...[2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g]), - 4094 - ) - }) -}) diff --git a/test/limbo/CovariantWithIndex.ts b/test/limbo/CovariantWithIndex.ts index 0f271d061..0ef9c20d9 100644 --- a/test/limbo/CovariantWithIndex.ts +++ b/test/limbo/CovariantWithIndex.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import type { Covariant } from "@fp-ts/core/typeclass/Covariant" /** diff --git a/test/limbo/FoldableWithIndex.ts b/test/limbo/FoldableWithIndex.ts index bb9df3470..1fb577e6c 100644 --- a/test/limbo/FoldableWithIndex.ts +++ b/test/limbo/FoldableWithIndex.ts @@ -2,8 +2,8 @@ * @since 1.0.0 */ +import { identity, pipe } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import { identity, pipe } from "@fp-ts/core/internal/Function" import type { Foldable } from "@fp-ts/core/typeclass/Foldable" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" diff --git a/test/typeclass/Applicative.ts b/test/typeclass/Applicative.ts index 97e5610d5..80d5a7908 100644 --- a/test/typeclass/Applicative.ts +++ b/test/typeclass/Applicative.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Applicative" import * as N from "../data/number" import * as O from "../data/Option" diff --git a/test/typeclass/Bicovariant.ts b/test/typeclass/Bicovariant.ts index 712cf4352..6b661cf0d 100644 --- a/test/typeclass/Bicovariant.ts +++ b/test/typeclass/Bicovariant.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Bicovariant" import type { EitherTypeLambda } from "../data/Either" import * as E from "../data/Either" diff --git a/test/typeclass/Chainable.ts b/test/typeclass/Chainable.ts index 4fffd748f..a608ff5ac 100644 --- a/test/typeclass/Chainable.ts +++ b/test/typeclass/Chainable.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Chainable" import * as O from "../data/Option" import * as U from "../util" diff --git a/test/typeclass/Contravariant.ts b/test/typeclass/Contravariant.ts index 0cd6b7613..ca8255303 100644 --- a/test/typeclass/Contravariant.ts +++ b/test/typeclass/Contravariant.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Contravariant" import * as order from "@fp-ts/core/typeclass/Order" import * as P from "../data/Predicate" diff --git a/test/typeclass/Coproduct.ts b/test/typeclass/Coproduct.ts index 2bc212210..9299dfa1a 100644 --- a/test/typeclass/Coproduct.ts +++ b/test/typeclass/Coproduct.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Coproduct" import * as O from "../data/Option" import * as U from "../util" diff --git a/test/typeclass/Covariant.ts b/test/typeclass/Covariant.ts index d5536d69e..309380285 100644 --- a/test/typeclass/Covariant.ts +++ b/test/typeclass/Covariant.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Covariant" import * as O from "../data/Option" import * as RA from "../data/ReadonlyArray" diff --git a/test/typeclass/CovariantWithIndex.ts b/test/typeclass/CovariantWithIndex.ts index c2f630bfb..a71e23efd 100644 --- a/test/typeclass/CovariantWithIndex.ts +++ b/test/typeclass/CovariantWithIndex.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as RA from "../data/ReadonlyArray" import * as _ from "../limbo/CovariantWithIndex" import * as U from "../util" diff --git a/test/typeclass/FlatMap.ts b/test/typeclass/FlatMap.ts index 331606b5b..3ee00c623 100644 --- a/test/typeclass/FlatMap.ts +++ b/test/typeclass/FlatMap.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/FlatMap" import * as O from "../data/Option" import * as U from "../util" diff --git a/test/typeclass/Foldable.ts b/test/typeclass/Foldable.ts index a25a5458f..dec700db0 100644 --- a/test/typeclass/Foldable.ts +++ b/test/typeclass/Foldable.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Foldable" import * as number from "../data/number" import * as O from "../data/Option" diff --git a/test/typeclass/FoldableWithIndex.ts b/test/typeclass/FoldableWithIndex.ts index 622781423..cdcffd5ee 100644 --- a/test/typeclass/FoldableWithIndex.ts +++ b/test/typeclass/FoldableWithIndex.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as number from "../data/number" import * as O from "../data/Option" import * as RA from "../data/ReadonlyArray" diff --git a/test/typeclass/Invariant.ts b/test/typeclass/Invariant.ts index 2c20678b1..808c746ea 100644 --- a/test/typeclass/Invariant.ts +++ b/test/typeclass/Invariant.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Invariant" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as O from "../data/Option" diff --git a/test/typeclass/Monoid.ts b/test/typeclass/Monoid.ts index 22a22d5b8..fd46f6808 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as number from "../data/number" import * as string from "../data/string" diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 382b3d9df..4fccb6243 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Order" import * as boolean from "../data/boolean" import * as number from "../data/number" diff --git a/test/typeclass/Product.ts b/test/typeclass/Product.ts index 787a127b6..16a74eb35 100644 --- a/test/typeclass/Product.ts +++ b/test/typeclass/Product.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as number from "../data/number" diff --git a/test/typeclass/SemiApplicative.ts b/test/typeclass/SemiApplicative.ts index a5ed18cc2..a1a81f87d 100644 --- a/test/typeclass/SemiApplicative.ts +++ b/test/typeclass/SemiApplicative.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/SemiApplicative" import * as O from "../data/Option" import * as string from "../data/string" diff --git a/test/typeclass/SemiCoproduct.ts b/test/typeclass/SemiCoproduct.ts index 3692c8384..55eec17be 100644 --- a/test/typeclass/SemiCoproduct.ts +++ b/test/typeclass/SemiCoproduct.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/SemiCoproduct" import * as E from "../data/Either" import * as U from "../util" diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 3f708d7ee..a822c89bf 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -1,5 +1,5 @@ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as _ from "@fp-ts/core/typeclass/SemiProduct" diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index afd9cb9c5..56d55d05c 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as order from "@fp-ts/core/typeclass/Order" import * as _ from "@fp-ts/core/typeclass/Semigroup" import * as number from "../data/number" diff --git a/test/typeclass/Traversable.ts b/test/typeclass/Traversable.ts index 2a78856f0..794205dbd 100644 --- a/test/typeclass/Traversable.ts +++ b/test/typeclass/Traversable.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/Traversable" import * as O from "../data/Option" import * as RA from "../data/ReadonlyArray" diff --git a/test/typeclass/TraversableWithIndex.ts b/test/typeclass/TraversableWithIndex.ts index 5a0cbbf1b..b35b3a699 100644 --- a/test/typeclass/TraversableWithIndex.ts +++ b/test/typeclass/TraversableWithIndex.ts @@ -1,4 +1,4 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" import * as O from "../data/Option" import * as RA from "../data/ReadonlyArray" import * as _ from "../limbo/TraversableWithIndex" From ef066c652ff432b1ab119c3d9630625eb87e709f Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 15:56:39 +0100 Subject: [PATCH 03/80] add Ordering module --- .changeset/silver-experts-punch.md | 5 +++ src/Ordering.ts | 54 ++++++++++++++++++++++++++++++ src/index.ts | 5 +++ test/Ordering.ts | 46 +++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 .changeset/silver-experts-punch.md create mode 100644 src/Ordering.ts create mode 100644 test/Ordering.ts diff --git a/.changeset/silver-experts-punch.md b/.changeset/silver-experts-punch.md new file mode 100644 index 000000000..ec7026dc6 --- /dev/null +++ b/.changeset/silver-experts-punch.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Ordering module diff --git a/src/Ordering.ts b/src/Ordering.ts new file mode 100644 index 000000000..9e9cc526a --- /dev/null +++ b/src/Ordering.ts @@ -0,0 +1,54 @@ +/** + * @since 1.0.0 + */ +import type { LazyArg } from "@fp-ts/core/Function" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @category model + * @since 1.0.0 + */ +export type Ordering = -1 | 0 | 1 + +/** + * @since 1.0.0 + */ +export const reverse = (o: Ordering): Ordering => (o === -1 ? 1 : o === 1 ? -1 : 0) + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const match = ( + onLessThan: LazyArg, + onEqual: LazyArg, + onGreaterThan: LazyArg +) => (o: Ordering): A | B | C => o === -1 ? onLessThan() : o === 0 ? onEqual() : onGreaterThan() + +/** + * @category instances + * @since 1.0.0 + */ +export const Semigroup: semigroup.Semigroup = { + combine: (that) => (self) => self !== 0 ? self : that, + combineMany: (collection) => + (self) => { + let ordering = self + if (ordering !== 0) { + return ordering + } + for (ordering of collection) { + if (ordering !== 0) { + return ordering + } + } + return ordering + } +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Monoid: monoid.Monoid = monoid.fromSemigroup(Semigroup, 0) diff --git a/src/index.ts b/src/index.ts index 386a6f144..5f93b54cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import * as hkt from "@fp-ts/core/HKT" // ------------------------------------------------------------------------------------- import * as _function from "@fp-ts/core/Function" +import * as ordering from "@fp-ts/core/Ordering" import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -127,6 +128,10 @@ export { * @since 1.0.0 */ order, + /** + * @since 1.0.0 + */ + ordering, /** * @category typeclass * @since 1.0.0 diff --git a/test/Ordering.ts b/test/Ordering.ts new file mode 100644 index 000000000..55580eaec --- /dev/null +++ b/test/Ordering.ts @@ -0,0 +1,46 @@ +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/Ordering" +import { deepStrictEqual } from "./util" + +describe("Ordering", () => { + it("match", () => { + const f = _.match( + () => "lt", + () => "eq", + () => "gt" + ) + deepStrictEqual(f(-1), "lt") + deepStrictEqual(f(0), "eq") + deepStrictEqual(f(1), "gt") + }) + + it("reverse", () => { + deepStrictEqual(_.reverse(-1), 1) + deepStrictEqual(_.reverse(0), 0) + deepStrictEqual(_.reverse(1), -1) + }) + + it("Semigroup", () => { + deepStrictEqual(pipe(0, _.Semigroup.combine(0)), 0) + deepStrictEqual(pipe(0, _.Semigroup.combine(1)), 1) + deepStrictEqual(pipe(1, _.Semigroup.combine(-1)), 1) + deepStrictEqual(pipe(-1, _.Semigroup.combine(1)), -1) + + deepStrictEqual(pipe(0, _.Semigroup.combineMany([])), 0) + deepStrictEqual(pipe(1, _.Semigroup.combineMany([])), 1) + deepStrictEqual(pipe(-1, _.Semigroup.combineMany([])), -1) + deepStrictEqual(pipe(0, _.Semigroup.combineMany([0, 0, 0])), 0) + deepStrictEqual(pipe(0, _.Semigroup.combineMany([0, 0, 1])), 1) + deepStrictEqual(pipe(1, _.Semigroup.combineMany([0, 0, -1])), 1) + deepStrictEqual(pipe(-1, _.Semigroup.combineMany([0, 0, 1])), -1) + }) + + it("Monoid", () => { + deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(0)), 0) + deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(1)), 1) + deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(-1)), -1) + deepStrictEqual(pipe(0, _.Monoid.combine(_.Monoid.empty)), 0) + deepStrictEqual(pipe(1, _.Monoid.combine(_.Monoid.empty)), 1) + deepStrictEqual(pipe(-1, _.Monoid.combine(_.Monoid.empty)), -1) + }) +}) From f418131627b7a5a7eb4964f4ca6e7f9b2461c8ee Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 16:12:21 +0100 Subject: [PATCH 04/80] add Predicate module --- .changeset/four-garlics-wonder.md | 5 + src/Predicate.ts | 282 ++++++++++++++++++++++++++++++ src/index.ts | 5 + src/internal/Iterable.ts | 7 + test/Predicate.ts | 160 +++++++++++++++++ test/internal/Iterable.ts | 12 ++ 6 files changed, 471 insertions(+) create mode 100644 .changeset/four-garlics-wonder.md create mode 100644 src/Predicate.ts create mode 100644 src/internal/Iterable.ts create mode 100644 test/Predicate.ts create mode 100644 test/internal/Iterable.ts diff --git a/.changeset/four-garlics-wonder.md b/.changeset/four-garlics-wonder.md new file mode 100644 index 000000000..75dd1000b --- /dev/null +++ b/.changeset/four-garlics-wonder.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Predicate module diff --git a/src/Predicate.ts b/src/Predicate.ts new file mode 100644 index 000000000..9dd93719a --- /dev/null +++ b/src/Predicate.ts @@ -0,0 +1,282 @@ +/** + * @since 1.0.0 + */ +import { constFalse, constTrue } from "@fp-ts/core/Function" +import type { TypeLambda } from "@fp-ts/core/HKT" +import * as I from "@fp-ts/core/internal/Iterable" +import * as contravariant from "@fp-ts/core/typeclass/Contravariant" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import * as product_ from "@fp-ts/core/typeclass/Product" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" + +/** + * @category models + * @since 1.0.0 + */ +export interface Predicate { + (a: A): boolean +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface PredicateTypeLambda extends TypeLambda { + readonly type: Predicate +} + +/** + * @since 1.0.0 + */ +export interface Refinement { + (a: A): a is B +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const id = (): Refinement => (_): _ is A => true + +/** + * @since 1.0.0 + */ +export const compose = (bc: Refinement) => + (ab: Refinement): Refinement => (i): i is C => ab(i) && bc(i) + +/** + * @since 1.0.0 + */ +export const contramap = (f: (b: B) => A) => + (self: Predicate): Predicate => (b) => self(f(b)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Contravariant: contravariant.Contravariant = contravariant.make( + contramap +) + +/** + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: Predicate) => Predicate = Contravariant.imap + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @since 1.0.0 + */ +export const tupled: (self: Predicate) => Predicate = invariant.tupled( + Invariant +) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: Predicate) => Predicate<{ readonly [K in N]: A }> = invariant.bindTo( + Invariant +) + +/** + * @since 1.0.0 + */ +export const of = (_: A): Predicate => id() + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const Do: Predicate<{}> = of_.Do(Of) + +/** + * @since 1.0.0 + */ +export const unit: Predicate = of_.unit(Of) + +/** + * @since 1.0.0 + */ +export const product = (that: Predicate) => + (self: Predicate): Predicate => ([a, b]) => self(a) && that(b) + +/** + * @since 1.0.0 + */ +export const productMany = (collection: Iterable>) => + (self: Predicate): Predicate]> => { + return ([head, ...tail]) => { + if (self(head) === false) { + return false + } + const predicates = I.fromIterable(collection) + for (let i = 0; i < predicates.length; i++) { + if (predicates[i](tail[i]) === false) { + return false + } + } + return true + } + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +/** + * @since 1.0.0 + */ +export const productAll = ( + collection: Iterable> +): Predicate> => + (as) => { + const predicates = I.fromIterable(collection) + for (let i = 0; i < predicates.length; i++) { + if (predicates[i](as[i]) === false) { + return false + } + } + return true + } + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + imap, + product, + productMany, + of, + productAll +} + +/** + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + self: Predicate +) => ( + self: Predicate +) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct + .andThenBind( + SemiProduct + ) + +/** + * @since 1.0.0 + */ +export const tuple: >>( + ...predicates: T +) => Predicate] ? A : never }>> = + product_ + .tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + predicates: R +) => Predicate<{ readonly [K in keyof R]: [R[K]] extends [Predicate] ? A : never }> = + product_ + .struct(Product) + +/** + * @since 1.0.0 + */ +export const not = (self: Predicate): Predicate => (a) => !self(a) + +/** + * @since 1.0.0 + */ +export const or = (that: Predicate) => + (self: Predicate): Predicate => (a) => self(a) || that(a) + +/** + * @since 1.0.0 + */ +export const and = (that: Predicate) => + (self: Predicate): Predicate => (a) => self(a) && that(a) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroupAny = (): semigroup.Semigroup> => semigroup.fromCombine(or) + +/** + * @category instances + * @since 1.0.0 + */ +export const getMonoidAny = (): monoid.Monoid> => { + const S = getSemigroupAny() + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany(collection)(constFalse), + empty: constFalse + }) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroupAll = (): semigroup.Semigroup> => + semigroup.fromCombine(and) + +/** + * @category instances + * @since 1.0.0 + */ +export const getMonoidAll = (): monoid.Monoid> => { + const S = getSemigroupAll() + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany(collection)(constTrue), + empty: constTrue + }) +} + +/** + * @since 1.0.0 + */ +export const all = (collection: Iterable>): Predicate => + getMonoidAll().combineAll(collection) + +/** + * @since 1.0.0 + */ +export const any = (collection: Iterable>): Predicate => + getMonoidAny().combineAll(collection) diff --git a/src/index.ts b/src/index.ts index 5f93b54cb..371fb8625 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import * as hkt from "@fp-ts/core/HKT" import * as _function from "@fp-ts/core/Function" import * as ordering from "@fp-ts/core/Ordering" +import * as predicate from "@fp-ts/core/Predicate" import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -137,6 +138,10 @@ export { * @since 1.0.0 */ pointed, + /** + * @since 1.0.0 + */ + predicate, /** * @category typeclass * @since 1.0.0 diff --git a/src/internal/Iterable.ts b/src/internal/Iterable.ts new file mode 100644 index 000000000..72b5eee8c --- /dev/null +++ b/src/internal/Iterable.ts @@ -0,0 +1,7 @@ +/** + * @since 1.0.0 + */ + +/** @internal */ +export const fromIterable = (collection: Iterable): ReadonlyArray => + Array.isArray(collection) ? collection : Array.from(collection) diff --git a/test/Predicate.ts b/test/Predicate.ts new file mode 100644 index 000000000..7b272faa4 --- /dev/null +++ b/test/Predicate.ts @@ -0,0 +1,160 @@ +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/Predicate" +import { deepStrictEqual } from "@fp-ts/core/test/util" + +const isPositive: _.Predicate = (n) => n > 0 +const isNegative: _.Predicate = (n) => n < 0 +const isLessThan2: _.Predicate = (n) => n < 2 +const isString: _.Refinement = (u: unknown): u is string => typeof u === "string" + +interface NonEmptyStringBrand { + readonly NonEmptyString: unique symbol +} + +type NonEmptyString = string & NonEmptyStringBrand + +const NonEmptyString: _.Refinement = (s): s is NonEmptyString => + s.length > 0 + +describe.concurrent("Predicate", () => { + it("instances and derived exports", () => { + expect(_.Invariant).exist + expect(_.imap).exist + expect(_.tupled).exist + expect(_.bindTo).exist + + expect(_.Contravariant).exist + expect(_.contramap).exist + + expect(_.Of).exist + expect(_.of).exist + expect(_.unit).exist + expect(_.Do).exist + + expect(_.SemiProduct).exist + expect(_.product).exist + expect(_.productMany).exist + expect(_.andThenBind).exist + + expect(_.Product).exist + expect(_.productAll).exist + expect(_.tuple).exist + expect(_.struct).exist + }) + + it("id", () => { + const refinement = _.id() + deepStrictEqual(refinement("a"), true) + }) + + it("compose", () => { + const refinement = pipe(isString, _.compose(NonEmptyString)) + deepStrictEqual(refinement("a"), true) + deepStrictEqual(refinement(null), false) + deepStrictEqual(refinement(""), false) + }) + + it("contramap", () => { + type A = { + readonly a: number + } + const predicate = pipe( + isPositive, + _.contramap((a: A) => a.a) + ) + deepStrictEqual(predicate({ a: -1 }), false) + deepStrictEqual(predicate({ a: 0 }), false) + deepStrictEqual(predicate({ a: 1 }), true) + }) + + it("product", () => { + const p = pipe(isPositive, _.product(isNegative)) + deepStrictEqual(p([1, -1]), true) + deepStrictEqual(p([1, 1]), false) + deepStrictEqual(p([-1, -1]), false) + deepStrictEqual(p([-1, 1]), false) + }) + + it("productMany", () => { + const p = pipe(isPositive, _.productMany([isNegative])) + deepStrictEqual(p([1, -1]), true) + deepStrictEqual(p([1, 1]), false) + deepStrictEqual(p([-1, -1]), false) + deepStrictEqual(p([-1, 1]), false) + }) + + it("productAll", () => { + const p = _.productAll([isPositive, isNegative]) + deepStrictEqual(p([1, -1]), true) + deepStrictEqual(p([1, 1]), false) + deepStrictEqual(p([-1, -1]), false) + deepStrictEqual(p([-1, 1]), false) + }) + + it("not", () => { + const p = _.not(isPositive) + deepStrictEqual(p(1), false) + deepStrictEqual(p(0), true) + deepStrictEqual(p(-1), true) + }) + + it("or", () => { + const p = pipe(isPositive, _.or(isNegative)) + deepStrictEqual(p(-1), true) + deepStrictEqual(p(1), true) + deepStrictEqual(p(0), false) + }) + + it("and", () => { + const p = pipe(isPositive, _.and(isLessThan2)) + deepStrictEqual(p(1), true) + deepStrictEqual(p(-1), false) + deepStrictEqual(p(3), false) + }) + + it("getSemigroupAny", () => { + const S = _.getSemigroupAny() + const predicate = pipe(isPositive, S.combine(isNegative)) + deepStrictEqual(predicate(0), false) + deepStrictEqual(predicate(-1), true) + deepStrictEqual(predicate(1), true) + }) + + it("getMonoidAny", () => { + const M = _.getMonoidAny() + const predicate = pipe(isPositive, M.combine(M.empty)) + deepStrictEqual(predicate(0), isPositive(0)) + deepStrictEqual(predicate(-1), isPositive(-1)) + deepStrictEqual(predicate(1), isPositive(1)) + }) + + it("getSemigroupAll", () => { + const S = _.getSemigroupAll() + const predicate = pipe(isPositive, S.combine(isLessThan2)) + deepStrictEqual(predicate(0), false) + deepStrictEqual(predicate(-2), false) + deepStrictEqual(predicate(1), true) + }) + + it("getMonoidAll", () => { + const M = _.getMonoidAll() + const predicate = pipe(isPositive, M.combine(M.empty)) + deepStrictEqual(predicate(0), isPositive(0)) + deepStrictEqual(predicate(-1), isPositive(-1)) + deepStrictEqual(predicate(1), isPositive(1)) + }) + + it("any", () => { + const predicate = _.any([isPositive, isNegative]) + deepStrictEqual(predicate(0), false) + deepStrictEqual(predicate(-1), true) + deepStrictEqual(predicate(1), true) + }) + + it("all", () => { + const predicate = _.all([isPositive, isLessThan2]) + deepStrictEqual(predicate(0), false) + deepStrictEqual(predicate(-2), false) + deepStrictEqual(predicate(1), true) + }) +}) diff --git a/test/internal/Iterable.ts b/test/internal/Iterable.ts new file mode 100644 index 000000000..c71b77e69 --- /dev/null +++ b/test/internal/Iterable.ts @@ -0,0 +1,12 @@ +import * as I from "@fp-ts/core/internal/Iterable" + +describe.concurrent("internal/Iterable", () => { + it("fromIterable/Array should return the same reference if the iterable is an Array", () => { + const iterable = [1, 2, 3] + expect(I.fromIterable(iterable) === iterable).toEqual(true) + }) + + it("fromIterable/Iterable", () => { + expect(I.fromIterable(new Set([1, 2, 3]))).toEqual([1, 2, 3]) + }) +}) From aed9c17e9327ee870d304555c995e128afbda23a Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 16:17:33 +0100 Subject: [PATCH 05/80] add Boolean module --- .changeset/unlucky-knives-exercise.md | 5 + src/Boolean.ts | 144 ++++++++++++++++++++++++++ src/index.ts | 5 + test/Boolean.ts | 78 ++++++++++++++ 4 files changed, 232 insertions(+) create mode 100644 .changeset/unlucky-knives-exercise.md create mode 100644 src/Boolean.ts create mode 100644 test/Boolean.ts diff --git a/.changeset/unlucky-knives-exercise.md b/.changeset/unlucky-knives-exercise.md new file mode 100644 index 000000000..7ef64bbad --- /dev/null +++ b/.changeset/unlucky-knives-exercise.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Boolean module diff --git a/src/Boolean.ts b/src/Boolean.ts new file mode 100644 index 000000000..0b6f092b9 --- /dev/null +++ b/src/Boolean.ts @@ -0,0 +1,144 @@ +/** + * @since 1.0.0 + */ +import type { LazyArg } from "@fp-ts/core/Function" +import type { Refinement } from "@fp-ts/core/Predicate" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import type * as order from "@fp-ts/core/typeclass/Order" +import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @category refinements + * @since 1.0.0 + */ +export const isBoolean: Refinement = (u: unknown): u is boolean => + typeof u === "boolean" + +/** + * @since 1.0.0 + */ +export const and = (that: boolean) => (self: boolean): boolean => self && that + +/** + * @since 1.0.0 + */ +export const or = (that: boolean) => (self: boolean): boolean => self || that + +/** + * Defines the match over a boolean value. + * Takes two thunks `onTrue`, `onFalse` and a `boolean` value. + * If `value` is `false`, `onFalse()` is returned, otherwise `onTrue()`. + * + * @example + * import { some, map } from '@fp-ts/data/Option' + * import { pipe } from '@fp-ts/data/Function' + * import { match } from '@fp-ts/data/Boolean' + * + * assert.deepStrictEqual( + * pipe( + * some(true), + * map(match(() => 'false', () => 'true')) + * ), + * some('true') + * ) + * + * @category pattern matching + * @since 1.0.0 + */ +export const match = (onFalse: LazyArg, onTrue: LazyArg) => + (value: boolean): A | B => value ? onTrue() : onFalse() + +/** + * `boolean` semigroup under conjunction. + * + * @example + * import { SemigroupAll } from '@fp-ts/data/Boolean' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(true)), true) + * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(false)), false) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupAll: semigroup.Semigroup = { + combine: and, + combineMany: (collection) => + (self) => { + if (self === false) { + return false + } + for (const b of collection) { + if (b === false) { + return false + } + } + return true + } +} + +/** + * `boolean` semigroup under disjunction. + * + * @example + * import { SemigroupAny } from '@fp-ts/data/Boolean' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(true)), true) + * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(false)), true) + * assert.deepStrictEqual(pipe(false, SemigroupAny.combine(false)), false) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupAny: semigroup.Semigroup = { + combine: or, + combineMany: (collection) => + (self) => { + if (self === true) { + return true + } + for (const b of collection) { + if (b === true) { + return true + } + } + return false + } +} + +/** + * `boolean` monoid under conjunction. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidAll: monoid.Monoid = { + ...SemigroupAll, + combineAll: (all) => SemigroupAll.combineMany(all)(true), + empty: true +} + +/** + * `boolean` monoid under disjunction. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidAny: monoid.Monoid = { + ...SemigroupAny, + combineAll: (all) => SemigroupAny.combineMany(all)(false), + empty: false +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} diff --git a/src/index.ts b/src/index.ts index 371fb8625..cbd714fc7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import * as hkt from "@fp-ts/core/HKT" // typeclasses // ------------------------------------------------------------------------------------- +import * as boolean from "@fp-ts/core/Boolean" import * as _function from "@fp-ts/core/Function" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" @@ -60,6 +61,10 @@ export { * @since 1.0.0 */ bicovariant, + /** + * @since 1.0.0 + */ + boolean, /** * @category typeclass * @since 1.0.0 diff --git a/test/Boolean.ts b/test/Boolean.ts new file mode 100644 index 000000000..745c44f16 --- /dev/null +++ b/test/Boolean.ts @@ -0,0 +1,78 @@ +import * as Boolean from "@fp-ts/core/Boolean" +import { pipe } from "@fp-ts/core/Function" +import { deepStrictEqual } from "@fp-ts/core/test/util" + +describe.concurrent("Boolean", () => { + it("instances and derived exports", () => { + expect(Boolean.SemigroupAll).exist + expect(Boolean.MonoidAll).exist + expect(Boolean.SemigroupAny).exist + expect(Boolean.MonoidAny).exist + }) + + it("isBoolean", () => { + expect(Boolean.isBoolean(true)).toEqual(true) + expect(Boolean.isBoolean(false)).toEqual(true) + expect(Boolean.isBoolean("a")).toEqual(false) + expect(Boolean.isBoolean(1)).toEqual(false) + }) + + it("and", () => { + deepStrictEqual(pipe(true, Boolean.and(true)), true) + deepStrictEqual(pipe(true, Boolean.and(false)), false) + deepStrictEqual(pipe(false, Boolean.and(true)), false) + deepStrictEqual(pipe(false, Boolean.and(false)), false) + }) + + it("or", () => { + deepStrictEqual(pipe(true, Boolean.or(true)), true) + deepStrictEqual(pipe(true, Boolean.or(false)), true) + deepStrictEqual(pipe(false, Boolean.or(true)), true) + deepStrictEqual(pipe(false, Boolean.or(false)), false) + }) + + describe.concurrent("MonoidAll", () => { + it("baseline", () => { + deepStrictEqual(Boolean.MonoidAll.combineMany([true, true])(true), true) + deepStrictEqual(Boolean.MonoidAll.combineMany([true, false])(true), false) + deepStrictEqual(Boolean.MonoidAll.combineMany([true, false])(false), false) + deepStrictEqual(Boolean.MonoidAll.combineAll([true, true, true]), true) + deepStrictEqual(Boolean.MonoidAll.combineAll([true, true, false]), false) + }) + + it("should handle iterables", () => { + deepStrictEqual(Boolean.MonoidAll.combineAll(new Set([true, true])), true) + deepStrictEqual(Boolean.MonoidAll.combineAll(new Set([true, false])), false) + deepStrictEqual(Boolean.MonoidAll.combineAll(new Set([false, false])), false) + }) + }) + + describe.concurrent("MonoidAny", () => { + it("baseline", () => { + deepStrictEqual(Boolean.MonoidAny.combineMany([true, true])(true), true) + deepStrictEqual(Boolean.MonoidAny.combineMany([true, false])(true), true) + deepStrictEqual(Boolean.MonoidAny.combineMany([false, false])(false), false) + deepStrictEqual(Boolean.MonoidAny.combineAll([true, true, true]), true) + deepStrictEqual(Boolean.MonoidAny.combineAll([true, true, false]), true) + deepStrictEqual(Boolean.MonoidAny.combineAll([false, false, false]), false) + }) + + it("should handle iterables", () => { + deepStrictEqual(Boolean.MonoidAny.combineAll(new Set([true, true])), true) + deepStrictEqual(Boolean.MonoidAny.combineAll(new Set([true, false])), true) + deepStrictEqual(Boolean.MonoidAny.combineAll(new Set([false, false])), false) + }) + }) + + it("match", () => { + const match = Boolean.match(() => "false", () => "true") + deepStrictEqual(match(true), "true") + deepStrictEqual(match(false), "false") + }) + + it("Order", () => { + deepStrictEqual(pipe(false, Boolean.Order.compare(true)), -1) + deepStrictEqual(pipe(true, Boolean.Order.compare(false)), 1) + deepStrictEqual(pipe(true, Boolean.Order.compare(true)), 0) + }) +}) From 723bb403c8818faa22163d59b7cd9f7950e5703d Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 16:25:05 +0100 Subject: [PATCH 06/80] add Number module --- .changeset/pink-gorillas-sneeze.md | 5 ++ src/Number.ts | 136 +++++++++++++++++++++++++++++ src/index.ts | 5 ++ test/Function.ts | 16 +--- test/Number.ts | 69 +++++++++++++++ 5 files changed, 218 insertions(+), 13 deletions(-) create mode 100644 .changeset/pink-gorillas-sneeze.md create mode 100644 src/Number.ts create mode 100644 test/Number.ts diff --git a/.changeset/pink-gorillas-sneeze.md b/.changeset/pink-gorillas-sneeze.md new file mode 100644 index 000000000..3b01876d4 --- /dev/null +++ b/.changeset/pink-gorillas-sneeze.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Number module diff --git a/src/Number.ts b/src/Number.ts new file mode 100644 index 000000000..100d871ab --- /dev/null +++ b/src/Number.ts @@ -0,0 +1,136 @@ +/** + * @since 1.0.0 + */ +import type { Ordering } from "@fp-ts/core/Ordering" +import type { Refinement } from "@fp-ts/core/Predicate" +import type * as bounded from "@fp-ts/core/typeclass/Bounded" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import type * as order from "@fp-ts/core/typeclass/Order" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @category refinements + * @since 1.0.0 + */ +export const isNumber: Refinement = (u: unknown): u is number => + typeof u === "number" + +/** + * @since 1.0.0 + */ +export const sum = (that: number) => (self: number): number => self + that + +/** + * @since 1.0.0 + */ +export const multiply = (that: number) => (self: number): number => self * that + +/** + * @since 1.0.0 + */ +export const sub = (that: number) => (self: number): number => self - that + +/** + * @since 1.0.0 + */ +export const increment = (n: number): number => n + 1 + +/** + * @since 1.0.0 + */ +export const decrement = (n: number): number => n - 1 + +/** + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Bounded: bounded.Bounded = { + compare: Order.compare, + maxBound: Infinity, + minBound: -Infinity +} + +/** + * `number` semigroup under addition. + * + * @example + * import { SemigroupSum } from '@fp-ts/data/Number' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(2, SemigroupSum.combine(3)), 5) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(sum) + +/** + * `number` semigroup under multiplication. + * + * @example + * import { SemigroupMultiply } from '@fp-ts/data/Number' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(2, SemigroupMultiply.combine(3)), 6) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMultiply: semigroup.Semigroup = { + combine: multiply, + combineMany: (collection) => + (self) => { + if (self === 0) { + return 0 + } + let out = self + for (const n of collection) { + if (n === 0) { + return 0 + } + out = out * n + } + return out + } +} + +/** + * `number` monoid under addition. + * + * The `empty` value is `0`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidSum: monoid.Monoid = { + ...SemigroupSum, + combineAll: (collection) => SemigroupSum.combineMany(collection)(0), + empty: 0 +} + +/** + * `number` monoid under multiplication. + * + * The `empty` value is `1`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMultiply: monoid.Monoid = { + ...SemigroupMultiply, + combineAll: (collection) => SemigroupMultiply.combineMany(collection)(1), + empty: 1 +} + +/** + * @since 1.0.0 + */ +export const sign = (n: number): Ordering => (n < 0 ? -1 : n > 0 ? 1 : 0) diff --git a/src/index.ts b/src/index.ts index cbd714fc7..c2d143034 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import * as hkt from "@fp-ts/core/HKT" import * as boolean from "@fp-ts/core/Boolean" import * as _function from "@fp-ts/core/Function" +import * as number from "@fp-ts/core/Number" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" import * as alternative from "@fp-ts/core/typeclass/Alternative" @@ -124,6 +125,10 @@ export { * @since 1.0.0 */ nonEmptyTraversable, + /** + * @since 1.0.0 + */ + number, /** * @category typeclass * @since 1.0.0 diff --git a/test/Function.ts b/test/Function.ts index d893e4d05..6f9c6e5e9 100644 --- a/test/Function.ts +++ b/test/Function.ts @@ -1,25 +1,15 @@ import * as Function from "@fp-ts/core/Function" +import * as Number from "@fp-ts/core/Number" import { deepStrictEqual, double } from "@fp-ts/core/test/util" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as assert from "assert" const f = (n: number): number => n + 1 const g = double const size = (s: string): number => s.length -// TODO: replace with "@fp-ts/core/Number" -const sum = (that: number) => (self: number): number => self + that -const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(sum) -const MonoidSum: monoid.Monoid = { - ...SemigroupSum, - combineAll: (collection) => SemigroupSum.combineMany(collection)(0), - empty: 0 -} - describe.concurrent("Function", () => { it("getSemigroup", () => { - const S = Function.getSemigroup(SemigroupSum)() + const S = Function.getSemigroup(Number.SemigroupSum)() const f = (s: string) => s === "a" ? 0 : 1 const g = Function.pipe(size, S.combine(f)) deepStrictEqual(g(""), 1) @@ -29,7 +19,7 @@ describe.concurrent("Function", () => { }) it("getMonoid", () => { - const M = Function.getMonoid(MonoidSum)() + const M = Function.getMonoid(Number.MonoidSum)() const f = (s: string) => s === "a" ? 0 : 1 const g = Function.pipe(size, M.combine(f)) deepStrictEqual(g(""), 1) diff --git a/test/Number.ts b/test/Number.ts new file mode 100644 index 000000000..2c4488ce4 --- /dev/null +++ b/test/Number.ts @@ -0,0 +1,69 @@ +import { pipe } from "@fp-ts/core/Function" +import * as Number from "@fp-ts/core/Number" +import { deepStrictEqual } from "@fp-ts/core/test/util" + +describe.concurrent("Number", () => { + it("isNumber", () => { + expect(Number.isNumber(1)).toEqual(true) + expect(Number.isNumber("a")).toEqual(false) + expect(Number.isNumber(true)).toEqual(false) + }) + + it("sum", () => { + deepStrictEqual(Number.sum(1)(2), 3) + }) + + it("sub", () => { + deepStrictEqual(Number.sub(1)(2), 1) + }) + + it("multiply", () => { + deepStrictEqual(Number.multiply(3)(2), 6) + }) + + it("increment", () => { + deepStrictEqual(Number.increment(2), 3) + }) + + it("decrement", () => { + deepStrictEqual(Number.decrement(2), 1) + }) + + it("Order", () => { + deepStrictEqual(pipe(1, Number.Order.compare(2)), -1) + deepStrictEqual(pipe(2, Number.Order.compare(1)), 1) + deepStrictEqual(pipe(2, Number.Order.compare(2)), 0) + }) + + it("Bounded", () => { + expect(Number.Bounded.maxBound).toEqual(Infinity) + expect(Number.Bounded.minBound).toEqual(-Infinity) + }) + + it("SemigroupSum", () => { + deepStrictEqual(pipe(2, Number.SemigroupSum.combine(3)), 5) + }) + + it("MonoidSum", () => { + deepStrictEqual(Number.MonoidSum.combineAll([1, 2, 3]), 6) + }) + + it("SemigroupMultiply", () => { + deepStrictEqual(pipe(2, Number.SemigroupMultiply.combine(3)), 6) + deepStrictEqual(pipe(0, Number.SemigroupMultiply.combineMany([1, 2, 3])), 0) + deepStrictEqual(pipe(2, Number.SemigroupMultiply.combineMany([1, 0, 3])), 0) + }) + + it("MonoidMultiply", () => { + deepStrictEqual(Number.MonoidMultiply.combineAll([2, 3, 4]), 24) + }) + + it("sign", () => { + deepStrictEqual(Number.sign(0), 0) + deepStrictEqual(Number.sign(0.0), 0) + deepStrictEqual(Number.sign(-0.1), -1) + deepStrictEqual(Number.sign(-10), -1) + deepStrictEqual(Number.sign(10), 1) + deepStrictEqual(Number.sign(0.1), 1) + }) +}) From 864a4c3aabadbfa9e5f3ff95de72e239f4574b96 Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 16:49:45 +0100 Subject: [PATCH 07/80] add String module --- .changeset/olive-apes-suffer.md | 5 + src/ReadonlyArray.ts | 14 ++ src/String.ts | 390 ++++++++++++++++++++++++++++++++ src/index.ts | 10 + test/String.ts | 191 ++++++++++++++++ 5 files changed, 610 insertions(+) create mode 100644 .changeset/olive-apes-suffer.md create mode 100644 src/ReadonlyArray.ts create mode 100644 src/String.ts create mode 100644 test/String.ts diff --git a/.changeset/olive-apes-suffer.md b/.changeset/olive-apes-suffer.md new file mode 100644 index 000000000..54975c6ab --- /dev/null +++ b/.changeset/olive-apes-suffer.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add String module diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts new file mode 100644 index 000000000..53c97154f --- /dev/null +++ b/src/ReadonlyArray.ts @@ -0,0 +1,14 @@ +/** + * @since 1.0.0 + */ + +/** + * @category models + * @since 1.0.0 + */ +export type NonEmptyReadonlyArray = readonly [A, ...Array] + +/** + * @since 1.0.0 + */ +export const isNonEmpty = (as: ReadonlyArray): as is NonEmptyReadonlyArray => as.length > 0 diff --git a/src/String.ts b/src/String.ts new file mode 100644 index 000000000..e78ed8cc1 --- /dev/null +++ b/src/String.ts @@ -0,0 +1,390 @@ +/** + * @since 1.0.0 + */ + +import type { Refinement } from "@fp-ts/core/Predicate" +import { isNonEmpty } from "@fp-ts/core/ReadonlyArray" +import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" +import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import type * as order from "@fp-ts/core/typeclass/Order" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @since 1.0.0 + */ +export const concat = (that: string) => (self: string): string => self + that + +/** + * `string` semigroup under concatenation. + * + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('a', S.Semigroup.combine('b')), 'ab') + * + * @category instances + * @since 1.0.0 + */ +export const Semigroup: semigroup.Semigroup = semigroup.fromCombine(concat) + +/** + * An empty `string`. + * + * @since 1.0.0 + */ +export const empty: "" = "" as const + +/** + * `string` monoid under concatenation. + * + * The `empty` value is `''`. + * + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('a', S.Monoid.combine('b')), 'ab') + * assert.deepStrictEqual(pipe('a', S.Monoid.combine(S.Monoid.empty)), 'a') + * + * @category instances + * @since 1.0.0 + */ +export const Monoid: monoid.Monoid = { + ...Semigroup, + combineAll: (collection) => Semigroup.combineMany(collection)(empty), + empty +} + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('a', S.Order.compare('a')), 0) + * assert.deepStrictEqual(pipe('a', S.Order.compare('b')), -1) + * assert.deepStrictEqual(pipe('b', S.Order.compare('a')), 1) + * + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + +/** + * @example + * import * as S from '@fp-ts/data/String' + * + * assert.deepStrictEqual(S.isString('a'), true) + * assert.deepStrictEqual(S.isString(1), false) + * + * @category refinements + * @since 1.0.0 + */ +export const isString: Refinement = (u: unknown): u is string => + typeof u === "string" + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('a', S.toUpperCase), 'A') + * + * @since 1.0.0 + */ +export const toUpperCase = (s: string): string => s.toUpperCase() + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('A', S.toLowerCase), 'a') + * + * @since 1.0.0 + */ +export const toLowerCase = (s: string): string => s.toLowerCase() + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.replace('b', 'd')), 'adc') + * + * @since 1.0.0 + */ +export const replace = (searchValue: string | RegExp, replaceValue: string) => + (s: string): string => s.replace(searchValue, replaceValue) + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(' a ', S.trim), 'a') + * + * @since 1.0.0 + */ +export const trim = (s: string): string => s.trim() + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(' a ', S.trimLeft), 'a ') + * + * @since 1.0.0 + */ +export const trimLeft = (s: string): string => s.trimLeft() + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe(' a ', S.trimRight), ' a') + * + * @since 1.0.0 + */ +export const trimRight = (s: string): string => s.trimRight() + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abcd', S.slice(1, 3)), 'bc') + * + * @since 1.0.0 + */ +export const slice = (start: number, end: number) => (s: string): string => s.slice(start, end) + +/** + * Test whether a `string` is empty. + * + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('', S.isEmpty), true) + * assert.deepStrictEqual(pipe('a', S.isEmpty), false) + * + * @since 1.0.0 + */ +export const isEmpty = (s: string): boolean => s.length === 0 + +/** + * Calculate the number of characters in a `string`. + * + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.size), 3) + * + * @since 1.0.0 + */ +export const size = (s: string): number => s.length + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.split('')), ['a', 'b', 'c']) + * assert.deepStrictEqual(pipe('', S.split('')), ['']) + * + * @since 1.0.0 + */ +export const split = (separator: string | RegExp) => + (s: string): NonEmptyReadonlyArray => { + const out = s.split(separator) + return isNonEmpty(out) ? out : [s] + } + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.includes('b')), true) + * assert.deepStrictEqual(pipe('abc', S.includes('d')), false) + * + * @since 1.0.0 + */ +export const includes = (searchString: string, position?: number) => + (s: string): boolean => s.includes(searchString, position) + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.startsWith('a')), true) + * assert.deepStrictEqual(pipe('bc', S.startsWith('a')), false) + * + * @since 1.0.0 + */ +export const startsWith = (searchString: string, position?: number) => + (s: string): boolean => s.startsWith(searchString, position) + +/** + * @example + * import * as S from '@fp-ts/data/String' + * import { pipe } from '@fp-ts/data/Function' + * + * assert.deepStrictEqual(pipe('abc', S.endsWith('c')), true) + * assert.deepStrictEqual(pipe('ab', S.endsWith('c')), false) + * + * @since 1.0.0 + */ +export const endsWith = (searchString: string, position?: number) => + (s: string): boolean => s.endsWith(searchString, position) + +/** + * Keep the specified number of characters from the start of a string. + * + * If `n` is larger than the available number of characters, the string will + * be returned whole. + * + * If `n` is not a positive number, an empty string will be returned. + * + * If `n` is a float, it will be rounded down to the nearest integer. + * + * @since 1.0.0 + */ +export const takeLeft = (n: number) => (self: string): string => self.slice(0, Math.max(n, 0)) + +/** + * Keep the specified number of characters from the end of a string. + * + * If `n` is larger than the available number of characters, the string will + * be returned whole. + * + * If `n` is not a positive number, an empty string will be returned. + * + * If `n` is a float, it will be rounded down to the nearest integer. + * + * @since 1.0.0 + */ +export const takeRight = (n: number) => + (s: string): string => s.slice(Math.max(0, s.length - Math.floor(n)), Infinity) + +// TODO: 100% coverage tests (ask Max) +// const CR = 0x0d +// const LF = 0x0a + +// /** +// * Returns an `IterableIterator` which yields each line contained within the +// * string, trimming off the trailing newline character. +// * +// * @since 1.0.0 +// */ +// // export const linesIterator = (self: string): LinesIterator => linesSeparated(self, true) + +// /** +// * Returns an `IterableIterator` which yields each line contained within the +// * string as well as the trailing newline character. +// * +// * @since 1.0.0 +// */ +// export const linesWithSeparators = (s: string): LinesIterator => linesSeparated(s, false) + +// /** +// * For every line in this string, strip a leading prefix consisting of blanks +// * or control characters followed by the character specified by `marginChar` +// * from the line. +// * +// * @since 1.0.0 +// */ +// export const stripMarginWith = (marginChar: string) => +// (self: string): string => { +// let out = "" + +// for (const line of linesWithSeparators(self)) { +// let index = 0 + +// while (index < line.length && line.charAt(index) <= " ") { +// index = index + 1 +// } + +// const stripped = index < line.length && line.charAt(index) === marginChar +// ? line.substring(index + 1) +// : line + +// out = out + stripped +// } + +// return out +// } + +// /** +// * For every line in this string, strip a leading prefix consisting of blanks +// * or control characters followed by the `"|"` character from the line. +// * +// * @since 1.0.0 +// */ +// export const stripMargin = (self: string): string => stripMarginWith("|")(self) + +// class LinesIterator implements IterableIterator { +// private index: number +// private readonly length: number + +// constructor(readonly s: string, readonly stripped: boolean = false) { +// this.index = 0 +// this.length = s.length +// } + +// next(): IteratorResult { +// if (this.done) { +// return { done: true, value: undefined } +// } +// const start = this.index +// while (!this.done && !isLineBreak(this.s[this.index]!)) { +// this.index = this.index + 1 +// } +// let end = this.index +// if (!this.done) { +// const char = this.s[this.index]! +// this.index = this.index + 1 +// if (!this.done && isLineBreak2(char, this.s[this.index]!)) { +// this.index = this.index + 1 +// } +// if (!this.stripped) { +// end = this.index +// } +// } +// return { done: false, value: this.s.substring(start, end) } +// } + +// [Symbol.iterator](): IterableIterator { +// return new LinesIterator(this.s, this.stripped) +// } + +// private get done(): boolean { +// return this.index >= this.length +// } +// } + +// /** +// * Test if the provided character is a line break character (i.e. either `"\r"` +// * or `"\n"`). +// */ +// const isLineBreak = (char: string): boolean => { +// const code = char.charCodeAt(0) +// return code === CR || code === LF +// } + +// /** +// * Test if the provided characters combine to form a carriage return/line-feed +// * (i.e. `"\r\n"`). +// */ +// const isLineBreak2 = (char0: string, char1: string): boolean => +// char0.charCodeAt(0) === CR && char1.charCodeAt(0) === LF + +// const linesSeparated = (self: string, stripped: boolean): LinesIterator => +// new LinesIterator(self, stripped) diff --git a/src/index.ts b/src/index.ts index c2d143034..184afd3a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,8 @@ import * as _function from "@fp-ts/core/Function" import * as number from "@fp-ts/core/Number" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" +import * as readonlyArray from "@fp-ts/core/ReadonlyArray" +import * as string from "@fp-ts/core/String" import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -157,6 +159,10 @@ export { * @since 1.0.0 */ product, + /** + * @since 1.0.0 + */ + readonlyArray, /** * @category typeclass * @since 1.0.0 @@ -182,6 +188,10 @@ export { * @since 1.0.0 */ semiProduct, + /** + * @since 1.0.0 + */ + string, /** * @category typeclass * @since 1.0.0 diff --git a/test/String.ts b/test/String.ts new file mode 100644 index 000000000..033124f35 --- /dev/null +++ b/test/String.ts @@ -0,0 +1,191 @@ +import { pipe } from "@fp-ts/core/Function" +import * as String from "@fp-ts/core/String" +import { deepStrictEqual } from "@fp-ts/core/test/util" +import * as Order from "@fp-ts/core/typeclass/Order" + +describe.concurrent("String", () => { + it("isString", () => { + expect(String.isString("a")).toEqual(true) + expect(String.isString(1)).toEqual(false) + expect(String.isString(true)).toEqual(false) + }) + + it("empty", () => { + expect(String.empty).toEqual("") + }) + + it("Semigroup", () => { + expect(String.Semigroup.combine("b")("a")).toEqual("ab") + expect(String.Semigroup.combineMany(["b", "c"])("a")).toEqual("abc") + expect(String.Semigroup.combineMany([])("a")).toEqual("a") + }) + + it("Monoid", () => { + expect(String.Monoid.combineAll([])).toEqual("") + }) + + it("Order", () => { + const lessThan = Order.lessThan(String.Order) + const lessThanOrEqualTo = Order.lessThanOrEqualTo(String.Order) + expect(pipe("a", lessThan("b"))).toEqual(true) + expect(pipe("a", lessThan("a"))).toEqual(false) + expect(pipe("a", lessThanOrEqualTo("a"))).toEqual(true) + expect(pipe("b", lessThan("a"))).toEqual(false) + expect(pipe("b", lessThanOrEqualTo("a"))).toEqual(false) + }) + + it("concat", () => { + deepStrictEqual(pipe("a", String.concat("b")), "ab") + }) + + it("isEmpty", () => { + deepStrictEqual(String.isEmpty(String.empty), true) + deepStrictEqual(String.isEmpty(""), true) + deepStrictEqual(String.isEmpty("a"), false) + }) + + it("size", () => { + deepStrictEqual(String.size(String.empty), 0) + deepStrictEqual(String.size(""), 0) + deepStrictEqual(String.size("a"), 1) + }) + + it("toUpperCase", () => { + deepStrictEqual(String.toUpperCase("a"), "A") + }) + + it("toLowerCase", () => { + deepStrictEqual(String.toLowerCase("A"), "a") + }) + + it("replace", () => { + deepStrictEqual(pipe("abc", String.replace("b", "d")), "adc") + }) + + it("split", () => { + deepStrictEqual(pipe("abc", String.split("")), ["a", "b", "c"]) + deepStrictEqual(pipe("", String.split("")), [""]) + }) + + it("trim", () => { + deepStrictEqual(pipe(" a ", String.trim), "a") + }) + + it("trimLeft", () => { + deepStrictEqual(pipe(" a ", String.trimLeft), "a ") + }) + + it("trimRight", () => { + deepStrictEqual(pipe(" a ", String.trimRight), " a") + }) + + it("includes", () => { + deepStrictEqual(pipe("abc", String.includes("b")), true) + deepStrictEqual(pipe("abc", String.includes("b", 2)), false) + }) + + it("startsWith", () => { + deepStrictEqual(pipe("abc", String.startsWith("a")), true) + }) + + it("endsWith", () => { + deepStrictEqual(pipe("abc", String.endsWith("c")), true) + }) + + it("slice", () => { + deepStrictEqual(pipe("abcd", String.slice(1, 3)), "bc") + }) + + describe.concurrent("takeLeft", () => { + it("should take the specified number of characters from the left side of a string", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeLeft(7)) + + assert.strictEqual(result, "Hello, ") + }) + + it("should return the string for `n` larger than the string length", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeLeft(100)) + + assert.strictEqual(result, string) + }) + + it("should return the empty string for a negative `n`", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeLeft(-1)) + + assert.strictEqual(result, "") + }) + + it("should round down if `n` is a float", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeLeft(5.5)) + + assert.strictEqual(result, "Hello") + }) + }) + + describe.concurrent("takeRight", () => { + it("should take the specified number of characters from the right side of a string", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeRight(7)) + + assert.strictEqual(result, " World!") + }) + + it("should return the string for `n` larger than the string length", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeRight(100)) + + assert.strictEqual(result, string) + }) + + it("should return the empty string for a negative `n`", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeRight(-1)) + + assert.strictEqual(result, "") + }) + + it("should round down if `n` is a float", () => { + const string = "Hello, World!" + + const result = pipe(string, String.takeRight(6.5)) + + assert.strictEqual(result, "World!") + }) + }) + + // TODO: 100% coverage tests (ask Max) + // describe.concurrent("stripMargin", () => { + // it("should strip a leading prefix from each line", () => { + // const string = `| + // |Hello, + // |World! + // |` + + // const result = pipe(string, String.stripMargin) + + // assert.strictEqual(result, "\nHello,\nWorld!\n") + // }) + + // it("should strip a leading prefix from each line using a margin character", () => { + // const string = `$ + // $Hello, + // $World! + // $` + + // const result = pipe(string, String.stripMarginWith("$")) + + // assert.strictEqual(result, "\nHello,\nWorld!\n") + // }) + // }) +}) From c4ed639ef12c9f9f4a049e152e62e644ff578031 Mon Sep 17 00:00:00 2001 From: gcanti Date: Sun, 15 Jan 2023 17:45:04 +0100 Subject: [PATCH 08/80] add Identity module --- .changeset/cyan-melons-own.md | 5 + src/Identity.ts | 534 ++++++++++++++++++++++++++++++++++ src/Predicate.ts | 6 +- src/index.ts | 5 + test/Identity.ts | 136 +++++++++ test/internal/Iterable.ts | 8 +- 6 files changed, 687 insertions(+), 7 deletions(-) create mode 100644 .changeset/cyan-melons-own.md create mode 100644 src/Identity.ts create mode 100644 test/Identity.ts diff --git a/.changeset/cyan-melons-own.md b/.changeset/cyan-melons-own.md new file mode 100644 index 000000000..7665266f3 --- /dev/null +++ b/.changeset/cyan-melons-own.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Identity module diff --git a/src/Identity.ts b/src/Identity.ts new file mode 100644 index 000000000..0f759d7f6 --- /dev/null +++ b/src/Identity.ts @@ -0,0 +1,534 @@ +/** + * @since 1.0.0 + */ +import { identity } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as iterable from "@fp-ts/core/internal/Iterable" +import * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as chainable from "@fp-ts/core/typeclass/Chainable" +import type * as coproduct_ from "@fp-ts/core/typeclass/Coproduct" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" +import * as foldable from "@fp-ts/core/typeclass/Foldable" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as monad from "@fp-ts/core/typeclass/Monad" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import type * as pointed from "@fp-ts/core/typeclass/Pointed" +import * as product_ from "@fp-ts/core/typeclass/Product" +import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" +import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import type * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as traversable from "@fp-ts/core/typeclass/Traversable" + +/** + * @category models + * @since 1.0.0 + */ +export type Identity = A + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface IdentityTypeLambda extends TypeLambda { + readonly type: Identity +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface IdentityTypeLambdaFix extends TypeLambda { + readonly type: Identity +} + +/** + * @since 1.0.0 + */ +export const map = (f: (a: A) => B) => (self: Identity): Identity => f(self) + +/** + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: Identity) => Identity = covariant + .imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @since 1.0.0 + */ +export const tupled: (self: Identity) => Identity = invariant.tupled(Invariant) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: Identity) => Identity<{ readonly [K in N]: A }> = invariant.bindTo(Invariant) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + ...Invariant, + map +} + +const let_: ( + name: Exclude, + f: (a: A) => B +) => ( + self: Identity +) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let( + Covariant +) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: (a: A) => (fab: Identity<(a: A) => B>) => Identity = covariant.flap( + Covariant +) + +/** + * Maps the success value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: (b: B) => <_>(self: Identity<_>) => Identity = covariant.as(Covariant) + +/** + * Returns the effect resulting from mapping the success of this effect to unit. + * + * @category mapping + * @since 1.0.0 + */ +export const asUnit: <_>(self: Identity<_>) => Identity = covariant.asUnit(Covariant) + +/** + * @category constructors + * @since 1.0.0 + */ +export const of: (a: A) => Identity = identity + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: Identity = of_.unit(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Identity<{}> = of_.Do(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + ...Of, + ...Covariant +} + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMap = (f: (a: A) => Identity) => + (self: Identity): Identity => f(self) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @since 1.0.0 + */ +export const flatten: (self: Identity>) => Identity = flatMap_ + .flatten(FlatMap) + +/** + * @since 1.0.0 + */ +export const andThen: (that: Identity) => <_>(self: Identity<_>) => Identity = flatMap_ + .andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: ( + bfc: (b: B) => Identity +) => (afb: (a: A) => Identity) => (a: A) => Identity = flatMap_ + .composeKleisliArrow(FlatMap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + ...FlatMap, + ...Covariant +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: ( + name: Exclude, + f: (a: A) => Identity +) => ( + self: Identity +) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind( + Chainable +) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @since 1.0.0 + */ +export const tap: (f: (a: A) => Identity<_>) => (self: Identity) => Identity = chainable + .tap( + Chainable + ) + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category sequencing + * @since 1.0.0 + */ +export const andThenDiscard: <_>(that: Identity<_>) => (self: Identity) => Identity = + chainable + .andThenDiscard(Chainable) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + ...Pointed, + ...FlatMap +} + +/** + * @since 1.0.0 + */ +export const product = ( + that: Identity +) => (self: Identity): Identity => [self, that] + +/** + * @since 1.0.0 + */ +export const productMany = (collection: Iterable>) => + (self: Identity): Identity]> => [self, ...collection] + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + ...Invariant, + product, + productMany +} + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + fb: Identity +) => ( + self: Identity +) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct + .andThenBind(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productFlatten: ( + fb: Identity +) => >(self: Identity) => Identity = + semiProduct + .productFlatten(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productAll = (collection: Iterable>): Identity> => + iterable.fromIterable(collection) + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...Of, + ...SemiProduct, + productAll +} + +/** + * @since 1.0.0 + */ +export const tuple: >>( + ...tuple: T +) => Identity] ? A : never }>> = + product_ + .tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + r: R +) => Identity<{ readonly [K in keyof R]: [R[K]] extends [Identity] ? A : never }> = + product_ + .struct(Product) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + ...SemiProduct, + ...Covariant +} + +/** + * @category instances + * @since 1.0.0 + */ +export const liftSemigroup: (S: Semigroup) => Semigroup> = semiApplicative + .liftSemigroup(SemiApplicative) + +/** + * Lifts a binary function into `Identity`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: ( + f: (a: A, b: B) => C +) => (fa: Identity, fb: Identity) => Identity = semiApplicative.lift2( + SemiApplicative +) + +/** + * Lifts a ternary function into `Identity`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: Identity, fb: Identity, fc: Identity) => Identity = semiApplicative.lift3( + SemiApplicative +) + +/** + * @since 1.0.0 + */ +export const ap: ( + fa: Identity +) => (self: Identity<(a: A) => B>) => Identity = semiApplicative.ap( + SemiApplicative +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} + +/** + * @since 1.0.0 + */ +export const liftMonoid: (M: Monoid) => Monoid> = applicative.liftMonoid( + Applicative +) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemiCoproduct = ( + S: Semigroup +): semiCoproduct.SemiCoproduct> => ({ + imap: Invariant.imap, + coproduct: S.combine, + coproductMany: S.combineMany +}) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemiAlternative = ( + S: Semigroup +): semiAlternative.SemiAlternative> => ({ + ...getSemiCoproduct(S), + map: Covariant.map +}) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduce = (b: B, f: (b: B, a: A) => B) => (self: Identity): B => f(b, self) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceRight = (b: B, f: (b: B, a: A) => B) => + (self: Identity): B => f(b, self) + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce +} + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMap: (M: Monoid) => (f: (a: A) => M) => (self: Identity) => M = + foldable + .foldMap(Foldable) + +/** + * @category conversions + * @since 1.0.0 + */ +export const toArray: (self: Identity) => Array = foldable.toArray(Foldable) + +/** + * @category conversions + * @since 1.0.0 + */ +export const toArrayWith: (f: (a: A) => B) => (self: Identity) => Array = foldable + .toArrayWith(Foldable) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceKind: ( + G: monad.Monad +) => ( + b: B, + f: (b: B, a: A) => Kind +) => (self: Identity) => Kind = foldable.reduceKind(Foldable) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceRightKind: ( + G: monad.Monad +) => ( + b: B, + f: (b: B, a: A) => Kind +) => (self: Identity) => Kind = foldable.reduceRightKind(Foldable) + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMapKind: ( + G: coproduct_.Coproduct +) => ( + f: (a: A) => Kind +) => (self: Identity) => Kind = foldable.foldMapKind(Foldable) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +) => + ( + f: (a: A) => Kind + ) => (self: Identity): Kind> => f(self) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => (fas: Identity>) => Kind> = traversable + .sequence(traverse) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse, + sequence +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind +) => (self: Identity) => Kind> = traversable + .traverseTap(Traversable) diff --git a/src/Predicate.ts b/src/Predicate.ts index 9dd93719a..641ba2169 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -3,7 +3,7 @@ */ import { constFalse, constTrue } from "@fp-ts/core/Function" import type { TypeLambda } from "@fp-ts/core/HKT" -import * as I from "@fp-ts/core/internal/Iterable" +import * as iterable from "@fp-ts/core/internal/Iterable" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monoid from "@fp-ts/core/typeclass/Monoid" @@ -132,7 +132,7 @@ export const productMany = (collection: Iterable>) => if (self(head) === false) { return false } - const predicates = I.fromIterable(collection) + const predicates = iterable.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](tail[i]) === false) { return false @@ -159,7 +159,7 @@ export const productAll = ( collection: Iterable> ): Predicate> => (as) => { - const predicates = I.fromIterable(collection) + const predicates = iterable.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](as[i]) === false) { return false diff --git a/src/index.ts b/src/index.ts index 184afd3a6..251fb5e74 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import * as hkt from "@fp-ts/core/HKT" import * as boolean from "@fp-ts/core/Boolean" import * as _function from "@fp-ts/core/Function" +import * as identity from "@fp-ts/core/Identity" import * as number from "@fp-ts/core/Number" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" @@ -107,6 +108,10 @@ export { * @since 1.0.0 */ hkt, + /** + * @since 1.0.0 + */ + identity, /** * @category typeclass * @since 1.0.0 diff --git a/test/Identity.ts b/test/Identity.ts new file mode 100644 index 000000000..1de2992f9 --- /dev/null +++ b/test/Identity.ts @@ -0,0 +1,136 @@ +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/Identity" +// import * as O from "@fp-ts/core/Option" +import * as String from "@fp-ts/core/String" +import * as U from "./util" + +describe.concurrent("Identity", () => { + it("instances and derived exports", () => { + expect(_.Invariant).exist + expect(_.imap).exist + expect(_.tupled).exist + expect(_.bindTo).exist + + expect(_.Covariant).exist + expect(_.map).exist + expect(_.let).exist + expect(_.flap).exist + expect(_.as).exist + expect(_.asUnit).exist + + expect(_.Of).exist + expect(_.of).exist + expect(_.Do).exist + + expect(_.Pointed).exist + + expect(_.FlatMap).exist + expect(_.flatMap).exist + expect(_.flatten).exist + expect(_.andThen).exist + expect(_.composeKleisliArrow).exist + + expect(_.Chainable).exist + expect(_.bind).exist + expect(_.tap).exist + expect(_.andThenDiscard).exist + + expect(_.Monad).exist + + expect(_.SemiProduct).exist + expect(_.product).exist + expect(_.productMany).exist + expect(_.andThenBind).exist + expect(_.productFlatten).exist + + expect(_.Product).exist + expect(_.productAll).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.liftSemigroup).exist + expect(_.lift2).exist + expect(_.lift3).exist + expect(_.ap).exist + expect(_.andThenDiscard).exist + expect(_.andThen).exist + + expect(_.Applicative).exist + expect(_.liftMonoid).exist + + expect(_.Foldable).exist + expect(_.reduce).exist + expect(_.reduceRight).exist + expect(_.foldMap).exist + expect(_.toArray).exist + expect(_.toArrayWith).exist + expect(_.reduceKind).exist + expect(_.reduceRightKind).exist + expect(_.foldMapKind).exist + + expect(_.Traversable).exist + expect(_.traverse).exist + expect(_.sequence).exist + expect(_.traverseTap).exist + }) + + it("unit", () => { + U.deepStrictEqual(_.unit, undefined) + }) + + it("of", () => { + U.deepStrictEqual(_.of("a"), "a") + }) + + it("SemiProduct", () => { + U.deepStrictEqual(pipe("a", _.SemiProduct.productMany(["b", "c"])), ["a", "b", "c"]) + }) + + it("Product", () => { + U.deepStrictEqual(_.Product.productAll([]), []) + U.deepStrictEqual(_.Product.productAll(["a", "b", "c"]), ["a", "b", "c"]) + }) + + it("flatMap", () => { + U.deepStrictEqual( + pipe("a", _.flatMap((a) => a + "b")), + "ab" + ) + }) + + it("product", () => { + U.deepStrictEqual(pipe("a", _.product("b")), ["a", "b"]) + }) + + it("getSemiCoproduct", () => { + const F = _.getSemiCoproduct(String.Semigroup) + U.deepStrictEqual(pipe("a", F.coproduct("b")), "ab") + U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") + }) + + it("getSemiAlternative", () => { + const F = _.getSemiAlternative(String.Semigroup) + U.deepStrictEqual(pipe("a", F.coproduct("b")), "ab") + U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") + }) + + it("reduce", () => { + U.deepStrictEqual(pipe("b", _.reduce("a", (b, a) => b + a)), "ab") + U.deepStrictEqual(pipe("b", _.Foldable.reduce("a", (b, a) => b + a)), "ab") + }) + + it("reduceRight", () => { + const f = (a: string, acc: string) => acc + a + U.deepStrictEqual(pipe("a", _.reduceRight("", f)), "a") + }) + + // TODO + // it("traverse", () => { + // U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(O.some)), O.some(1)) + // U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(() => O.none)), O.none) + + // U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(O.some)), O.some(1)) + // U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(() => O.none)), O.none) + // }) +}) diff --git a/test/internal/Iterable.ts b/test/internal/Iterable.ts index c71b77e69..7ee04a196 100644 --- a/test/internal/Iterable.ts +++ b/test/internal/Iterable.ts @@ -1,12 +1,12 @@ -import * as I from "@fp-ts/core/internal/Iterable" +import * as iterable from "@fp-ts/core/internal/Iterable" describe.concurrent("internal/Iterable", () => { it("fromIterable/Array should return the same reference if the iterable is an Array", () => { - const iterable = [1, 2, 3] - expect(I.fromIterable(iterable) === iterable).toEqual(true) + const i = [1, 2, 3] + expect(iterable.fromIterable(i) === i).toEqual(true) }) it("fromIterable/Iterable", () => { - expect(I.fromIterable(new Set([1, 2, 3]))).toEqual([1, 2, 3]) + expect(iterable.fromIterable(new Set([1, 2, 3]))).toEqual([1, 2, 3]) }) }) From c80e35033625b86ea4a614845f1c42b4b55674c1 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 06:46:33 +0100 Subject: [PATCH 09/80] add Equivalence module --- .changeset/polite-mice-begin.md | 5 + src/Boolean.ts | 9 ++ src/Number.ts | 7 ++ src/String.ts | 9 +- src/index.ts | 6 + src/typeclass/Equivalence.ts | 209 ++++++++++++++++++++++++++++++++ src/typeclass/Product.ts | 2 +- src/typeclass/SemiProduct.ts | 4 +- test/Boolean.ts | 7 ++ test/Number.ts | 5 + test/String.ts | 5 + test/typeclass/Equivalence.ts | 168 +++++++++++++++++++++++++ 12 files changed, 432 insertions(+), 4 deletions(-) create mode 100644 .changeset/polite-mice-begin.md create mode 100644 src/typeclass/Equivalence.ts create mode 100644 test/typeclass/Equivalence.ts diff --git a/.changeset/polite-mice-begin.md b/.changeset/polite-mice-begin.md new file mode 100644 index 000000000..a4b66a2e5 --- /dev/null +++ b/.changeset/polite-mice-begin.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Equivalence module diff --git a/src/Boolean.ts b/src/Boolean.ts index 0b6f092b9..7db4ed1d3 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -3,6 +3,7 @@ */ import type { LazyArg } from "@fp-ts/core/Function" import type { Refinement } from "@fp-ts/core/Predicate" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import type * as monoid from "@fp-ts/core/typeclass/Monoid" import type * as order from "@fp-ts/core/typeclass/Order" import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" @@ -15,11 +16,13 @@ export const isBoolean: Refinement = (u: unknown): u is boolea typeof u === "boolean" /** + * @category combinators * @since 1.0.0 */ export const and = (that: boolean) => (self: boolean): boolean => self && that /** + * @category combinators * @since 1.0.0 */ export const or = (that: boolean) => (self: boolean): boolean => self || that @@ -135,6 +138,12 @@ export const MonoidAny: monoid.Monoid = { empty: false } +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.boolean + /** * @category instances * @since 1.0.0 diff --git a/src/Number.ts b/src/Number.ts index 100d871ab..83080a28f 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -4,6 +4,7 @@ import type { Ordering } from "@fp-ts/core/Ordering" import type { Refinement } from "@fp-ts/core/Predicate" import type * as bounded from "@fp-ts/core/typeclass/Bounded" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import type * as monoid from "@fp-ts/core/typeclass/Monoid" import type * as order from "@fp-ts/core/typeclass/Order" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" @@ -40,6 +41,12 @@ export const increment = (n: number): number => n + 1 */ export const decrement = (n: number): number => n - 1 +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.number + /** * @category instances * @since 1.0.0 diff --git a/src/String.ts b/src/String.ts index e78ed8cc1..98921c6de 100644 --- a/src/String.ts +++ b/src/String.ts @@ -5,6 +5,7 @@ import type { Refinement } from "@fp-ts/core/Predicate" import { isNonEmpty } from "@fp-ts/core/ReadonlyArray" import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import type * as monoid from "@fp-ts/core/typeclass/Monoid" import type * as order from "@fp-ts/core/typeclass/Order" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" @@ -56,6 +57,12 @@ export const Monoid: monoid.Monoid = { empty } +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.string + /** * @example * import * as S from '@fp-ts/data/String' @@ -175,7 +182,7 @@ export const slice = (start: number, end: number) => (s: string): string => s.sl * * @since 1.0.0 */ -export const isEmpty = (s: string): boolean => s.length === 0 +export const isEmpty = (s: string): s is "" => s.length === 0 /** * Calculate the number of characters in a `string`. diff --git a/src/index.ts b/src/index.ts index 251fb5e74..7830f9c29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,7 @@ import * as chainable from "@fp-ts/core/typeclass/Chainable" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as coproduct from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as flatMap from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -94,6 +95,11 @@ export { * @since 1.0.0 */ covariant, + /** + * @category typeclass + * @since 1.0.0 + */ + equivalence, /** * @category typeclass * @since 1.0.0 diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts new file mode 100644 index 000000000..762f7790c --- /dev/null +++ b/src/typeclass/Equivalence.ts @@ -0,0 +1,209 @@ +/** + * This module provides an implementation of the `Equivalence` type class. + * An `Equivalence` is a binary relation that is reflexive, symmetric and transitive. + * + * @since 1.0.0 + */ +import type { TypeLambda } from "@fp-ts/core/HKT" +import * as contravariant from "@fp-ts/core/typeclass/Contravariant" +import type * as invariant from "@fp-ts/core/typeclass/Invariant" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import type * as product from "@fp-ts/core/typeclass/Product" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" + +/** + * @category type class + * @since 1.0.0 + */ +export interface Equivalence { + (x: A, y: A): boolean +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface EquivalenceTypeLambda extends TypeLambda { + readonly type: Equivalence +} + +/** + * Return an `Equivalence` that uses strict equality (===) to compare values + * + * @since 1.0.0 + * @category constructors + */ +export const strict: () => Equivalence = () => (x, y) => x === y + +/** + * @category instances + * @since 1.0.0 + */ +export const string: Equivalence = strict() + +/** + * @category instances + * @since 1.0.0 + */ +export const number: Equivalence = strict() + +/** + * @category instances + * @since 1.0.0 + */ +export const boolean: Equivalence = strict() + +/** + * @category instances + * @since 1.0.0 + */ +export const bigint: Equivalence = strict() + +/** + * @category instances + * @since 1.0.0 + */ +export const symbol: Equivalence = strict() + +/** + * Given a tuple of `Equivalence`s returns a new `Equivalence` that compares values of a tuple + * by applying each `Equivalence` to the corresponding element of the tuple. + * + * @category constructors + * @since 1.0.0 + */ +export const tuple = >( + ...equivalences: { readonly [K in keyof A]: Equivalence } +): Equivalence> => + (x, y) => equivalences.every((equivalence, i) => equivalence(x[i], y[i])) + +/** + * Given an `Equivalence` of type `A`, returns a new `Equivalence` of type `ReadonlyArray`. + * The returned `Equivalence` compares arrays by first checking their length and then applying the provided `Equivalence` to each element. + * If all comparisons return true, the arrays are considered equal. + * + * @category constructors + * @since 1.0.0 + */ +export const array = ( + equivalence: Equivalence +): Equivalence> => + (x, y) => x.length === y.length && x.every((a, i) => equivalence(a, y[i])) + +/** + * Given a struct of `Equivalence`s returns a new `Equivalence` that compares values of a struct + * by applying each `Equivalence` to the corresponding property of the struct. + * + * @category constructors + * @since 1.0.0 + */ +export const struct = ( + equivalences: { [K in keyof A]: Equivalence } +): Equivalence<{ readonly [K in keyof A]: A[K] }> => + (x, y) => { + for (const key in equivalences) { + if (!equivalences[key](x[key], y[key])) { + return false + } + } + return true + } + +/** + * Given an `Equivalence` of type `A`, returns a new `Equivalence` of type `{ readonly [x: string]: A }`. + * The returned `Equivalence` compares records by first checking their number of keys and then applying the provided `Equivalence` to each value. + * If all comparisons return true, the records are considered equal. + * + * @category constructors + * @since 1.0.0 + */ +export const record = ( + equivalence: Equivalence +): Equivalence<{ readonly [x: string]: A }> => + (x, y) => { + const keys = Object.keys(x) + if (Object.keys(y).length !== keys.length) { + return false + } + for (const key of keys) { + if (!equivalence(x[key], y[key])) { + return false + } + } + return true + } + +/** + * @category instances + * @since 2.10.0 + */ +export const getSemigroup = (): Semigroup> => ({ + combine: (that) => (self) => (x, y) => self(x, y) && that(x, y), + combineMany: (collection) => + self => + (x, y) => { + if (!self(x, y)) { + return false + } + for (const equivalence of collection) { + if (!equivalence(x, y)) { + return false + } + } + return true + } +}) + +const empty: Equivalence = () => true + +/** + * @category instances + * @since 2.6.0 + */ +export const getMonoid = (): Monoid> => + monoid.fromSemigroup(getSemigroup(), empty) + +/** + * @category combinators + * @since 1.0.0 + */ +export const contramap = (f: (b: B) => A) => + (self: Equivalence): Equivalence => (x, y) => self(f(x), f(y)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Contravariant: contravariant.Contravariant = contravariant.make( + contramap +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap: Contravariant.imap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap: Contravariant.imap, + product: that => self => tuple(self, that), + productMany: collection => self => tuple(self, ...collection) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product.Product = { + ...SemiProduct, + of: () => empty, + productAll: (collection: Iterable>) => tuple>(...collection) +} diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index 675d7c733..e6129ea94 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -32,7 +32,7 @@ export const tuple = (F: Product) => * @since 1.0.0 */ export const struct = (F: Product) => - }>(fields: R): Kind< + }>(fields: R): Kind< F, ([R[keyof R]] extends [Kind] ? R : never), ([R[keyof R]] extends [Kind] ? O : never), diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 9bc244c14..833aa743c 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -156,8 +156,8 @@ type EnforceNonEmptyRecord = keyof R extends never ? never : R * @since 1.0.0 */ export const nonEmptyStruct = (F: SemiProduct) => - }>( - fields: EnforceNonEmptyRecord & { readonly [x: PropertyKey]: Kind } + }>( + fields: EnforceNonEmptyRecord & { readonly [x: string]: Kind } ): Kind< F, ([R[keyof R]] extends [Kind] ? R : never), diff --git a/test/Boolean.ts b/test/Boolean.ts index 745c44f16..d9d7a3aae 100644 --- a/test/Boolean.ts +++ b/test/Boolean.ts @@ -70,6 +70,13 @@ describe.concurrent("Boolean", () => { deepStrictEqual(match(false), "false") }) + it("Equivalence", () => { + expect(Boolean.Equivalence(true, true)).toBe(true) + expect(Boolean.Equivalence(false, false)).toBe(true) + expect(Boolean.Equivalence(true, false)).toBe(false) + expect(Boolean.Equivalence(false, true)).toBe(false) + }) + it("Order", () => { deepStrictEqual(pipe(false, Boolean.Order.compare(true)), -1) deepStrictEqual(pipe(true, Boolean.Order.compare(false)), 1) diff --git a/test/Number.ts b/test/Number.ts index 2c4488ce4..a68aa5692 100644 --- a/test/Number.ts +++ b/test/Number.ts @@ -29,6 +29,11 @@ describe.concurrent("Number", () => { deepStrictEqual(Number.decrement(2), 1) }) + it("Equivalence", () => { + expect(Number.Equivalence(1, 1)).toBe(true) + expect(Number.Equivalence(1, 2)).toBe(false) + }) + it("Order", () => { deepStrictEqual(pipe(1, Number.Order.compare(2)), -1) deepStrictEqual(pipe(2, Number.Order.compare(1)), 1) diff --git a/test/String.ts b/test/String.ts index 033124f35..31f099c75 100644 --- a/test/String.ts +++ b/test/String.ts @@ -24,6 +24,11 @@ describe.concurrent("String", () => { expect(String.Monoid.combineAll([])).toEqual("") }) + it("Equivalence", () => { + expect(String.Equivalence("a", "a")).toBe(true) + expect(String.Equivalence("a", "b")).toBe(false) + }) + it("Order", () => { const lessThan = Order.lessThan(String.Order) const lessThanOrEqualTo = Order.lessThanOrEqualTo(String.Order) diff --git a/test/typeclass/Equivalence.ts b/test/typeclass/Equivalence.ts new file mode 100644 index 000000000..6411c8247 --- /dev/null +++ b/test/typeclass/Equivalence.ts @@ -0,0 +1,168 @@ +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/typeclass/Equivalence" + +describe("Equivalence", () => { + it("exports", () => { + expect(_.Contravariant).exists + }) + + test("strict returns an Equivalence that uses strict equality (===) to compare values", () => { + const eq = _.strict<{ a: number }>() + const a = { a: 1 } + expect(eq(a, a)).toBe(true) + expect(eq({ a: 1 }, { a: 1 })).toBe(false) + }) + + it("bigint", () => { + const eq = _.bigint + expect(eq(1n, 1n)).toBe(true) + expect(eq(1n, 2n)).toBe(false) + }) + + it("symbol", () => { + const eq = _.symbol + expect(eq(Symbol.for("@fp-ts/core/test/a"), Symbol.for("@fp-ts/core/test/a"))).toBe(true) + expect(eq(Symbol.for("@fp-ts/core/test/a"), Symbol.for("@fp-ts/core/test/b"))).toBe(false) + }) + + it("tuple", () => { + const eqTuple = _.tuple(_.string, _.number, _.boolean) + expect(eqTuple(["a", 1, true], ["a", 1, true])).toEqual(true) + expect(eqTuple(["a", 1, true], ["b", 1, true])).toEqual(false) + expect(eqTuple(["a", 1, true], ["a", 2, true])).toEqual(false) + expect(eqTuple(["a", 1, true], ["a", 1, false])).toEqual(false) + }) + + describe("array", () => { + it("returns true when all the elements of the arrays are equal according to the provided Equivalence", () => { + const eqA = _.string + const eqArray = _.array(eqA) + expect(eqArray(["a", "b"], ["a", "b"])).toBe(true) + }) + + it("returns false when at least one element of the arrays is not equal according to the provided Equivalence", () => { + const eqA = _.string + const eqArray = _.array(eqA) + expect(eqArray(["a", "b"], ["b", "b"])).toBe(false) + expect(eqArray(["a", "b"], ["a", "c"])).toBe(false) + }) + + it("returns false when comparing arrays of different length", () => { + const eqA = _.string + const eqArray = _.array(eqA) + expect(eqArray(["a"], ["a", "b"])).toBe(false) + expect(eqArray(["a", "b"], ["a"])).toBe(false) + }) + }) + + describe("record", () => { + it("returns true when all the values of the records are equal according to the provided Equivalence", () => { + const eqA = _.string + const eqRecord = _.record(eqA) + expect(eqRecord({ a: "a", b: "b" }, { a: "a", b: "b" })).toBe(true) + }) + + it("returns false when at least one value of the records is not equal according to the provided Equivalence", () => { + const eqA = _.string + const eqRecord = _.record(eqA) + expect(eqRecord({ a: "a", b: "b" }, { a: "b", b: "b" })).toBe(false) + expect(eqRecord({ a: "a", b: "b" }, { a: "a", b: "c" })).toBe(false) + }) + + it("returns false when comparing records with a different number of keys", () => { + const eqA = _.string + const eqRecord = _.record(eqA) + expect(eqRecord({ a: "a" }, { a: "a", b: "b" })).toBe(false) + expect(eqRecord({ a: "a", b: "b" }, { a: "a" })).toBe(false) + }) + }) + + it("struct", () => { + const eqStruct = _.struct({ a: _.string, b: _.number, c: _.boolean }) + expect(eqStruct({ a: "a", b: 1, c: true }, { a: "a", b: 1, c: true })).toEqual(true) + expect(eqStruct({ a: "a", b: 1, c: true }, { a: "b", b: 1, c: true })).toEqual(false) + expect(eqStruct({ a: "a", b: 1, c: true }, { a: "a", b: 2, c: true })).toEqual(false) + expect(eqStruct({ a: "a", b: 1, c: true }, { a: "a", b: 1, c: false })).toEqual(false) + }) + + it("contramap", () => { + interface Person { + readonly name: string + readonly age: number + } + const eqPerson = pipe(_.string, _.contramap((p: Person) => p.name)) + expect(eqPerson({ name: "a", age: 1 }, { name: "a", age: 2 })).toEqual(true) + expect(eqPerson({ name: "a", age: 1 }, { name: "a", age: 1 })).toEqual(true) + expect(eqPerson({ name: "a", age: 1 }, { name: "b", age: 1 })).toEqual(false) + expect(eqPerson({ name: "a", age: 1 }, { name: "b", age: 2 })).toEqual(false) + }) + + it("getSemigroup", () => { + type T = readonly [string, number, boolean] + const S = _.getSemigroup() + const E0: _.Equivalence = _.contramap((x: T) => x[0])(_.string) + const E1: _.Equivalence = _.contramap((x: T) => x[1])(_.number) + const eqE0E1 = pipe(E0, S.combine(E1)) + expect(eqE0E1(["a", 1, true], ["a", 1, true])).toEqual(true) + expect(eqE0E1(["a", 1, true], ["a", 1, false])).toEqual(true) + expect(eqE0E1(["a", 1, true], ["b", 1, true])).toEqual(false) + expect(eqE0E1(["a", 1, true], ["a", 2, false])).toEqual(false) + const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) + const eqE0E1E2 = S.combineMany([E1, E2])(E0) + expect(eqE0E1E2(["a", 1, true], ["a", 1, true])).toEqual(true) + expect(eqE0E1E2(["a", 1, true], ["b", 1, true])).toEqual(false) + expect(eqE0E1E2(["a", 1, true], ["a", 2, true])).toEqual(false) + expect(eqE0E1E2(["a", 1, true], ["a", 1, false])).toEqual(false) + }) + + it("getMonoid", () => { + type T = readonly [string, number, boolean] + const M = _.getMonoid() + const E0: _.Equivalence = _.contramap((x: T) => x[0])(_.string) + const E1: _.Equivalence = _.contramap((x: T) => x[1])(_.number) + const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) + const eqE0E1E2 = M.combineAll([E0, E1, E2]) + expect(eqE0E1E2(["a", 1, true], ["a", 1, true])).toEqual(true) + expect(eqE0E1E2(["a", 1, true], ["b", 1, true])).toEqual(false) + expect(eqE0E1E2(["a", 1, true], ["a", 2, true])).toEqual(false) + expect(eqE0E1E2(["a", 1, true], ["a", 1, false])).toEqual(false) + }) + + it("Invariant", () => { + const eq = _.Invariant.imap((s: string) => [s], ([s]) => s)( + _.string + ) + expect(eq(["a"], ["a"])).toEqual(true) + expect(eq(["a"], ["b"])).toEqual(false) + }) + + it("SemiProduct/product", () => { + const eq = pipe( + _.string, + _.SemiProduct.product(_.string) + ) + expect(eq(["a", "b"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["a", "c"])).toEqual(false) + }) + + it("SemiProduct/productMany", () => { + const eq = pipe( + _.string, + _.SemiProduct.productMany([_.string]) + ) + expect(eq(["a"], ["a"])).toEqual(true) + expect(eq(["a"], ["b"])).toEqual(false) + expect(eq(["a"], ["a", "b"])).toEqual(false) + expect(eq(["a", "b"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["a", "b", "d"])).toEqual(true) + expect(eq(["a", "b", "c"], ["a", "b", "d"])).toEqual(true) + }) + + it("SemiProduct/productAll", () => { + const eq = _.Product.productAll([_.Product.of(""), _.string, _.string]) + expect(eq(["a"], ["a"])).toEqual(true) + expect(eq(["a"], ["b"])).toEqual(true) + expect(eq(["a", "c"], ["b", "c"])).toEqual(true) + expect(eq(["a", "c"], ["b", "d"])).toEqual(false) + }) +}) From 2caf47ae21194eec5e825ef0dfee242147a90a7f Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 08:10:47 +0100 Subject: [PATCH 10/80] add Option module --- .changeset/brave-poems-appear.md | 5 + src/Either.ts | 59 ++ src/Option.ts | 1216 ++++++++++++++++++++++++++++++ src/ReadonlyArray.ts | 169 +++++ src/index.ts | 22 + src/internal/Either.ts | 33 + src/internal/Option.ts | 27 + src/typeclass/Compactable.ts | 46 ++ src/typeclass/Filterable.ts | 89 +++ test/Identity.ts | 15 +- test/Option.ts | 563 ++++++++++++++ 11 files changed, 2236 insertions(+), 8 deletions(-) create mode 100644 .changeset/brave-poems-appear.md create mode 100644 src/Either.ts create mode 100644 src/Option.ts create mode 100644 src/internal/Either.ts create mode 100644 src/internal/Option.ts create mode 100644 src/typeclass/Compactable.ts create mode 100644 src/typeclass/Filterable.ts create mode 100644 test/Option.ts diff --git a/.changeset/brave-poems-appear.md b/.changeset/brave-poems-appear.md new file mode 100644 index 000000000..e0dd1b389 --- /dev/null +++ b/.changeset/brave-poems-appear.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Option module diff --git a/src/Either.ts b/src/Either.ts new file mode 100644 index 000000000..ba1185b3a --- /dev/null +++ b/src/Either.ts @@ -0,0 +1,59 @@ +/** + * ```ts + * type Either = Left | Right + * ``` + * + * Represents a value of one of two possible types (a disjoint union). + * + * An instance of `Either` is either an instance of `Left` or `Right`. + * + * A common use of `Either` is as an alternative to `Option` for dealing with possible missing values. In this usage, + * `None` is replaced with a `Left` which can contain useful information. `Right` takes the place of `Some`. Convention + * dictates that `Left` is used for failure and `Right` is used for success. + * + * @since 1.0.0 + */ + +import * as either from "@fp-ts/core/internal/Either" + +/** + * @category models + * @since 1.0.0 + */ +export type Left = { + readonly _tag: "Left" + readonly left: E +} + +/** + * @category models + * @since 1.0.0 + */ +export type Right = { + readonly _tag: "Right" + readonly right: A +} + +/** + * @category models + * @since 1.0.0 + */ +export type Either = Left | Right + +/** + * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias + * of this structure. + * + * @category constructors + * @since 1.0.0 + */ +export const right: (a: A) => Either = either.right + +/** + * Constructs a new `Either` holding a `Left` value. This usually represents a failure, due to the right-bias of this + * structure. + * + * @category constructors + * @since 1.0.0 + */ +export const left: (e: E) => Either = either.left diff --git a/src/Option.ts b/src/Option.ts new file mode 100644 index 000000000..13736839a --- /dev/null +++ b/src/Option.ts @@ -0,0 +1,1216 @@ +/** + * ```ts + * type Option = None | Some + * ``` + * + * `Option` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is + * an instance of `Some`, containing the present value of type `A`. If the value is absent, the `Option` is an + * instance of `None`. + * + * An option could be looked at as a collection or foldable structure with either one or zero elements. + * Another way to look at `Option` is: it represents the effect of a possibly failing computation. + * + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, pipe } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as either from "@fp-ts/core/internal/Either" +import * as iterable from "@fp-ts/core/internal/Iterable" +import * as option from "@fp-ts/core/internal/Option" +import type { Predicate, Refinement } from "@fp-ts/core/Predicate" +import type * as alternative from "@fp-ts/core/typeclass/Alternative" +import * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as chainable from "@fp-ts/core/typeclass/Chainable" +import * as compactable from "@fp-ts/core/typeclass/Compactable" +import type * as coproduct_ from "@fp-ts/core/typeclass/Coproduct" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" +import * as filterable from "@fp-ts/core/typeclass/Filterable" +import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" +import * as foldable from "@fp-ts/core/typeclass/Foldable" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as monad from "@fp-ts/core/typeclass/Monad" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import type { Order } from "@fp-ts/core/typeclass/Order" +import * as order from "@fp-ts/core/typeclass/Order" +import type * as pointed from "@fp-ts/core/typeclass/Pointed" +import * as product_ from "@fp-ts/core/typeclass/Product" +import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" +import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as traversable from "@fp-ts/core/typeclass/Traversable" + +/** + * @category models + * @since 1.0.0 + */ +export type None = { + readonly _tag: "None" +} + +/** + * @category models + * @since 1.0.0 + */ +export type Some = { + readonly _tag: "Some" + readonly value: A +} + +/** + * @category models + * @since 1.0.0 + */ +export type Option = None | Some + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface OptionTypeLambda extends TypeLambda { + readonly type: Option +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const none = (): Option => option.none + +/** + * @category constructors + * @since 1.0.0 + */ +export const some: (a: A) => Option = option.some + +/** + * Returns `true` if the specified value is an instance of `Option`, `false` + * otherwise. + * + * @example + * import { some, none, isOption } from '@fp-ts/core/Option' + * + * assert.strictEqual(isOption(some(1)), true) + * assert.strictEqual(isOption(none), true) + * assert.strictEqual(isOption({}), false) + * + * @category guards + * @since 1.0.0 + */ +export const isOption: (u: unknown) => u is Option = option.isOption + +/** + * Constructs a new `Option` from a nullable type. If the value is `null` or `undefined`, returns `None`, otherwise + * returns the value wrapped in a `Some`. + * + * @example + * import { none, some, fromNullable } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(fromNullable(undefined), none) + * assert.deepStrictEqual(fromNullable(null), none) + * assert.deepStrictEqual(fromNullable(1), some(1)) + * + * @category conversions + * @since 1.0.0 + */ +export const fromNullable: (a: A) => Option> = option.fromNullable + +/** + * Returns a `Refinement` from a `Option` returning function. + * This function ensures that a `Refinement` definition is type-safe. + * + * @category conversions + * @since 1.0.0 + */ +export const toRefinement = (f: (a: A) => Option): Refinement => + (a: A): a is B => isSome(f(a)) + +/** + * Converts an exception into an `Option`. If `f` throws, returns `None`, otherwise returns the output wrapped in a + * `Some`. + * + * @example + * import { none, some, fromThrowable } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual( + * fromThrowable(() => { + * throw new Error() + * }), + * none + * ) + * assert.deepStrictEqual(fromThrowable(() => 1), some(1)) + * + * @category interop + * @since 1.0.0 + */ +export const fromThrowable = (f: () => A): Option => { + try { + return some(f()) + } catch (e) { + return option.none + } +} + +/** + * Lifts a function that may throw to one returning a `Option`. + * + * @category interop + * @since 1.0.0 + */ +export const liftThrowable = , B>( + f: (...a: A) => B +): ((...a: A) => Option) => (...a) => fromThrowable(() => f(...a)) + +/** + * @category interop + * @since 1.0.0 + */ +export const getOrThrow = (onError: LazyArg) => + (self: Option): A => { + if (isSome(self)) { + return self.value + } + throw onError() + } + +/** + * Returns an effect whose success is mapped by the specified `f` function. + * + * @category mapping + * @since 1.0.0 + */ +export const map = (f: (a: A) => B) => + (self: Option): Option => isNone(self) ? option.none : some(f(self.value)) + +/** + * @category mapping + * @since 1.0.0 + */ +export const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @since 1.0.0 + */ +export const tupled: (self: Option) => Option = invariant.tupled(Invariant) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: Option) => Option<{ readonly [K in N]: A }> = invariant.bindTo(Invariant) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + ...Invariant, + map +} + +const let_: ( + name: Exclude, + f: (a: A) => B +) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = + covariant.let(Covariant) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: (a: A) => (fab: Option<(a: A) => B>) => Option = covariant.flap( + Covariant +) + +/** + * Maps the success value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: (b: B) => <_>(self: Option<_>) => Option = covariant.as(Covariant) + +/** + * Returns the effect resulting from mapping the success of this effect to unit. + * + * @category mapping + * @since 1.0.0 + */ +export const asUnit: <_>(self: Option<_>) => Option = covariant.asUnit(Covariant) + +/** + * @category constructors + * @since 1.0.0 + */ +export const of: (a: A) => Option = some + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of: some +} + +/** + * @since 1.0.0 + */ +export const unit: Option = of_.unit(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Option<{}> = of_.Do(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + ...Of, + ...Covariant +} + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMap = (f: (a: A) => Option) => + (self: Option): Option => isNone(self) ? option.none : f(self.value) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @since 1.0.0 + */ +export const flatten: (self: Option>) => Option = flatMap_ + .flatten(FlatMap) + +/** + * @since 1.0.0 + */ +export const andThen: (that: Option) => <_>(self: Option<_>) => Option = flatMap_ + .andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: ( + bfc: (b: B) => Option +) => (afb: (a: A) => Option) => (a: A) => Option = flatMap_ + .composeKleisliArrow(FlatMap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + ...FlatMap, + ...Covariant +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: ( + name: Exclude, + f: (a: A) => Option +) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = + chainable.bind(Chainable) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @since 1.0.0 + */ +export const tap: (f: (a: A) => Option<_>) => (self: Option) => Option = chainable.tap( + Chainable +) + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectSome = ( + onSome: (a: A) => void +) => + (self: Option): Option => { + if (isSome(self)) { + onSome(self.value) + } + return self + } + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectNone = ( + onNone: () => void +) => + (self: Option): Option => { + if (isNone(self)) { + onNone() + } + return self + } + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category sequencing + * @since 1.0.0 + */ +export const andThenDiscard: <_>(that: Option<_>) => (self: Option) => Option = chainable + .andThenDiscard(Chainable) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + ...Pointed, + ...FlatMap +} + +/** + * @since 1.0.0 + */ +export const product = ( + that: Option +) => + (self: Option): Option => + isSome(self) && isSome(that) ? some([self.value, that.value]) : option.none + +/** + * @since 1.0.0 + */ +export const productMany = (collection: Iterable>) => + (self: Option): Option]> => { + if (isNone(self)) { + return option.none + } + const out: [A, ...Array] = [self.value] + for (const o of collection) { + if (isNone(o)) { + return option.none + } + out.push(o.value) + } + return some(out) + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + ...Invariant, + product, + productMany +} + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + fb: Option +) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = + semiProduct.andThenBind(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productFlatten: ( + fb: Option +) => >(self: Option) => Option = semiProduct + .productFlatten(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productAll = (collection: Iterable>): Option> => { + const out: Array = [] + for (const o of collection) { + if (isNone(o)) { + return option.none + } + out.push(o.value) + } + return some(out) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...Of, + ...SemiProduct, + productAll +} + +/** + * @since 1.0.0 + */ +export const tuple: >>( + ...tuple: T +) => Option] ? A : never }>> = product_ + .tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + r: R +) => Option<{ readonly [K in keyof R]: [R[K]] extends [Option] ? A : never }> = product_ + .struct(Product) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + ...SemiProduct, + ...Covariant +} + +/** + * Monoid returning the left-most non-`None` value. If both operands are `Some`s then the inner values are + * combined using the provided `Semigroup` + * + * | x | y | combine(y)(x) | + * | ------- | ------- | ------------------- | + * | none | none | none | + * | some(a) | none | some(a) | + * | none | some(a) | some(a) | + * | some(a) | some(b) | some(combine(b)(a)) | + * + * @example + * import { getMonoid, some, none } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/Number' + * import { pipe } from '@fp-ts/core/Function' + * + * const M = getMonoid(N.SemigroupSum) + * assert.deepStrictEqual(pipe(none, M.combine(none)), none) + * assert.deepStrictEqual(pipe(some(1), M.combine(none)), some(1)) + * assert.deepStrictEqual(pipe(none, M.combine(some(1))), some(1)) + * assert.deepStrictEqual(pipe(some(1), M.combine(some(2))), some(3)) + * + * @category lifting + * @since 1.0.0 + */ +export const getMonoid = ( + Semigroup: Semigroup +): Monoid> => { + const combine = (that: Option) => + (self: Option): Option => + isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(that.value)(self.value)) + return ({ + combine, + combineMany: (others) => + (start) => { + let c = start + for (const o of others) { + c = combine(o)(c) + } + return c + }, + combineAll: (collection: Iterable>): Option => { + let c: Option = option.none + for (const o of collection) { + c = combine(o)(c) + } + return c + }, + empty: option.none + }) +} + +/** + * Lifts a binary function into `Option`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: (f: (a: A, b: B) => C) => (fa: Option, fb: Option) => Option = + semiApplicative.lift2(SemiApplicative) + +/** + * Lifts a ternary function into `Option`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: Option, fb: Option, fc: Option) => Option = semiApplicative.lift3( + SemiApplicative +) + +/** + * @since 1.0.0 + */ +export const ap: ( + fa: Option +) => (self: Option<(a: A) => B>) => Option = semiApplicative.ap( + SemiApplicative +) + +/** + * Semigroup returning the left-most `None` value. If both operands are `Right`s then the inner values + * are concatenated using the provided `Semigroup`. + * + * @category combining + * @since 1.0.0 + */ +export const getFirstNoneSemigroup: (S: Semigroup) => Semigroup> = semiApplicative + .liftSemigroup(SemiApplicative) + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} + +/** + * Monoid returning the left-most `None` value. If both operands are `Right`s then the inner values + * are concatenated using the provided `Monoid`. + * + * The `empty` value is `some(M.empty)`. + * + * @category combining + * @since 1.0.0 + */ +export const getFirstNoneMonoid: (M: Monoid) => Monoid> = applicative.liftMonoid( + Applicative +) + +/** + * @since 1.0.0 + */ +export const coproduct = (that: Option) => + (self: Option): Option => isSome(self) ? self : that + +/** + * @category error handling + * @since 1.0.0 + */ +export const firstSomeOf = (collection: Iterable>) => + (self: Option): Option => { + let out = self + if (isSome(out)) { + return out + } + for (out of collection) { + if (isSome(out)) { + return out + } + } + return out + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiCoproduct: semiCoproduct.SemiCoproduct = { + ...Invariant, + coproduct, + coproductMany: firstSomeOf +} + +/** + * Semigroup returning the left-most `Some` value. + * + * @category combining + * @since 1.0.0 + */ +export const getFirstSomeSemigroup: () => Semigroup> = semiCoproduct + .getSemigroup( + SemiCoproduct + ) + +/** + * @since 1.0.0 + */ +export const coproductEither = (that: Option) => + (self: Option): Option> => + isNone(self) ? pipe(that, map(either.right)) : pipe(self, map(either.left)) + +/** + * @since 1.0.0 + */ +export const coproductAll = (collection: Iterable>): Option => { + const options = iterable.fromIterable(collection) + return options.length > 0 ? + SemiCoproduct.coproductMany(options.slice(1))(options[0]) : + option.none +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Coproduct: coproduct_.Coproduct = { + ...SemiCoproduct, + zero: none, + coproductAll +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + ...Covariant, + ...SemiCoproduct +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Alternative: alternative.Alternative = { + ...SemiAlternative, + ...Coproduct +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: (b, f) => (self) => isNone(self) ? b : f(b, self.value) +} + +/** + * @since 1.0.0 + */ +export const toArray: (self: Option) => Array = foldable.toArray(Foldable) + +/** + * Alias of `flatten`. + * + * @category filtering + * @since 1.0.0 + */ +export const compact: (self: Option>) => Option = flatten + +/** + * @category instances + * @since 1.0.0 + */ +export const Compactable: compactable.Compactable = { + compact +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const separate: (self: Option>) => readonly [Option, Option] = + compactable.separate({ ...Covariant, ...Compactable }) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMap = (f: (a: A) => Option) => + (self: Option): Option => isNone(self) ? option.none : f(self.value) + +/** + * @category instances + * @since 1.0.0 + */ +export const Filterable: filterable.Filterable = { + filterMap +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + (refinement: Refinement): (fc: Option) => Option + (predicate: Predicate): (fb: Option) => Option +} = filterable.filter(Filterable) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +) => + ( + f: (a: A) => Kind + ) => + (self: Option): Kind> => + isNone(self) ? F.of>(option.none) : pipe(f(self.value), F.map(some)) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => (fas: Option>) => Kind> = traversable + .sequence(traverse) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse, + sequence +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind +) => (self: Option) => Kind> = traversable + .traverseTap(Traversable) + +/** + * Returns `true` if the option is `None`, `false` otherwise. + * + * @example + * import { some, none, isNone } from '@fp-ts/core/Option' + * + * assert.strictEqual(isNone(some(1)), false) + * assert.strictEqual(isNone(none), true) + * + * @category guards + * @since 1.0.0 + */ +export const isNone: (self: Option) => self is None = option.isNone + +/** + * Returns `true` if the option is an instance of `Some`, `false` otherwise. + * + * @example + * import { some, none, isSome } from '@fp-ts/core/Option' + * + * assert.strictEqual(isSome(some(1)), true) + * assert.strictEqual(isSome(none), false) + * + * @category guards + * @since 1.0.0 + */ +export const isSome: (self: Option) => self is Some = option.isSome + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable = (collection: Iterable): Option => { + for (const a of collection) { + return some(a) + } + return option.none +} + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * @example + * import * as O from '@fp-ts/core/Option' + * import * as E from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(O.fromEither(E.right(1)), O.some(1)) + * assert.deepStrictEqual(O.fromEither(E.left('a')), O.none) + * + * @category conversions + * @since 1.0.0 + */ +export const fromEither: (self: Either) => Option = either.getRight + +/** + * @category conversions + * @since 1.0.0 + */ +export const toEither: (onNone: LazyArg) => (self: Option) => Either = + either.fromOption + +/** + * Takes a (lazy) default value, a function, and an `Option` value, if the `Option` value is `None` the default value is + * returned, otherwise the function is applied to the value inside the `Some` and the result is returned. + * + * @example + * import { some, none, match } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.strictEqual( + * pipe( + * some(1), + * match(() => 'a none', a => `a some containing ${a}`) + * ), + * 'a some containing 1' + * ) + * + * assert.strictEqual( + * pipe( + * none, + * match(() => 'a none', a => `a some containing ${a}`) + * ), + * 'a none' + * ) + * + * @category pattern matching + * @since 1.0.0 + */ +export const match = (onNone: LazyArg, onSome: (a: A) => C) => + (self: Option): B | C => isNone(self) ? onNone() : onSome(self.value) + +/** + * Extracts the value out of the structure, if it exists. Otherwise returns the given default value + * + * @example + * import { some, none, getOrElse } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.strictEqual(pipe(some(1), getOrElse(() => 0)), 1) + * assert.strictEqual(pipe(none, getOrElse(() => 0)), 0) + * + * @category error handling + * @since 1.0.0 + */ +export const getOrElse = (onNone: LazyArg) => + (self: Option): A | B => isNone(self) ? onNone() : self.value + +/** + * Returns a *smart constructor* from a function that returns a nullable value. + * + * @example + * import { liftNullable, none, some } from '@fp-ts/core/Option' + * + * const f = (s: string): number | undefined => { + * const n = parseFloat(s) + * return isNaN(n) ? undefined : n + * } + * + * const g = liftNullable(f) + * + * assert.deepStrictEqual(g('1'), some(1)) + * assert.deepStrictEqual(g('a'), none) + * + * @category lifting + * @since 1.0.0 + */ +export const liftNullable = , B>( + f: (...a: A) => B | null | undefined +): ((...a: A) => Option>) => (...a) => fromNullable(f(...a)) + +/** + * This is `flatMap` + `fromNullable`, useful when working with optional values. + * + * @example + * import { some, none, fromNullable, flatMapNullable } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * interface Employee { + * company?: { + * address?: { + * street?: { + * name?: string + * } + * } + * } + * } + * + * const employee1: Employee = { company: { address: { street: { name: 'high street' } } } } + * + * assert.deepStrictEqual( + * pipe( + * fromNullable(employee1.company), + * flatMapNullable(company => company.address), + * flatMapNullable(address => address.street), + * flatMapNullable(street => street.name) + * ), + * some('high street') + * ) + * + * const employee2: Employee = { company: { address: { street: {} } } } + * + * assert.deepStrictEqual( + * pipe( + * fromNullable(employee2.company), + * flatMapNullable(company => company.address), + * flatMapNullable(address => address.street), + * flatMapNullable(street => street.name) + * ), + * none + * ) + * + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNullable = (f: (a: A) => B | null | undefined) => + (self: Option): Option> => + isNone(self) ? option.none : fromNullable(f(self.value)) + +/** + * Extracts the value out of the structure, if it exists. Otherwise returns `null`. + * + * @example + * import { some, none, getOrNull } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.strictEqual(pipe(some(1), getOrNull), 1) + * assert.strictEqual(pipe(none, getOrNull), null) + * + * @category conversions + * @since 1.0.0 + */ +export const getOrNull: (self: Option) => A | null = getOrElse(constNull) + +/** + * Extracts the value out of the structure, if it exists. Otherwise returns `undefined`. + * + * @example + * import { some, none, getOrUndefined } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.strictEqual(pipe(some(1), getOrUndefined), 1) + * assert.strictEqual(pipe(none, getOrUndefined), undefined) + * + * @category conversions + * @since 1.0.0 + */ +export const getOrUndefined: (self: Option) => A | undefined = getOrElse(constUndefined) + +/** + * Lazy version of `orElse`. + * + * @category error handling + * @since 1.0.0 + */ +export const catchAll = (that: LazyArg>) => + (self: Option): Option => isNone(self) ? that() : self + +/** + * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to + * types of kind `* -> *`. + * + * In case of `Option` returns the left-most non-`None` value. + * + * | x | y | pipe(x, orElse(y) | + * | ------- | ------- | ------------------| + * | none | none | none | + * | some(a) | none | some(a) | + * | none | some(b) | some(b) | + * | some(a) | some(b) | some(a) | + * + * @example + * import * as O from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual( + * pipe( + * O.none, + * O.orElse(O.none) + * ), + * O.none + * ) + * assert.deepStrictEqual( + * pipe( + * O.some('a'), + * O.orElse(O.none) + * ), + * O.some('a') + * ) + * assert.deepStrictEqual( + * pipe( + * O.none, + * O.orElse(O.some('b')) + * ), + * O.some('b') + * ) + * assert.deepStrictEqual( + * pipe( + * O.some('a'), + * O.orElse(O.some('b')) + * ), + * O.some('a') + * ) + * + * @category error handling + * @since 1.0.0 + */ +export const orElse = (that: Option): ((self: Option) => Option) => + catchAll(() => that) + +/** + * Returns an effect that will produce the value of this effect, unless it + * fails, in which case, it will produce the value of the specified effect. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseEither = ( + that: Option +) => + (self: Option): Option> => + isNone(self) ? + pipe(that, map(either.right)) : + pipe, Option>>(self, map(either.left)) + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * succeeds with the specified value. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseSucceed = ( + onNone: () => B +): (self: Option) => Option => catchAll(() => some(onNone())) + +/** + * The `Order` instance allows `Option` values to be compared with + * `compare`, whenever there is an `Order` instance for + * the type the `Option` contains. + * + * `None` is considered to be less than any `Some` value. + * + * @example + * import { none, some, liftOrder } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/Number' + * import { pipe } from '@fp-ts/core/Function' + * + * const O = liftOrder(N.Order) + * assert.strictEqual(pipe(none, O.compare(none)), 0) + * assert.strictEqual(pipe(none, O.compare(some(1))), -1) + * assert.strictEqual(pipe(some(1), O.compare(none)), 1) + * assert.strictEqual(pipe(some(1), O.compare(some(2))), -1) + * assert.strictEqual(pipe(some(1), O.compare(some(1))), 0) + * + * @category sorting + * @since 1.0.0 + */ +export const liftOrder = (O: Order): Order> => + order.fromCompare((that) => + (self) => isSome(self) ? (isSome(that) ? O.compare(that.value)(self.value) : 1) : -1 + ) + +/** + * Returns a *smart constructor* based on the given predicate. + * + * @example + * import * as O from '@fp-ts/core/Option' + * + * const getOption = O.liftPredicate((n: number) => n >= 0) + * + * assert.deepStrictEqual(getOption(-1), O.none) + * assert.deepStrictEqual(getOption(1), O.some(1)) + * + * @category lifting + * @since 1.0.0 + */ +export const liftPredicate: { + (refinement: Refinement): (c: C) => Option + (predicate: Predicate): (b: B) => Option +} = (predicate: Predicate) => (b: B) => predicate(b) ? some(b) : option.none + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftEither = , E, B>( + f: (...a: A) => Either +) => (...a: A): Option => fromEither(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapEither = (f: (a: A) => Either) => + (self: Option): Option => pipe(self, flatMap(liftEither(f))) + +/** + * Returns a function that checks if an `Option` contains a given value using a provided `equivalence` function. + * + * @since 1.0.0 + */ +export const contains = (equivalence: Equivalence) => + (a: A) => (self: Option): boolean => isNone(self) ? false : equivalence(self.value, a) + +/** + * Returns `true` if the predicate is satisfied by the wrapped value + * + * @example + * import { some, none, exists } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.strictEqual( + * pipe( + * some(1), + * exists(n => n > 0) + * ), + * true + * ) + * assert.strictEqual( + * pipe( + * some(1), + * exists(n => n > 1) + * ), + * false + * ) + * assert.strictEqual( + * pipe( + * none, + * exists(n => n > 0) + * ), + * false + * ) + * + * @since 1.0.0 + */ +export const exists = (predicate: Predicate) => + (self: Option): boolean => isNone(self) ? false : predicate(self.value) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 53c97154f..367a320a5 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2,13 +2,182 @@ * @since 1.0.0 */ +import type { TypeLambda } from "@fp-ts/core/HKT" +import type * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as of_ from "@fp-ts/core/typeclass/Of" +import type * as product_ from "@fp-ts/core/typeclass/Product" +import type * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface ReadonlyArrayTypeLambda extends TypeLambda { + readonly type: ReadonlyArray +} + /** * @category models * @since 1.0.0 */ export type NonEmptyReadonlyArray = readonly [A, ...Array] +/** + * @category models + * @since 1.0.0 + */ +export type NonEmptyArray = [A, ...Array] + /** * @since 1.0.0 */ export const isNonEmpty = (as: ReadonlyArray): as is NonEmptyReadonlyArray => as.length > 0 + +/** + * @category constructors + * @since 1.0.0 + */ +export const empty: () => Array = () => [] + +/** + * Test whether a `ReadonlyArray` is empty narrowing down the type to `[]`. + * + * @category predicates + * @since 1.0.0 + */ +export const isEmpty = (self: ReadonlyArray): self is readonly [] => self.length === 0 + +/** + * @since 1.0.0 + */ +export const product = ( + that: ReadonlyArray +) => + (self: ReadonlyArray): Array<[A, B]> => { + if (isEmpty(self) || isEmpty(that)) { + return empty() + } + const out: Array<[A, B]> = [] + for (let i = 0; i < self.length; i++) { + for (let j = 0; j < that.length; j++) { + out.push([self[i], that[j]]) + } + } + return out + } + +/** + * @category mapping + * @since 1.0.0 + */ +export const map = (f: (a: A) => B): (self: ReadonlyArray) => Array => + mapWithIndex((a) => f(a)) + +/** + * @category mapping + * @since 1.0.0 + */ +export const mapWithIndex = ( + f: (a: A, i: number) => B +) => (self: ReadonlyArray): Array => self.map((a, i) => f(a, i)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = covariant.make(map) + +/** + * @since 1.0.0 + */ +export const productMany: ( + collection: Iterable> +) => (self: ReadonlyArray) => ReadonlyArray> = semiProduct + .productMany( + Covariant, + product + ) + +/** + * @since 1.0.0 + */ +export const productAll = ( + collection: Iterable> +): ReadonlyArray> => { + const arrays = Array.from(collection) + if (isEmpty(arrays)) { + return empty() + } + return productMany(arrays.slice(1))(arrays[0]) +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: ReadonlyArray) => ReadonlyArray = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + ...Invariant, + product, + productMany +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + ...SemiProduct, + ...Covariant +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const of = (a: A): NonEmptyArray => [a] + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...Of, + ...SemiProduct, + productAll +} +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} diff --git a/src/index.ts b/src/index.ts index 7830f9c29..18842e414 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,9 +13,11 @@ import * as hkt from "@fp-ts/core/HKT" // ------------------------------------------------------------------------------------- import * as boolean from "@fp-ts/core/Boolean" +import * as either from "@fp-ts/core/Either" import * as _function from "@fp-ts/core/Function" import * as identity from "@fp-ts/core/Identity" import * as number from "@fp-ts/core/Number" +import * as option from "@fp-ts/core/Option" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" import * as readonlyArray from "@fp-ts/core/ReadonlyArray" @@ -25,10 +27,12 @@ import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" import * as bounded from "@fp-ts/core/typeclass/Bounded" import * as chainable from "@fp-ts/core/typeclass/Chainable" +import * as compactable from "@fp-ts/core/typeclass/Compactable" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as coproduct from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" +import * as filterable from "@fp-ts/core/typeclass/Filterable" import * as flatMap from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -80,6 +84,11 @@ export { * @since 1.0.0 */ chainable, + /** + * @category typeclass + * @since 1.0.0 + */ + compactable, /** * @category typeclass * @since 1.0.0 @@ -95,11 +104,20 @@ export { * @since 1.0.0 */ covariant, + /** + * @since 1.0.0 + */ + either, /** * @category typeclass * @since 1.0.0 */ equivalence, + /** + * @category typeclass + * @since 1.0.0 + */ + filterable, /** * @category typeclass * @since 1.0.0 @@ -147,6 +165,10 @@ export { * @since 1.0.0 */ of, + /** + * @since 1.0.0 + */ + option, /** * @category typeclass * @since 1.0.0 diff --git a/src/internal/Either.ts b/src/internal/Either.ts new file mode 100644 index 000000000..215be4841 --- /dev/null +++ b/src/internal/Either.ts @@ -0,0 +1,33 @@ +/** + * @since 1.0.0 + */ + +import type { Either, Left, Right } from "@fp-ts/core/Either" +import * as option from "@fp-ts/core/internal/Option" +import type { Option } from "@fp-ts/core/Option" + +/** @internal */ +export const isLeft = (ma: Either): ma is Left => ma._tag === "Left" + +/** @internal */ +export const isRight = (ma: Either): ma is Right => ma._tag === "Right" + +/** @internal */ +export const left = (e: E): Either => ({ _tag: "Left", left: e }) + +/** @internal */ +export const right = (a: A): Either => ({ _tag: "Right", right: a }) + +/** @internal */ +export const getLeft = ( + self: Either +): Option => (isRight(self) ? option.none : option.some(self.left)) + +/** @internal */ +export const getRight = ( + self: Either +): Option => (isLeft(self) ? option.none : option.some(self.right)) + +/** @internal */ +export const fromOption = (onNone: () => E) => + (fa: Option): Either => option.isNone(fa) ? left(onNone()) : right(fa.value) diff --git a/src/internal/Option.ts b/src/internal/Option.ts new file mode 100644 index 000000000..c3a101ea6 --- /dev/null +++ b/src/internal/Option.ts @@ -0,0 +1,27 @@ +/** + * @since 1.0.0 + */ + +import type { None, Option, Some } from "@fp-ts/core/Option" + +/** @internal */ +export const isOption = (u: unknown): u is Option => + typeof u === "object" && u != null && "_tag" in u && + (u["_tag"] === "None" || u["_tag"] === "Some") + +/** @internal */ +export const isNone = (fa: Option): fa is None => fa._tag === "None" + +/** @internal */ +export const isSome = (fa: Option): fa is Some => fa._tag === "Some" + +/** @internal */ +export const none: Option = { _tag: "None" } + +/** @internal */ +export const some = (a: A): Option => ({ _tag: "Some", value: a }) + +/** @internal */ +export const fromNullable = ( + a: A +): Option> => (a == null ? none : some(a as NonNullable)) diff --git a/src/typeclass/Compactable.ts b/src/typeclass/Compactable.ts new file mode 100644 index 000000000..ef0c79de7 --- /dev/null +++ b/src/typeclass/Compactable.ts @@ -0,0 +1,46 @@ +/** + * `Compactable` represents data structures which can be _compacted_/_separated_. + * + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" +import * as either from "@fp-ts/core/internal/Either" +import type { Option } from "@fp-ts/core/Option" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" + +/** + * @category models + * @since 1.0.0 + */ +export interface Compactable extends TypeClass { + readonly compact: (self: Kind>) => Kind +} + +/** + * Returns a default `compact` composition. + * + * @since 1.0.0 + */ +export const compactComposition = ( + F: Covariant, + G: Compactable +): (( + self: Kind>> +) => Kind>) => F.map(G.compact) + +/** + * @since 1.0.0 + */ +export const separate = ( + F: Covariant & Compactable +) => + ( + self: Kind> + ): readonly [Kind, Kind] => { + return [ + pipe(self, F.map(either.getLeft), F.compact), + pipe(self, F.map(either.getRight), F.compact) + ] + } diff --git a/src/typeclass/Filterable.ts b/src/typeclass/Filterable.ts new file mode 100644 index 000000000..03c5befeb --- /dev/null +++ b/src/typeclass/Filterable.ts @@ -0,0 +1,89 @@ +/** + * `Filterable` represents data structures which can be _partitioned_/_filtered_. + * + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" +import * as either from "@fp-ts/core/internal/Either" +import * as option from "@fp-ts/core/internal/Option" +import type { Option } from "@fp-ts/core/Option" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" + +/** + * @category models + * @since 1.0.0 + */ +export interface Filterable extends TypeClass { + readonly filterMap: ( + f: (a: A) => Option + ) => (self: Kind) => Kind +} + +/** + * Returns a default `filterMap` composition. + * + * @since 1.0.0 + */ +export const filterMapComposition = ( + F: Covariant, + G: Filterable +) => + ( + f: (a: A) => Option + ): ( + self: Kind> + ) => Kind> => F.map(G.filterMap(f)) + +/** + * @since 1.0.0 + */ +export const filter: ( + F: Filterable +) => { + (refinement: (a: A) => a is B): ( + self: Kind + ) => Kind + ( + predicate: (a: A) => boolean + ): (self: Kind) => Kind +} = (Filterable: Filterable) => + ( + predicate: (a: A) => boolean + ): ((self: Kind) => Kind) => + Filterable.filterMap((b) => (predicate(b) ? option.some(b) : option.none)) + +/** + * @since 1.0.0 + */ +export const partitionMap = (F: Filterable) => + (f: (a: A) => Either) => + ( + self: Kind + ): readonly [Kind, Kind] => { + return [ + pipe(self, F.filterMap((a) => either.getLeft(f(a)))), + pipe(self, F.filterMap((a) => either.getRight(f(a)))) + ] + } + +/** + * @since 1.0.0 + */ +export const partition: ( + F: Filterable +) => { + (refinement: (a: A) => a is B): ( + self: Kind + ) => readonly [Kind, Kind] + (predicate: (a: A) => boolean): ( + self: Kind + ) => readonly [Kind, Kind] +} = (Filterable: Filterable) => + ( + predicate: (a: A) => boolean + ): (( + self: Kind + ) => readonly [Kind, Kind]) => + partitionMap(Filterable)((b) => (predicate(b) ? either.right(b) : either.left(b))) diff --git a/test/Identity.ts b/test/Identity.ts index 1de2992f9..f840be294 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -1,6 +1,6 @@ import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/Identity" -// import * as O from "@fp-ts/core/Option" +import * as O from "@fp-ts/core/Option" import * as String from "@fp-ts/core/String" import * as U from "./util" @@ -125,12 +125,11 @@ describe.concurrent("Identity", () => { U.deepStrictEqual(pipe("a", _.reduceRight("", f)), "a") }) - // TODO - // it("traverse", () => { - // U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(O.some)), O.some(1)) - // U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(() => O.none)), O.none) + it("traverse", () => { + U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(O.some)), O.some(1)) + U.deepStrictEqual(pipe(1, _.traverse(O.Applicative)(() => O.none())), O.none()) - // U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(O.some)), O.some(1)) - // U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(() => O.none)), O.none) - // }) + U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(O.some)), O.some(1)) + U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(() => O.none())), O.none()) + }) }) diff --git a/test/Option.ts b/test/Option.ts new file mode 100644 index 000000000..f0c9f641a --- /dev/null +++ b/test/Option.ts @@ -0,0 +1,563 @@ +import { equivalence } from "@fp-ts/core" +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/Option" +import * as ReadonlyArray from "@fp-ts/core/ReadonlyArray" +import * as S from "@fp-ts/core/String" +import { deepStrictEqual, double } from "@fp-ts/core/test/util" + +const p = (n: number): boolean => n > 2 + +describe.concurrent("Option", () => { + it("instances and derived exports", () => { + expect(_.Invariant).exist + expect(_.imap).exist + expect(_.tupled).exist + expect(_.bindTo).exist + + expect(_.Covariant).exist + expect(_.map).exist + expect(_.let).exist + expect(_.flap).exist + expect(_.as).exist + expect(_.asUnit).exist + + expect(_.Of).exist + expect(_.of).exist + expect(_.Do).exist + + expect(_.Pointed).exist + + expect(_.FlatMap).exist + expect(_.flatMap).exist + expect(_.flatten).exist + expect(_.andThen).exist + expect(_.composeKleisliArrow).exist + + expect(_.Chainable).exist + expect(_.bind).exist + expect(_.tap).exist + expect(_.andThenDiscard).exist + + expect(_.Monad).exist + + expect(_.SemiProduct).exist + expect(_.product).exist + expect(_.productMany).exist + + expect(_.Product).exist + expect(_.productAll).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.getFirstNoneSemigroup).exist // liftSemigroup + expect(_.lift2).exist + expect(_.lift3).exist + expect(_.ap).exist + expect(_.andThenDiscard).exist + expect(_.andThen).exist + + expect(_.Applicative).exist + expect(_.getFirstNoneMonoid).exist // liftMonoid + + expect(_.SemiCoproduct).exist + expect(_.getFirstSomeSemigroup).exist // getSemigroup + expect(_.coproduct).exist + + expect(_.Coproduct).exist + expect(_.coproductAll).exist + + expect(_.SemiAlternative).exist + + expect(_.Alternative).exist + + expect(_.Foldable).exist + expect(_.toArray).exist + + expect(_.Traversable).exist + expect(_.traverse).exist + expect(_.sequence).exist + expect(_.traverseTap).exist + + expect(_.Compactable).exist + expect(_.compact).exist + expect(_.separate).exist + + expect(_.Filterable).exist + expect(_.filterMap).exist + expect(_.filter).exist + }) + + it("toRefinement", () => { + const f = ( + s: string | number + ): _.Option => (typeof s === "string" ? _.some(s) : _.none()) + const isString = _.toRefinement(f) + deepStrictEqual(isString("s"), true) + deepStrictEqual(isString(1), false) + type A = { readonly type: "A" } + type B = { readonly type: "B" } + type C = A | B + const isA = _.toRefinement((c) => (c.type === "A" ? _.some(c) : _.none())) + deepStrictEqual(isA({ type: "A" }), true) + deepStrictEqual(isA({ type: "B" }), false) + }) + + it("isOption", () => { + deepStrictEqual(pipe(_.some(1), _.isOption), true) + deepStrictEqual(pipe(_.none(), _.isOption), true) + deepStrictEqual(pipe(E.right(1), _.isOption), false) + }) + + it("coproductEither", () => { + deepStrictEqual(pipe(_.none(), _.coproductEither(_.none())), _.none()) + deepStrictEqual(pipe(_.none(), _.coproductEither(_.some("a"))), _.some(E.right("a"))) + deepStrictEqual(pipe(_.some(1), _.coproductEither(_.none())), _.some(E.left(1))) + deepStrictEqual(pipe(_.some(1), _.coproductEither(_.some("a"))), _.some(E.left(1))) + }) + + it("firstSomeOf", () => { + deepStrictEqual(pipe(_.some(1), _.firstSomeOf([])), _.some(1)) + deepStrictEqual(pipe(_.none(), _.firstSomeOf([])), _.none()) + deepStrictEqual( + pipe(_.none(), _.firstSomeOf([_.none(), _.none(), _.none(), _.some(1)])), + _.some(1) + ) + deepStrictEqual( + pipe(_.none(), _.firstSomeOf([_.none(), _.none(), _.none()])), + _.none() + ) + }) + + it("catchAll", () => { + deepStrictEqual(pipe(_.some(1), _.catchAll(() => _.some(2))), _.some(1)) + deepStrictEqual(pipe(_.some(1), _.catchAll(() => _.none())), _.some(1)) + deepStrictEqual(pipe(_.none(), _.catchAll(() => _.some(1))), _.some(1)) + deepStrictEqual(pipe(_.none(), _.catchAll(() => _.none())), _.none()) + }) + + it("orElseEither", () => { + expect(pipe(_.some(1), _.orElseEither(_.some(2)))).toEqual(_.some(E.left(1))) + expect(pipe(_.some(1), _.orElseEither(_.none()))).toEqual(_.some(E.left(1))) + expect(pipe(_.none(), _.orElseEither(_.some(2)))).toEqual(_.some(E.right(2))) + expect(pipe(_.none(), _.orElseEither(_.none()))).toEqual(_.none()) + }) + + it("orElseSucceed", () => { + deepStrictEqual(pipe(_.some(1), _.orElseSucceed(() => 2)), _.some(1)) + deepStrictEqual(pipe(_.none(), _.orElseSucceed(() => 2)), _.some(2)) + }) + + it("inspectSome", () => { + const log: Array = [] + pipe( + _.some(1), + _.inspectSome(() => log.push(1)) + ) + pipe( + _.none(), + _.inspectSome(() => log.push(2)) + ) + deepStrictEqual( + log, + [1] + ) + }) + + it("inspectNone", () => { + const log: Array = [] + pipe( + _.some(1), + _.inspectNone(() => log.push(1)) + ) + pipe( + _.none(), + _.inspectNone(() => log.push(2)) + ) + deepStrictEqual( + log, + [2] + ) + }) + + it("getOrThrow", () => { + expect(pipe(_.some(1), _.getOrThrow(() => new Error("e")))).toEqual(1) + expect(() => pipe(_.none(), _.getOrThrow(() => new Error("e")))).toThrow( + new Error("e") + ) + }) + + it("of", () => { + deepStrictEqual(_.of(1), _.some(1)) + }) + + it("Foldable", () => { + expect(pipe(_.none(), _.Foldable.reduce("a", (s, n: number) => s + String(n)))).toEqual("a") + expect(pipe(_.some(1), _.Foldable.reduce("a", (s, n: number) => s + String(n)))).toEqual( + "a1" + ) + }) + + it("coproductAll", () => { + deepStrictEqual(_.coproductAll([]), _.none()) + deepStrictEqual(_.coproductAll([_.some(1)]), _.some(1)) + deepStrictEqual(_.coproductAll([_.none(), _.some(1)]), _.some(1)) + deepStrictEqual(_.coproductAll([_.some(1), _.some(2)]), _.some(1)) + }) + + it("unit", () => { + deepStrictEqual(_.unit, _.some(undefined)) + }) + + it("product", () => { + deepStrictEqual(pipe(_.none(), _.product(_.none())), _.none()) + deepStrictEqual(pipe(_.some(1), _.product(_.none())), _.none()) + deepStrictEqual(pipe(_.none(), _.product(_.some("a"))), _.none()) + deepStrictEqual( + pipe(_.some(1), _.product(_.some("a"))), + _.some([1, "a"] as const) + ) + }) + + it("productMany", () => { + deepStrictEqual(pipe(_.none(), _.SemiProduct.productMany([])), _.none()) + deepStrictEqual(pipe(_.some(1), _.SemiProduct.productMany([])), _.some([1] as const)) + deepStrictEqual( + pipe(_.some(1), _.SemiProduct.productMany([_.none() as _.Option])), + _.none() + ) + deepStrictEqual( + pipe(_.some(1), _.SemiProduct.productMany([_.some(2)])), + _.some([1, 2] as const) + ) + }) + + it("productAll", () => { + const productAll = _.Applicative.productAll + deepStrictEqual(productAll([]), _.some([])) + deepStrictEqual(productAll([_.none()]), _.none()) + deepStrictEqual(productAll([_.some(1), _.some(2)]), _.some([1, 2])) + deepStrictEqual(productAll([_.some(1), _.none()]), _.none()) + }) + + it("SemiCoproduct", () => { + const coproduct = _.SemiCoproduct.coproduct + deepStrictEqual(pipe(_.none(), coproduct(_.none())), _.none()) + deepStrictEqual(pipe(_.none(), coproduct(_.some(2))), _.some(2)) + deepStrictEqual(pipe(_.some(1), coproduct(_.none())), _.some(1)) + deepStrictEqual(pipe(_.some(1), coproduct(_.some(2))), _.some(1)) + + const coproductMany = _.SemiCoproduct.coproductMany + deepStrictEqual(pipe(_.none(), coproductMany([])), _.none()) + deepStrictEqual(pipe(_.none(), coproductMany([_.none()])), _.none()) + deepStrictEqual(pipe(_.none(), coproductMany([_.some(2)])), _.some(2)) + deepStrictEqual(pipe(_.some(1), coproductMany([])), _.some(1)) + deepStrictEqual(pipe(_.some(1), coproductMany([_.none() as _.Option])), _.some(1)) + deepStrictEqual(pipe(_.some(1), coproductMany([_.some(2)])), _.some(1)) + }) + + it("fromIterable", () => { + deepStrictEqual(_.fromIterable([]), _.none()) + deepStrictEqual(_.fromIterable(["a"]), _.some("a")) + }) + + it("map", () => { + deepStrictEqual(pipe(_.some(2), _.map(double)), _.some(4)) + deepStrictEqual(pipe(_.none(), _.map(double)), _.none()) + }) + + it("flatMap", () => { + const f = (n: number) => _.some(n * 2) + const g = () => _.none() + deepStrictEqual(pipe(_.some(1), _.flatMap(f)), _.some(2)) + deepStrictEqual(pipe(_.none(), _.flatMap(f)), _.none()) + deepStrictEqual(pipe(_.some(1), _.flatMap(g)), _.none()) + deepStrictEqual(pipe(_.none(), _.flatMap(g)), _.none()) + }) + + it("orElse", () => { + const assertAlt = ( + a: _.Option, + b: _.Option, + expected: _.Option + ) => { + deepStrictEqual(pipe(a, _.orElse(b)), expected) + } + assertAlt(_.some(1), _.some(2), _.some(1)) + assertAlt(_.some(1), _.none(), _.some(1)) + assertAlt(_.none(), _.some(2), _.some(2)) + assertAlt(_.none(), _.none(), _.none()) + }) + + it("compact", () => { + deepStrictEqual(_.compact(_.none()), _.none()) + deepStrictEqual(_.compact(_.some(_.none())), _.none()) + deepStrictEqual(_.compact(_.some(_.some("123"))), _.some("123")) + }) + + it("filterMap", () => { + const f = (n: number) => (p(n) ? _.some(n + 1) : _.none()) + deepStrictEqual(pipe(_.none(), _.filterMap(f)), _.none()) + deepStrictEqual(pipe(_.some(1), _.filterMap(f)), _.none()) + deepStrictEqual(pipe(_.some(3), _.filterMap(f)), _.some(4)) + }) + + it("traverse", () => { + deepStrictEqual( + pipe( + _.some("hello"), + _.traverse(ReadonlyArray.Applicative)(() => []) + ), + [] + ) + deepStrictEqual( + pipe( + _.some("hello"), + _.traverse(ReadonlyArray.Applicative)((s) => [s.length]) + ), + [_.some(5)] + ) + deepStrictEqual( + pipe( + _.none(), + _.traverse(ReadonlyArray.Applicative)((s) => [s]) + ), + [_.none()] + ) + }) + + it("toEither", () => { + deepStrictEqual(pipe(_.none(), _.toEither(() => "e")), E.left("e")) + deepStrictEqual(pipe(_.some(1), _.toEither(() => "e")), E.right(1)) + }) + + it("match", () => { + const f = () => "none" + const g = (s: string) => `some${s.length}` + const match = _.match(f, g) + deepStrictEqual(match(_.none()), "none") + deepStrictEqual(match(_.some("abc")), "some3") + }) + + it("getOrElse", () => { + deepStrictEqual(pipe(_.some(1), _.getOrElse(() => 0)), 1) + deepStrictEqual(pipe(_.none(), _.getOrElse(() => 0)), 0) + }) + + it("getOrNull", () => { + deepStrictEqual(_.getOrNull(_.none()), null) + deepStrictEqual(_.getOrNull(_.some(1)), 1) + }) + + it("getOrUndefined", () => { + deepStrictEqual(_.getOrUndefined(_.none()), undefined) + deepStrictEqual(_.getOrUndefined(_.some(1)), 1) + }) + + it("liftOrder", () => { + const OS = _.liftOrder(S.Order) + deepStrictEqual(pipe(_.none(), OS.compare(_.none())), 0) + deepStrictEqual(pipe(_.some("a"), OS.compare(_.none())), 1) + deepStrictEqual(pipe(_.none(), OS.compare(_.some("a"))), -1) + deepStrictEqual(pipe(_.some("a"), OS.compare(_.some("a"))), 0) + deepStrictEqual(pipe(_.some("a"), OS.compare(_.some("b"))), -1) + deepStrictEqual(pipe(_.some("b"), OS.compare(_.some("a"))), 1) + }) + + it("flatMapNullable", () => { + interface X { + readonly a?: { + readonly b?: { + readonly c?: { + readonly d: number + } + } + } + } + const x1: X = { a: {} } + const x2: X = { a: { b: {} } } + const x3: X = { a: { b: { c: { d: 1 } } } } + deepStrictEqual( + pipe( + _.fromNullable(x1.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.none() + ) + deepStrictEqual( + pipe( + _.fromNullable(x2.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.none() + ) + deepStrictEqual( + pipe( + _.fromNullable(x3.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.some(1) + ) + }) + + it("getMonoid", () => { + const M = _.getMonoid(S.Semigroup) + deepStrictEqual(pipe(_.none(), M.combine(_.none())), _.none()) + deepStrictEqual(pipe(_.none(), M.combine(_.some("a"))), _.some("a")) + deepStrictEqual(pipe(_.some("a"), M.combine(_.none())), _.some("a")) + deepStrictEqual(pipe(_.some("b"), M.combine(_.some("a"))), _.some("ba")) + deepStrictEqual(pipe(_.some("a"), M.combine(_.some("b"))), _.some("ab")) + + deepStrictEqual(pipe(_.some("a"), M.combineMany([_.some("b")])), _.some("ab")) + deepStrictEqual(pipe(_.none(), M.combineMany([_.some("b")])), _.some("b")) + deepStrictEqual(pipe(_.some("a"), M.combineMany([_.none()])), _.some("a")) + + deepStrictEqual(pipe(M.combineAll([])), _.none()) + deepStrictEqual(pipe(M.combineAll([_.some("a")])), _.some("a")) + deepStrictEqual(pipe(M.combineAll([_.some("a"), _.some("b")])), _.some("ab")) + deepStrictEqual(pipe(M.combineAll([_.some("a"), _.none()])), _.some("a")) + }) + + it("fromNullable", () => { + deepStrictEqual(_.fromNullable(2), _.some(2)) + deepStrictEqual(_.fromNullable(null), _.none()) + deepStrictEqual(_.fromNullable(undefined), _.none()) + }) + + it("liftPredicate", () => { + const f = _.liftPredicate(p) + deepStrictEqual(f(1), _.none()) + deepStrictEqual(f(3), _.some(3)) + + type Direction = "asc" | "desc" + const parseDirection = _.liftPredicate((s: string): s is Direction => + s === "asc" || s === "desc" + ) + deepStrictEqual(parseDirection("asc"), _.some("asc")) + deepStrictEqual(parseDirection("foo"), _.none()) + }) + + it("contains", () => { + const contains = _.contains(equivalence.number) + deepStrictEqual(pipe(_.none(), contains(2)), false) + deepStrictEqual(pipe(_.some(2), contains(2)), true) + deepStrictEqual(pipe(_.some(2), contains(1)), false) + }) + + it("isNone", () => { + deepStrictEqual(_.isNone(_.none()), true) + deepStrictEqual(_.isNone(_.some(1)), false) + }) + + it("isSome", () => { + deepStrictEqual(_.isSome(_.none()), false) + deepStrictEqual(_.isSome(_.some(1)), true) + }) + + it("exists", () => { + const predicate = (a: number) => a === 2 + deepStrictEqual(pipe(_.none(), _.exists(predicate)), false) + deepStrictEqual(pipe(_.some(1), _.exists(predicate)), false) + deepStrictEqual(pipe(_.some(2), _.exists(predicate)), true) + }) + + it("fromThrowable", () => { + deepStrictEqual( + _.fromThrowable(() => JSON.parse("2")), + _.some(2) + ) + deepStrictEqual( + _.fromThrowable(() => JSON.parse("(")), + _.none() + ) + }) + + it("fromEither", () => { + deepStrictEqual(_.fromEither(E.right(1)), _.some(1)) + deepStrictEqual(_.fromEither(E.left("e")), _.none()) + }) + + it("do notation", () => { + deepStrictEqual( + pipe( + _.some(1), + _.bindTo("a"), + _.bind("b", () => _.some("b")) + ), + _.some({ a: 1, b: "b" }) + ) + }) + + it("andThenBind", () => { + deepStrictEqual( + pipe(_.some(1), _.bindTo("a"), _.andThenBind("b", _.some("b"))), + _.some({ a: 1, b: "b" }) + ) + }) + + it("productFlatten", () => { + deepStrictEqual( + pipe(_.some(1), _.tupled, _.productFlatten(_.some("b"))), + _.some([1, "b"] as const) + ) + }) + + it("liftNullable", () => { + const f = _.liftNullable((n: number) => (n > 0 ? n : null)) + deepStrictEqual(f(1), _.some(1)) + deepStrictEqual(f(-1), _.none()) + }) + + it("liftThrowable", () => { + const f = _.liftThrowable((s: string) => { + const len = s.length + if (len > 0) { + return len + } + throw new Error("empty string") + }) + deepStrictEqual(f("a"), _.some(1)) + deepStrictEqual(f(""), _.none()) + }) + + it("liftEither", () => { + const f = _.liftEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + deepStrictEqual(f(1), _.some(1)) + deepStrictEqual(f(-1), _.none()) + }) + + it("flatMapEither", () => { + const f = _.flatMapEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + deepStrictEqual(pipe(_.none(), f), _.none()) + deepStrictEqual(pipe(_.some(0), f), _.none()) + deepStrictEqual(pipe(_.some(1), f), _.some(1)) + }) + + it("guard", () => { + deepStrictEqual( + pipe( + _.Do, + _.bind("x", () => _.some("a")), + _.bind("y", () => _.some("a")), + _.filter(({ x, y }) => x === y) + ), + _.some({ x: "a", y: "a" }) + ) + deepStrictEqual( + pipe( + _.Do, + _.bind("x", () => _.some("a")), + _.bind("y", () => _.some("b")), + _.filter(({ x, y }) => x === y) + ), + _.none() + ) + }) +}) From 781d23cb4b14838a0825b66a6e34f958257aa87e Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 08:17:24 +0100 Subject: [PATCH 11/80] add Either module --- .changeset/afraid-cars-lay.md | 5 + src/Either.ts | 1087 ++++++++++++++++++++++++++++++++- src/internal/Either.ts | 11 + test/Either.ts | 483 +++++++++++++++ 4 files changed, 1585 insertions(+), 1 deletion(-) create mode 100644 .changeset/afraid-cars-lay.md create mode 100644 test/Either.ts diff --git a/.changeset/afraid-cars-lay.md b/.changeset/afraid-cars-lay.md new file mode 100644 index 000000000..ea1e3c0ad --- /dev/null +++ b/.changeset/afraid-cars-lay.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add Either module diff --git a/src/Either.ts b/src/Either.ts index ba1185b3a..421875f7a 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -13,8 +13,32 @@ * * @since 1.0.0 */ - +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, identity, pipe } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import * as either from "@fp-ts/core/internal/Either" +import * as option from "@fp-ts/core/internal/Option" +import type { Option } from "@fp-ts/core/Option" +import type { Predicate, Refinement } from "@fp-ts/core/Predicate" +import * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" +import * as chainable from "@fp-ts/core/typeclass/Chainable" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" +import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" +import type * as foldable from "@fp-ts/core/typeclass/Foldable" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as monad from "@fp-ts/core/typeclass/Monad" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import type * as pointed from "@fp-ts/core/typeclass/Pointed" +import * as product_ from "@fp-ts/core/typeclass/Product" +import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" +import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as traversable from "@fp-ts/core/typeclass/Traversable" /** * @category models @@ -40,6 +64,14 @@ export type Right = { */ export type Either = Left | Right +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface EitherTypeLambda extends TypeLambda { + readonly type: Either +} + /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure. @@ -57,3 +89,1056 @@ export const right: (a: A) => Either = either.right * @since 1.0.0 */ export const left: (e: E) => Either = either.left + +/** + * Alias of `right`. + * + * @category constructors + * @since 1.0.0 + */ +export const of: (a: A) => Either = right + +/** + * Returns `true` if the specified value is an instance of `Either`, `false` + * otherwise. + * + * @category guards + * @since 1.0.0 + */ +export const isEither: (u: unknown) => u is Either = either.isEither + +/** + * Returns `true` if the either is an instance of `Left`, `false` otherwise. + * + * @category guards + * @since 1.0.0 + */ +export const isLeft: (self: Either) => self is Left = either.isLeft + +/** + * Returns `true` if the either is an instance of `Right`, `false` otherwise. + * + * @category guards + * @since 1.0.0 + */ +export const isRight: (self: Either) => self is Right = either.isRight + +/** + * Returns an effect whose Right is mapped by the specified `f` function. + * + * @category mapping + * @since 1.0.0 + */ +export const map = (f: (a: A) => B) => + (self: Either): Either => isRight(self) ? right(f(self.right)) : self + +/** + * @category mapping + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: Either) => Either = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const tupled: (self: Either) => Either = invariant.tupled( + Invariant +) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: Either) => Either = invariant.bindTo(Invariant) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + ...Invariant, + map +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: (a: A) => (self: Either B>) => Either = covariant + .flap( + Covariant + ) + +/** + * Maps the Right value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: (b: B) => (self: Either) => Either = covariant.as( + Covariant +) + +/** + * Returns the effect Eithering from mapping the Right of this effect to unit. + * + * @category mapping + * @since 1.0.0 + */ +export const asUnit: (self: Either) => Either = covariant.asUnit( + Covariant +) + +const let_: ( + name: Exclude, + f: (a: A) => B +) => ( + self: Either +) => Either = covariant.let( + Covariant +) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * Returns an effect whose Left and Right channels have been mapped by + * the specified pair of functions, `f` and `g`. + * + * @category mapping + * @since 1.0.0 + */ +export const bimap = ( + f: (e: E) => G, + g: (a: A) => B +) => (self: Either): Either => isLeft(self) ? left(f(self.left)) : right(g(self.right)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bicovariant: bicovariant.Bicovariant = { + bimap +} + +/** + * Returns an effect with its error channel mapped using the specified + * function. This can be used to lift a "smaller" error into a "larger" error. + * + * @category error handling + * @since 1.0.0 + */ +export const mapLeft: (f: (e: E) => G) => (self: Either) => Either = + bicovariant + .mapLeft(Bicovariant) + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: Either = of_.unit(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Either = of_.Do(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + ...Of, + ...Covariant +} + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMap = ( + f: (a: A) => Either +) => (self: Either): Either => isLeft(self) ? self : f(self.right) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @since 1.0.0 + */ +export const flatten: (self: Either>) => Either = flatMap_ + .flatten(FlatMap) + +/** + * @since 1.0.0 + */ +export const andThen: ( + that: Either +) => (self: Either) => Either = flatMap_ + .andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: ( + bfc: (b: B) => Either +) => (afb: (a: A) => Either) => (a: A) => Either = flatMap_ + .composeKleisliArrow(FlatMap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + ...FlatMap, + ...Covariant +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: ( + name: Exclude, + f: (a: A) => Either +) => ( + self: Either +) => Either = chainable + .bind(Chainable) + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category sequencing + * @since 1.0.0 + */ +export const andThenDiscard: ( + that: Either +) => (self: Either) => Either = chainable + .andThenDiscard(Chainable) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + ...Pointed, + ...FlatMap +} + +/** + * @since 1.0.0 + */ +export const product = ( + that: Either +) => + (self: Either): Either => + isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self + +/** + * @category error handling + * @since 1.0.0 + */ +export const productMany = ( + collection: Iterable> +) => + (self: Either): Either]> => { + if (isLeft(self)) { + return self + } + const out: [A, ...Array] = [self.right] + for (const e of collection) { + if (isLeft(e)) { + return e + } + out.push(e.right) + } + return right(out) + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + ...Invariant, + product, + productMany +} + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + fb: Either +) => ( + self: Either +) => Either = semiProduct + .andThenBind(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productFlatten: ( + that: Either +) => >( + self: Either +) => Either = semiProduct + .productFlatten(SemiProduct) + +/** + * @since 1.0.0 + */ +export const productAll = ( + collection: Iterable> +): Either> => { + const out: Array = [] + for (const e of collection) { + if (isLeft(e)) { + return e + } + out.push(e.right) + } + return right(out) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...Of, + ...SemiProduct, + productAll +} + +/** + * @since 1.0.0 + */ +export const tuple: >>( + ...tuple: T +) => Either< + [T[number]] extends [Either] ? E : never, + Readonly<{ [I in keyof T]: [T[I]] extends [Either] ? A : never }> +> = product_ + .tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + r: R +) => Either< + [R[keyof R]] extends [Either] ? E : never, + { readonly [K in keyof R]: [R[K]] extends [Either] ? A : never } +> = product_ + .struct(Product) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + ...SemiProduct, + ...Covariant +} + +/** + * Semigroup returning the left-most `Left` value. If both operands are `Right`s then the inner values + * are concatenated using the provided `Semigroup`. + * + * | x | y | x |> combine(y) | + * | ---------| ---------| -----------------------| + * | left(a) | left(b) | left(a) | + * | left(a) | right(2) | left(a) | + * | right(1) | left(b) | left(b) | + * | right(1) | right(2) | right(1 |> combine(2)) | + * + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> = + semiApplicative + .liftSemigroup(SemiApplicative) + +/** + * @category lifting + * @since 1.0.0 + */ +export const lift2: ( + f: (a: A, b: B) => C +) => (fa: Either, fb: Either) => Either = semiApplicative + .lift2(SemiApplicative) + +/** + * @category lifting + * @since 1.0.0 + */ +export const lift3: ( + f: (a: A, b: B, c: C) => D +) => ( + fa: Either, + fb: Either, + fc: Either +) => Either = semiApplicative.lift3( + SemiApplicative +) + +/** + * @since 1.0.0 + */ +export const ap: ( + fa: Either +) => (self: Either B>) => Either = semiApplicative.ap( + SemiApplicative +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} + +/** + * Monoid returning the left-most `Left` value. If both operands are `Right`s then the inner values + * are concatenated using the provided `Monoid`. + * + * The `empty` value is `right(M.empty)`. + * + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative + .liftMonoid( + Applicative + ) + +/** + * @category error handling + * @since 1.0.0 + */ +export const firstSuccessOf = (collection: Iterable>) => + (self: Either): Either => { + let out = self + if (isRight(out)) { + return out + } + for (out of collection) { + if (isRight(out)) { + return out + } + } + return out + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiCoproduct: semiCoproduct.SemiCoproduct = { + ...Invariant, + coproduct: (that) => (self) => isRight(self) ? self : that, + coproductMany: firstSuccessOf +} + +/** + * Semigroup returning the left-most `Right` value. + * + * | x | y | x |> combine(y) | + * | ---------| ---------| ----------------| + * | left(a) | left(b) | left(b) | + * | left(a) | right(2) | right(2) | + * | right(1) | left(b) | right(1) | + * | right(1) | right(2) | right(1) | + * + * @category combining + * @since 1.0.0 + */ +export const getFirstRightSemigroup: () => Semigroup> = semiCoproduct + .getSemigroup(SemiCoproduct) + +/** + * Returns the wrapped value if it's a `Right` or a default value if is a `Left`. + * + * @example + * import * as E from '@fp-ts/core/Either' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual( + * pipe( + * E.right(1), + * E.getOrElse(() => 0) + * ), + * 1 + * ) + * assert.deepStrictEqual( + * pipe( + * E.left('error'), + * E.getOrElse(() => 0) + * ), + * 0 + * ) + * + * @category getters + * @since 1.0.0 + */ +export const getOrElse = (onLeft: LazyArg) => + (self: Either): A | B => isLeft(self) ? onLeft() : self.right + +/** + * Recovers from all errors. + * + * @category error handling + * @since 1.0.0 + */ +export const catchAll = ( + onLeft: (e: E1) => Either +) => (self: Either): Either => isLeft(self) ? onLeft(self.left) : self + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * executes the specified effect. + * + * | x | y | x |> orElse(y) | + * | ---------- | ---------- | ---------------| + * | left(a) | left(b) | left(b) | + * | left(a) | right(2) | right(2) | + * | right(1) | left(b) | right(1) | + * | right(1) | right(2) | right(1) | + * + * @category error handling + * @since 1.0.0 + */ +export const orElse = ( + that: Either +) => (self: Either): Either => isLeft(self) ? that : self + +/** + * Returns an effect that will produce the value of this effect, unless it + * fails, in which case, it will produce the value of the specified effect. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseEither = ( + that: Either +) => + (self: Either): Either> => + isLeft(self) ? + pipe(that, map(right)) : + pipe, Either>>(self, map(left)) + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * fails with the specified error. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseFail = ( + onLeft: LazyArg +): (self: Either) => Either => catchAll(() => left(onLeft())) + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * succeeds with the specified value. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseSucceed = ( + onLeft: LazyArg +): (self: Either) => Either => catchAll(() => right(onLeft())) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + ...Covariant, + ...SemiCoproduct +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: (b, f) => (self) => isLeft(self) ? b : f(b, self.right) +} + +/** + * Takes two functions and an `Either` value, if the value is a `Left` the inner value is applied to the first function, + * if the value is a `Right` the inner value is applied to the second function. + * + * @example + * import * as E from '@fp-ts/core/Either' + * import { pipe } from '@fp-ts/core/Function' + * + * const onLeft = (errors: ReadonlyArray): string => `Errors: ${errors.join(', ')}` + * + * const onRight = (value: number): string => `Ok: ${value}` + * + * assert.strictEqual( + * pipe( + * E.right(1), + * E.match(onLeft , onRight) + * ), + * 'Ok: 1' + * ) + * assert.strictEqual( + * pipe( + * E.left(['error 1', 'error 2']), + * E.match(onLeft , onRight) + * ), + * 'Errors: error 1, error 2' + * ) + * + * @category pattern matching + * @since 1.0.0 + */ +export const match = (onLeft: (e: E) => B, onRight: (a: A) => C) => + (self: Either): B | C => isLeft(self) ? onLeft(self.left) : onRight(self.right) + +/** + * Takes a lazy default and a nullable value, if the value is not nully, turn it into a `Right`, if the value is nully use + * the provided default as a `Left`. + * + * @example + * import * as E from '@fp-ts/core/Either' + * + * const parse = E.fromNullable(() => 'nullable') + * + * assert.deepStrictEqual(parse(1), E.right(1)) + * assert.deepStrictEqual(parse(null), E.left('nullable')) + * + * @category conversions + * @since 1.0.0 + */ +export const fromNullable: (onNullable: LazyArg) => (a: A) => Either> = + either.fromNullable + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftNullable = , B, E>( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A): Either> => fromNullable(() => onNullable(...a))(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNullable = ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +): ((self: Either) => Either>) => + flatMap(liftNullable(f, onNullable)) + +/** + * Returns a `Refinement` from a `Either` returning function. + * This function ensures that a `Refinement` definition is type-safe. + * + * @category conversions + * @since 1.0.0 + */ +export const toRefinement = (f: (a: A) => Either): Refinement => + (a: A): a is B => isRight(f(a)) + +/** + * Constructs a new `Either` from a function that might throw. + * + * @example + * import * as E from '@fp-ts/core/Either' + * import { identity } from '@fp-ts/core/Function' + * + * const unsafeHead = (as: ReadonlyArray): A => { + * if (as.length > 0) { + * return as[0] + * } else { + * throw new Error('empty array') + * } + * } + * + * const head = (as: ReadonlyArray): E.Either => + * E.fromThrowable(() => unsafeHead(as), identity) + * + * assert.deepStrictEqual(head([]), E.left(new Error('empty array'))) + * assert.deepStrictEqual(head([1, 2, 3]), E.right(1)) + * + * @category interop + * @since 1.0.0 + */ +export const fromThrowable = ( + f: () => A, + onThrow: (error: unknown) => E +): Either => { + try { + return right(f()) + } catch (e) { + return left(onThrow(e)) + } +} + +/** + * @category interop + * @since 1.0.0 + */ +export const getOrThrow = (onLeft: (e: E) => unknown) => + (self: Either): A => { + if (isRight(self)) { + return self.right + } + throw onLeft(self.left) + } + +/** + * Lifts a function that may throw to one returning a `Either`. + * + * @category interop + * @since 1.0.0 + */ +export const liftThrowable = , B, E>( + f: (...a: A) => B, + onThrow: (error: unknown) => E +): ((...a: A) => Either) => (...a) => fromThrowable(() => f(...a), onThrow) + +/** + * @category getters + * @since 1.0.0 + */ +export const merge: (self: Either) => E | A = match(identity, identity) + +/** + * @category mutations + * @since 1.0.0 + */ +export const reverse = (self: Either): Either => + isLeft(self) ? right(self.left) : left(self.right) + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact = (onNone: LazyArg) => + (self: Either>): Either => + isLeft(self) ? self : option.isNone(self.right) ? left(onNone()) : right(self.right.value) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: Either + ) => Either + ( + predicate: Predicate, + onFalse: LazyArg + ): (self: Either) => Either +} = ( + predicate: Predicate, + onFalse: LazyArg +) => + (self: Either): Either => + isLeft(self) ? self : predicate(self.right) ? self : left(onFalse()) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMap = ( + f: (a: A) => Option, + onNone: LazyArg +) => + (self: Either): Either => + pipe( + self, + flatMap((a) => { + const ob = f(a) + return option.isNone(ob) ? left(onNone()) : right(ob.value) + }) + ) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = (F: applicative.Applicative) => + (f: (a: A) => Kind) => + (self: Either): Kind> => + isLeft(self) ? + F.of>(left(self.left)) : + pipe(f(self.right), F.map>(right)) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => ( + self: Either> +) => Kind> = traversable.sequence(traverse) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse, + sequence +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind +) => (self: Either) => Kind> = traversable + .traverseTap(Traversable) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @since 1.0.0 + */ +export const tap: ( + f: (a: A) => Either +) => (self: Either) => Either = chainable.tap( + Chainable +) + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectRight = ( + onRight: (a: A) => void +) => + (self: Either): Either => { + if (isRight(self)) { + onRight(self.right) + } + return self + } + +/** + * Returns an effect that effectfully "peeks" at the failure of this effect. + * + * @category error handling + * @since 1.0.0 + */ +export const tapError = ( + onLeft: (e: E1) => Either +) => + (self: Either): Either => { + if (isRight(self)) { + return self + } + const out = onLeft(self.left) + return isLeft(out) ? out : self + } + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectLeft = ( + onLeft: (e: E) => void +) => + (self: Either): Either => { + if (isLeft(self)) { + onLeft(self.left) + } + return self + } + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable = (onEmpty: LazyArg) => + (collection: Iterable): Either => { + for (const a of collection) { + return right(a) + } + return left(onEmpty()) + } + +/** + * @example + * import * as E from '@fp-ts/core/Either' + * import { pipe } from '@fp-ts/core/Function' + * import * as O from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(pipe(O.some(1), E.fromOption(() => 'error')), E.right(1)) + * assert.deepStrictEqual(pipe(O.none, E.fromOption(() => 'error')), E.left('error')) + * + * @category conversions + * @since 1.0.0 + */ +export const fromOption: (onNone: LazyArg) => (self: Option) => Either = + either.fromOption + +/** + * Converts a `Either` to an `Option` discarding the Right. + * + * @example + * import * as O from '@fp-ts/core/Option' + * import * as E from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(E.getLeft(E.right('ok')), O.none) + * assert.deepStrictEqual(E.getLeft(E.left('err')), O.some('err')) + * + * @category getters + * @since 1.0.0 + */ +export const getLeft: (self: Either) => Option = either.getLeft + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * @example + * import * as O from '@fp-ts/core/Option' + * import * as E from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(E.getRight(E.right('ok')), O.some('ok')) + * assert.deepStrictEqual(E.getRight(E.left('err')), O.none) + * + * @category getters + * @since 1.0.0 + */ +export const getRight: (self: Either) => Option = either.getRight + +/** + * @category getters + * @since 1.0.0 + */ +export const getOrNull: (self: Either) => A | null = getOrElse(constNull) + +/** + * @category getters + * @since 1.0.0 + */ +export const getOrUndefined: (self: Either) => A | undefined = getOrElse(constUndefined) + +/** + * @example + * import { liftPredicate, left, right } from '@fp-ts/core/Either' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual( + * pipe( + * 1, + * liftPredicate((n) => n > 0, () => 'error') + * ), + * right(1) + * ) + * assert.deepStrictEqual( + * pipe( + * -1, + * liftPredicate((n) => n > 0, () => 'error') + * ), + * left('error') + * ) + * + * @category lifting + * @since 1.0.0 + */ +export const liftPredicate: { + ( + refinement: Refinement, + onFalse: LazyArg + ): (c: C) => Either + (predicate: Predicate, onFalse: LazyArg): (b: B) => Either +} = (predicate: Predicate, onFalse: LazyArg) => + (b: B) => predicate(b) ? right(b) : left(onFalse()) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftOption = , B, E>( + f: (...a: A) => Option, + onNone: (...a: A) => E +) => (...a: A): Either => fromOption(() => onNone(...a))(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapOption = ( + f: (a: A) => Option, + onNone: (a: A) => E2 +) => (self: Either): Either => pipe(self, flatMap(liftOption(f, onNone))) + +/** + * Returns a function that checks if an `Either` contains a given value using a provided `equivalence` function. + * + * @since 1.0.0 + */ +export const contains = (equivalence: Equivalence) => + (a: A) => (self: Either): boolean => isLeft(self) ? false : equivalence(self.right, a) + +/** + * Returns `false` if `Left` or returns the Either of the application of the given predicate to the `Right` value. + * + * @example + * import * as E from '@fp-ts/core/Either' + * + * const f = E.exists((n: number) => n > 2) + * + * assert.strictEqual(f(E.left('a')), false) + * assert.strictEqual(f(E.right(1)), false) + * assert.strictEqual(f(E.right(3)), true) + * + * @since 1.0.0 + */ +export const exists = (predicate: Predicate) => + (self: Either): boolean => isLeft(self) ? false : predicate(self.right) diff --git a/src/internal/Either.ts b/src/internal/Either.ts index 215be4841..fbc5bce28 100644 --- a/src/internal/Either.ts +++ b/src/internal/Either.ts @@ -3,9 +3,15 @@ */ import type { Either, Left, Right } from "@fp-ts/core/Either" +import type { LazyArg } from "@fp-ts/core/Function" import * as option from "@fp-ts/core/internal/Option" import type { Option } from "@fp-ts/core/Option" +/** @internal */ +export const isEither = (u: unknown): u is Either => + typeof u === "object" && u != null && "_tag" in u && + (u["_tag"] === "Left" || u["_tag"] === "Right") + /** @internal */ export const isLeft = (ma: Either): ma is Left => ma._tag === "Left" @@ -28,6 +34,11 @@ export const getRight = ( self: Either ): Option => (isLeft(self) ? option.none : option.some(self.right)) +/** @internal */ +export const fromNullable = (onNullable: LazyArg) => + (a: A): Either> => + a == null ? left(onNullable()) : right(a as NonNullable) + /** @internal */ export const fromOption = (onNone: () => E) => (fa: Option): Either => option.isNone(fa) ? left(onNone()) : right(fa.value) diff --git a/test/Either.ts b/test/Either.ts new file mode 100644 index 000000000..83e429746 --- /dev/null +++ b/test/Either.ts @@ -0,0 +1,483 @@ +import * as _ from "@fp-ts/core/Either" +import { flow, identity, pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as String from "@fp-ts/core/String" +import { deepStrictEqual, double } from "@fp-ts/core/test/util" +import { number } from "@fp-ts/core/typeclass/Equivalence" + +describe.concurrent("Either", () => { + it("instances and derived exports", () => { + expect(_.Invariant).exist + expect(_.imap).exist + expect(_.tupled).exist + expect(_.bindTo).exist + + expect(_.Covariant).exist + expect(_.map).exist + expect(_.let).exist + expect(_.flap).exist + expect(_.as).exist + expect(_.asUnit).exist + + expect(_.Bicovariant).exist + expect(_.bimap).exist + expect(_.mapLeft).exist + + expect(_.Of).exist + expect(_.of).exist + expect(_.unit).exist + expect(_.Do).exist + + expect(_.Pointed).exist + + expect(_.FlatMap).exist + expect(_.flatMap).exist + expect(_.flatten).exist + expect(_.andThen).exist + expect(_.composeKleisliArrow).exist + + expect(_.Chainable).exist + expect(_.bind).exist + expect(_.tap).exist + expect(_.andThenDiscard).exist + + expect(_.Monad).exist + + expect(_.SemiProduct).exist + expect(_.product).exist + + expect(_.Product).exist + expect(_.productAll).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.getFirstLeftSemigroup).exist // liftSemigroup + expect(_.lift2).exist + expect(_.lift3).exist + expect(_.ap).exist + expect(_.andThenDiscard).exist + expect(_.andThen).exist + + expect(_.Applicative).exist + expect(_.getFirstLeftMonoid).exist // liftMonoid + + expect(_.SemiCoproduct).exist + expect(_.getFirstRightSemigroup).exist // getSemigroup + expect(_.firstSuccessOf).exist + + expect(_.SemiAlternative).exist + + expect(_.Foldable).exist + + expect(_.Traversable).exist + expect(_.traverse).exist + expect(_.sequence).exist + expect(_.traverseTap).exist + }) + + it("toRefinement", () => { + const f = (s: string | number): _.Either => + typeof s === "string" ? _.right(s) : _.left("not a string") + const isString = _.toRefinement(f) + deepStrictEqual(isString("s"), true) + deepStrictEqual(isString(1), false) + type A = { readonly type: "A" } + type B = { readonly type: "B" } + type C = A | B + const isA = _.toRefinement(( + c: C + ) => (c.type === "A" ? _.right(c) : _.left("not as A"))) + deepStrictEqual(isA({ type: "A" }), true) + deepStrictEqual(isA({ type: "B" }), false) + }) + + it("isEither", () => { + deepStrictEqual(pipe(_.right(1), _.isEither), true) + deepStrictEqual(pipe(_.left("e"), _.isEither), true) + deepStrictEqual(pipe(O.some(1), _.isEither), false) + }) + + it("orElseFail", () => { + deepStrictEqual(pipe(_.right(1), _.orElseFail(() => "e2")), _.right(1)) + deepStrictEqual(pipe(_.left("e1"), _.orElseFail(() => "e2")), _.left("e2")) + }) + + it("orElseSucceed", () => { + deepStrictEqual(pipe(_.right(1), _.orElseSucceed(() => 2)), _.right(1)) + deepStrictEqual(pipe(_.left("e"), _.orElseSucceed(() => 2)), _.right(2)) + }) + + it("reduce", () => { + deepStrictEqual(pipe(_.right("bar"), _.Foldable.reduce("foo", (b, a) => b + a)), "foobar") + deepStrictEqual(pipe(_.left("bar"), _.Foldable.reduce("foo", (b, a) => b + a)), "foo") + }) + + it("getRight", () => { + deepStrictEqual(pipe(_.right(1), _.getRight), O.some(1)) + deepStrictEqual(pipe(_.left("a"), _.getRight), O.none()) + }) + + it("getLeft", () => { + deepStrictEqual(pipe(_.right(1), _.getLeft), O.none()) + deepStrictEqual(pipe(_.left("e"), _.getLeft), O.some("e")) + }) + + it("getOrNull", () => { + deepStrictEqual(pipe(_.right(1), _.getOrNull), 1) + deepStrictEqual(pipe(_.left("a"), _.getOrNull), null) + }) + + it("getOrUndefined", () => { + deepStrictEqual(pipe(_.right(1), _.getOrUndefined), 1) + deepStrictEqual(pipe(_.left("a"), _.getOrUndefined), undefined) + }) + + it("compact", () => { + deepStrictEqual(pipe(_.right(O.some(1)), _.compact(() => "e2")), _.right(1)) + deepStrictEqual(pipe(_.right(O.none()), _.compact(() => "e2")), _.left("e2")) + deepStrictEqual(pipe(_.left("e1"), _.compact(() => "e2")), _.left("e1")) + }) + + it("inspectRight", () => { + const log: Array = [] + pipe(_.right(1), _.inspectRight((e) => log.push(e))) + pipe(_.left("e"), _.inspectRight((e) => log.push(e))) + deepStrictEqual(log, [1]) + }) + + it("tapError", () => { + deepStrictEqual(pipe(_.right(1), _.tapError(() => _.right(2))), _.right(1)) + deepStrictEqual(pipe(_.left("a"), _.tapError(() => _.right(2))), _.left("a")) + deepStrictEqual(pipe(_.left("a"), _.tapError(() => _.left("b"))), _.left("b")) + }) + + it("inspectLeft", () => { + const log: Array = [] + pipe(_.right(1), _.inspectLeft((e) => log.push(e))) + pipe(_.left("e"), _.inspectLeft((e) => log.push(e))) + deepStrictEqual(log, ["e"]) + }) + + it("getOrThrow", () => { + expect(pipe(_.right(1), _.getOrThrow((e: string) => new Error(e)))).toEqual(1) + expect(() => pipe(_.left("e"), _.getOrThrow((e: string) => new Error(e)))).toThrow( + new Error("e") + ) + }) + + it("andThenDiscard", () => { + deepStrictEqual(pipe(_.right(1), _.andThenDiscard(_.right("a"))), _.right(1)) + deepStrictEqual(pipe(_.right(1), _.andThenDiscard(_.left(true))), _.left(true)) + deepStrictEqual(pipe(_.left(1), _.andThenDiscard(_.right("a"))), _.left(1)) + deepStrictEqual(pipe(_.left(1), _.andThenDiscard(_.left(true))), _.left(1)) + }) + + it("andThen", () => { + deepStrictEqual(pipe(_.right(1), _.andThen(_.right("a"))), _.right("a")) + deepStrictEqual(pipe(_.right(1), _.andThen(_.left(true))), _.left(true)) + deepStrictEqual(pipe(_.left(1), _.andThen(_.right("a"))), _.left(1)) + deepStrictEqual(pipe(_.left(1), _.andThen(_.left(true))), _.left(1)) + }) + + it("orElse", () => { + deepStrictEqual(pipe(_.right(1), _.orElse(_.right(2))), _.right(1)) + deepStrictEqual(pipe(_.right(1), _.orElse(_.left("b"))), _.right(1)) + deepStrictEqual(pipe(_.left("a"), _.orElse(_.right(2))), _.right(2)) + deepStrictEqual(pipe(_.left("a"), _.orElse(_.left("b"))), _.left("b")) + }) + + it("orElseEither", () => { + expect(pipe(_.right(1), _.orElseEither(_.right(2)))).toEqual(_.right(_.left(1))) + expect(pipe(_.right(1), _.orElseEither(_.left("b")))).toEqual(_.right(_.left(1))) + expect(pipe(_.left("a"), _.orElseEither(_.right(2)))).toEqual(_.right(_.right(2))) + expect(pipe(_.left("a"), _.orElseEither(_.left("b")))).toEqual(_.left("b")) + }) + + it("map", () => { + const f = _.map(String.size) + deepStrictEqual(pipe(_.right("abc"), f), _.right(3)) + deepStrictEqual(pipe(_.left("s"), f), _.left("s")) + }) + + it("flatMap", () => { + const f = _.flatMap(flow(String.size, _.right)) + deepStrictEqual(pipe(_.right("abc"), f), _.right(3)) + deepStrictEqual(pipe(_.left("maError"), f), _.left("maError")) + }) + + it("bimap", () => { + const f = _.bimap(String.size, (n: number) => n > 2) + deepStrictEqual(pipe(_.right(1), f), _.right(false)) + }) + + it("mapLeft", () => { + const f = _.mapLeft(double) + deepStrictEqual(pipe(_.right("a"), f), _.right("a")) + deepStrictEqual(pipe(_.left(1), f), _.left(2)) + }) + + it("traverse", () => { + const traverse = _.traverse(O.Applicative)(( + n: number + ) => (n >= 2 ? O.some(n) : O.none())) + deepStrictEqual(pipe(_.left("a"), traverse), O.some(_.left("a"))) + deepStrictEqual(pipe(_.right(1), traverse), O.none()) + deepStrictEqual(pipe(_.right(3), traverse), O.some(_.right(3))) + }) + + it("sequence", () => { + const sequence = _.sequence(O.Applicative) + deepStrictEqual(sequence(_.right(O.some(1))), O.some(_.right(1))) + deepStrictEqual(sequence(_.left("a")), O.some(_.left("a"))) + deepStrictEqual(sequence(_.right(O.none())), O.none()) + }) + + it("match", () => { + const f = (s: string) => `left${s.length}` + const g = (s: string) => `right${s.length}` + const match = _.match(f, g) + deepStrictEqual(match(_.left("abc")), "left3") + deepStrictEqual(match(_.right("abc")), "right3") + }) + + it("getOrElse", () => { + deepStrictEqual(pipe(_.right(12), _.getOrElse(() => 17)), 12) + deepStrictEqual(pipe(_.left("a"), _.getOrElse(() => 17)), 17) + }) + + it("contains", () => { + const contains = _.contains(number) + deepStrictEqual(pipe(_.left("a"), contains(2)), false) + deepStrictEqual(pipe(_.right(2), contains(2)), true) + deepStrictEqual(pipe(_.right(2), contains(1)), false) + }) + + it("filter", () => { + const predicate = (n: number) => n > 10 + deepStrictEqual(pipe(_.right(12), _.filter(predicate, () => -1)), _.right(12)) + deepStrictEqual(pipe(_.right(7), _.filter(predicate, () => -1)), _.left(-1)) + deepStrictEqual(pipe(_.left(12), _.filter(predicate, () => -1)), _.left(12)) + }) + + it("isLeft", () => { + deepStrictEqual(_.isLeft(_.right(1)), false) + deepStrictEqual(_.isLeft(_.left(1)), true) + }) + + it("isRight", () => { + deepStrictEqual(_.isRight(_.right(1)), true) + deepStrictEqual(_.isRight(_.left(1)), false) + }) + + it("catchAll", () => { + deepStrictEqual(pipe(_.right(1), _.catchAll(() => _.right(2))), _.right(1)) + deepStrictEqual(pipe(_.right(1), _.catchAll(() => _.left("foo"))), _.right(1)) + deepStrictEqual(pipe(_.left("a"), _.catchAll(() => _.right(1))), _.right(1)) + deepStrictEqual(pipe(_.left("a"), _.catchAll(() => _.left("b"))), _.left("b")) + }) + + it("swap", () => { + deepStrictEqual(_.reverse(_.right("a")), _.left("a")) + deepStrictEqual(_.reverse(_.left("b")), _.right("b")) + }) + + it("liftPredicate", () => { + const f = _.liftPredicate((n: number) => n >= 2, () => "e") + deepStrictEqual(f(3), _.right(3)) + deepStrictEqual(f(1), _.left("e")) + }) + + it("fromNullable", () => { + deepStrictEqual(_.fromNullable(() => "default")(null), _.left("default")) + deepStrictEqual(_.fromNullable(() => "default")(undefined), _.left("default")) + deepStrictEqual(_.fromNullable(() => "default")(1), _.right(1)) + }) + + it("fromThrowable", () => { + deepStrictEqual( + _.fromThrowable(() => { + return 1 + }, identity), + _.right(1) + ) + + deepStrictEqual( + _.fromThrowable(() => { + throw "string error" + }, identity), + _.left("string error") + ) + }) + + it("filterMap", () => { + const p = (n: number) => n > 2 + const f = (n: number) => (p(n) ? O.some(n + 1) : O.none()) + deepStrictEqual(pipe(_.left("123"), _.filterMap(f, () => "")), _.left("123")) + deepStrictEqual(pipe(_.right(1), _.filterMap(f, () => "")), _.left(String.Monoid.empty)) + deepStrictEqual(pipe(_.right(3), _.filterMap(f, () => "")), _.right(4)) + }) + + it("fromIterable", () => { + deepStrictEqual(_.fromIterable(() => "e")([]), _.left("e")) + deepStrictEqual(_.fromIterable(() => "e")(["a"]), _.right("a")) + }) + + it("firstSuccessOf", () => { + deepStrictEqual(pipe(_.right(1), _.firstSuccessOf([])), _.right(1)) + deepStrictEqual(pipe(_.left("e"), _.firstSuccessOf([])), _.left("e")) + deepStrictEqual( + pipe(_.left("e1"), _.firstSuccessOf([_.left("e2"), _.left("e3"), _.left("e4"), _.right(1)])), + _.right(1) + ) + deepStrictEqual( + pipe(_.left("e1"), _.firstSuccessOf([_.left("e2"), _.left("e3"), _.left("e4")])), + _.left("e4") + ) + }) + + it("fromOption", () => { + deepStrictEqual(_.fromOption(() => "none")(O.none()), _.left("none")) + deepStrictEqual(_.fromOption(() => "none")(O.some(1)), _.right(1)) + }) + + it("liftOption", () => { + const f = _.liftOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "a") + deepStrictEqual(f(1), _.right(1)) + deepStrictEqual(f(-1), _.left("a")) + }) + + it("flatMapOption", () => { + const f = _.flatMapOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "a") + deepStrictEqual(f(_.right(1)), _.right(1)) + deepStrictEqual(f(_.right(-1)), _.left("a")) + deepStrictEqual(f(_.left("b")), _.left("b")) + }) + + it("exists", () => { + const gt2 = _.exists((n: number) => n > 2) + deepStrictEqual(gt2(_.left("a")), false) + deepStrictEqual(gt2(_.right(1)), false) + deepStrictEqual(gt2(_.right(3)), true) + }) + + it("do notation", () => { + deepStrictEqual( + pipe( + _.right(1), + _.bindTo("a"), + _.bind("b", () => _.right("b")), + _.let("c", ({ a, b }) => [a, b]) + ), + _.right({ a: 1, b: "b", c: [1, "b"] }) + ) + }) + + it("andThenBind", () => { + deepStrictEqual( + pipe(_.right(1), _.bindTo("a"), _.andThenBind("b", _.right("b"))), + _.right({ a: 1, b: "b" }) + ) + }) + + it("product", () => { + deepStrictEqual(pipe(_.right(1), _.product(_.right("a"))), _.right([1, "a"] as const)) + deepStrictEqual(pipe(_.right(1), _.product(_.left("e2"))), _.left("e2")) + deepStrictEqual(pipe(_.left("e1"), _.product(_.right("a"))), _.left("e1")) + deepStrictEqual(pipe(_.left("e1"), _.product(_.left("2"))), _.left("e1")) + }) + + it("productMany", () => { + deepStrictEqual(pipe(_.right(1), _.productMany([])), _.right([1] as const)) + deepStrictEqual( + pipe(_.right(1), _.productMany([_.right(2), _.right(3)])), + _.right([1, 2, 3] as const) + ) + deepStrictEqual( + pipe(_.right(1), _.productMany([_.left("e"), _.right(3)])), + _.left("e") + ) + deepStrictEqual( + pipe(_.left("e"), _.productMany([_.right(2), _.right(3)])), + _.left("e") + ) + }) + + it("productAll", () => { + deepStrictEqual(_.productAll([]), _.right([])) + deepStrictEqual( + _.productAll([_.right(1), _.right(2), _.right(3)]), + _.right([1, 2, 3]) + ) + deepStrictEqual( + _.productAll([_.left("e"), _.right(2), _.right(3)]), + _.left("e") + ) + }) + + it("coproduct", () => { + deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.right(2))), _.right(1)) + deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.left("e2"))), _.right(1)) + deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.right(2))), _.right(2)) + deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.left("e2"))), _.left("e2")) + }) + + it("coproductMany", () => { + deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproductMany([_.right(2)])), _.right(1)) + deepStrictEqual( + pipe( + _.right(1) as _.Either, + _.SemiCoproduct.coproductMany([_.left("e2") as _.Either]) + ), + _.right(1) + ) + deepStrictEqual( + pipe( + _.left("e1") as _.Either, + _.SemiCoproduct.coproductMany([_.right(2) as _.Either]) + ), + _.right(2) + ) + deepStrictEqual( + pipe(_.left("e1"), _.SemiCoproduct.coproductMany([_.left("e2")])), + _.left("e2") + ) + }) + + it("productFlatten", () => { + deepStrictEqual( + pipe(_.right(1), _.tupled, _.productFlatten(_.right("b"))), + _.right([1, "b"] as const) + ) + }) + + it("liftNullable", () => { + const f = _.liftNullable((n: number) => (n > 0 ? n : null), () => "error") + deepStrictEqual(f(1), _.right(1)) + deepStrictEqual(f(-1), _.left("error")) + }) + + it("flatMapNullable", () => { + const f = _.flatMapNullable((n: number) => (n > 0 ? n : null), () => "error") + deepStrictEqual(f(_.right(1)), _.right(1)) + deepStrictEqual(f(_.right(-1)), _.left("error")) + deepStrictEqual(f(_.left("a")), _.left("a")) + }) + + it("merge", () => { + deepStrictEqual(_.merge(_.right(1)), 1) + deepStrictEqual(_.merge(_.left("a")), "a") + }) + + it("liftThrowable", () => { + const f = _.liftThrowable((s: string) => { + const len = s.length + if (len > 0) { + return len + } + throw new Error("empty string") + }, identity) + deepStrictEqual(f("a"), _.right(1)) + deepStrictEqual(f(""), _.left(new Error("empty string"))) + }) +}) From 4e73d6611fc87d25052a29b79f853ceea858d201 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 08:28:37 +0100 Subject: [PATCH 12/80] add These module --- .changeset/heavy-suns-buy.md | 5 + src/Boolean.ts | 2 +- src/Number.ts | 2 +- src/ReadonlyArray.ts | 30 + src/String.ts | 2 +- src/These.ts | 1321 ++++++++++++++++++++++++++++++++++ src/index.ts | 5 + test/These.ts | 816 +++++++++++++++++++++ 8 files changed, 2180 insertions(+), 3 deletions(-) create mode 100644 .changeset/heavy-suns-buy.md create mode 100644 src/These.ts create mode 100644 test/These.ts diff --git a/.changeset/heavy-suns-buy.md b/.changeset/heavy-suns-buy.md new file mode 100644 index 000000000..8c5199636 --- /dev/null +++ b/.changeset/heavy-suns-buy.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add These module diff --git a/src/Boolean.ts b/src/Boolean.ts index 7db4ed1d3..f10ed523f 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -9,7 +9,7 @@ import type * as order from "@fp-ts/core/typeclass/Order" import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** - * @category refinements + * @category guards * @since 1.0.0 */ export const isBoolean: Refinement = (u: unknown): u is boolean => diff --git a/src/Number.ts b/src/Number.ts index 83080a28f..ba33b56db 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -10,7 +10,7 @@ import type * as order from "@fp-ts/core/typeclass/Order" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** - * @category refinements + * @category guards * @since 1.0.0 */ export const isNumber: Refinement = (u: unknown): u is number => diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 367a320a5..509507fa2 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -3,6 +3,7 @@ */ import type { TypeLambda } from "@fp-ts/core/HKT" +import * as iterable from "@fp-ts/core/internal/Iterable" import type * as applicative from "@fp-ts/core/typeclass/Applicative" import * as covariant from "@fp-ts/core/typeclass/Covariant" import type * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -181,3 +182,32 @@ export const Applicative: applicative.Applicative = { ...SemiApplicative, ...Product } + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable: (collection: Iterable) => ReadonlyArray = iterable.fromIterable + +/** + * @category mutations + * @since 1.0.0 + */ +export const appendAll = (that: Iterable) => + (self: Iterable): Array => fromIterable(self).concat(fromIterable(that)) + +/** + * @category mutations + * @since 1.0.0 + */ +export function appendAllNonEmpty( + that: NonEmptyReadonlyArray +): (self: Iterable) => NonEmptyArray +export function appendAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => NonEmptyArray +export function appendAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => Array { + return appendAll(that) +} diff --git a/src/String.ts b/src/String.ts index 98921c6de..9700222b8 100644 --- a/src/String.ts +++ b/src/String.ts @@ -86,7 +86,7 @@ export const Order: order.Order = { * assert.deepStrictEqual(S.isString('a'), true) * assert.deepStrictEqual(S.isString(1), false) * - * @category refinements + * @category guards * @since 1.0.0 */ export const isString: Refinement = (u: unknown): u is string => diff --git a/src/These.ts b/src/These.ts new file mode 100644 index 000000000..61a16d21d --- /dev/null +++ b/src/These.ts @@ -0,0 +1,1321 @@ +/** + * A data structure providing "inclusive-or" as opposed to `Either`'s "exclusive-or". + * + * If you interpret `Either` as suggesting the computation may either fail or of (exclusively), then + * `These` may fail, of, or do both at the same time. + * + * There are a few ways to interpret the both case: + * + * - You can think of a computation that has a non-fatal error. + * - You can think of a computation that went as far as it could before erroring. + * - You can think of a computation that keeps track of errors as it completes. + * + * Another way you can think of `These` is saying that we want to handle `E` kind of data, `A` kind of data, or + * both `E` and `A` kind of data at the same time. This is particularly useful when it comes to displaying UI's. + * + * (description adapted from https://package.elm-lang.org/packages/joneshf/elm-these) + * + * Adapted from https://github.com/purescript-contrib/purescript-these + * + * @since 1.0.0 + */ +import type { Either, Left, Right } from "@fp-ts/core/Either" +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, pipe } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as either from "@fp-ts/core/internal/Either" +import * as option from "@fp-ts/core/internal/Option" +import type { Option } from "@fp-ts/core/Option" +import type { Predicate, Refinement } from "@fp-ts/core/Predicate" +import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" +import * as chainable from "@fp-ts/core/typeclass/Chainable" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" +import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" +import type * as foldable from "@fp-ts/core/typeclass/Foldable" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import type * as monad from "@fp-ts/core/typeclass/Monad" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import type * as pointed from "@fp-ts/core/typeclass/Pointed" +import * as product_ from "@fp-ts/core/typeclass/Product" +import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" +import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as traversable from "@fp-ts/core/typeclass/Traversable" + +/** + * @category model + * @since 1.0.0 + */ +export type Both = { + readonly _tag: "Both" + readonly left: E + readonly right: A +} + +/** + * @category model + * @since 1.0.0 + */ +export type These = Either | Both + +/** + * @category model + * @since 1.0.0 + */ +export type Validated = These, A> + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface TheseTypeLambda extends TypeLambda { + readonly type: These +} + +/** + * @category type lambdas + * @since 3.0.0 + */ +export interface ValidatedTypeLambda extends TypeLambda { + readonly type: Validated +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const left = (left: E): These => ({ _tag: "Left", left }) + +/** + * @category constructors + * @since 1.0.0 + */ +export const right = (right: A): These => ({ _tag: "Right", right }) + +/** + * Alias of `right`. + * + * @category constructors + * @since 1.0.0 + */ +export const of = right + +/** + * @category constructors + * @since 1.0.0 + */ +export const both = (left: E, right: A): These => ({ + _tag: "Both", + left, + right +}) + +/** + * @category constructors + * @since 1.0.0 + */ +export const fail = (e: E): Validated => left([e]) + +/** + * Alias of `right`. + * + * @category constructors + * @since 1.0.0 + */ +export const succeed: (a: A) => Validated = right + +/** + * @category constructors + * @since 1.0.0 + */ +export const warn = (e: E, a: A): Validated => both([e], a) + +/** + * @category constructors + * @since 1.0.0 + */ +export const leftOrBoth = (e: LazyArg) => + (self: Option): These => option.isNone(self) ? left(e()) : both(e(), self.value) + +/** + * @category constructors + * @since 1.0.0 + */ +export const rightOrBoth = (a: () => A) => + (self: Option): These => option.isNone(self) ? right(a()) : both(self.value, a()) + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const match = ( + onLeft: (e: E) => B, + onRight: (a: A) => C, + onBoth: (e: E, a: A) => D +) => + (self: These): B | C | D => { + switch (self._tag) { + case "Left": + return onLeft(self.left) + case "Right": + return onRight(self.right) + case "Both": + return onBoth(self.left, self.right) + } + } + +/** + * @since 1.0.0 + */ +export const reverse: (self: These) => These = match( + right, + left, + (e, a) => both(a, e) +) + +/** + * Returns `true` if the these is an instance of `Left`, `false` otherwise + * + * @category guards + * @since 1.0.0 + */ +export const isLeft = (self: These): self is Left => self._tag === "Left" + +/** + * @category guards + * @since 1.0.0 + */ +export const isLeftOrBoth = (self: These): self is Left | Both => + self._tag !== "Right" + +/** + * Returns `true` if the these is an instance of `Right`, `false` otherwise + * + * @category guards + * @since 1.0.0 + */ +export const isRight = (self: These): self is Right => self._tag === "Right" + +/** + * @category guards + * @since 1.0.0 + */ +export const isRightOrBoth = (self: These): self is Right | Both => + self._tag !== "Left" + +/** + * Returns `true` if the these is an instance of `Both`, `false` otherwise + * + * @category guards + * @since 1.0.0 + */ +export const isBoth = (self: These): self is Both => self._tag === "Both" + +/** + * Returns `true` if the specified value is an instance of `These`, `false` + * otherwise. + * + * @category guards + * @since 1.0.0 + */ +export const isThese = (u: unknown): u is These => + typeof u === "object" && + u != null && "_tag" in u && + (u["_tag"] === "Left" || u["_tag"] === "Right" || u["_tag"] === "Both") + +/** + * Constructs a new `These` from a function that might throw. + * + * @category interop + * @since 1.0.0 + */ +export const fromThrowable = ( + f: () => A, + onThrow: (error: unknown) => E +): These => { + try { + return right(f()) + } catch (e) { + return left(onThrow(e)) + } +} + +/** + * Lifts a function that may throw to one returning a `These`. + * + * @category interop + * @since 1.0.0 + */ +export const liftThrowable = , B, E>( + f: (...a: A) => B, + onThrow: (error: unknown) => E +): ((...a: A) => These) => (...a) => fromThrowable(() => f(...a), onThrow) + +/** + * @category interop + * @since 1.0.0 + */ +export const getOrThrow = (onLeft: (e: E) => unknown) => + (self: These): A => { + if (isRightOrBoth(self)) { + return self.right + } + throw onLeft(self.left) + } + +/** + * @category interop + * @since 1.0.0 + */ +export const getRightOnlyOrThrow = (onLeft: (e: E) => unknown) => + (self: These): A => { + if (isRight(self)) { + return self.right + } + throw onLeft(self.left) + } + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromNullable = (onNullable: LazyArg) => + (a: A): These> => a == null ? left(onNullable()) : right(a as NonNullable) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromEither = (self: Either): Validated => + either.isLeft(self) ? left([self.left]) : self + +/** + * @category conversions + * @since 1.0.0 + */ +export const toEither = ( + onBoth: (e: E, a: A) => Either +) => (self: These): Either => isBoth(self) ? onBoth(self.left, self.right) : self + +/** + * @category conversions + * @since 1.0.0 + */ +export const absolve: (self: These) => Either = toEither(( + _, + a +) => either.right(a)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const condemn: (self: These) => Either = toEither(( + e, + _ +) => either.left(e)) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftNullable = , B, E>( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A): These> => fromNullable(() => onNullable(...a))(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNullable = ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +): ((self: Validated) => Validated>) => + flatMap(liftNullable(f, (a) => [onNullable(a)])) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftPredicate: { + ( + refinement: Refinement, + onFalse: LazyArg + ): (c: C) => These + (predicate: Predicate, onFalse: LazyArg): (b: B) => These +} = (predicate: Predicate, onFalse: LazyArg) => + (b: B) => predicate(b) ? right(b) : left(onFalse()) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable = (onEmpty: LazyArg) => + (collection: Iterable): These => { + for (const a of collection) { + return right(a) + } + return left(onEmpty()) + } + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromOption = (onNone: LazyArg) => + (self: Option): These => option.isNone(self) ? left(onNone()) : right(self.value) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromTuple = (self: readonly [E, A]): These => both(self[0], self[1]) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftOption = , B, E>( + f: (...a: A) => Option, + onNone: (...a: A) => E +) => (...a: A): These => fromOption(() => onNone(...a))(f(...a)) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftEither = , E, B>( + f: (...a: A) => Either +) => (...a: A): Validated => fromEither(f(...a)) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftThese = , E, B>( + f: (...a: A) => These +) => (...a: A): Validated => fromThese(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapOption = ( + f: (a: A) => Option, + onNone: (a: A) => E2 +) => + (self: Validated): Validated => + pipe(self, flatMap(liftOption(f, (a) => [onNone(a)]))) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapEither = ( + f: (a: A) => Either +) => (self: Validated): Validated => pipe(self, flatMap(liftEither(f))) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapThese = ( + f: (a: A) => These +) => (self: Validated): Validated => pipe(self, flatMap(liftThese(f))) + +/** + * @category getters + * @since 1.0.0 + */ +export const getRight = ( + self: These +): Option => isLeft(self) ? option.none : option.some(self.right) + +/** + * Returns the `A` value if and only if the value is constructed with `Right` + * + * @category getters + * @since 1.0.0 + */ +export const getRightOnly = ( + self: These +): Option => isRight(self) ? option.some(self.right) : option.none + +/** + * @category getters + * @since 1.0.0 + */ +export const getLeft = ( + self: These +): Option => isRight(self) ? option.none : option.some(self.left) + +/** + * Returns the `E` value if and only if the value is constructed with `Left` + * + * @category getters + * @since 1.0.0 + */ +export const getLeftOnly = ( + self: These +): Option => isLeft(self) ? option.some(self.left) : option.none + +/** + * @category getters + * @since 1.0.0 + */ +export const getBoth = ( + self: These +): Option => isBoth(self) ? option.some([self.left, self.right]) : option.none + +/** + * @category getters + * @since 1.0.0 + */ +export const getBothOrElse = (e: LazyArg, a: LazyArg) => + ( + self: These + ): readonly [E, A] => + isLeft(self) ? + [self.left, a()] : + isRight(self) ? + [e(), self.right] : + [self.left, self.right] + +/** + * @category getters + * @since 1.0.0 + */ +export const getOrElse = (onLeft: LazyArg) => + (self: These): A | B => isLeft(self) ? onLeft() : self.right + +/** + * @category getters + * @since 1.0.0 + */ +export const getOrNull: (self: These) => A | null = getOrElse(constNull) + +/** + * @category getters + * @since 1.0.0 + */ +export const getOrUndefined: (self: These) => A | undefined = getOrElse(constUndefined) + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectRight = ( + onRight: (a: A) => void +) => + (self: These): These => { + if (isRight(self)) { + onRight(self.right) + } + return self + } + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectRightOrBoth = ( + onRightOrBoth: (a: A) => void +) => + (self: These): These => { + if (isRightOrBoth(self)) { + onRightOrBoth(self.right) + } + return self + } + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectLeft = ( + onLeft: (e: E) => void +) => + (self: These): These => { + if (isLeft(self)) { + onLeft(self.left) + } + return self + } + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectBoth = ( + onBoth: (e: E, a: A) => void +) => + (self: These): These => { + if (isBoth(self)) { + onBoth(self.left, self.right) + } + return self + } + +/** + * Returns an effect whose left and right channels have been mapped by + * the specified pair of functions, `f` and `g`. + * + * @category mapping + * @since 1.0.0 + */ +export const bimap: ( + f: (e: E) => G, + g: (a: A) => B +) => (self: These) => These = (f, g) => + (fa) => + isLeft(fa) ? + left(f(fa.left)) : + isRight(fa) ? + right(g(fa.right)) : + both(f(fa.left), g(fa.right)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bicovariant: bicovariant.Bicovariant = { + bimap +} + +/** + * Returns an effect with its error channel mapped using the specified + * function. This can be used to lift a "smaller" error into a "larger" error. + * + * @category error handling + * @since 1.0.0 + */ +export const mapLeft: (f: (e: E) => G) => (self: These) => These = bicovariant + .mapLeft(Bicovariant) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromThese: (self: These) => Validated = mapLeft((e) => [e]) + +/** + * Returns an effect whose right is mapped by the specified `f` function. + * + * @category mapping + * @since 1.0.0 + */ +export const map: (f: (a: A) => B) => (self: These) => These = bicovariant.map( + Bicovariant +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = covariant.make(map) + +/** + * @category mapping + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: These) => These = Covariant.imap + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const tupled: (self: These) => These = invariant.tupled( + Invariant +) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: These) => These = invariant.bindTo(Invariant) + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: (a: A) => (self: These B>) => These = covariant + .flap( + Covariant + ) + +/** + * Maps the right value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: (b: B) => (self: These) => These = covariant.as( + Covariant +) + +/** + * Returns the effect resulting from mapping the right of this effect to unit. + * + * @category mapping + * @since 1.0.0 + */ +export const asUnit: (self: These) => These = covariant.asUnit(Covariant) + +const let_: ( + name: Exclude, + f: (a: A) => B +) => ( + self: These +) => These = covariant.let( + Covariant +) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: These = of_.unit(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: These = of_.Do(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + ...Of, + ...Covariant +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +) => + ( + f: (a: A) => Kind + ) => + (self: These): Kind> => + isLeft(self) + ? F.of>(self) + : isRight(self) + ? pipe(f(self.right), F.map>(right)) + : pipe( + f(self.right), + F.map((b) => both(self.left, b)) + ) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => ( + self: These> +) => Kind> = traversable.sequence(traverse) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse, + sequence +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind +) => (self: These) => Kind> = traversable + .traverseTap(Traversable) + +/** + * Returns a function that checks if a `These` contains a given value using a provided `equivalence` function. + * + * @since 1.0.0 + */ +export const contains = (equivalence: Equivalence) => + (a: A) => (self: These): boolean => isLeft(self) ? false : equivalence(self.right, a) + +/** + * @category predicates + * @since 1.0.0 + */ +export const exists = (predicate: Predicate) => + (self: These): boolean => isLeft(self) ? false : predicate(self.right) + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: (b, f) => (self) => isLeft(self) ? b : f(b, self.right) +} + +/** + * Recovers from all errors. + * + * @category error handling + * @since 1.0.0 + */ +export const catchAll = ( + onLeft: (e: E1) => These +) => (self: These): These => isLeft(self) ? onLeft(self.left) : self + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * executes the specified effect. + * + * @category error handling + * @since 1.0.0 + */ +export const orElse = ( + that: These +) => (self: These): These => isLeft(self) ? that : self + +/** + * Returns an effect that will produce the value of this effect, unless it + * fails, in which case, it will produce the value of the specified effect. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseEither = ( + that: These +) => + (self: These): These> => + isLeft(self) ? + pipe(that, map(either.right)) : + pipe(self, map(either.left)) + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * fails with the specified error. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseFail = ( + onLeft: LazyArg +): (self: These) => These => catchAll(() => left(onLeft())) + +/** + * Executes this effect and returns its value, if it succeeds, but otherwise + * succeeds with the specified value. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseSucceed = ( + onLeft: LazyArg +): (self: These) => These => catchAll(() => right(onLeft())) + +/** + * @category error handling + * @since 1.0.0 + */ +export const firstRightOrBothOf = (collection: Iterable>) => + (self: These): These => { + let out = self + if (isRightOrBoth(out)) { + return out + } + for (out of collection) { + if (isRightOrBoth(out)) { + return out + } + } + return out + } + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiCoproduct: semiCoproduct.SemiCoproduct = { + ...Invariant, + coproduct: (that) => (self) => isRightOrBoth(self) ? self : that, + coproductMany: firstRightOrBothOf +} + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstRightOrBothSemigroup: () => Semigroup> = semiCoproduct + .getSemigroup(SemiCoproduct) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + ...Covariant, + ...SemiCoproduct +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact = (onNone: LazyArg) => + (self: These>): These => + isLeft(self) ? + self : + option.isNone(self.right) ? + left(onNone()) : + isBoth(self) ? + both(self.left, self.right.value) : + right(self.right.value) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: These + ) => These + ( + predicate: Predicate, + onFalse: LazyArg + ): (self: These) => These +} = ( + predicate: Predicate, + onFalse: LazyArg +) => + (self: These): These => + isLeft(self) ? self : predicate(self.right) ? self : left(onFalse()) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMap = ( + f: (a: A) => Option, + onNone: LazyArg +) => + (self: These): These => { + if (isLeft(self)) { + return self + } + if (isRight(self)) { + const ob = f(self.right) + return option.isNone(ob) ? left(onNone()) : right(ob.value) + } + const ob = f(self.right) + return option.isNone(ob) ? left(onNone()) : both(self.left, ob.value) + } + +/** + * @since 1.0.0 + */ +export const product = (that: Validated) => + ( + self: Validated + ): Validated => { + if (isLeft(self)) { + return self + } + if (isRight(self)) { + if (isLeft(that)) { + return that + } + if (isRight(that)) { + return right([self.right, that.right]) + } + return both(that.left, [self.right, that.right]) + } + if (isLeft(that)) { + return left(RA.appendAllNonEmpty(that.left)(self.left)) + } + if (isRight(that)) { + return both(self.left, [self.right, that.right]) + } + return both(RA.appendAllNonEmpty(that.left)(self.left), [self.right, that.right]) + } + +/** + * @since 1.0.0 + */ +export const productMany = ( + collection: Iterable> +) => + ( + self: Validated + ): Validated]> => + pipe(self, product(productAll(collection)), map(([a, as]) => [a, ...as])) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + map, + ...SemiProduct +} + +/** + * @category lifting + * @since 1.0.0 + */ +export const lift2: ( + f: (a: A, b: B) => C +) => ( + fa: Validated, + fb: Validated +) => Validated = semiApplicative + .lift2(SemiApplicative) + +/** + * @category lifting + * @since 1.0.0 + */ +export const lift3: ( + f: (a: A, b: B, c: C) => D +) => ( + fa: Validated, + fb: Validated, + fc: Validated +) => Validated = semiApplicative.lift3( + SemiApplicative +) + +/** + * @since 1.0.0 + */ +export const ap: ( + fa: Validated +) => (self: Validated B>) => Validated = semiApplicative.ap( + SemiApplicative +) + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftSemigroup: ( + S: Semigroup +) => Semigroup> = semiApplicative + .liftSemigroup(SemiApplicative) + +/** + * @since 1.0.0 + */ +export const productAll = ( + collection: Iterable> +): Validated> => { + const rights: Array = [] + const lefts: Array = [] + let isFatal = false + for (const t of collection) { + if (isLeft(t)) { + lefts.push(...t.left) + isFatal = true + break + } else if (isRight(t)) { + rights.push(t.right) + } else { + lefts.push(...t.left) + rights.push(t.right) + } + } + if (RA.isNonEmpty(lefts)) { + return isFatal ? left(lefts) : both(lefts, rights) + } + return right(rights) +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + fb: Validated +) => ( + self: Validated +) => Validated = semiProduct + .andThenBind(SemiProduct) + +/** + * @category do notation + * @since 1.0.0 + */ +export const andThenBindEither = ( + name: Exclude, + fb: Either +): ( + self: Validated +) => Validated => + andThenBind(name, fromEither(fb)) + +/** + * @category do notation + * @since 1.0.0 + */ +export const andThenBindThese = ( + name: Exclude, + fb: These +): ( + self: Validated +) => Validated => + andThenBind(name, fromThese(fb)) + +/** + * @since 1.0.0 + */ +export const productFlatten: ( + that: Validated +) => >( + self: Validated +) => Validated = semiProduct + .productFlatten(SemiProduct) + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...SemiProduct, + of, + productAll +} + +/** + * @since 1.0.0 + */ +export const tuple: >>( + ...tuple: T +) => Validated< + [T[number]] extends [Validated] ? E : never, + Readonly<{ [I in keyof T]: [T[I]] extends [Validated] ? A : never }> +> = product_ + .tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + r: R +) => Validated< + [R[keyof R]] extends [Validated] ? E : never, + { readonly [K in keyof R]: [R[K]] extends [Validated] ? A : never } +> = product_ + .struct(Product) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMap = ( + f: (a: A) => Validated +) => + (self: Validated): Validated => { + if (isLeft(self)) { + return self + } + if (isRight(self)) { + return f(self.right) + } + const that = f(self.right) + if (isLeft(that)) { + return left(RA.appendAllNonEmpty(that.left)(self.left)) + } + if (isRight(that)) { + return both(self.left, that.right) + } + return both(RA.appendAllNonEmpty(that.left)(self.left), that.right) + } + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative + .liftMonoid( + Applicative + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @since 1.0.0 + */ +export const flatten: ( + self: Validated> +) => Validated = flatMap_ + .flatten(FlatMap) + +/** + * @since 1.0.0 + */ +export const andThen: ( + that: Validated +) => ( + self: Validated +) => Validated = flatMap_ + .andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: ( + bfc: (b: B) => Validated +) => ( + afb: (a: A) => Validated +) => (a: A) => Validated = flatMap_ + .composeKleisliArrow(FlatMap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + imap, + map, + flatMap +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: ( + name: Exclude, + f: (a: A) => Validated +) => ( + self: Validated +) => Validated = chainable + .bind(Chainable) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindEither = ( + name: Exclude, + f: (a: A) => Either +): ( + self: Validated +) => Validated => + bind(name, (a) => fromEither(f(a))) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindThese = ( + name: Exclude, + f: (a: A) => These +): ( + self: Validated +) => Validated => + bind(name, (a) => fromThese(f(a))) + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category sequencing + * @since 1.0.0 + */ +export const andThenDiscard: ( + that: Validated +) => (self: Validated) => Validated = chainable + .andThenDiscard(Chainable) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @since 1.0.0 + */ +export const tap: ( + f: (a: A) => Validated +) => (self: Validated) => Validated = chainable.tap( + Chainable +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + map, + of, + flatMap +} diff --git a/src/index.ts b/src/index.ts index 18842e414..f4a28145d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" import * as readonlyArray from "@fp-ts/core/ReadonlyArray" import * as string from "@fp-ts/core/String" +import * as these from "@fp-ts/core/These" import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -225,6 +226,10 @@ export { * @since 1.0.0 */ string, + /** + * @since 1.0.0 + */ + these, /** * @category typeclass * @since 1.0.0 diff --git a/test/These.ts b/test/These.ts new file mode 100644 index 000000000..5dcd2eddb --- /dev/null +++ b/test/These.ts @@ -0,0 +1,816 @@ +import * as E from "@fp-ts/core/Either" +import { identity, pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as S from "@fp-ts/core/String" +import * as _ from "@fp-ts/core/These" +import { number } from "@fp-ts/core/typeclass/Equivalence" +import * as U from "./util" + +describe("These", () => { + it("instances and derived exports", () => { + expect(_.Invariant).exist + expect(_.imap).exist + expect(_.tupled).exist + expect(_.bindTo).exist + + expect(_.Bicovariant).exist + expect(_.mapLeft).exist + + expect(_.Covariant).exist + expect(_.map).exist + expect(_.let).exist + expect(_.flap).exist + expect(_.as).exist + expect(_.asUnit).exist + + expect(_.Of).exist + expect(_.of).exist + expect(_.unit).exist + expect(_.Do).exist + + expect(_.Pointed).exist + + expect(_.FlatMap).exist + expect(_.flatMap).exist + expect(_.flatten).exist + expect(_.andThen).exist + expect(_.composeKleisliArrow).exist + + expect(_.Chainable).exist + expect(_.bind).exist + expect(_.tap).exist + expect(_.andThenDiscard).exist + + expect(_.Monad).exist + + expect(_.SemiProduct).exist + expect(_.product).exist + expect(_.productMany).exist + expect(_.andThenBind).exist + expect(_.productFlatten).exist + + expect(_.Product).exist + expect(_.productAll).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.getFirstLeftSemigroup).exist // liftSemigroup + expect(_.lift2).exist + expect(_.lift3).exist + expect(_.ap).exist + expect(_.andThenDiscard).exist + expect(_.andThen).exist + + expect(_.Applicative).exist + expect(_.getFirstLeftMonoid).exist // liftMonoid + + expect(_.SemiCoproduct).exist + // expect(_.coproduct).exist + expect(_.firstRightOrBothOf).exist // coproductMany + expect(_.getFirstRightOrBothSemigroup).exist // getSemigroup + // expect(_.coproductEither).exist // orElseEither + + expect(_.SemiAlternative).exist + + expect(_.Foldable).exist + + expect(_.Traversable).exist + expect(_.traverse).exist + expect(_.sequence).exist + expect(_.traverseTap).exist + }) + + it("reduce", () => { + U.deepStrictEqual(pipe(_.right("a"), _.Foldable.reduce("-", (b, a) => b + a)), "-a") + U.deepStrictEqual(pipe(_.left("e"), _.Foldable.reduce("-", (b, a) => b + a)), "-") + U.deepStrictEqual(pipe(_.both("e", "a"), _.Foldable.reduce("-", (b, a) => b + a)), "-a") + }) + + it("map", () => { + U.deepStrictEqual(pipe(_.left("e"), _.map(U.double)), _.left("e")) + U.deepStrictEqual(pipe(_.right(2), _.map(U.double)), _.right(4)) + U.deepStrictEqual(pipe(_.both("e", 2), _.map(U.double)), _.both("e", 4)) + }) + + it("bimap", () => { + const f = _.bimap(S.size, U.double) + U.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) + U.deepStrictEqual(pipe(_.right(2), f), _.right(4)) + U.deepStrictEqual(pipe(_.both("eee", 1), f), _.both(3, 2)) + }) + + it("mapLeft", () => { + const f = _.mapLeft(S.size) + U.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) + U.deepStrictEqual(pipe(_.right(2), f), _.right(2)) + U.deepStrictEqual(pipe(_.both("eee", 1), f), _.both(3, 1)) + }) + + it("traverse", () => { + const traverse = _.traverse(O.Applicative)((n: number) => (n > 1 ? O.some(n) : O.none())) + U.deepStrictEqual(pipe(_.left("a"), traverse), O.some(_.left("a"))) + U.deepStrictEqual(pipe(_.right(2), traverse), O.some(_.right(2))) + U.deepStrictEqual(pipe(_.right(1), traverse), O.none()) + U.deepStrictEqual(pipe(_.both("a", 2), traverse), O.some(_.both("a", 2))) + U.deepStrictEqual( + pipe( + _.both("a", 1), + _.traverse(O.Applicative)((n) => (n >= 2 ? O.some(n) : O.none())) + ), + O.none() + ) + }) + + it("andThenBindEither", () => { + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.andThenBindEither("b", E.right(2))), + _.succeed({ a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.andThenBindEither("b", E.left("e2"))), + _.fail("e2") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.andThenBindEither("b", E.right(2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.andThenBindEither("b", E.left("e2"))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.andThenBindEither("b", E.right(2))), + _.warn("e1", { a: 1, b: 2 }) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.andThenBindEither("b", E.left("e2"))) + ).toEqual( + _.left(["e1", "e2"]) + ) + }) + + it("andThenBindThese", () => { + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.andThenBindThese("b", _.right(2))), + _.succeed({ a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.andThenBindThese("b", _.left("e2"))), + _.fail("e2") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.andThenBindThese("b", _.both("e2", 2))), + _.warn("e2", { a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.andThenBindThese("b", _.right(2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.andThenBindThese("b", _.left("e2"))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.andThenBindThese("b", _.both("e2", 2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.andThenBindThese("b", _.right(2))), + _.warn("e1", { a: 1, b: 2 }) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.andThenBindThese("b", _.left("e2"))) + ).toEqual( + _.left(["e1", "e2"]) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.andThenBindThese("b", _.both("e2", 2))) + ).toEqual( + _.both(["e1", "e2"], { a: 1, b: 2 }) + ) + }) + + it("andThenBind", () => { + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.bindEither("b", () => E.right(2))), + _.succeed({ a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.bindEither("b", () => E.left("e2"))), + _.fail("e2") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindEither("b", () => E.right(2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindEither("b", () => E.left("e2"))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.bindEither("b", () => E.right(2))), + _.warn("e1", { a: 1, b: 2 }) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.bindEither("b", () => E.left("e2"))) + ).toEqual( + _.left(["e1", "e2"]) + ) + }) + + it("andThenBind", () => { + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.bindThese("b", () => _.right(2))), + _.succeed({ a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.bindThese("b", () => _.left("e2"))), + _.fail("e2") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.succeed(1)), _.bindThese("b", () => _.both("e2", 2))), + _.warn("e2", { a: 1, b: 2 }) + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.right(2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.left("e2"))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.both("e2", 2))), + _.fail("e1") + ) + U.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.bindThese("b", () => _.right(2))), + _.warn("e1", { a: 1, b: 2 }) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.bindThese("b", () => _.left("e2"))) + ).toEqual( + _.left(["e1", "e2"]) + ) + expect( + pipe(_.Do, _.bind("a", () => _.warn("e1", 1)), _.bindThese("b", () => _.both("e2", 2))) + ).toEqual( + _.both(["e1", "e2"], { a: 1, b: 2 }) + ) + }) + + it("sequence", () => { + const sequence = _.sequence(O.Applicative) + U.deepStrictEqual(sequence(_.left("a")), O.some(_.left("a"))) + U.deepStrictEqual(sequence(_.right(O.some(1))), O.some(_.right(1))) + U.deepStrictEqual(sequence(_.right(O.none())), O.none()) + U.deepStrictEqual(sequence(_.both("a", O.some(1))), O.some(_.both("a", 1))) + U.deepStrictEqual(sequence(_.both("a", O.none())), O.none()) + }) + + it("product", () => { + const a = ["a"] as const + const b = ["b"] as const + const ab = ["a", "b"] as const + + U.deepStrictEqual(pipe(_.right(1), _.product(_.right(2))), _.right([1, 2] as const)) + U.deepStrictEqual(pipe(_.right(1), _.product(_.left(b))), _.left(b)) + U.deepStrictEqual(pipe(_.right(1), _.product(_.both(b, 2))), _.both(b, [1, 2] as const)) + + U.deepStrictEqual(pipe(_.left(a), _.product(_.right(2))), _.left(a)) + U.deepStrictEqual(pipe(_.left(a), _.product(_.left(b))), _.left(a)) + U.deepStrictEqual(pipe(_.left(a), _.product(_.both(b, 2))), _.left(a)) + + U.deepStrictEqual(pipe(_.both(a, 1), _.product(_.right(2))), _.both(a, [1, 2] as const)) + expect(pipe(_.both(a, 1), _.product(_.left(b)))).toEqual(_.left(ab)) + expect(pipe(_.both(a, 1), _.product(_.both(b, 2)))).toEqual(_.both(ab, [1, 2])) + }) + + it("productMany", () => { + const a = ["a"] as const + const b = ["b"] as const + const ab = ["a", "b"] as const + + U.deepStrictEqual(pipe(_.right(1), _.productMany([_.right(2)])), _.right([1, 2] as const)) + U.deepStrictEqual( + pipe(_.right(1), _.productMany([_.left(b)])), + _.left(b) + ) + U.deepStrictEqual( + pipe(_.right(1), _.productMany([_.both(b, 2)])), + _.both(b, [1, 2] as const) + ) + + U.deepStrictEqual(pipe(_.left(a), _.productMany([_.right(2)])), _.left(a)) + U.deepStrictEqual(pipe(_.left(a), _.productMany([_.left(b)])), _.left(a)) + U.deepStrictEqual( + pipe(_.left(a), _.productMany([_.both(b, 2)])), + _.left(a) + ) + + U.deepStrictEqual(pipe(_.both(a, 1), _.productMany([_.right(2)])), _.both(a, [1, 2] as const)) + expect(pipe(_.both(a, 1), _.productMany([_.left(b)]))).toEqual(_.left(ab)) + expect(pipe(_.both(a, 1), _.productMany([_.both(b, 2)]))).toEqual( + _.both(ab, [1, 2]) + ) + }) + + it("productAll", () => { + const a = ["a"] as const + const b = ["b"] as const + const ab = ["a", "b"] as const + + U.deepStrictEqual(_.productAll([_.right(1), _.right(2)]), _.right([1, 2] as const)) + U.deepStrictEqual(_.productAll([_.right(1), _.left(b)]), _.left(b)) + U.deepStrictEqual(_.productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2] as const)) + + U.deepStrictEqual(_.productAll([_.left(a), _.right(2)]), _.left(a)) + U.deepStrictEqual(_.productAll([_.left(a), _.left(b)]), _.left(a)) + U.deepStrictEqual(_.productAll([_.left(a), _.both(b, 2)]), _.left(a)) + + U.deepStrictEqual(_.productAll([_.both(a, 1), _.right(2)]), _.both(a, [1, 2])) + expect(_.productAll([_.both(a, 1), _.left(b)])).toEqual(_.left(ab)) + expect(_.productAll([_.both(a, 1), _.both(b, 2)])).toEqual(_.both(ab, [1, 2])) + }) + + it("flatMap", () => { + const f = ( + n: number + ) => (n >= 2 ? + (n <= 5 ? _.succeed(n * 2) : _.warn("e2", n)) : + _.fail("e3")) + U.deepStrictEqual( + pipe(_.fail("e1"), _.flatMap(f)), + _.fail("e1") + ) + U.deepStrictEqual(pipe(_.succeed(2), _.flatMap(f)), _.succeed(4)) + U.deepStrictEqual(pipe(_.succeed(1), _.flatMap(f)), _.fail("e3")) + U.deepStrictEqual(pipe(_.succeed(6), _.flatMap(f)), _.warn("e2", 6)) + U.deepStrictEqual( + pipe(_.warn("e1", 2), _.flatMap(f)), + _.warn("e1", 4) + ) + U.deepStrictEqual( + pipe( + _.warn("e1", 1), + _.flatMap(f) + ), + _.left(["e1", "e3"] as const) + ) + U.deepStrictEqual( + pipe( + _.warn("e1", 6), + _.flatMap(f) + ), + _.both(["e1", "e2"] as const, 6) + ) + }) + + it("flatMapNullable", () => { + const f = _.flatMapNullable((n: number) => (n > 0 ? n : null), () => "e2") + U.deepStrictEqual(f(_.succeed(1)), _.succeed(1)) + U.deepStrictEqual(f(_.succeed(-1)), _.fail("e2")) + U.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + U.deepStrictEqual(f(_.warn("e1", 1)), _.warn("e1", 1)) + expect(f(_.warn("e1", -1))).toEqual(_.left(["e1", "e2"])) + }) + + it("flatMapOption", () => { + const f = _.flatMapOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "e2") + U.deepStrictEqual(f(_.succeed(1)), _.succeed(1)) + U.deepStrictEqual(f(_.succeed(-1)), _.fail("e2")) + U.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + U.deepStrictEqual(f(_.warn("e1", 1)), _.warn("e1", 1)) + expect(f(_.warn("e1", -1))).toEqual(_.left(["e1", "e2"])) + }) + + it("flatMapEither", () => { + const f = _.flatMapEither((n: number) => (n > 0 ? E.right(n) : E.left("e2"))) + U.deepStrictEqual(f(_.succeed(1)), _.succeed(1)) + U.deepStrictEqual(f(_.succeed(-1)), _.fail("e2")) + U.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + U.deepStrictEqual(f(_.warn("e1", 1)), _.warn("e1", 1)) + expect(f(_.warn("e1", -1))).toEqual(_.left(["e1", "e2"])) + }) + + it("flatMapThese", () => { + const f = _.flatMapThese(( + n: number + ) => (n > 10 ? _.both("e3", n) : n > 0 ? _.right(n) : _.left("e2"))) + U.deepStrictEqual(f(_.succeed(1)), _.succeed(1)) + U.deepStrictEqual(f(_.succeed(-1)), _.fail("e2")) + U.deepStrictEqual(f(_.succeed(11)), _.warn("e3", 11)) + U.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + U.deepStrictEqual(f(_.warn("e1", 1)), _.warn("e1", 1)) + expect(f(_.warn("e1", -1))).toEqual(_.left(["e1", "e2"])) + expect(f(_.warn("e1", 11))).toEqual(_.both(["e1", "e3"], 11)) + }) + + it("leftOrBoth", () => { + U.deepStrictEqual(_.leftOrBoth(() => "a")(O.none()), _.left("a")) + U.deepStrictEqual(_.leftOrBoth(() => "a")(O.some(1)), _.both("a", 1)) + }) + + it("rightOrBoth", () => { + U.deepStrictEqual(_.rightOrBoth(() => 1)(O.none()), _.right(1)) + U.deepStrictEqual(_.rightOrBoth(() => 1)(O.some("a")), _.both("a", 1)) + }) + + it("match", () => { + const f = (s: string, n: number) => S.size(s) + U.double(n) + const match = _.match(S.size, U.double, f) + U.deepStrictEqual(match(_.left("foo")), 3) + U.deepStrictEqual(match(_.right(1)), 2) + U.deepStrictEqual(match(_.both("foo", 1)), 5) + }) + + it("getBothOrElse", () => { + const f = _.getBothOrElse(() => "a", () => 1) + U.deepStrictEqual(pipe(_.left("b"), f), ["b", 1]) + U.deepStrictEqual(pipe(_.right(2), f), ["a", 2]) + U.deepStrictEqual(pipe(_.both("b", 2), f), ["b", 2]) + }) + + it("getBoth", () => { + U.deepStrictEqual(pipe(_.left("e"), _.getBoth), O.none()) + U.deepStrictEqual(pipe(_.right(1), _.getBoth), O.none()) + U.deepStrictEqual(pipe(_.both("e", 1), _.getBoth), O.some(["e", 1] as const)) + }) + + it("getLeft", () => { + U.deepStrictEqual(_.getLeft(_.left("e")), O.some("e")) + U.deepStrictEqual(_.getLeft(_.right(1)), O.none()) + U.deepStrictEqual(_.getLeft(_.both("e", 1)), O.some("e")) + }) + + it("getRight", () => { + U.deepStrictEqual(_.getRight(_.left("e")), O.none()) + U.deepStrictEqual(_.getRight(_.right(1)), O.some(1)) + U.deepStrictEqual(_.getRight(_.both("e", 1)), O.some(1)) + }) + + it("getLeftOnly", () => { + U.deepStrictEqual(_.getLeftOnly(_.left("e")), O.some("e")) + U.deepStrictEqual(_.getLeftOnly(_.right(1)), O.none()) + U.deepStrictEqual(_.getLeftOnly(_.both("e", 1)), O.none()) + }) + + it("getRightOnly", () => { + U.deepStrictEqual(_.getRightOnly(_.left("e")), O.none()) + U.deepStrictEqual(_.getRightOnly(_.right(1)), O.some(1)) + U.deepStrictEqual(_.getRightOnly(_.both("e", 1)), O.none()) + }) + + it("isLeft", () => { + U.deepStrictEqual(_.isLeft(_.left("e")), true) + U.deepStrictEqual(_.isLeft(_.right(1)), false) + U.deepStrictEqual(_.isLeft(_.both("e", 1)), false) + }) + + it("isLeftOrBoth", () => { + U.deepStrictEqual(_.isLeftOrBoth(_.left("e")), true) + U.deepStrictEqual(_.isLeftOrBoth(_.right(1)), false) + U.deepStrictEqual(_.isLeftOrBoth(_.both("e", 1)), true) + }) + + it("isRight", () => { + U.deepStrictEqual(_.isRight(_.left("e")), false) + U.deepStrictEqual(_.isRight(_.right(1)), true) + U.deepStrictEqual(_.isRight(_.both("", 1)), false) + }) + + it("isRightOrBoth", () => { + U.deepStrictEqual(_.isRightOrBoth(_.left("e")), false) + U.deepStrictEqual(_.isRightOrBoth(_.right(1)), true) + U.deepStrictEqual(_.isRightOrBoth(_.both("e", 1)), true) + }) + + it("isThese", () => { + U.deepStrictEqual(_.isThese(_.left("e")), true) + U.deepStrictEqual(_.isThese(_.right(1)), true) + U.deepStrictEqual(_.isThese(_.both("e", 1)), true) + U.deepStrictEqual(_.isThese(E.left("e")), true) + U.deepStrictEqual(_.isThese(E.right(1)), true) + U.deepStrictEqual(_.isThese(O.some(1)), false) + }) + + it("isBoth", () => { + U.deepStrictEqual(_.isBoth(_.left("e")), false) + U.deepStrictEqual(_.isBoth(_.right(1)), false) + U.deepStrictEqual(_.isBoth(_.both("e", 1)), true) + }) + + it("fromThrowable", () => { + U.deepStrictEqual( + _.fromThrowable(() => { + return 1 + }, identity), + _.right(1) + ) + + U.deepStrictEqual( + _.fromThrowable(() => { + throw "string error" + }, identity), + _.left("string error") + ) + }) + + it("liftThrowable", () => { + const f = _.liftThrowable((s: string) => { + const len = s.length + if (len > 0) { + return len + } + throw new Error("empty string") + }, identity) + U.deepStrictEqual(f("a"), _.right(1)) + U.deepStrictEqual(f(""), _.left(new Error("empty string"))) + }) + + it("inspectRight", () => { + const log: Array = [] + pipe(_.right(1), _.inspectRight((a) => log.push(a))) + pipe(_.left("e1"), _.inspectRight((a) => log.push(a))) + pipe(_.both("e2", 1), _.inspectRight((a) => log.push(a))) + U.deepStrictEqual(log, [1]) + }) + + it("inspectRightOrBoth", () => { + const log: Array = [] + pipe(_.right(1), _.inspectRightOrBoth((a) => log.push(a))) + pipe(_.left("e1"), _.inspectRightOrBoth((a) => log.push(a))) + pipe(_.both("e2", 2), _.inspectRightOrBoth((a) => log.push(a))) + U.deepStrictEqual(log, [1, 2]) + }) + + it("inspectBoth", () => { + const log: Array = [] + pipe(_.right(1), _.inspectBoth((e, a) => log.push(e, a))) + pipe(_.left("e1"), _.inspectBoth((e, a) => log.push(e, a))) + pipe(_.both("e2", 2), _.inspectBoth((e, a) => log.push(e, a))) + U.deepStrictEqual(log, ["e2", 2]) + }) + + it("inspectLeft", () => { + const log: Array = [] + pipe(_.right(1), _.inspectLeft((e) => log.push(e))) + pipe(_.left("e1"), _.inspectLeft((e) => log.push(e))) + pipe(_.both("e2", 1), _.inspectLeft((e) => log.push(e))) + U.deepStrictEqual(log, ["e1"]) + }) + + it("getOrThrow", () => { + expect(pipe(_.right(1), _.getOrThrow((e: string) => new Error(e)))).toEqual(1) + expect(() => pipe(_.left("e"), _.getOrThrow((e: string) => new Error(e)))).toThrow( + new Error("e") + ) + expect(pipe(_.both("e", 1), _.getOrThrow((e: string) => new Error(e)))).toEqual(1) + }) + + it("getRightOnlyOrThrow", () => { + expect(pipe(_.right(1), _.getRightOnlyOrThrow((e: string) => new Error(e)))).toEqual(1) + expect(() => pipe(_.left("e"), _.getRightOnlyOrThrow((e: string) => new Error(e)))).toThrow( + new Error("e") + ) + expect(() => pipe(_.both("e", 1), _.getRightOnlyOrThrow((e: string) => new Error(e)))).toThrow( + new Error("e") + ) + }) + + it("getOrElse", () => { + U.deepStrictEqual(pipe(_.right(1), _.getOrElse(() => 2)), 1) + U.deepStrictEqual(pipe(_.left("e"), _.getOrElse(() => 2)), 2) + U.deepStrictEqual(pipe(_.both("e", 1), _.getOrElse(() => 2)), 1) + }) + + it("getOrNull", () => { + U.deepStrictEqual(pipe(_.right(1), _.getOrNull), 1) + U.deepStrictEqual(pipe(_.left("e"), _.getOrNull), null) + U.deepStrictEqual(pipe(_.both("e", 1), _.getOrNull), 1) + }) + + it("getOrUndefined", () => { + U.deepStrictEqual(pipe(_.right(1), _.getOrUndefined), 1) + U.deepStrictEqual(pipe(_.left("e"), _.getOrUndefined), undefined) + U.deepStrictEqual(pipe(_.both("e", 1), _.getOrUndefined), 1) + }) + + it("fromNullable", () => { + U.deepStrictEqual(_.fromNullable(() => "default")(null), _.left("default")) + U.deepStrictEqual(_.fromNullable(() => "default")(undefined), _.left("default")) + U.deepStrictEqual(_.fromNullable(() => "default")(1), _.right(1)) + }) + + it("liftNullable", () => { + const f = _.liftNullable((n: number) => (n > 0 ? n : null), () => "error") + U.deepStrictEqual(f(1), _.right(1)) + U.deepStrictEqual(f(-1), _.left("error")) + }) + + it("liftPredicate", () => { + const f = _.liftPredicate((n: number) => n >= 2, () => "e") + U.deepStrictEqual(f(3), _.right(3)) + U.deepStrictEqual(f(1), _.left("e")) + }) + + it("fromIterable", () => { + U.deepStrictEqual(_.fromIterable(() => "e")([]), _.left("e")) + U.deepStrictEqual(_.fromIterable(() => "e")(["a"]), _.right("a")) + }) + + it("fromOption", () => { + U.deepStrictEqual(_.fromOption(() => "e")(O.none()), _.left("e")) + U.deepStrictEqual(_.fromOption(() => "e")(O.some(1)), _.right(1)) + }) + + it("fromEither", () => { + U.deepStrictEqual(_.fromEither(E.right(1)), _.right(1)) + U.deepStrictEqual(_.fromEither(E.left("e")), _.left(["e"] as const)) + }) + + it("fromThese", () => { + U.deepStrictEqual(_.fromThese(_.right(1)), _.succeed(1)) + U.deepStrictEqual(_.fromThese(_.left("e")), _.fail("e")) + U.deepStrictEqual(_.fromThese(_.both("e", 1)), _.warn("e", 1)) + }) + + it("toEither", () => { + expect(_.toEither).exist + }) + + it("absolve", () => { + U.deepStrictEqual(_.absolve(_.right(1)), E.right(1)) + U.deepStrictEqual(_.absolve(_.left("e")), E.left("e")) + U.deepStrictEqual(_.absolve(_.both("e", 1)), E.right(1)) + }) + + it("condemn", () => { + U.deepStrictEqual(_.condemn(_.right(1)), E.right(1)) + U.deepStrictEqual(_.condemn(_.left("e")), E.left("e")) + U.deepStrictEqual(_.condemn(_.both("e", 1)), E.left("e")) + }) + + it("liftOption", () => { + const f = _.liftOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "e") + U.deepStrictEqual(f(1), _.right(1)) + U.deepStrictEqual(f(-1), _.left("e")) + }) + + it("liftEither", () => { + const f = _.liftEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + U.deepStrictEqual(f(1), _.succeed(1)) + U.deepStrictEqual(f(-1), _.fail("e")) + }) + + it("liftThese", () => { + const f = _.liftThese((n: number) => (n > 0 ? _.right(n) : _.left("e"))) + U.deepStrictEqual(f(1), _.succeed(1)) + U.deepStrictEqual(f(-1), _.fail("e")) + }) + + it("fromTuple", () => { + U.deepStrictEqual(pipe(["e", 1] as const, _.fromTuple), _.both("e", 1)) + }) + + it("reverse", () => { + U.deepStrictEqual(_.reverse(_.left("e")), _.right("e")) + U.deepStrictEqual(_.reverse(_.right(1)), _.left(1)) + U.deepStrictEqual(_.reverse(_.both("e", 1)), _.both(1, "e")) + }) + + it("exists", () => { + const gt2 = _.exists((n: number) => n > 2) + U.deepStrictEqual(gt2(_.left("a")), false) + U.deepStrictEqual(gt2(_.right(1)), false) + U.deepStrictEqual(gt2(_.right(3)), true) + U.deepStrictEqual(gt2(_.both("a", 1)), false) + U.deepStrictEqual(gt2(_.both("a", 3)), true) + }) + + it("contains", () => { + const contains = _.contains(number) + U.deepStrictEqual(contains(2)(_.left("a")), false) + U.deepStrictEqual(contains(2)(_.right(2)), true) + U.deepStrictEqual(contains(1)(_.right(2)), false) + U.deepStrictEqual(contains(2)(_.both("a", 2)), true) + U.deepStrictEqual(contains(1)(_.both("a", 2)), false) + }) + + it("of", () => { + U.deepStrictEqual(_.of(1), _.right(1)) + }) + + it("catchAll", () => { + U.deepStrictEqual(pipe(_.right(1), _.catchAll(() => _.right(2))), _.right(1)) + U.deepStrictEqual(pipe(_.right(1), _.catchAll(() => _.left("b"))), _.right(1)) + U.deepStrictEqual(pipe(_.right(1), _.catchAll(() => _.both("b", 2))), _.right(1)) + U.deepStrictEqual(pipe(_.left("a"), _.catchAll(() => _.right(2))), _.right(2)) + U.deepStrictEqual(pipe(_.left("a"), _.catchAll(() => _.left("b"))), _.left("b")) + U.deepStrictEqual(pipe(_.left("a"), _.catchAll(() => _.both("b", 2))), _.both("b", 2)) + U.deepStrictEqual(pipe(_.both("a", 1), _.catchAll(() => _.right(2))), _.both("a", 1)) + U.deepStrictEqual(pipe(_.both("a", 1), _.catchAll(() => _.left("b"))), _.both("a", 1)) + U.deepStrictEqual(pipe(_.both("a", 1), _.catchAll(() => _.both("b", 2))), _.both("a", 1)) + }) + + it("orElse", () => { + U.deepStrictEqual(pipe(_.right(1), _.orElse(_.right(2))), _.right(1)) + U.deepStrictEqual(pipe(_.right(1), _.orElse(_.left("b"))), _.right(1)) + U.deepStrictEqual(pipe(_.right(1), _.orElse(_.both("b", 2))), _.right(1)) + U.deepStrictEqual(pipe(_.left("a"), _.orElse(_.right(2))), _.right(2)) + U.deepStrictEqual(pipe(_.left("a"), _.orElse(_.left("b"))), _.left("b")) + U.deepStrictEqual(pipe(_.left("a"), _.orElse(_.both("b", 2))), _.both("b", 2)) + U.deepStrictEqual(pipe(_.both("a", 1), _.orElse(_.right(2))), _.both("a", 1)) + U.deepStrictEqual(pipe(_.both("a", 1), _.orElse(_.left("b"))), _.both("a", 1)) + U.deepStrictEqual(pipe(_.both("a", 1), _.orElse(_.both("b", 2))), _.both("a", 1)) + }) + + it("orElseEither", () => { + expect(pipe(_.right(1), _.orElseEither(_.right(2)))).toEqual(_.right(E.left(1))) + expect(pipe(_.right(1), _.orElseEither(_.left("b")))).toEqual(_.right(E.left(1))) + expect(pipe(_.right(1), _.orElseEither(_.both("b", 2)))).toEqual(_.right(E.left(1))) + expect(pipe(_.left("a"), _.orElseEither(_.right(2)))).toEqual(_.right(E.right(2))) + expect(pipe(_.left("a"), _.orElseEither(_.left("b")))).toEqual(_.left("b")) + expect(pipe(_.left("a"), _.orElseEither(_.both("b", 2)))).toEqual(_.both("b", E.right(2))) + expect(pipe(_.both("a", 1), _.orElseEither(_.right(2)))).toEqual(_.both("a", E.left(1))) + expect(pipe(_.both("a", 1), _.orElseEither(_.left("b")))).toEqual(_.both("a", E.left(1))) + expect(pipe(_.both("a", 1), _.orElseEither(_.both("b", 2)))).toEqual(_.both("a", E.left(1))) + }) + + it("orElseFail", () => { + U.deepStrictEqual(pipe(_.right(1), _.orElseFail(() => "e2")), _.right(1)) + U.deepStrictEqual(pipe(_.left("e1"), _.orElseFail(() => "e2")), _.left("e2")) + U.deepStrictEqual(pipe(_.both("e1", 1), _.orElseFail(() => "e2")), _.both("e1", 1)) + }) + + it("orElseSucceed", () => { + U.deepStrictEqual(pipe(_.right(1), _.orElseSucceed(() => 2)), _.right(1)) + U.deepStrictEqual(pipe(_.left("e"), _.orElseSucceed(() => 2)), _.right(2)) + U.deepStrictEqual(pipe(_.both("e", 1), _.orElseSucceed(() => 2)), _.both("e", 1)) + }) + + it("firstSuccessOf", () => { + U.deepStrictEqual(pipe(_.right(1), _.firstRightOrBothOf([])), _.right(1)) + U.deepStrictEqual(pipe(_.left("e"), _.firstRightOrBothOf([])), _.left("e")) + U.deepStrictEqual( + pipe( + _.left("e1"), + _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4"), _.right(1)]) + ), + _.right(1) + ) + U.deepStrictEqual( + pipe( + _.left("e1"), + _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4"), _.both("e5", 1)]) + ), + _.both("e5", 1) + ) + U.deepStrictEqual( + pipe(_.left("e1"), _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4")])), + _.left("e4") + ) + }) + + it("coproduct", () => { + U.deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.right(2))), _.right(1)) + U.deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.left("e2"))), _.right(1)) + U.deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.right(2))), _.right(2)) + U.deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.left("e2"))), _.left("e2")) + U.deepStrictEqual( + pipe(_.both("e1", 1), _.SemiCoproduct.coproduct(_.right(2))), + _.both("e1", 1) + ) + U.deepStrictEqual( + pipe(_.both("e1", 1), _.SemiCoproduct.coproduct(_.left("e2"))), + _.both("e1", 1) + ) + }) + + it("compact", () => { + U.deepStrictEqual(pipe(_.right(O.some(1)), _.compact(() => "e2")), _.right(1)) + U.deepStrictEqual(pipe(_.right(O.none()), _.compact(() => "e2")), _.left("e2")) + U.deepStrictEqual(pipe(_.left("e1"), _.compact(() => "e2")), _.left("e1")) + U.deepStrictEqual(pipe(_.both("e1", O.some(1)), _.compact(() => "e2")), _.both("e1", 1)) + U.deepStrictEqual(pipe(_.both("e1", O.none()), _.compact(() => "e2")), _.left("e2")) + }) + + it("filter", () => { + const predicate = (n: number) => n > 10 + U.deepStrictEqual(pipe(_.right(12), _.filter(predicate, () => "e2")), _.right(12)) + U.deepStrictEqual(pipe(_.right(7), _.filter(predicate, () => "e2")), _.left("e2")) + U.deepStrictEqual(pipe(_.left("e1"), _.filter(predicate, () => "e2")), _.left("e1")) + U.deepStrictEqual(pipe(_.both("e1", 12), _.filter(predicate, () => "e2")), _.both("e1", 12)) + U.deepStrictEqual(pipe(_.both("e1", 7), _.filter(predicate, () => "e2")), _.left("e2")) + }) + + it("filterMap", () => { + const f = (n: number) => (n > 2 ? O.some(n + 1) : O.none()) + U.deepStrictEqual(pipe(_.left("e1"), _.filterMap(f, () => "e2")), _.left("e1")) + U.deepStrictEqual(pipe(_.right(1), _.filterMap(f, () => "e2")), _.left("e2")) + U.deepStrictEqual(pipe(_.right(3), _.filterMap(f, () => "e2")), _.right(4)) + U.deepStrictEqual(pipe(_.both("e1", 1), _.filterMap(f, () => "e2")), _.left("e2")) + U.deepStrictEqual(pipe(_.both("e1", 3), _.filterMap(f, () => "e2")), _.both("e1", 4)) + }) +}) From 9cbf072f27864c5862d61a11b502dba9d6a5a880 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 08:39:20 +0100 Subject: [PATCH 13/80] add CovariantWithIndex, FilterableWithIndex, TraversableFilterable modules --- .changeset/purple-meals-explode.md | 5 ++ src/index.ts | 20 ++++- src/typeclass/CovariantWithIndex.ts | 40 +++++++++ src/typeclass/FilterableWithIndex.ts | 104 ++++++++++++++++++++++++ src/typeclass/TraversableFilterable.ts | 107 +++++++++++++++++++++++++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 .changeset/purple-meals-explode.md create mode 100644 src/typeclass/CovariantWithIndex.ts create mode 100644 src/typeclass/FilterableWithIndex.ts create mode 100644 src/typeclass/TraversableFilterable.ts diff --git a/.changeset/purple-meals-explode.md b/.changeset/purple-meals-explode.md new file mode 100644 index 000000000..3af08023c --- /dev/null +++ b/.changeset/purple-meals-explode.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add CovariantWithIndex, FilterableWithIndex, TraversableFilterable modules diff --git a/src/index.ts b/src/index.ts index f4a28145d..97ddb8daf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,8 +32,10 @@ import * as compactable from "@fp-ts/core/typeclass/Compactable" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as coproduct from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" +import * as covariantWithIndex from "@fp-ts/core/typeclass/CovariantWithIndex" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" +import * as filterableWithIndex from "@fp-ts/core/typeclass/FilterableWithIndex" import * as flatMap from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -50,6 +52,7 @@ import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" +import * as traversableFilterable from "@fp-ts/core/typeclass/TraversableFilterable" export { /** @@ -105,6 +108,11 @@ export { * @since 1.0.0 */ covariant, + /** + * @category typeclass + * @since 1.0.0 + */ + covariantWithIndex, /** * @since 1.0.0 */ @@ -119,6 +127,11 @@ export { * @since 1.0.0 */ filterable, + /** + * @category typeclass + * @since 1.0.0 + */ + filterableWithIndex, /** * @category typeclass * @since 1.0.0 @@ -234,5 +247,10 @@ export { * @category typeclass * @since 1.0.0 */ - traversable + traversable, + /** + * @category typeclass + * @since 1.0.0 + */ + traversableFilterable } diff --git a/src/typeclass/CovariantWithIndex.ts b/src/typeclass/CovariantWithIndex.ts new file mode 100644 index 000000000..d9e8fb7ec --- /dev/null +++ b/src/typeclass/CovariantWithIndex.ts @@ -0,0 +1,40 @@ +/** + * @since 1.0.0 + */ +import { pipe } from "@fp-ts/core/Function" +import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" + +/** + * @category type class + * @since 1.0.0 + */ +export interface CovariantWithIndex extends TypeClass { + readonly mapWithIndex: ( + f: (a: A, i: I) => B + ) => (self: Kind) => Kind +} + +/** + * Returns a default `mapWithIndex` composition. + * + * @since 1.0.0 + */ +export const mapWithIndexComposition = ( + F: CovariantWithIndex, + G: CovariantWithIndex +): (( + f: (a: A, ij: readonly [I, J]) => B +) => ( + self: Kind> +) => Kind>) => + (f) => F.mapWithIndex((ga, i) => pipe(ga, G.mapWithIndex((a, j) => f(a, [i, j])))) + +/** + * Returns a default `map` implementation. + * + * @since 1.0.0 + */ +export const map = ( + F: CovariantWithIndex +): Covariant["map"] => (f) => F.mapWithIndex(f) diff --git a/src/typeclass/FilterableWithIndex.ts b/src/typeclass/FilterableWithIndex.ts new file mode 100644 index 000000000..7525a7a4a --- /dev/null +++ b/src/typeclass/FilterableWithIndex.ts @@ -0,0 +1,104 @@ +/** + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" +import * as O from "@fp-ts/core/Option" +import type { Option } from "@fp-ts/core/Option" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" +import type { Filterable } from "@fp-ts/core/typeclass/Filterable" + +/** + * @category models + * @since 1.0.0 + */ +export interface FilterableWithIndex extends TypeClass { + readonly filterMapWithIndex: ( + f: (a: A, i: I) => Option + ) => (self: Kind) => Kind +} + +/** + * Returns a default `filterMapWithIndex` composition. + * + * @since 1.0.0 + */ +export const filterMapWithIndexComposition = ( + F: Covariant, + G: FilterableWithIndex +) => + ( + f: (a: A, i: I) => Option + ): ( + self: Kind> + ) => Kind> => F.map(G.filterMapWithIndex(f)) + +/** + * Returns a default `filterMap` implementation. + * + * @since 1.0.0 + */ +export const filterMap = ( + F: FilterableWithIndex +): Filterable["filterMap"] => (f) => F.filterMapWithIndex(f) + +/** + * @since 1.0.0 + */ +export const filterWithIndex: ( + F: FilterableWithIndex +) => { + (refinement: (a: A, i: I) => a is B): ( + self: Kind + ) => Kind + ( + predicate: (a: A, i: I) => boolean + ): (self: Kind) => Kind +} = (FilterableWithIndex: FilterableWithIndex) => + ( + predicate: (a: A, i: I) => boolean + ): ((self: Kind) => Kind) => + FilterableWithIndex.filterMapWithIndex(( + b, + i + ) => (predicate(b, i) ? O.some(b) : O.none())) + +/** + * @since 1.0.0 + */ +export const partitionMapWithIndex = ( + F: FilterableWithIndex +) => + (f: (a: A, i: I) => Either) => + ( + self: Kind + ): readonly [Kind, Kind] => { + return [ + pipe(self, F.filterMapWithIndex((a, i) => E.getLeft(f(a, i)))), + pipe(self, F.filterMapWithIndex((a, i) => E.getRight(f(a, i)))) + ] + } + +/** + * @since 1.0.0 + */ +export const partitionWithIndex: ( + F: FilterableWithIndex +) => { + (refinement: (a: A, i: I) => a is B): ( + self: Kind + ) => readonly [Kind, Kind] + (predicate: (a: A, i: I) => boolean): ( + self: Kind + ) => readonly [Kind, Kind] +} = (FilterableWithIndex: FilterableWithIndex) => { + const partitionMapWithIndex_ = partitionMapWithIndex(FilterableWithIndex) + return ( + predicate: (a: A, i: I) => boolean + ): (( + self: Kind + ) => readonly [Kind, Kind]) => + partitionMapWithIndex_((b, i) => (predicate(b, i) ? E.right(b) : E.left(b))) +} diff --git a/src/typeclass/TraversableFilterable.ts b/src/typeclass/TraversableFilterable.ts new file mode 100644 index 000000000..2afc083c7 --- /dev/null +++ b/src/typeclass/TraversableFilterable.ts @@ -0,0 +1,107 @@ +/** + * `TraversableFilterable` represents data structures which can be _partitioned_ with effects in some `Applicative` functor. + * + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" +import type { Option } from "@fp-ts/core/Option" +import * as O from "@fp-ts/core/Option" +import type { Applicative } from "@fp-ts/core/typeclass/Applicative" +import * as compactable from "@fp-ts/core/typeclass/Compactable" +import type { Compactable } from "@fp-ts/core/typeclass/Compactable" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" +import type { Traversable } from "@fp-ts/core/typeclass/Traversable" + +/** + * @category models + * @since 1.0.0 + */ +export interface TraversableFilterable extends TypeClass { + readonly traversePartitionMap: ( + F: Applicative + ) => ( + f: (a: A) => Kind> + ) => ( + self: Kind + ) => Kind, Kind]> + + readonly traverseFilterMap: ( + F: Applicative + ) => ( + f: (a: A) => Kind> + ) => ( + self: Kind + ) => Kind> +} + +/** + * Returns a default `traversePartitionMap` implementation. + * + * @since 1.0.0 + */ +export const traversePartitionMap = ( + T: Traversable & Covariant & Compactable +): TraversableFilterable["traversePartitionMap"] => + (F) => + (f) => + (ta) => + pipe( + ta, + T.traverse(F)(f), + F.map(compactable.separate(T)) + ) + +/** + * Returns a default `traverseFilterMap` implementation. + * + * @since 1.0.0 + */ +export const traverseFilterMap = ( + T: Traversable & Compactable +): TraversableFilterable["traverseFilterMap"] => + (F) => (f) => (ta) => pipe(ta, T.traverse(F)(f), F.map(T.compact)) + +/** + * @since 1.0.0 + */ +export const traverseFilter = ( + T: TraversableFilterable +) => + ( + F: Applicative + ): (( + predicate: (a: A) => Kind + ) => ( + self: Kind + ) => Kind>) => + (predicate) => + T.traverseFilterMap(F)((b) => + pipe( + predicate(b), + F.map((keep) => (keep ? O.some(b) : O.none())) + ) + ) + +/** + * @since 1.0.0 + */ +export const traversePartition = ( + T: TraversableFilterable +) => + ( + F: Applicative + ): (( + predicate: (a: A) => Kind + ) => ( + self: Kind + ) => Kind, Kind]>) => + (predicate) => + T.traversePartitionMap(F)((b) => + pipe( + predicate(b), + F.map((keep) => (keep ? E.right(b) : E.left(b))) + ) + ) From ee777eb1d731d4d2c0041d4b21edee34544c492f Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 15:02:42 +0100 Subject: [PATCH 14/80] add ReadonlyArray module --- .changeset/clean-goats-learn.md | 5 + src/Identity.ts | 4 +- src/Option.ts | 4 +- src/Predicate.ts | 6 +- src/ReadonlyArray.ts | 2240 +++++++++++++++++++++++++++++-- src/String.ts | 4 +- src/internal/Iterable.ts | 7 - src/internal/ReadonlyArray.ts | 11 + test/ReadonlyArray.ts | 1670 +++++++++++++++++++++++ test/internal/Iterable.ts | 12 - 10 files changed, 3841 insertions(+), 122 deletions(-) create mode 100644 .changeset/clean-goats-learn.md delete mode 100644 src/internal/Iterable.ts create mode 100644 src/internal/ReadonlyArray.ts create mode 100644 test/ReadonlyArray.ts delete mode 100644 test/internal/Iterable.ts diff --git a/.changeset/clean-goats-learn.md b/.changeset/clean-goats-learn.md new file mode 100644 index 000000000..2f4981c7f --- /dev/null +++ b/.changeset/clean-goats-learn.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add ReadonlyArray module diff --git a/src/Identity.ts b/src/Identity.ts index 0f759d7f6..41ba1275d 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -3,7 +3,7 @@ */ import { identity } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import * as iterable from "@fp-ts/core/internal/Iterable" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as chainable from "@fp-ts/core/typeclass/Chainable" import type * as coproduct_ from "@fp-ts/core/typeclass/Coproduct" @@ -301,7 +301,7 @@ export const productFlatten: ( * @since 1.0.0 */ export const productAll = (collection: Iterable>): Identity> => - iterable.fromIterable(collection) + readonlyArray.fromIterable(collection) /** * @category instances diff --git a/src/Option.ts b/src/Option.ts index 13736839a..819f08f3a 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -17,8 +17,8 @@ import type { LazyArg } from "@fp-ts/core/Function" import { constNull, constUndefined, pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import * as either from "@fp-ts/core/internal/Either" -import * as iterable from "@fp-ts/core/internal/Iterable" import * as option from "@fp-ts/core/internal/Option" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import type { Predicate, Refinement } from "@fp-ts/core/Predicate" import type * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" @@ -681,7 +681,7 @@ export const coproductEither = (that: Option) => * @since 1.0.0 */ export const coproductAll = (collection: Iterable>): Option => { - const options = iterable.fromIterable(collection) + const options = readonlyArray.fromIterable(collection) return options.length > 0 ? SemiCoproduct.coproductMany(options.slice(1))(options[0]) : option.none diff --git a/src/Predicate.ts b/src/Predicate.ts index 641ba2169..dce414810 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -3,7 +3,7 @@ */ import { constFalse, constTrue } from "@fp-ts/core/Function" import type { TypeLambda } from "@fp-ts/core/HKT" -import * as iterable from "@fp-ts/core/internal/Iterable" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monoid from "@fp-ts/core/typeclass/Monoid" @@ -132,7 +132,7 @@ export const productMany = (collection: Iterable>) => if (self(head) === false) { return false } - const predicates = iterable.fromIterable(collection) + const predicates = readonlyArray.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](tail[i]) === false) { return false @@ -159,7 +159,7 @@ export const productAll = ( collection: Iterable> ): Predicate> => (as) => { - const predicates = iterable.fromIterable(collection) + const predicates = readonlyArray.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](as[i]) === false) { return false diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 509507fa2..b299f7ce3 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1,16 +1,42 @@ /** * @since 1.0.0 */ - -import type { TypeLambda } from "@fp-ts/core/HKT" -import * as iterable from "@fp-ts/core/internal/Iterable" -import type * as applicative from "@fp-ts/core/typeclass/Applicative" +import type { Either } from "@fp-ts/core/Either" +import { identity, pipe } from "@fp-ts/core/Function" +import type { LazyArg } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as either from "@fp-ts/core/internal/Either" +import * as option from "@fp-ts/core/internal/Option" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" +import * as number from "@fp-ts/core/Number" +import type { Option } from "@fp-ts/core/Option" +import * as O from "@fp-ts/core/Option" +import type { Predicate, Refinement } from "@fp-ts/core/Predicate" +import * as string from "@fp-ts/core/String" +import * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as chainable from "@fp-ts/core/typeclass/Chainable" +import type * as compactable from "@fp-ts/core/typeclass/Compactable" +import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" +import * as filterable from "@fp-ts/core/typeclass/Filterable" +import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" +import * as foldable from "@fp-ts/core/typeclass/Foldable" import type * as invariant from "@fp-ts/core/typeclass/Invariant" -import type * as of_ from "@fp-ts/core/typeclass/Of" +import type * as monad from "@fp-ts/core/typeclass/Monad" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as of_ from "@fp-ts/core/typeclass/Of" +import * as order from "@fp-ts/core/typeclass/Order" +import type { Order } from "@fp-ts/core/typeclass/Order" +import type * as pointed from "@fp-ts/core/typeclass/Pointed" import type * as product_ from "@fp-ts/core/typeclass/Product" -import type * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import { fromCombine } from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as traversable from "@fp-ts/core/typeclass/Traversable" +import * as traversableFilterable from "@fp-ts/core/typeclass/TraversableFilterable" /** * @category type lambdas @@ -33,181 +59,2207 @@ export type NonEmptyReadonlyArray = readonly [A, ...Array] export type NonEmptyArray = [A, ...Array] /** + * Builds a `NonEmptyArray` from an non-empty collection of elements. + * + * @category constructors * @since 1.0.0 */ -export const isNonEmpty = (as: ReadonlyArray): as is NonEmptyReadonlyArray => as.length > 0 +export const make = >( + ...elements: Elements +): NonEmptyArray => elements /** + * Return a `NonEmptyArray` of length `n` with element `i` initialized with `f(i)`. + * + * **Note**. `n` is normalized to an integer >= 1. + * * @category constructors * @since 1.0.0 */ -export const empty: () => Array = () => [] +export const makeBy = (f: (i: number) => A) => + (n: number): NonEmptyArray => { + const max = Math.max(1, Math.floor(n)) + const out: NonEmptyArray = [f(0)] + for (let i = 1; i < max; i++) { + out.push(f(i)) + } + return out + } /** - * Test whether a `ReadonlyArray` is empty narrowing down the type to `[]`. + * Return a `NonEmptyArray` containing a range of integers, including both endpoints. * - * @category predicates + * @category constructors * @since 1.0.0 */ -export const isEmpty = (self: ReadonlyArray): self is readonly [] => self.length === 0 +export const range = (start: number, end: number): NonEmptyArray => + start <= end ? makeBy((i) => start + i)(end - start + 1) : [start] + +/** + * Return a `NonEmptyArray` containing a value repeated the specified number of times. + * + * **Note**. `n` is normalized to an integer >= 1. + * + * @category constructors + * @since 1.0.0 + */ +export const replicate = (a: A): ((n: number) => NonEmptyArray) => makeBy(() => a) /** + * @category conversions * @since 1.0.0 */ -export const product = ( - that: ReadonlyArray +export const fromIterable: (collection: Iterable) => Array = readonlyArray.fromIterable + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromOption = ( + self: Option +): Array => (option.isNone(self) ? [] : [self.value]) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromEither = ( + self: Either +): Array => (either.isLeft(self) ? [] : [self.right]) + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const match = ( + onEmpty: LazyArg, + onNonEmpty: (head: A, tail: Array) => C ) => - (self: ReadonlyArray): Array<[A, B]> => { - if (isEmpty(self) || isEmpty(that)) { - return empty() + (self: ReadonlyArray): B | C => + isNonEmpty(self) ? onNonEmpty(headNonEmpty(self), tailNonEmpty(self)) : onEmpty() + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const matchRight = ( + onEmpty: LazyArg, + onNonEmpty: (init: Array, last: A) => C +) => + (self: ReadonlyArray): B | C => + isNonEmpty(self) ? + onNonEmpty(initNonEmpty(self), lastNonEmpty(self)) : + onEmpty() + +/** + * Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const prepend = ( + head: B +) => (self: Iterable): NonEmptyArray => [head, ...self] + +/** + * @category mutations + * @since 1.0.0 + */ +export const prependAll = (that: Iterable) => + (self: Iterable): Array => fromIterable(that).concat(fromIterable(self)) + +/** + * @category mutations + * @since 1.0.0 + */ +export function prependAllNonEmpty( + that: NonEmptyReadonlyArray +): (self: Iterable) => NonEmptyArray +export function prependAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => NonEmptyArray +export function prependAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => Array { + return prependAll(that) +} + +/** + * Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const append = ( + last: B +) => (self: Iterable): NonEmptyArray => [...self, last] as any + +/** + * @category mutations + * @since 1.0.0 + */ +export const appendAll = (that: Iterable) => + (self: Iterable): Array => fromIterable(self).concat(fromIterable(that)) + +/** + * @category mutations + * @since 1.0.0 + */ +export function appendAllNonEmpty( + that: NonEmptyReadonlyArray +): (self: Iterable) => NonEmptyArray +export function appendAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => NonEmptyArray +export function appendAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => Array { + return appendAll(that) +} + +/** + * Fold an `Iterable` from the left, keeping all intermediate results instead of only the final result. + * + * @category folding + * @since 1.0.0 + */ +export const scan = (b: B, f: (b: B, a: A) => B) => + (self: Iterable): NonEmptyArray => { + const out: NonEmptyArray = [b] + let i = 0 + for (const a of self) { + out[i + 1] = f(out[i], a) + i++ } - const out: Array<[A, B]> = [] - for (let i = 0; i < self.length; i++) { - for (let j = 0; j < that.length; j++) { - out.push([self[i], that[j]]) - } + return out + } + +/** + * Fold an `Iterable` from the right, keeping all intermediate results instead of only the final result. + * + * @category folding + * @since 1.0.0 + */ +export const scanRight = (b: B, f: (b: B, a: A) => B) => + (self: Iterable): NonEmptyArray => { + const input = fromIterable(self) + const out: NonEmptyArray = new Array(input.length + 1) as any + out[input.length] = b + for (let i = input.length - 1; i >= 0; i--) { + out[i] = f(out[i + 1], input[i]) } return out } /** - * @category mapping + * Test whether a `ReadonlyArray` is empty narrowing down the type to `[]`. + * + * @category predicates * @since 1.0.0 */ -export const map = (f: (a: A) => B): (self: ReadonlyArray) => Array => - mapWithIndex((a) => f(a)) +export const isEmpty = (self: ReadonlyArray): self is readonly [] => self.length === 0 /** - * @category mapping + * Test whether a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`. + * + * @category predicates * @since 1.0.0 */ -export const mapWithIndex = ( - f: (a: A, i: number) => B -) => (self: ReadonlyArray): Array => self.map((a, i) => f(a, i)) +export const isNonEmpty: (self: ReadonlyArray) => self is NonEmptyReadonlyArray = + readonlyArray.isNonEmpty /** - * @category instances + * Return the number of elements in a `ReadonlyArray`. + * + * @category getters * @since 1.0.0 */ -export const Covariant: covariant.Covariant = covariant.make(map) +export const size = (self: ReadonlyArray): number => self.length + +const isOutOfBound = (i: number, as: ReadonlyArray): boolean => i < 0 || i >= as.length + +const clamp = (i: number, as: ReadonlyArray): number => + Math.floor(Math.min(Math.max(0, i), as.length)) /** + * This function provides a safe way to read a value at a particular index from a `ReadonlyArray`. + * + * @category getters * @since 1.0.0 */ -export const productMany: ( - collection: Iterable> -) => (self: ReadonlyArray) => ReadonlyArray> = semiProduct - .productMany( - Covariant, - product - ) +export const get = (index: number) => + (self: ReadonlyArray): Option => { + const i = Math.floor(index) + return isOutOfBound(i, self) ? option.none : option.some(self[i]) + } /** + * Return a tuple containing the first element, and a new `Array` of the remaining elements, if any. + * + * @category getters * @since 1.0.0 */ -export const productAll = ( - collection: Iterable> -): ReadonlyArray> => { - const arrays = Array.from(collection) - if (isEmpty(arrays)) { - return empty() +export const unprepend = ( + self: NonEmptyReadonlyArray +): [A, Array] => [headNonEmpty(self), tailNonEmpty(self)] + +/** + * Return a tuple containing a copy of the `NonEmptyReadonlyArray` without its last element, and that last element. + * + * @category getters + * @since 1.0.0 + */ +export const unappend = ( + self: NonEmptyReadonlyArray +): [Array, A] => [initNonEmpty(self), lastNonEmpty(self)] + +/** + * Get the first element of a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. + * + * @category getters + * @since 1.0.0 + */ +export const head: (self: ReadonlyArray) => Option = get(0) + +/** + * Gets an element unsafely, will throw on out of bounds. + * + * @since 1.0.0 + * @category unsafe + */ +export const unsafeGet = (index: number) => + (self: ReadonlyArray): A => { + const i = Math.floor(index) + if (isOutOfBound(i, self)) { + throw new Error(`Index ${i} out of bounds`) + } + return self[i] } - return productMany(arrays.slice(1))(arrays[0]) -} /** - * @category mapping + * @category getters * @since 1.0.0 */ -export const imap: ( - to: (a: A) => B, - from: (b: B) => A -) => (self: ReadonlyArray) => ReadonlyArray = covariant.imap(map) +export const headNonEmpty: (self: NonEmptyReadonlyArray) => A = unsafeGet(0) /** - * @category instances + * Get the last element in a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. + * + * @category getters * @since 1.0.0 */ -export const Invariant: invariant.Invariant = { - imap -} +export const last = (self: ReadonlyArray): Option => + isNonEmpty(self) ? option.some(lastNonEmpty(self)) : option.none /** - * @category instances + * @category getters * @since 1.0.0 */ -export const SemiProduct: semiProduct.SemiProduct = { - ...Invariant, - product, - productMany -} +export const lastNonEmpty = (as: NonEmptyReadonlyArray): A => as[as.length - 1] /** - * @category instances + * Get all but the first element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. + * + * @category getters * @since 1.0.0 */ -export const SemiApplicative: semiApplicative.SemiApplicative = { - ...SemiProduct, - ...Covariant +export const tail = (self: Iterable): Option> => { + const input = fromIterable(self) + return isNonEmpty(input) ? option.some(tailNonEmpty(input)) : option.none } /** - * @category constructors + * @category getters * @since 1.0.0 */ -export const of = (a: A): NonEmptyArray => [a] +export const tailNonEmpty = (self: NonEmptyReadonlyArray): Array => self.slice(1) /** - * @category instances + * Get all but the last element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. + * + * @category getters * @since 1.0.0 */ -export const Of: of_.Of = { - of +export const init = (self: Iterable): Option> => { + const input = fromIterable(self) + return isNonEmpty(input) ? option.some(initNonEmpty(input)) : option.none } /** - * @category instances + * Get all but the last element of a non empty array, creating a new array. + * + * @category getters * @since 1.0.0 */ -export const Product: product_.Product = { - ...Of, - ...SemiProduct, - productAll +export const initNonEmpty = (self: NonEmptyReadonlyArray): Array => self.slice(0, -1) + +/** + * Keep only a max number of elements from the start of an `Iterable`, creating a new `Array`. + * + * **Note**. `n` is normalized to a non negative integer. + * + * @category getters + * @since 1.0.0 + */ +export const take = (n: number) => + (self: Iterable): Array => { + const input = fromIterable(self) + return input.slice(0, clamp(n, input)) + } + +/** + * Keep only a max number of elements from the end of an `Iterable`, creating a new `Array`. + * + * **Note**. `n` is normalized to a non negative integer. + * + * @category getters + * @since 1.0.0 + */ +export const takeRight = (n: number) => + (self: Iterable): Array => { + const input = fromIterable(self) + const i = clamp(n, input) + return i === 0 ? [] : input.slice(-i) + } + +/** + * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. + * + * @category getters + * @since 1.0.0 + */ +export function takeWhile( + refinement: Refinement +): (self: Iterable) => Array +export function takeWhile( + predicate: Predicate +): (self: Iterable) => Array +export function takeWhile( + predicate: Predicate +): (self: Iterable) => Array { + return (self: Iterable) => { + const out: Array = [] + for (const a of self) { + if (!predicate(a)) { + break + } + out.push(a) + } + return out + } +} + +const spanIndex = (self: Iterable, predicate: Predicate): number => { + let i = 0 + for (const a of self) { + if (!predicate(a)) { + break + } + i++ + } + return i } + /** - * @category instances + * Split an `Iterable` into two parts: + * + * 1. the longest initial subarray for which all elements satisfy the specified predicate + * 2. the remaining elements + * + * @category filtering * @since 1.0.0 */ -export const Applicative: applicative.Applicative = { - ...SemiApplicative, - ...Product +export function span( + refinement: Refinement +): (self: Iterable) => [init: Array, rest: Array] +export function span( + predicate: Predicate +): ( + self: Iterable +) => [init: Array, rest: Array] +export function span( + predicate: Predicate +): (self: Iterable) => [init: Array, rest: Array] +export function span( + predicate: Predicate +): (self: Iterable) => [init: Array, rest: Array] { + return (self) => splitAt(spanIndex(self, predicate))(self) } /** - * @category conversions + * Drop a max number of elements from the start of an `Iterable`, creating a new `Array`. + * + * **Note**. `n` is normalized to a non negative integer. + * + * @category getters * @since 1.0.0 */ -export const fromIterable: (collection: Iterable) => ReadonlyArray = iterable.fromIterable +export const drop = (n: number) => + (self: Iterable): Array => { + const input = fromIterable(self) + return input.slice(clamp(n, input), input.length) + } /** - * @category mutations + * Drop a max number of elements from the end of an `Iterable`, creating a new `Array`. + * + * **Note**. `n` is normalized to a non negative integer. + * + * @category getters * @since 1.0.0 */ -export const appendAll = (that: Iterable) => - (self: Iterable): Array => fromIterable(self).concat(fromIterable(that)) +export const dropRight = (n: number) => + (self: Iterable): Array => { + const input = fromIterable(self) + return input.slice(0, input.length - clamp(n, input)) + } /** - * @category mutations + * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. + * + * @category getters * @since 1.0.0 */ -export function appendAllNonEmpty( - that: NonEmptyReadonlyArray -): (self: Iterable) => NonEmptyArray -export function appendAllNonEmpty( - that: Iterable -): (self: NonEmptyReadonlyArray) => NonEmptyArray -export function appendAllNonEmpty( - that: Iterable -): (self: NonEmptyReadonlyArray) => Array { - return appendAll(that) +export function dropWhile( + refinement: Refinement +): (self: Iterable) => Array +export function dropWhile( + predicate: Predicate +): (self: Iterable) => Array +export function dropWhile( + predicate: Predicate +): (self: Iterable) => Array +export function dropWhile( + predicate: Predicate +): (self: Iterable) => Array { + return (self) => fromIterable(self).slice(spanIndex(self, predicate)) } + +/** + * Return the first index for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export const findFirstIndex = (predicate: Predicate) => + (self: Iterable): Option => { + let i = 0 + for (const a of self) { + if (predicate(a)) { + return option.some(i) + } + i++ + } + return option.none + } + +/** + * Return the last index for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export const findLastIndex = (predicate: Predicate) => + (self: Iterable): Option => { + const input = fromIterable(self) + for (let i = input.length - 1; i >= 0; i--) { + if (predicate(input[i])) { + return option.some(i) + } + } + return option.none + } + +/** + * Find the first element for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export function findFirst( + refinement: Refinement +): (self: Iterable) => Option +export function findFirst( + predicate: Predicate +): (self: Iterable) => Option +export function findFirst(predicate: Predicate): (self: Iterable) => Option +export function findFirst(predicate: Predicate): (self: Iterable) => Option { + return (self) => { + const input = fromIterable(self) + for (let i = 0; i < input.length; i++) { + if (predicate(input[i])) { + return option.some(input[i]) + } + } + return option.none + } +} + +/** + * Find the last element for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export function findLast( + refinement: Refinement +): (self: Iterable) => Option +export function findLast( + predicate: Predicate +): (self: Iterable) => Option +export function findLast(predicate: Predicate): (self: Iterable) => Option +export function findLast(predicate: Predicate): (self: Iterable) => Option { + return (self) => { + const input = fromIterable(self) + for (let i = input.length - 1; i >= 0; i--) { + if (predicate(input[i])) { + return option.some(input[i]) + } + } + return option.none + } +} + +/** + * Insert an element at the specified index, creating a new `NonEmptyArray`, + * or return `None` if the index is out of bounds. + * + * @category mutations + * @since 1.0.0 + */ +export const insertAt = (i: number, b: B) => + (self: Iterable): Option> => { + const out: Array = Array.from(self) + // v--- `= self.length` ok, it means inserting in last position + if (i < 0 || i > out.length) { + return option.none + } + out.splice(i, 0, b) + return option.some(out) as any + } + +/** + * Change the element at the specified index, creating a new `Array`, + * or return a copy of the input if the index is out of bounds. + * + * @category mutations + * @since 1.0.0 + */ +export const replace = ( + i: number, + b: B +): ((self: Iterable) => Array) => modify(i, () => b) + +/** + * @category mutations + * @since 1.0.0 + */ +export const replaceOption = ( + i: number, + b: B +): ((self: Iterable) => Option>) => modifyOption(i, () => b) + +/** + * Apply a function to the element at the specified index, creating a new `Array`, + * or return a copy of the input if the index is out of bounds. + * + * @category mutations + * @since 1.0.0 + */ +export const modify = (i: number, f: (a: A) => B) => + (self: Iterable): Array => + pipe(modifyOption(i, f)(self), O.getOrElse(() => Array.from(self))) + +/** + * Apply a function to the element at the specified index, creating a new `Array`, + * or return `None` if the index is out of bounds. + +* @category mutations + * @since 1.0.0 + */ +export const modifyOption = (i: number, f: (a: A) => B) => + (self: Iterable): Option> => { + const out = Array.from(self) + if (isOutOfBound(i, out)) { + return O.none() + } + const next = f(out[i]) + // @ts-expect-error + out[i] = next + return O.some(out) + } + +/** + * Delete the element at the specified index, creating a new `Array`, + * or return a copy of the input if the index is out of bounds. + * + * @category mutations + * @since 1.0.0 + */ +export const remove = (i: number) => + (self: Iterable): Array => { + const out = Array.from(self) + if (isOutOfBound(i, out)) { + return out + } + out.splice(i, 1) + return out + } + +/** + * Reverse an `Iterable`, creating a new `Array`. + * + * @category mutations + * @since 1.0.0 + */ +export const reverse = ( + self: Iterable +): Array => Array.from(self).reverse() + +/** + * @since 1.0.0 + */ +export const reverseNonEmpty = ( + self: NonEmptyReadonlyArray +): NonEmptyArray => [lastNonEmpty(self), ...self.slice(0, -1).reverse()] + +/** + * Return all the `Right` elements from an `Interable` of `Either`s. + * + * @category getters + * @since 1.0.0 + */ +export const rights = (self: Iterable>): Array => { + const out: Array = [] + for (const a of self) { + if (either.isRight(a)) { + out.push(a.right) + } + } + return out +} + +/** + * Return all the `Left` elements from an `Interable` of `Either`s. + * + * @category getters + * @since 1.0.0 + */ +export const lefts = (self: Iterable>): Array => { + const out: Array = [] + for (const a of self) { + if (either.isLeft(a)) { + out.push(a.left) + } + } + return out +} + +/** + * Sort the elements of an `Iterable` in increasing order, creating a new `Array`. + * + * @category sorting + * @since 1.0.0 + */ +export const sort = (O: Order) => + (self: Iterable): Array => { + const out = Array.from(self) + out.sort((self, that) => O.compare(that)(self)) + return out + } + +/** + * Sort the elements of a `NonEmptyReadonlyArray` in increasing order, creating a new `NonEmptyArray`. + * + * @category sorting + * @since 1.0.0 + */ +export const sortNonEmpty = (O: Order) => + (self: NonEmptyReadonlyArray): NonEmptyArray => sort(O)(self) as any + +/** + * Sort the elements of an `Iterable` in increasing order, where elements are compared + * using first `orders[0]`, then `orders[1]`, etc... + * + * @category sorting + * @since 1.0.0 + */ +export const sortBy = ( + ...orders: ReadonlyArray> +) => + ( + self: Iterable + ): Array => { + const input = fromIterable(self) + return (isNonEmpty(input) ? sortByNonEmpty(...orders)(input) : []) + } + +/** + * @category sorting + * @since 1.0.0 + */ +export const sortByNonEmpty = ( + ...orders: ReadonlyArray> +): ((as: NonEmptyReadonlyArray) => NonEmptyArray) => + sortNonEmpty(order.getMonoid().combineAll(orders)) + +/** + * Takes two `Iterable`s and returns an `Array` of corresponding pairs. + * If one input `Iterable` is short, excess elements of the + * longer `Iterable` are discarded. + * + * @category mutations + * @since 1.0.0 + */ +export const zip = ( + that: Iterable +): (self: Iterable) => Array<[A, B]> => zipWith(that, (a, b) => [a, b]) + +/** + * Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one + * input `Iterable` is short, excess elements of the longer `Iterable` are discarded. + * + * @category mutations + * @since 1.0.0 + */ +export const zipWith = (that: Iterable, f: (a: A, b: B) => C) => + (self: Iterable): Array => { + const as = fromIterable(self) + const bs = fromIterable(that) + return isNonEmpty(as) && isNonEmpty(bs) ? zipNonEmptyWith(bs, f)(as) : [] + } + +/** + * @category mutations + * @since 1.0.0 + */ +export const zipNonEmpty = (that: NonEmptyReadonlyArray) => + (self: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> => + pipe( + self, + zipNonEmptyWith(that, (a, b) => [a, b]) + ) + +/** + * @category mutations + * @since 1.0.0 + */ +export const zipNonEmptyWith = (that: NonEmptyReadonlyArray, f: (a: A, b: B) => C) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const cs: NonEmptyArray = [f(headNonEmpty(self), headNonEmpty(that))] + const len = Math.min(self.length, that.length) + for (let i = 1; i < len; i++) { + cs[i] = f(self[i], that[i]) + } + return cs + } + +/** + * This function is the inverse of `zip`. Takes an `Iterable` of pairs and return two corresponding `Array`s. + * + * @category mutations + * @since 1.0.0 + */ +export const unzip = ( + self: Iterable<[A, B]> +): [Array, Array] => { + const input = fromIterable(self) + return isNonEmpty(input) ? unzipNonEmpty(input) : [[], []] +} + +/** + * @category mutations + * @since 1.0.0 + */ +export const unzipNonEmpty = ( + self: NonEmptyReadonlyArray<[A, B]> +): [NonEmptyArray, NonEmptyArray] => { + const fa: NonEmptyArray = [self[0][0]] + const fb: NonEmptyArray = [self[0][1]] + for (let i = 1; i < self.length; i++) { + fa[i] = self[i][0] + fb[i] = self[i][1] + } + return [fa, fb] +} + +/** + * Places an element in between members of an `Iterable` + * + * @category mutations + * @since 1.0.0 + */ +export const intersperse = (middle: B) => + (self: Iterable): Array => { + const input = fromIterable(self) + return (isNonEmpty(input) ? intersperseNonEmpty(middle)(input) : []) + } + +/** + * Places an element in between members of a `NonEmptyReadonlyArray` + * + * @category mutations + * @since 1.0.0 + */ +export const intersperseNonEmpty = (middle: B) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const out: NonEmptyArray = [headNonEmpty(self)] + const tail = tailNonEmpty(self) + for (let i = 0; i < tail.length; i++) { + if (i < tail.length) { + out.push(middle) + } + out.push(tail[i]) + } + return out + } + +/** + * Apply a function to the head, creating a new `NonEmptyReadonlyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const modifyNonEmptyHead = (f: (a: A) => B) => + ( + self: NonEmptyReadonlyArray + ): NonEmptyArray => [f(headNonEmpty(self)), ...tailNonEmpty(self)] + +/** + * Change the head, creating a new `NonEmptyReadonlyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const setNonEmptyHead = ( + b: B +): ((self: NonEmptyReadonlyArray) => NonEmptyArray) => modifyNonEmptyHead(() => b) + +/** + * Apply a function to the last element, creating a new `NonEmptyReadonlyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const modifyNonEmptyLast = (f: (a: A) => B) => + (self: NonEmptyReadonlyArray): NonEmptyArray => + pipe(initNonEmpty(self), append(f(lastNonEmpty(self)))) + +/** + * Change the last element, creating a new `NonEmptyReadonlyArray`. + * + * @category mutations + * @since 1.0.0 + */ +export const setNonEmptyLast = ( + b: B +): ((self: NonEmptyReadonlyArray) => NonEmptyArray) => modifyNonEmptyLast(() => b) + +/** + * Rotate an `Iterable` by `n` steps. + * + * @category mutations + * @since 1.0.0 + */ +export const rotate = (n: number) => + (self: Iterable): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? rotateNonEmpty(n)(input) : [] + } + +/** + * Rotate a `NonEmptyReadonlyArray` by `n` steps. + * + * @category mutations + * @since 1.0.0 + */ +export const rotateNonEmpty = (n: number) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const len = self.length + const m = Math.round(n) % len + if (isOutOfBound(Math.abs(m), self) || m === 0) { + return copy(self) + } + if (m < 0) { + const [f, s] = splitNonEmptyAt(-m)(self) + return appendAllNonEmpty(f)(s) + } else { + return rotateNonEmpty(m - len)(self) + } + } + +/** + * Returns a function that checks if a `ReadonlyArray` contains a given value using a provided `equivalence` function. + * + * @category predicates + * @since 1.0.0 + */ +export const contains = (equivalence: Equivalence) => + (a: A) => + (self: Iterable): boolean => { + for (const i of self) { + if (equivalence(a, i)) { + return true + } + } + return false + } + +/** + * Remove duplicates from am `Iterable`, keeping the first occurrence of an element. + * + * @category mutations + * @since 1.0.0 + */ +export const uniq = (equivalence: Equivalence) => + (self: Iterable): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? uniqNonEmpty(equivalence)(input) : [] + } + +/** + * Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element. + * + * @category mutations + * @since 1.0.0 + */ +export const uniqNonEmpty = (equivalence: Equivalence) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const out: NonEmptyArray = [headNonEmpty(self)] + const rest = tailNonEmpty(self) + for (const a of rest) { + if (out.every((o) => !equivalence(a, o))) { + out.push(a) + } + } + return out + } + +/** + * A useful recursion pattern for processing an `Iterable` to produce a new `Array`, often used for "chopping" up the input + * `Iterable`. Typically chop is called with some function that will consume an initial prefix of the `Iterable` and produce a + * value and the rest of the `Array`. + * + * @since 1.0.0 + */ +export const chop = ( + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] +) => + (self: Iterable): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? chopNonEmpty(f)(input) : [] + } + +/** + * A useful recursion pattern for processing a `NonEmptyReadonlyArray` to produce a new `NonEmptyReadonlyArray`, often used for "chopping" up the input + * `NonEmptyReadonlyArray`. Typically `chop` is called with some function that will consume an initial prefix of the `NonEmptyReadonlyArray` and produce a + * value and the tail of the `NonEmptyReadonlyArray`. + * + * @since 1.0.0 + */ +export const chopNonEmpty = ( + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] +) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const [b, rest] = f(self) + const out: NonEmptyArray = [b] + let next: ReadonlyArray = rest + while (readonlyArray.isNonEmpty(next)) { + const [b, rest] = f(next) + out.push(b) + next = rest + } + return out + } + +/** + * Splits an `Iterable` into two pieces, the first piece has max `n` elements. + * + * @category getters + * @since 1.0.0 + */ +export const splitAt = (n: number) => + (self: Iterable): [Array, Array] => { + const input = Array.from(self) + return n >= 1 && isNonEmpty(input) ? + splitNonEmptyAt(n)(input) : + isEmpty(input) ? + [input, []] : + [[], input] + } + +/** + * @since 1.0.0 + */ +export function copy(self: NonEmptyReadonlyArray): NonEmptyArray +export function copy(self: ReadonlyArray): Array +export function copy(self: ReadonlyArray): Array { + return self.slice() +} + +/** + * Splits a `NonEmptyReadonlyArray` into two pieces, the first piece has max `n` elements. + * + * @category getters + * @since 1.0.0 + */ +export const splitNonEmptyAt = (n: number) => + (self: NonEmptyReadonlyArray): [NonEmptyArray, Array] => { + const m = Math.max(1, n) + return m >= self.length ? + [copy(self), []] : + [pipe(self.slice(1, m), prepend(headNonEmpty(self))), self.slice(m)] + } + +/** + * Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of + * the `Iterable`. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive + * definition of `chunksOf`; it satisfies the property that + * + * ```ts + * chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys))) + * ``` + * + * whenever `n` evenly divides the length of `self`. + * + * @category getters + * @since 1.0.0 + */ +export const chunksOf = (n: number) => + (self: Iterable): Array> => { + const input = fromIterable(self) + return isNonEmpty(input) ? chunksOfNonEmpty(n)(input) : [] + } + +/** + * Splits a `NonEmptyReadonlyArray` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of + * the `NonEmptyReadonlyArray`. + * + * @category getters + * @since 1.0.0 + */ +export const chunksOfNonEmpty = ( + n: number +): ((self: NonEmptyReadonlyArray) => NonEmptyArray>) => + chopNonEmpty(splitNonEmptyAt(n)) + +/** + * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s. + * + * @category grouping + * @since 1.0.0 + */ +export const group = (equivalence: Equivalence) => + (self: NonEmptyReadonlyArray): NonEmptyArray> => + pipe( + self, + chopNonEmpty((as) => { + const h = headNonEmpty(as) + const out: NonEmptyArray = [h] + let i = 1 + for (; i < as.length; i++) { + const a = as[i] + if (equivalence(a, h)) { + out.push(a) + } else { + break + } + } + return [out, as.slice(i)] + }) + ) + +/** + * Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning + * function on each element, and grouping the results according to values returned + * + * @category grouping + * @since 1.0.0 + */ +export const groupBy = (f: (a: A) => string) => + (self: Iterable): Record> => { + const out: Record> = {} + for (const a of self) { + const k = f(a) + if (Object.prototype.hasOwnProperty.call(out, k)) { + out[k].push(a) + } else { + out[k] = [a] + } + } + return out + } + +/** + * @category mutations + * @since 1.0.0 + */ +export const union = (equivalence: Equivalence) => + (that: ReadonlyArray) => + (self: ReadonlyArray): Array => { + const a = Array.from(self) + const b = Array.from(that) + return isNonEmpty(a) && isNonEmpty(b) ? + unionNonEmpty(equivalence)(b)(a) : + isNonEmpty(a) ? + a : + b + } + +/** + * @category mutations + * @since 1.0.0 + */ +export const unionNonEmpty = (equivalence: Equivalence): { + (that: NonEmptyReadonlyArray): (self: ReadonlyArray) => NonEmptyArray + (that: ReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray +} => + // @ts-expect-error + (that: ReadonlyArray) => + (self: NonEmptyReadonlyArray): NonEmptyArray => + uniqNonEmpty(equivalence)(appendAllNonEmpty(that)(self)) + +/** + * Creates an `Array` of unique values that are included in all given `Iterable`s. + * The order and references of result values are determined by the first `Iterable`. + * + * @category mutations + * @since 1.0.0 + */ +export const intersection = (equivalence: Equivalence) => + (that: Iterable) => + (self: Iterable): Array => + fromIterable(self).filter((a) => contains(equivalence)(a)(that)) + +/** + * Creates a `Array` of values not included in the other given `Iterable`. + * The order and references of result values are determined by the first `Iterable`. + * + * @category mutations + * @since 1.0.0 + */ +export const difference = (equivalence: Equivalence) => + (that: Iterable) => + (self: Iterable): Array => + fromIterable(self).filter((a) => !contains(equivalence)(a)(that)) + +/** + * @category constructors + * @since 1.0.0 + */ +export const of = (a: A): NonEmptyArray => [a] + +/** + * @category constructors + * @since 1.0.0 + */ +export const empty: () => Array = () => [] + +/** + * @category mapping + * @since 1.0.0 + */ +export const map = (f: (a: A) => B): (self: ReadonlyArray) => Array => + mapWithIndex((a) => f(a)) + +/** + * @category mapping + * @since 1.0.0 + */ +export const mapNonEmpty = ( + f: (a: A) => B +): (self: NonEmptyReadonlyArray) => NonEmptyArray => mapNonEmptyWithIndex(f) + +/** + * @category mapping + * @since 1.0.0 + */ +export const mapWithIndex = ( + f: (a: A, i: number) => B +) => (self: ReadonlyArray): Array => self.map((a, i) => f(a, i)) + +/** + * @category mapping + * @since 1.0.0 + */ +export const mapNonEmptyWithIndex = ( + f: (a: A, i: number) => B +) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const out: NonEmptyArray = [f(headNonEmpty(self), 0)] + for (let i = 1; i < self.length; i++) { + out.push(f(self[i], i)) + } + return out + } + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: ReadonlyArray<{}> = of_.Do(Of) + +/** + * @category mapping + * @since 1.0.0 + */ +export const imap: ( + to: (a: A) => B, + from: (b: B) => A +) => (self: ReadonlyArray) => ReadonlyArray = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = covariant.make(map) + +const let_: ( + name: Exclude, + f: (a: A) => B +) => ( + self: ReadonlyArray +) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let(Covariant) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: (a: A) => ( + self: ReadonlyArray<(a: A) => B> +) => ReadonlyArray = covariant.flap(Covariant) + +/** + * Maps the success value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: (b: B) => (self: ReadonlyArray) => ReadonlyArray = covariant.as( + Covariant +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + ...Of, + ...Covariant +} + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapWithIndex = (f: (a: A, i: number) => ReadonlyArray) => + (self: ReadonlyArray): Array => { + if (isEmpty(self)) { + return [] + } + const out: Array = [] + for (let i = 0; i < self.length; i++) { + out.push(...f(self[i], i)) + } + return out + } + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMap: ( + f: (a: A) => ReadonlyArray +) => (self: ReadonlyArray) => Array = (f) => flatMapWithIndex(f) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNonEmptyWithIndex = (f: (a: A, i: number) => NonEmptyReadonlyArray) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const out: NonEmptyArray = copy(f(headNonEmpty(self), 0)) + for (let i = 1; i < self.length; i++) { + out.push(...f(self[i], i)) + } + return out + } + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNonEmpty: ( + f: (a: A) => NonEmptyReadonlyArray +) => (self: NonEmptyReadonlyArray) => NonEmptyArray = (f) => flatMapNonEmptyWithIndex(f) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatten: (self: ReadonlyArray>) => ReadonlyArray = flatMap_ + .flatten(FlatMap) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flattenNonEmpty: ( + self: NonEmptyReadonlyArray> +) => NonEmptyArray = flatMapNonEmpty(identity) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: ( + bfc: (b: B) => ReadonlyArray +) => (afb: (a: A) => ReadonlyArray) => (a: A) => ReadonlyArray = flatMap_ + .composeKleisliArrow(FlatMap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + ...FlatMap, + ...Covariant +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: ( + name: Exclude, + f: (a: A) => ReadonlyArray +) => ( + self: ReadonlyArray +) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind(Chainable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMapWithIndex = (f: (a: A, i: number) => Option) => + (self: Iterable): Array => { + const as = fromIterable(self) + const out: Array = [] + for (let i = 0; i < as.length; i++) { + const o = f(as[i], i) + if (option.isSome(o)) { + out.push(o.value) + } + } + return out + } + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMap: (f: (a: A) => Option) => (self: Iterable) => Array = (f) => + filterMapWithIndex(f) + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact: (self: Iterable>) => Array = filterMap(identity) + +/** + * @category instances + * @since 1.0.0 + */ +export const Compactable: compactable.Compactable = { + compact +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const separate = ( + self: ReadonlyArray> +): [Array, Array] => { + const left: Array = [] + const right: Array = [] + for (const e of self) { + if (either.isLeft(e)) { + left.push(e.left) + } else { + right.push(e.right) + } + } + return [left, right] +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Filterable: filterable.Filterable = { + filterMap +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + ( + refinement: Refinement + ): (self: ReadonlyArray) => ReadonlyArray + (predicate: Predicate): (self: ReadonlyArray) => ReadonlyArray +} = filterable.filter(Filterable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterWithIndex: { + ( + refinement: (a: A, i: number) => a is B + ): (self: ReadonlyArray) => Array + ( + predicate: (a: A, i: number) => boolean + ): (self: ReadonlyArray) => Array +} = ( + predicate: (a: A, i: number) => boolean +): ((self: ReadonlyArray) => Array) => + filterMapWithIndex((b, i) => (predicate(b, i) ? option.some(b) : option.none)) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partition: { + (refinement: Refinement): ( + self: ReadonlyArray + ) => readonly [ReadonlyArray, ReadonlyArray] + ( + predicate: Predicate + ): (self: ReadonlyArray) => readonly [ReadonlyArray, ReadonlyArray] +} = filterable.partition(Filterable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partitionWithIndex: { + (refinement: (a: A, i: number) => a is B): ( + self: ReadonlyArray + ) => [Array, Array] + (predicate: (a: A, i: number) => boolean): ( + self: ReadonlyArray + ) => [Array, Array] +} = ( + predicate: (a: A, i: number) => boolean +): ((self: ReadonlyArray) => [Array, Array]) => + partitionMapWithIndex((b, i) => (predicate(b, i) ? either.right(b) : either.left(b))) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partitionMap: ( + f: (a: A) => Either +) => (self: ReadonlyArray) => [Array, Array] = (f) => partitionMapWithIndex(f) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partitionMapWithIndex = (f: (a: A, i: number) => Either) => + (self: ReadonlyArray): [Array, Array] => { + const left: Array = [] + const right: Array = [] + for (let i = 0; i < self.length; i++) { + const e = f(self[i], i) + if (either.isLeft(e)) { + left.push(e.left) + } else { + right.push(e.right) + } + } + return [left, right] + } + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = (F: applicative.Applicative) => + ( + f: (a: A) => Kind + ): ((self: ReadonlyArray) => Kind>) => traverseWithIndex(F)(f) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseWithIndex = (F: applicative.Applicative) => + (f: (a: A, i: number) => Kind) => + (self: ReadonlyArray): Kind> => F.productAll(self.map(f)) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseNonEmpty = ( + F: semiApplicative.SemiApplicative +) => + ( + f: (a: A) => Kind + ): ((self: NonEmptyReadonlyArray) => Kind>) => + traverseNonEmptyWithIndex(F)(f) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseNonEmptyWithIndex = ( + F: semiApplicative.SemiApplicative +) => + (f: (a: A, i: number) => Kind) => + (self: NonEmptyReadonlyArray): Kind> => { + const fbs = pipe(self, mapNonEmptyWithIndex(f)) + return pipe(headNonEmpty(fbs), F.productMany(tailNonEmpty(fbs))) + } + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => ( + self: ReadonlyArray> +) => Kind> = traversable.sequence(traverse) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse, + sequence +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind +) => (self: ReadonlyArray) => Kind> = traversable + .traverseTap( + Traversable + ) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequenceNonEmpty = ( + F: semiApplicative.SemiApplicative +): (( + self: NonEmptyReadonlyArray> +) => Kind>) => traverseNonEmpty(F)(identity) + +/** + * @since 1.0.0 + */ +export const product = ( + that: ReadonlyArray +) => + (self: ReadonlyArray): Array<[A, B]> => { + if (isEmpty(self) || isEmpty(that)) { + return empty() + } + const out: Array<[A, B]> = [] + for (let i = 0; i < self.length; i++) { + for (let j = 0; j < that.length; j++) { + out.push([self[i], that[j]]) + } + } + return out + } + +/** + * @since 1.0.0 + */ +export const productMany: ( + collection: Iterable> +) => (self: ReadonlyArray) => ReadonlyArray> = semiProduct + .productMany( + Covariant, + product + ) + +/** + * @since 1.0.0 + */ +export const productAll = ( + collection: Iterable> +): ReadonlyArray> => { + const arrays = Array.from(collection) + if (isEmpty(arrays)) { + return empty() + } + return productMany(arrays.slice(1))(arrays[0]) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + ...Invariant, + product, + productMany +} + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: ( + name: Exclude, + fb: ReadonlyArray +) => ( + self: ReadonlyArray +) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct + .andThenBind(SemiProduct) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + ...SemiProduct, + ...Covariant +} + +/** + * @since 1.0.0 + */ +export const ap: ( + fa: ReadonlyArray +) => (self: ReadonlyArray<(a: A) => B>) => ReadonlyArray = semiApplicative.ap( + SemiApplicative +) + +/** + * Lifts a binary function into `ReadonlyArray`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: ( + f: (a: A, b: B) => C +) => (fa: ReadonlyArray, fb: ReadonlyArray) => ReadonlyArray = semiApplicative.lift2( + SemiApplicative +) + +/** + * Lifts a ternary function into `ReadonlyArray`. + * + * @category lifting + * @since 1.0.0 + */ +export const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: ReadonlyArray, fb: ReadonlyArray, fc: ReadonlyArray) => ReadonlyArray = + semiApplicative.lift3(SemiApplicative) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftSemigroup: (S: Semigroup) => Semigroup> = semiApplicative + .liftSemigroup( + SemiApplicative + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + ...Of, + ...SemiProduct, + productAll +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + ...SemiApplicative, + ...Product +} + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftMonoid: (M: Monoid) => Monoid> = applicative + .liftMonoid( + Applicative + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + ...Pointed, + ...FlatMap +} + +/** + * @category folding + * @since 1.0.0 + */ +export const reduce = (b: B, f: (b: B, a: A) => B) => + (self: ReadonlyArray): B => self.reduce((b, a) => f(b, a), b) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceWithIndex = (b: B, f: (b: B, a: A, i: number) => B) => + (self: ReadonlyArray): B => self.reduce((b, a, i) => f(b, a, i), b) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceRight = (b: B, f: (b: B, a: A) => B) => + (self: ReadonlyArray): B => self.reduceRight((b, a) => f(b, a), b) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceRightWithIndex = (b: B, f: (b: B, a: A, i: number) => B) => + (self: ReadonlyArray): B => self.reduceRight((b, a, i) => f(b, a, i), b) + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce +} + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMap: ( + M: Monoid +) => (f: (a: A) => M) => (self: ReadonlyArray) => M = foldable.foldMap( + Foldable +) + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMapWithIndex = (Monoid: Monoid) => + (f: (a: A, i: number) => M) => + (self: ReadonlyArray): M => + self.reduce((m, a, i) => Monoid.combine(f(a, i))(m), Monoid.empty) + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMapNonEmpty = (S: Semigroup) => + (f: (a: A) => S): (self: NonEmptyReadonlyArray) => S => foldMapNonEmptyWithIndex(S)(f) + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMapNonEmptyWithIndex = (S: Semigroup) => + (f: (a: A, i: number) => S) => + (self: NonEmptyReadonlyArray): S => + tailNonEmpty(self).reduce((s, a, i) => S.combine(f(a, i + 1))(s), f(headNonEmpty(self), 0)) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceKind: ( + F: monad.Monad +) => ( + b: B, + f: (b: B, a: A) => Kind +) => (self: ReadonlyArray) => Kind = foldable.reduceKind( + Foldable +) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceRightKind: ( + F: monad.Monad +) => ( + b: B, + f: (b: B, a: A) => Kind +) => (self: ReadonlyArray) => Kind = foldable + .reduceRightKind( + Foldable + ) + +/** + * @category folding + * @since 1.0.0 + */ +export const foldMapKind: ( + F: Coproduct +) => ( + f: (a: A) => Kind +) => (self: ReadonlyArray) => Kind = foldable.foldMapKind( + Foldable +) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traverseFilterMap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind> +) => (ta: ReadonlyArray) => Kind> = traversableFilterable + .traverseFilterMap({ ...Traversable, ...Compactable }) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traversePartitionMap: ( + F: applicative.Applicative +) => ( + f: (a: A) => Kind> +) => (self: ReadonlyArray) => Kind, ReadonlyArray]> = + traversableFilterable + .traversePartitionMap({ ...Traversable, ...Covariant, ...Compactable }) + +/** + * @category instances + * @since 1.0.0 + */ +export const TraversableFilterable: traversableFilterable.TraversableFilterable< + ReadonlyArrayTypeLambda +> = { + traverseFilterMap, + traversePartitionMap +} + +/** + * Filter values inside a context. + * + * @since 1.0.0 + */ +export const traverseFilter: ( + F: applicative.Applicative +) => ( + predicate: (a: A) => Kind +) => (self: ReadonlyArray) => Kind> = traversableFilterable + .traverseFilter(TraversableFilterable) + +/** + * @since 1.0.0 + */ +export const traversePartition: ( + F: applicative.Applicative +) => ( + predicate: (a: A) => Kind +) => ( + self: ReadonlyArray +) => Kind, ReadonlyArray]> = traversableFilterable + .traversePartition(TraversableFilterable) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftPredicate: { + (refinement: Refinement): (c: C) => Array + (predicate: Predicate): (b: B) => Array +} = (predicate: Predicate) => (b: B) => predicate(b) ? [b] : [] + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftOption = , B>( + f: (...a: A) => Option +) => (...a: A): Array => fromOption(f(...a)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromNullable = (a: A): Array> => + a == null ? empty() : [a as NonNullable] + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftNullable = , B>( + f: (...a: A) => B | null | undefined +): (...a: A) => Array> => (...a) => fromNullable(f(...a)) + +/** + * @category sequencing + * @since 1.0.0 + */ +export const flatMapNullable = ( + f: (a: A) => B | null | undefined +) => + (self: ReadonlyArray): Array> => + isNonEmpty(self) ? fromNullable(f(headNonEmpty(self))) : empty() + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftEither = , E, B>( + f: (...a: A) => Either +) => + (...a: A): Array => { + const e = f(...a) + return either.isLeft(e) ? [] : [e.right] + } + +/** + * Check if a predicate holds true for every `ReadonlyArray` member. + * + * @category lifting + * @since 1.0.0 + */ +export function every( + refinement: Refinement +): Refinement, Array> +export function every(predicate: Predicate): Predicate> +export function every(predicate: Predicate): Predicate> { + return (self) => self.every(predicate) +} + +/** + * Check if a predicate holds true for any `ReadonlyArray` member. + * + * @category predicates + * @since 1.0.0 + */ +export const some = (predicate: Predicate) => + (self: ReadonlyArray): self is NonEmptyArray => self.some(predicate) + +/** + * Alias of [`some`](#some) + * + * @since 1.0.0 + */ +export const has = some + +/** + * Fold a data structure, accumulating values in some `Monoid`, combining adjacent elements + * using the specified separator. + * + * @since 1.0.0 + */ +export const intercalate = (M: Monoid) => + (middle: A) => + (self: ReadonlyArray): A => isNonEmpty(self) ? intercalateNonEmpty(M)(middle)(self) : M.empty + +/** + * Places an element in between members of a `NonEmptyReadonlyArray`, then folds the results using the provided `Semigroup`. + * + * @since 1.0.0 + */ +export const intercalateNonEmpty = ( + S: Semigroup +) => + (middle: A) => + (self: NonEmptyReadonlyArray): A => + semigroup.intercalate(middle)(S).combineMany(tailNonEmpty(self))(headNonEmpty(self)) + +/** + * @since 1.0.0 + */ +export const join: (sep: string) => (self: ReadonlyArray) => string = intercalate( + string.Monoid +) + +/** + * @since 1.0.0 + */ +export const productFlatten: ( + that: ReadonlyArray +) => >( + self: ReadonlyArray +) => ReadonlyArray = semiProduct.productFlatten(SemiProduct) + +/** + * @since 1.0.0 + */ +export const extend = ( + f: (as: ReadonlyArray) => B +) => (self: ReadonlyArray): Array => self.map((_, i, as) => f(as.slice(i))) + +/** + * @since 1.0.0 + */ +export const min = (O: Order): ((self: NonEmptyReadonlyArray) => A) => { + const S = semigroup.min(O) + return (self) => self.reduce((a, acc) => S.combine(acc)(a)) +} + +/** + * @since 1.0.0 + */ +export const max = (O: Order): ((self: NonEmptyReadonlyArray) => A) => { + const S = semigroup.max(O) + return (self) => self.reduce((a, acc) => S.combine(acc)(a)) +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const unfold = (b: B, f: (b: B) => Option): Array => { + const out: Array = [] + let next: B = b + let o: Option + while (option.isSome(o = f(next))) { + const [a, b] = o.value + out.push(a) + next = b + } + return out +} + +/** + * @category instances + * @since 1.0.0 + */ +export const getUnionSemigroup = (equivalence: Equivalence): Semigroup> => + // @ts-expect-error + fromCombine(union(equivalence)) + +/** + * @category instances + * @since 1.0.0 + */ +export const getUnionMonoid = (equivalence: Equivalence): Monoid> => { + const S = getUnionSemigroup(equivalence) + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany(collection)(empty()), + empty: empty() + }) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const getIntersectionSemigroup = ( + equivalence: Equivalence +): Semigroup> => + // @ts-expect-error + fromCombine(intersection(equivalence)) + +/** + * Returns a `Semigroup` for `ReadonlyArray`. + * + * @category instances + * @since 1.0.0 + */ +export const getSemigroup = (): Semigroup> => fromCombine(appendAll) + +/** + * Returns a `Monoid` for `ReadonlyArray`. + * + * @category instances + * @since 1.0.0 + */ +export const getMonoid = (): Monoid> => { + const S = getSemigroup() + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany(collection)(empty()), + empty: empty() + }) +} + +/** + * Derives an `Order` over the `ReadonlyArray` of a given element type from the `Order` of that type. The ordering between two such + * `ReadonlyArray`s is equal to: the first non equal comparison of each `ReadonlyArray`s elements taken pairwise in increasing order, in + * case of equality over all the pairwise elements; the longest `ReadonlyArray` is considered the greatest, if both `ReadonlyArray`s have + * the same length, the result is equality. + * + * @category lifting + * @since 1.0.0 + */ +export const liftOrder = (O: Order): Order> => + order.fromCompare((that) => + (self) => { + const aLen = self.length + const bLen = that.length + const len = Math.min(aLen, bLen) + for (let i = 0; i < len; i++) { + const o = O.compare(that[i])(self[i]) + if (o !== 0) { + return o + } + } + return number.Order.compare(bLen)(aLen) + } + ) diff --git a/src/String.ts b/src/String.ts index 9700222b8..74ecc7a96 100644 --- a/src/String.ts +++ b/src/String.ts @@ -2,8 +2,8 @@ * @since 1.0.0 */ +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import type { Refinement } from "@fp-ts/core/Predicate" -import { isNonEmpty } from "@fp-ts/core/ReadonlyArray" import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import type * as monoid from "@fp-ts/core/typeclass/Monoid" @@ -210,7 +210,7 @@ export const size = (s: string): number => s.length export const split = (separator: string | RegExp) => (s: string): NonEmptyReadonlyArray => { const out = s.split(separator) - return isNonEmpty(out) ? out : [s] + return readonlyArray.isNonEmpty(out) ? out : [s] } /** diff --git a/src/internal/Iterable.ts b/src/internal/Iterable.ts deleted file mode 100644 index 72b5eee8c..000000000 --- a/src/internal/Iterable.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @since 1.0.0 - */ - -/** @internal */ -export const fromIterable = (collection: Iterable): ReadonlyArray => - Array.isArray(collection) ? collection : Array.from(collection) diff --git a/src/internal/ReadonlyArray.ts b/src/internal/ReadonlyArray.ts new file mode 100644 index 000000000..0754a3224 --- /dev/null +++ b/src/internal/ReadonlyArray.ts @@ -0,0 +1,11 @@ +/** + * @since 1.0.0 + */ +import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" + +/** @internal */ +export const isNonEmpty = (as: ReadonlyArray): as is NonEmptyReadonlyArray => as.length > 0 + +/** @internal */ +export const fromIterable = (collection: Iterable): Array => + Array.isArray(collection) ? collection : Array.from(collection) diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts new file mode 100644 index 000000000..550933a4e --- /dev/null +++ b/test/ReadonlyArray.ts @@ -0,0 +1,1670 @@ +import * as E from "@fp-ts/core/Either" +import { identity, pipe } from "@fp-ts/core/Function" +import * as Number from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import type { Predicate } from "@fp-ts/core/Predicate" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as String from "@fp-ts/core/String" +import { deepStrictEqual, double, strictEqual } from "@fp-ts/core/test/util" +import * as Order from "@fp-ts/core/typeclass/Order" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" +import * as assert from "assert" +import * as fc from "fast-check" + +describe.concurrent("ReadonlyArray", () => { + it("instances and derived exports", () => { + expect(RA.Invariant).exist + expect(RA.imap).exist + + expect(RA.Covariant).exist + expect(RA.map).exist + expect(RA.let).exist + expect(RA.flap).exist + expect(RA.as).exist + + expect(RA.Of).exist + expect(RA.of).exist + expect(RA.Do).exist + + expect(RA.Pointed).exist + + expect(RA.FlatMap).exist + expect(RA.flatMap).exist + expect(RA.flatten).exist + expect(RA.composeKleisliArrow).exist + + expect(RA.Chainable).exist + expect(RA.bind).exist + + expect(RA.Monad).exist + + expect(RA.SemiProduct).exist + expect(RA.product).exist + expect(RA.productMany).exist + expect(RA.andThenBind).exist + expect(RA.productFlatten).exist + + expect(RA.Product).exist + expect(RA.productAll).exist + // expect(ReadonlyArray.tuple).exist + // expect(ReadonlyArray.struct).exist + + expect(RA.SemiApplicative).exist + expect(RA.liftSemigroup).exist + expect(RA.lift2).exist + expect(RA.lift3).exist + expect(RA.ap).exist + + expect(RA.Applicative).exist + expect(RA.liftMonoid).exist + + expect(RA.Foldable).exist + expect(RA.reduce).exist + expect(RA.reduceRight).exist + expect(RA.foldMap).exist + expect(RA.reduceKind).exist + expect(RA.reduceRightKind).exist + expect(RA.foldMapKind).exist + + expect(RA.Traversable).exist + expect(RA.traverse).exist + expect(RA.sequence).exist + expect(RA.traverseTap).exist + + expect(RA.Compactable).exist + expect(RA.compact).exist + expect(RA.separate).exist + + expect(RA.Filterable).exist + expect(RA.filterMap).exist + expect(RA.filter).exist + expect(RA.partition).exist + expect(RA.partitionMap).exist + + expect(RA.TraversableFilterable).exist + expect(RA.traverseFilterMap).exist + expect(RA.traversePartitionMap).exist + expect(RA.traverseFilter).exist + expect(RA.traversePartition).exist + + expect(RA.liftPredicate).exist + expect(RA.liftOption).exist + expect(RA.liftNullable).exist + expect(RA.flatMapNullable).exist + }) + + it("fromIterable/Array should return the same reference if the iterable is an Array", () => { + const i = [1, 2, 3] + expect(RA.fromIterable(i) === i).toEqual(true) + }) + + it("fromIterable/Iterable", () => { + expect(RA.fromIterable(new Set([1, 2, 3]))).toEqual([1, 2, 3]) + }) + + describe("iterable inputs", () => { + it("prepend", () => { + deepStrictEqual(pipe([1, 2, 3], RA.prepend(0)), [0, 1, 2, 3]) + deepStrictEqual(pipe([[2]], RA.prepend([1])), [[1], [2]]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.prepend(0)), [0, 1, 2, 3]) + deepStrictEqual(pipe(new Set([[2]]), RA.prepend([1])), [[1], [2]]) + }) + + it("prependAll", () => { + deepStrictEqual(pipe([3, 4], RA.prependAll([1, 2])), [1, 2, 3, 4]) + + deepStrictEqual(pipe([3, 4], RA.prependAll(new Set([1, 2]))), [1, 2, 3, 4]) + deepStrictEqual(pipe(new Set([3, 4]), RA.prependAll([1, 2])), [1, 2, 3, 4]) + }) + + it("prependAllNonEmpty", () => { + deepStrictEqual(pipe([3, 4], RA.prependAllNonEmpty([1, 2])), [1, 2, 3, 4]) + + deepStrictEqual(pipe(RA.make(3, 4), RA.prependAllNonEmpty(new Set([1, 2]))), [1, 2, 3, 4]) + deepStrictEqual(pipe(new Set([3, 4]), RA.prependAllNonEmpty([1, 2])), [1, 2, 3, 4]) + }) + + it("append", () => { + deepStrictEqual(pipe([1, 2, 3], RA.append(4)), [1, 2, 3, 4]) + deepStrictEqual(pipe([[1]], RA.append([2])), [[1], [2]]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.append(4)), [1, 2, 3, 4]) + deepStrictEqual(pipe(new Set([[1]]), RA.append([2])), [[1], [2]]) + }) + + it("appendAll", () => { + deepStrictEqual(pipe([1, 2], RA.appendAll([3, 4])), [1, 2, 3, 4]) + + deepStrictEqual(pipe([1, 2], RA.appendAll(new Set([3, 4]))), [1, 2, 3, 4]) + deepStrictEqual(pipe(new Set([1, 2]), RA.appendAll([3, 4])), [1, 2, 3, 4]) + }) + + it("appendAllNonEmpty", () => { + deepStrictEqual(pipe([1, 2], RA.appendAllNonEmpty([3, 4])), [1, 2, 3, 4]) + + deepStrictEqual(pipe(RA.make(1, 2), RA.appendAllNonEmpty(new Set([3, 4]))), [1, 2, 3, 4]) + deepStrictEqual(pipe(new Set([1, 2]), RA.appendAllNonEmpty([3, 4])), [1, 2, 3, 4]) + }) + + it("scan", () => { + const f = (b: number, a: number) => b - a + deepStrictEqual(pipe([1, 2, 3], RA.scan(10, f)), [10, 9, 7, 4]) + deepStrictEqual(pipe([0], RA.scan(10, f)), [10, 10]) + deepStrictEqual(pipe([], RA.scan(10, f)), [10]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.scan(10, f)), [10, 9, 7, 4]) + deepStrictEqual(pipe(new Set([0]), RA.scan(10, f)), [10, 10]) + deepStrictEqual(pipe(new Set([]), RA.scan(10, f)), [10]) + }) + + it("scanRight", () => { + const f = (b: number, a: number) => a - b + deepStrictEqual(pipe([1, 2, 3], RA.scanRight(10, f)), [-8, 9, -7, 10]) + deepStrictEqual(pipe([0], RA.scanRight(10, f)), [-10, 10]) + deepStrictEqual(pipe([], RA.scanRight(10, f)), [10]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.scanRight(10, f)), [-8, 9, -7, 10]) + deepStrictEqual(pipe(new Set([0]), RA.scanRight(10, f)), [-10, 10]) + deepStrictEqual(pipe(new Set([]), RA.scanRight(10, f)), [10]) + }) + + it("tail", () => { + deepStrictEqual(RA.tail([1, 2, 3]), O.some([2, 3])) + deepStrictEqual(RA.tail([]), O.none()) + + deepStrictEqual(RA.tail(new Set([1, 2, 3])), O.some([2, 3])) + deepStrictEqual(RA.tail(new Set([])), O.none()) + }) + + it("init", () => { + deepStrictEqual(RA.init([1, 2, 3]), O.some([1, 2])) + deepStrictEqual(RA.init([]), O.none()) + + deepStrictEqual(RA.init(new Set([1, 2, 3])), O.some([1, 2])) + deepStrictEqual(RA.init(new Set([])), O.none()) + }) + + it("take", () => { + expect(pipe([1, 2, 3, 4], RA.take(2))).toEqual([1, 2]) + expect(pipe([1, 2, 3, 4], RA.take(0))).toEqual([]) + // out of bounds + expect(pipe([1, 2, 3, 4], RA.take(-10))).toEqual([]) + expect(pipe([1, 2, 3, 4], RA.take(10))).toEqual([1, 2, 3, 4]) + + expect(pipe(new Set([1, 2, 3, 4]), RA.take(2))).toEqual([1, 2]) + expect(pipe(new Set([1, 2, 3, 4]), RA.take(0))).toEqual([]) + // out of bounds + expect(pipe(new Set([1, 2, 3, 4]), RA.take(-10))).toEqual([]) + expect(pipe(new Set([1, 2, 3, 4]), RA.take(10))).toEqual([1, 2, 3, 4]) + }) + + it("takeRight", () => { + deepStrictEqual(pipe(RA.empty(), RA.takeRight(0)), []) + deepStrictEqual(pipe([1, 2], RA.takeRight(0)), []) + deepStrictEqual(pipe([1, 2], RA.takeRight(1)), [2]) + deepStrictEqual(pipe([1, 2], RA.takeRight(2)), [1, 2]) + // out of bound + deepStrictEqual(pipe(RA.empty(), RA.takeRight(1)), []) + deepStrictEqual(pipe(RA.empty(), RA.takeRight(-1)), []) + deepStrictEqual(pipe([1, 2], RA.takeRight(3)), [1, 2]) + deepStrictEqual(pipe([1, 2], RA.takeRight(-1)), []) + + deepStrictEqual(pipe(new Set(), RA.takeRight(0)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.takeRight(0)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.takeRight(1)), [2]) + deepStrictEqual(pipe(new Set([1, 2]), RA.takeRight(2)), [1, 2]) + // out of bound + deepStrictEqual(pipe(new Set(), RA.takeRight(1)), []) + deepStrictEqual(pipe(new Set(), RA.takeRight(-1)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.takeRight(3)), [1, 2]) + deepStrictEqual(pipe(new Set([1, 2]), RA.takeRight(-1)), []) + }) + + it("takeWhile", () => { + const f = (n: number) => n % 2 === 0 + deepStrictEqual(pipe([2, 4, 3, 6], RA.takeWhile(f)), [2, 4]) + deepStrictEqual(pipe(RA.empty(), RA.takeWhile(f)), []) + deepStrictEqual(pipe([1, 2, 4], RA.takeWhile(f)), []) + deepStrictEqual(pipe([2, 4], RA.takeWhile(f)), [2, 4]) + + deepStrictEqual(pipe(new Set([2, 4, 3, 6]), RA.takeWhile(f)), [2, 4]) + deepStrictEqual(pipe(new Set(), RA.takeWhile(f)), []) + deepStrictEqual(pipe(new Set([1, 2, 4]), RA.takeWhile(f)), []) + deepStrictEqual(pipe(new Set([2, 4]), RA.takeWhile(f)), [2, 4]) + }) + + it("span", () => { + const f = RA.span((n: number) => n % 2 === 1) + const assertSpan = ( + input: Iterable, + expectedInit: ReadonlyArray, + expectedRest: ReadonlyArray + ) => { + const [init, rest] = f(input) + deepStrictEqual(init, expectedInit) + deepStrictEqual(rest, expectedRest) + } + assertSpan([1, 3, 2, 4, 5], [1, 3], [2, 4, 5]) + assertSpan(RA.empty(), RA.empty(), RA.empty()) + assertSpan([1, 3], [1, 3], RA.empty()) + assertSpan([2, 4], RA.empty(), [2, 4]) + + assertSpan(new Set([1, 3, 2, 4, 5]), [1, 3], [2, 4, 5]) + assertSpan(new Set(), RA.empty(), RA.empty()) + assertSpan(new Set([1, 3]), [1, 3], RA.empty()) + assertSpan(new Set([2, 4]), RA.empty(), [2, 4]) + }) + + it("drop", () => { + deepStrictEqual(pipe(RA.empty(), RA.drop(0)), []) + deepStrictEqual(pipe([1, 2], RA.drop(0)), [1, 2]) + deepStrictEqual(pipe([1, 2], RA.drop(1)), [2]) + deepStrictEqual(pipe([1, 2], RA.drop(2)), []) + // out of bound + deepStrictEqual(pipe(RA.empty(), RA.drop(1)), []) + deepStrictEqual(pipe(RA.empty(), RA.drop(-1)), []) + deepStrictEqual(pipe([1, 2], RA.drop(3)), []) + deepStrictEqual(pipe([1, 2], RA.drop(-1)), [1, 2]) + + deepStrictEqual(pipe(new Set(), RA.drop(0)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.drop(0)), [1, 2]) + deepStrictEqual(pipe(new Set([1, 2]), RA.drop(1)), [2]) + deepStrictEqual(pipe(new Set([1, 2]), RA.drop(2)), []) + // out of bound + deepStrictEqual(pipe(new Set(), RA.drop(1)), []) + deepStrictEqual(pipe(new Set(), RA.drop(-1)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.drop(3)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.drop(-1)), [1, 2]) + }) + + it("dropRight", () => { + deepStrictEqual(pipe([], RA.dropRight(0)), []) + deepStrictEqual(pipe([1, 2], RA.dropRight(0)), [1, 2]) + deepStrictEqual(pipe([1, 2], RA.dropRight(1)), [1]) + deepStrictEqual(pipe([1, 2], RA.dropRight(2)), []) + // out of bound + deepStrictEqual(pipe([], RA.dropRight(1)), []) + deepStrictEqual(pipe([1, 2], RA.dropRight(3)), []) + deepStrictEqual(pipe([], RA.dropRight(-1)), []) + deepStrictEqual(pipe([1, 2], RA.dropRight(-1)), [1, 2]) + + deepStrictEqual(pipe(new Set(), RA.dropRight(0)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.dropRight(0)), [1, 2]) + deepStrictEqual(pipe(new Set([1, 2]), RA.dropRight(1)), [1]) + deepStrictEqual(pipe(new Set([1, 2]), RA.dropRight(2)), []) + // out of bound + deepStrictEqual(pipe(new Set(), RA.dropRight(1)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.dropRight(3)), []) + deepStrictEqual(pipe(new Set(), RA.dropRight(-1)), []) + deepStrictEqual(pipe(new Set([1, 2]), RA.dropRight(-1)), [1, 2]) + }) + + it("dropWhile", () => { + const f = RA.dropWhile((n: number) => n > 0) + + deepStrictEqual(f([]), []) + deepStrictEqual(f([1, 2]), RA.empty()) + deepStrictEqual(f([-1, -2]), [-1, -2]) + deepStrictEqual(f([-1, 2]), [-1, 2]) + deepStrictEqual(f([1, -2, 3]), [-2, 3]) + + deepStrictEqual(f(new Set()), []) + deepStrictEqual(f(new Set([1, 2])), RA.empty()) + deepStrictEqual(f(new Set([-1, -2])), [-1, -2]) + deepStrictEqual(f(new Set([-1, 2])), [-1, 2]) + deepStrictEqual(f(new Set([1, -2, 3])), [-2, 3]) + }) + + it("findFirstIndex", () => { + deepStrictEqual(pipe([], RA.findFirstIndex((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.findFirstIndex((n) => n % 2 === 0)), O.some(1)) + deepStrictEqual(pipe([1, 2, 3, 1], RA.findFirstIndex((n) => n % 2 === 0)), O.some(1)) + + deepStrictEqual(pipe(new Set(), RA.findFirstIndex((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findFirstIndex((n) => n % 2 === 0)), O.some(1)) + deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findFirstIndex((n) => n % 2 === 0)), O.some(1)) + }) + + it("findLastIndex", () => { + deepStrictEqual(pipe([], RA.findLastIndex((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.findLastIndex((n) => n % 2 === 0)), O.some(1)) + deepStrictEqual(pipe([1, 2, 3, 4], RA.findLastIndex((n) => n % 2 === 0)), O.some(3)) + + deepStrictEqual(pipe(new Set(), RA.findLastIndex((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findLastIndex((n) => n % 2 === 0)), O.some(1)) + deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findLastIndex((n) => n % 2 === 0)), O.some(3)) + }) + + it("findFirst", () => { + deepStrictEqual(pipe([], RA.findFirst((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.findFirst((n) => n % 2 === 0)), O.some(2)) + deepStrictEqual(pipe([1, 2, 3, 4], RA.findFirst((n) => n % 2 === 0)), O.some(2)) + + deepStrictEqual(pipe(new Set(), RA.findFirst((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findFirst((n) => n % 2 === 0)), O.some(2)) + deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findFirst((n) => n % 2 === 0)), O.some(2)) + }) + + it("findLast", () => { + deepStrictEqual(pipe([], RA.findLast((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.findLast((n) => n % 2 === 0)), O.some(2)) + deepStrictEqual(pipe([1, 2, 3, 4], RA.findLast((n) => n % 2 === 0)), O.some(4)) + + deepStrictEqual(pipe(new Set(), RA.findLast((n) => n % 2 === 0)), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.findLast((n) => n % 2 === 0)), O.some(2)) + deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.findLast((n) => n % 2 === 0)), O.some(4)) + }) + + it("insertAt", () => { + deepStrictEqual(RA.insertAt(1, 1)([]), O.none()) + deepStrictEqual(RA.insertAt(0, 1)([]), O.some([1])) + deepStrictEqual(RA.insertAt(2, 5)([1, 2, 3, 4]), O.some([1, 2, 5, 3, 4])) + // out of bound + deepStrictEqual(RA.insertAt(-1, 5)([1, 2, 3, 4]), O.none()) + deepStrictEqual(RA.insertAt(10, 5)([1, 2, 3, 4]), O.none()) + + deepStrictEqual(RA.insertAt(1, 1)(new Set([])), O.none()) + deepStrictEqual(RA.insertAt(0, 1)(new Set([])), O.some([1])) + deepStrictEqual(RA.insertAt(2, 5)(new Set([1, 2, 3, 4])), O.some([1, 2, 5, 3, 4])) + // out of bound + deepStrictEqual(RA.insertAt(-1, 5)(new Set([1, 2, 3, 4])), O.none()) + deepStrictEqual(RA.insertAt(10, 5)(new Set([1, 2, 3, 4])), O.none()) + }) + + it("replace", () => { + deepStrictEqual(pipe([1, 2, 3], RA.replace(1, "a")), [1, "a", 3]) + // out of bound + deepStrictEqual(pipe([], RA.replace(1, "a")), []) + deepStrictEqual(pipe([1, 2, 3], RA.replace(-1, "a")), [1, 2, 3]) + deepStrictEqual(pipe([1, 2, 3], RA.replace(10, "a")), [1, 2, 3]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replace(1, "a")), [1, "a", 3]) + // out of bound + deepStrictEqual(pipe(new Set([]), RA.replace(1, "a")), []) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replace(-1, "a")), [1, 2, 3]) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replace(10, "a")), [1, 2, 3]) + }) + + it("replaceOption", () => { + deepStrictEqual(pipe([1, 2, 3], RA.replaceOption(1, "a")), O.some([1, "a", 3])) + // out of bound + deepStrictEqual(pipe([], RA.replaceOption(1, "a")), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.replaceOption(-1, "a")), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.replaceOption(10, "a")), O.none()) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replaceOption(1, "a")), O.some([1, "a", 3])) + // out of bound + deepStrictEqual(pipe(new Set([]), RA.replaceOption(1, "a")), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replaceOption(-1, "a")), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.replaceOption(10, "a")), O.none()) + }) + + it("modify", () => { + deepStrictEqual(pipe([1, 2, 3], RA.modify(1, double)), [1, 4, 3]) + // out of bound + deepStrictEqual(pipe([], RA.modify(1, double)), []) + deepStrictEqual(pipe([1, 2, 3], RA.modify(10, double)), [1, 2, 3]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.modify(1, double)), [1, 4, 3]) + // out of bound + deepStrictEqual(pipe(new Set([]), RA.modify(1, double)), []) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.modify(10, double)), [1, 2, 3]) + }) + + it("modifyOption", () => { + deepStrictEqual(pipe([1, 2, 3], RA.modifyOption(1, double)), O.some([1, 4, 3])) + // out of bound + deepStrictEqual(pipe([], RA.modifyOption(1, double)), O.none()) + deepStrictEqual(pipe([1, 2, 3], RA.modifyOption(10, double)), O.none()) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.modifyOption(1, double)), O.some([1, 4, 3])) + // out of bound + deepStrictEqual(pipe(new Set([]), RA.modifyOption(1, double)), O.none()) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.modifyOption(10, double)), O.none()) + }) + + it("remove", () => { + deepStrictEqual(pipe([1, 2, 3], RA.remove(0)), [2, 3]) + // out of bound + deepStrictEqual(pipe([], RA.remove(0)), []) + deepStrictEqual(pipe([1, 2, 3], RA.remove(-1)), [1, 2, 3]) + deepStrictEqual(pipe([1, 2, 3], RA.remove(10)), [1, 2, 3]) + + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.remove(0)), [2, 3]) + // out of bound + deepStrictEqual(pipe(new Set([]), RA.remove(0)), []) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.remove(-1)), [1, 2, 3]) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.remove(10)), [1, 2, 3]) + }) + + it("reverse", () => { + deepStrictEqual(RA.reverse([]), []) + deepStrictEqual(RA.reverse([1]), [1]) + deepStrictEqual(RA.reverse([1, 2, 3]), [3, 2, 1]) + + deepStrictEqual(RA.reverse(new Set([])), []) + deepStrictEqual(RA.reverse(new Set([1])), [1]) + deepStrictEqual(RA.reverse(new Set([1, 2, 3])), [3, 2, 1]) + }) + + it("rights", () => { + deepStrictEqual(RA.rights([]), []) + deepStrictEqual(RA.rights([E.right(1), E.left("a"), E.right(2)]), [1, 2]) + + deepStrictEqual(RA.rights(new Set>()), []) + deepStrictEqual(RA.rights(new Set([E.right(1), E.left("a"), E.right(2)])), [1, 2]) + }) + + it("lefts", () => { + deepStrictEqual(RA.lefts([]), []) + deepStrictEqual(RA.lefts([E.right(1), E.left("a"), E.right(2)]), ["a"]) + + deepStrictEqual(RA.lefts(new Set>()), []) + deepStrictEqual(RA.lefts(new Set([E.right(1), E.left("a"), E.right(2)])), ["a"]) + }) + + it("sort", () => { + deepStrictEqual(RA.sort(Number.Order)([]), []) + deepStrictEqual(RA.sort(Number.Order)([1, 3, 2]), [1, 2, 3]) + + deepStrictEqual(RA.sort(Number.Order)(new Set()), []) + deepStrictEqual(RA.sort(Number.Order)(new Set([1, 3, 2])), [1, 2, 3]) + }) + + it("zip", () => { + deepStrictEqual(pipe(new Set([]), RA.zip(new Set(["a", "b", "c", "d"]))), []) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.zip(new Set([]))), []) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.zip(new Set(["a", "b", "c", "d"]))), [ + [1, "a"], + [2, "b"], + [3, "c"] + ]) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.zip(new Set(["a", "b", "c", "d"]))), [ + [1, "a"], + [2, "b"], + [3, "c"] + ]) + }) + + it("zipWith", () => { + deepStrictEqual( + pipe(new Set([1, 2, 3]), RA.zipWith(new Set([]), (n, s) => s + n)), + [] + ) + deepStrictEqual( + pipe(new Set([]), RA.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)), + [] + ) + deepStrictEqual( + pipe(new Set([]), RA.zipWith(new Set([]), (n, s) => s + n)), + [] + ) + deepStrictEqual( + pipe(new Set([1, 2, 3]), RA.zipWith(new Set(["a", "b", "c", "d"]), (n, s) => s + n)), + ["a1", "b2", "c3"] + ) + }) + + it("unzip", () => { + deepStrictEqual(RA.unzip(new Set([])), [[], []]) + deepStrictEqual( + RA.unzip( + new Set([ + [1, "a"], + [2, "b"], + [3, "c"] + ]) + ), + [ + [1, 2, 3], + ["a", "b", "c"] + ] + ) + }) + + it("intersperse", () => { + deepStrictEqual(pipe([], RA.intersperse(0)), []) + deepStrictEqual(pipe([1], RA.intersperse(0)), [1]) + deepStrictEqual(pipe([1, 2, 3], RA.intersperse(0)), [1, 0, 2, 0, 3]) + deepStrictEqual(pipe([1, 2], RA.intersperse(0)), [1, 0, 2]) + deepStrictEqual(pipe([1, 2, 3, 4], RA.intersperse(0)), [1, 0, 2, 0, 3, 0, 4]) + + deepStrictEqual(pipe(new Set([]), RA.intersperse(0)), []) + deepStrictEqual(pipe(new Set([1]), RA.intersperse(0)), [1]) + deepStrictEqual(pipe(new Set([1, 2, 3]), RA.intersperse(0)), [1, 0, 2, 0, 3]) + deepStrictEqual(pipe(new Set([1, 2]), RA.intersperse(0)), [1, 0, 2]) + deepStrictEqual(pipe(new Set([1, 2, 3, 4]), RA.intersperse(0)), [1, 0, 2, 0, 3, 0, 4]) + }) + + it("rotate", () => { + deepStrictEqual(RA.rotate(0)(RA.empty()), RA.empty()) + deepStrictEqual(RA.rotate(1)(RA.empty()), RA.empty()) + deepStrictEqual(RA.rotate(1)([1]), [1]) + deepStrictEqual(RA.rotate(2)([1]), [1]) + deepStrictEqual(RA.rotate(-1)([1]), [1]) + deepStrictEqual(RA.rotate(-2)([1]), [1]) + deepStrictEqual(RA.rotate(2)([1, 2]), [1, 2]) + deepStrictEqual(RA.rotate(0)([1, 2]), [1, 2]) + deepStrictEqual(RA.rotate(-2)([1, 2]), [1, 2]) + deepStrictEqual(RA.rotate(1)([1, 2]), [2, 1]) + deepStrictEqual(RA.rotate(1)(new Set([1, 2, 3, 4, 5])), [5, 1, 2, 3, 4]) + deepStrictEqual(RA.rotate(2)(new Set([1, 2, 3, 4, 5])), [4, 5, 1, 2, 3]) + deepStrictEqual(RA.rotate(-1)(new Set([1, 2, 3, 4, 5])), [2, 3, 4, 5, 1]) + deepStrictEqual(RA.rotate(-2)(new Set([1, 2, 3, 4, 5])), [3, 4, 5, 1, 2]) + // out of bounds + deepStrictEqual(RA.rotate(7)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) + deepStrictEqual(RA.rotate(-7)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2]) + deepStrictEqual(RA.rotate(2.2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) + deepStrictEqual(RA.rotate(-2.2)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2]) + }) + + it("contains", () => { + const contains = RA.contains(Number.Equivalence) + deepStrictEqual(pipe([1, 2, 3], contains(2)), true) + deepStrictEqual(pipe([1, 2, 3], contains(0)), false) + + deepStrictEqual(pipe(new Set([1, 2, 3]), contains(2)), true) + deepStrictEqual(pipe(new Set([1, 2, 3]), contains(0)), false) + }) + + it("uniq", () => { + const uniq = RA.uniq(Number.Equivalence) + deepStrictEqual(uniq([]), []) + deepStrictEqual(uniq([-0, -0]), [-0]) + deepStrictEqual(uniq([0, -0]), [0]) + deepStrictEqual(uniq([1]), [1]) + deepStrictEqual(uniq([2, 1, 2]), [2, 1]) + deepStrictEqual(uniq([1, 2, 1]), [1, 2]) + deepStrictEqual(uniq([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + deepStrictEqual(uniq([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), [1, 2, 3, 4, 5]) + deepStrictEqual(uniq([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + }) + + it("splitAt", () => { + const assertSplitAt = ( + input: ReadonlyArray, + index: number, + expectedInit: ReadonlyArray, + expectedRest: ReadonlyArray + ) => { + const [init, rest] = RA.splitAt(index)(input) + deepStrictEqual(init, expectedInit) + deepStrictEqual(rest, expectedRest) + } + deepStrictEqual(RA.splitAt(1)([1, 2]), [[1], [2]]) + assertSplitAt([1, 2], 2, [1, 2], []) + deepStrictEqual(RA.splitAt(2)([1, 2, 3, 4, 5]), [ + [1, 2], + [3, 4, 5] + ]) + deepStrictEqual(RA.splitAt(2)(new Set([1, 2, 3, 4, 5])), [ + [1, 2], + [3, 4, 5] + ]) + assertSplitAt([], 0, [], []) + assertSplitAt([1, 2], 0, [], [1, 2]) + + // out of bounds + assertSplitAt([], -1, [], []) + assertSplitAt([1, 2], -1, [], [1, 2]) + assertSplitAt([1, 2], 3, [1, 2], []) + assertSplitAt([], 3, [], []) + }) + }) + + it("splitNonEmptyAt", () => { + deepStrictEqual(pipe(RA.make(1, 2, 3, 4), RA.splitNonEmptyAt(2)), [[1, 2], [3, 4]]) + }) + + it("rotateNonEmpty", () => { + deepStrictEqual(RA.rotateNonEmpty(2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) + }) + + it("intersperseNonEmpty", () => { + deepStrictEqual(pipe(RA.make(1), RA.intersperseNonEmpty(0)), [1]) + deepStrictEqual(pipe(RA.make(1, 2, 3), RA.intersperseNonEmpty(0)), [1, 0, 2, 0, 3]) + }) + + it("sortNonEmpty", () => { + deepStrictEqual(RA.sortNonEmpty(Number.Order)([1]), [1]) + deepStrictEqual(RA.sortNonEmpty(Number.Order)([1, 3, 2]), [1, 2, 3]) + }) + + describe("unsafeGet", () => { + it("should throw on index out of bound", () => { + expect(() => pipe([], RA.unsafeGet(100))).toThrowError(new Error("Index 100 out of bounds")) + }) + }) + + it("fromNullable", () => { + deepStrictEqual(RA.fromNullable(undefined), []) + deepStrictEqual(RA.fromNullable(null), []) + deepStrictEqual(RA.fromNullable(1), [1]) + }) + + it("liftNullable", () => { + const f = RA.liftNullable((n: number) => (n > 0 ? n : null)) + deepStrictEqual(f(1), [1]) + deepStrictEqual(f(-1), []) + }) + + it("flatMapNullable", () => { + const f = RA.flatMapNullable((n: number) => (n > 0 ? n : null)) + deepStrictEqual(pipe([], f), []) + deepStrictEqual(pipe([1], f), [1]) + deepStrictEqual(pipe([-1], f), []) + }) + + it("liftPredicate", () => { + const p = (n: number): boolean => n > 2 + const f = RA.liftPredicate(p) + deepStrictEqual(f(1), []) + deepStrictEqual(f(3), [3]) + }) + + it("liftOption", () => { + const f = RA.liftOption((n: number) => (n > 0 ? O.some(n) : O.none())) + deepStrictEqual(f(1), [1]) + deepStrictEqual(f(-1), []) + }) + + it("unprepend", () => { + deepStrictEqual(RA.unprepend([0]), [0, []]) + deepStrictEqual(RA.unprepend([1, 2, 3, 4]), [1, [2, 3, 4]]) + }) + + it("unappend", () => { + deepStrictEqual(RA.unappend([0]), [[], 0]) + deepStrictEqual(RA.unappend([1, 2, 3, 4]), [ + RA.make(1, 2, 3), + 4 + ]) + deepStrictEqual(RA.unappend([0]), [[], 0]) + deepStrictEqual(RA.unappend([1, 2, 3, 4]), [ + RA.make(1, 2, 3), + 4 + ]) + }) + + it("modifyNonEmptyHead", () => { + const f = (s: string) => s + "!" + deepStrictEqual(pipe(["a"], RA.modifyNonEmptyHead(f)), ["a!"]) + deepStrictEqual(pipe(["a", "b"], RA.modifyNonEmptyHead(f)), ["a!", "b"]) + deepStrictEqual(pipe(["a", "b", "c"], RA.modifyNonEmptyHead(f)), ["a!", "b", "c"]) + }) + + it("modifyNonEmptyLast", () => { + const f = (s: string) => s + "!" + deepStrictEqual(pipe(["a"], RA.modifyNonEmptyLast(f)), ["a!"]) + deepStrictEqual(pipe(["a", "b"], RA.modifyNonEmptyLast(f)), ["a", "b!"]) + deepStrictEqual(pipe(["a", "b", "c"], RA.modifyNonEmptyLast(f)), ["a", "b", "c!"]) + }) + + it("setNonEmptyHead", () => { + deepStrictEqual(pipe(RA.make("a"), RA.setNonEmptyHead("d")), ["d"]) + deepStrictEqual(pipe(RA.make("a", "b"), RA.setNonEmptyHead("d")), ["d", "b"]) + deepStrictEqual(pipe(RA.make("a", "b", "c"), RA.setNonEmptyHead("d")), ["d", "b", "c"]) + }) + + it("setNonEmptyLast", () => { + deepStrictEqual(pipe(RA.make("a"), RA.setNonEmptyLast("d")), ["d"]) + deepStrictEqual(pipe(RA.make("a", "b"), RA.setNonEmptyLast("d")), ["a", "d"]) + deepStrictEqual(pipe(RA.make("a", "b", "c"), RA.setNonEmptyLast("d")), ["a", "b", "d"]) + }) + + it("liftEither", () => { + const f = RA.liftEither((s: string) => s.length > 2 ? E.right(s.length) : E.left("e")) + deepStrictEqual(f("a"), []) + deepStrictEqual(f("aaa"), [3]) + }) + + it("headNonEmpty", () => { + deepStrictEqual(RA.headNonEmpty(RA.make(1, 2)), 1) + }) + + it("tailNonEmpty", () => { + deepStrictEqual(RA.tailNonEmpty(RA.make(1, 2)), [2]) + }) + + it("lastNonEmpty", () => { + deepStrictEqual(RA.lastNonEmpty(RA.make(1, 2, 3)), 3) + deepStrictEqual(RA.lastNonEmpty([1]), 1) + }) + + it("initNonEmpty", () => { + deepStrictEqual( + RA.initNonEmpty(RA.make(1, 2, 3)), + RA.make(1, 2) + ) + deepStrictEqual(RA.initNonEmpty([1]), []) + }) + + it("traverse", () => { + const traverse = RA.traverse(O.Applicative)(( + n: number + ): O.Option => (n % 2 === 0 ? O.none() : O.some(n))) + deepStrictEqual(traverse([1, 2]), O.none()) + deepStrictEqual(traverse([1, 3]), O.some([1, 3])) + }) + + it("sequence", () => { + const sequence = RA.sequence(O.Applicative) + deepStrictEqual(sequence([O.some(1), O.some(3)]), O.some([1, 3])) + deepStrictEqual(sequence([O.some(1), O.none()]), O.none()) + }) + + it("traverseWithIndex", () => { + deepStrictEqual( + pipe( + ["a", "bb"], + RA.traverseWithIndex(O.Applicative)(( + s, + i + ) => (s.length >= 1 ? O.some(s + i) : O.none())) + ), + O.some(["a0", "bb1"]) + ) + deepStrictEqual( + pipe( + ["a", "bb"], + RA.traverseWithIndex(O.Applicative)(( + s, + i + ) => (s.length > 1 ? O.some(s + i) : O.none())) + ), + O.none() + ) + }) + + it("get", () => { + deepStrictEqual(pipe([1, 2, 3], RA.get(0)), O.some(1)) + deepStrictEqual(pipe([1, 2, 3], RA.get(3)), O.none()) + }) + + it("unfold", () => { + const as = RA.unfold(5, (n) => (n > 0 ? O.some([n, n - 1]) : O.none())) + deepStrictEqual(as, [5, 4, 3, 2, 1]) + }) + + it("map", () => { + deepStrictEqual( + pipe( + [1, 2, 3], + RA.map((n) => n * 2) + ), + [2, 4, 6] + ) + }) + + it("mapWithIndex", () => { + deepStrictEqual( + pipe( + ["a", "b"], + RA.mapWithIndex((s, i) => s + i) + ), + ["a0", "b1"] + ) + }) + + it("ap", () => { + deepStrictEqual( + pipe([(x: number) => x * 2, (x: number) => x * 3], RA.ap([1, 2, 3])), + [ + 2, + 4, + 6, + 3, + 6, + 9 + ] + ) + }) + + it("flatMap", () => { + deepStrictEqual( + pipe( + [1, 2, 3], + RA.flatMap((n) => [n, n + 1]) + ), + [1, 2, 2, 3, 3, 4] + ) + }) + + it("flatMapWithIndex", () => { + const f = RA.flatMapWithIndex((n: number, i) => [n + i]) + deepStrictEqual(pipe([1, 2, 3], f), [1, 3, 5]) + deepStrictEqual(pipe(RA.empty(), f), RA.empty()) + const empty: ReadonlyArray = [] + deepStrictEqual(pipe(empty, f), RA.empty()) + }) + + it("extend", () => { + const sum = (as: ReadonlyArray) => Number.MonoidSum.combineAll(as) + deepStrictEqual(pipe([1, 2, 3, 4], RA.extend(sum)), [10, 9, 7, 4]) + deepStrictEqual(pipe([1, 2, 3, 4], RA.extend(identity)), [ + [1, 2, 3, 4], + [2, 3, 4], + [3, 4], + [ + 4 + ] + ]) + }) + + it("foldMap", () => { + deepStrictEqual(pipe(["a", "b", "c"], RA.foldMap(String.Monoid)(identity)), "abc") + deepStrictEqual(pipe([], RA.foldMap(String.Monoid)(identity)), "") + }) + + it("compact", () => { + deepStrictEqual(RA.compact([]), []) + deepStrictEqual(RA.compact([O.some(1), O.some(2), O.some(3)]), [ + 1, + 2, + 3 + ]) + deepStrictEqual(RA.compact([O.some(1), O.none(), O.some(3)]), [ + 1, + 3 + ]) + }) + + it("separate", () => { + deepStrictEqual(RA.separate([]), [[], []]) + deepStrictEqual( + RA.separate([E.left(123), E.right("123")]), + [[123], ["123"]] + ) + }) + + it("filter", () => { + const g = (n: number) => n % 2 === 1 + deepStrictEqual(pipe([1, 2, 3], RA.filter(g)), [1, 3]) + const x = pipe( + [O.some(3), O.some(2), O.some(1)], + RA.filter(O.isSome) + ) + assert.deepStrictEqual(x, [O.some(3), O.some(2), O.some(1)]) + const y = pipe( + [O.some(3), O.none(), O.some(1)], + RA.filter(O.isSome) + ) + assert.deepStrictEqual(y, [O.some(3), O.some(1)]) + }) + + it("filterWithIndex", () => { + const f = (n: number) => n % 2 === 0 + deepStrictEqual(pipe(["a", "b", "c"], RA.filterWithIndex((_, i) => f(i))), [ + "a", + "c" + ]) + }) + + it("filterMap", () => { + const f = (n: number) => (n % 2 === 0 ? O.none() : O.some(n)) + deepStrictEqual(pipe([1, 2, 3], RA.filterMap(f)), [1, 3]) + deepStrictEqual(pipe([], RA.filterMap(f)), []) + }) + + it("foldMapWithIndex", () => { + deepStrictEqual( + pipe( + ["a", "b"], + RA.foldMapWithIndex(String.Monoid)((a, i) => i + a) + ), + "0a1b" + ) + }) + + it("filterMapWithIndex", () => { + const f = (n: number, i: number) => ((i + n) % 2 === 0 ? O.none() : O.some(n)) + deepStrictEqual(pipe([1, 2, 4], RA.filterMapWithIndex(f)), [1, 2]) + deepStrictEqual(pipe([], RA.filterMapWithIndex(f)), []) + }) + + it("partitionMap", () => { + deepStrictEqual(pipe([], RA.partitionMap(identity)), [[], []]) + deepStrictEqual( + pipe( + [E.right(1), E.left("foo"), E.right(2)], + RA.partitionMap(identity) + ), + [["foo"], [1, 2]] + ) + }) + + it("partition", () => { + deepStrictEqual( + pipe([], RA.partition((n) => n > 2)), + [[], []] + ) + deepStrictEqual( + pipe([1, 3], RA.partition((n) => n > 2)), + [[1], [3]] + ) + }) + + it("partitionMapWithIndex", () => { + deepStrictEqual( + pipe([], RA.partitionMapWithIndex((a) => a)), + [[], []] + ) + deepStrictEqual( + pipe( + [E.right(1), E.left("foo"), E.right(2)], + RA.partitionMapWithIndex((a, i) => pipe(a, E.filter((n) => n > i, () => "err"))) + ), + [["foo", "err"], [1]] + ) + }) + + it("partitionWithIndex", () => { + deepStrictEqual( + pipe([], RA.partitionWithIndex((i, n) => i + n > 2)), + [[], []] + ) + deepStrictEqual( + pipe([1, 2], RA.partitionWithIndex((i, n) => i + n > 2)), + [[1], [2]] + ) + }) + + it("reduce", () => { + deepStrictEqual(pipe(["a", "b", "c"], RA.reduce("", (b, a) => b + a)), "abc") + }) + + it("reduceRight", () => { + const f = (b: string, a: string) => b + a + deepStrictEqual(pipe(["a", "b", "c"], RA.reduceRight("", f)), "cba") + deepStrictEqual(pipe([], RA.reduceRight("", f)), "") + }) + + it("reduceWithIndex", () => { + deepStrictEqual( + pipe( + ["a", "b"], + RA.reduceWithIndex("", (b, a, i) => b + i + a) + ), + "0a1b" + ) + }) + + it("reduceRightWithIndex", () => { + deepStrictEqual( + pipe( + ["a", "b"], + RA.reduceRightWithIndex("", (b, a, i) => b + i + a) + ), + "1b0a" + ) + }) + + it("traverseNonEmpty", () => { + const traverseNonEmpty = RA.traverseNonEmpty(O.Applicative) + deepStrictEqual( + pipe( + RA.make(1, 2, 3), + traverseNonEmpty((n) => (n >= 0 ? O.some(n) : O.none())) + ), + O.some(RA.make(1, 2, 3)) + ) + deepStrictEqual( + pipe( + RA.make(1, 2, 3), + traverseNonEmpty((n) => (n >= 2 ? O.some(n) : O.none())) + ), + O.none() + ) + }) + + it("traverseNonEmptyWithIndex", () => { + deepStrictEqual( + pipe( + RA.make("a", "bb"), + RA.traverseNonEmptyWithIndex(O.Applicative)(( + s, + i + ) => (s.length >= 1 ? O.some(s + i) : O.none())) + ), + O.some(RA.make("a0", "bb1")) + ) + deepStrictEqual( + pipe( + RA.make("a", "bb"), + RA.traverseNonEmptyWithIndex(O.Applicative)(( + s, + i + ) => (s.length > 1 ? O.some(s + i) : O.none())) + ), + O.none() + ) + }) + + it("getMonoid", () => { + const M = RA.getMonoid() + deepStrictEqual(M.combine([3, 4])([1, 2]), [1, 2, 3, 4]) + const x = [1, 2] + deepStrictEqual(M.combine(M.empty)(x), x) + deepStrictEqual(M.combine(x)(M.empty), x) + + deepStrictEqual(M.combineAll([[1, 2], [3, 4, 5], [5, 6, 7, 1]]), [1, 2, 3, 4, 5, 5, 6, 7, 1]) + }) + + it("liftOrder", () => { + const O = RA.liftOrder(String.Order) + deepStrictEqual(O.compare([])([]), 0) + deepStrictEqual(O.compare(["a"])(["a"]), 0) + + deepStrictEqual(O.compare(["b"])(["a"]), -1) + deepStrictEqual(O.compare(["a"])(["b"]), 1) + + deepStrictEqual(O.compare(["a"])([]), -1) + deepStrictEqual(O.compare([])(["a"]), 1) + deepStrictEqual(O.compare(["a", "a"])(["a"]), -1) + deepStrictEqual(O.compare(["a", "a"])(["b"]), 1) + + deepStrictEqual(O.compare(["a", "a"])(["a", "a"]), 0) + deepStrictEqual(O.compare(["a", "b"])(["a", "b"]), 0) + + deepStrictEqual(O.compare(["a", "a"])(["a", "b"]), 1) + deepStrictEqual(O.compare(["a", "b"])(["a", "a"]), -1) + + deepStrictEqual(O.compare(["a", "b"])(["b", "a"]), 1) + deepStrictEqual(O.compare(["b", "a"])(["a", "a"]), -1) + deepStrictEqual(O.compare(["b", "a"])(["a", "b"]), -1) + deepStrictEqual(O.compare(["b", "b"])(["b", "a"]), -1) + deepStrictEqual(O.compare(["b", "a"])(["b", "b"]), 1) + }) + + it("isEmpty", () => { + const as: ReadonlyArray = [1, 2, 3] + deepStrictEqual(RA.isEmpty(as), false) + deepStrictEqual(RA.isEmpty([]), true) + }) + + it("isNotEmpty", () => { + const as: ReadonlyArray = [1, 2, 3] + deepStrictEqual(RA.isNonEmpty(as), true) + deepStrictEqual(RA.isNonEmpty([]), false) + }) + + it("head", () => { + const as: ReadonlyArray = [1, 2, 3] + deepStrictEqual(RA.head(as), O.some(1)) + deepStrictEqual(RA.head([]), O.none()) + }) + + it("last", () => { + const as: ReadonlyArray = [1, 2, 3] + deepStrictEqual(RA.last(as), O.some(3)) + deepStrictEqual(RA.last([]), O.none()) + }) + + it("zipNonEmptyWith", () => { + deepStrictEqual( + pipe([1, 2, 3], RA.zipNonEmptyWith(["a", "b", "c", "d"], (n, s) => s + n)), + ["a1", "b2", "c3"] + ) + }) + + it("zipNonEmpty", () => { + deepStrictEqual(pipe(RA.make(1, 2, 3), RA.zipNonEmpty(["a", "b", "c", "d"])), [ + [1, "a"], + [2, "b"], + [3, "c"] + ]) + }) + + it("unzipNonEmpty", () => { + deepStrictEqual( + RA.unzipNonEmpty([ + [1, "a"], + [2, "b"], + [3, "c"] + ]), + [ + [1, 2, 3], + ["a", "b", "c"] + ] + ) + }) + + it("flatMapNonEmpty", () => { + const f = (a: number): RA.NonEmptyReadonlyArray => [a, 4] + deepStrictEqual(pipe(RA.make(1, 2), RA.flatMapNonEmpty(f)), [1, 4, 2, 4]) + }) + + it("flatMapNonEmptyWithIndex", () => { + const f = (a: number, i: number): RA.NonEmptyReadonlyArray => [a + i, 4] + deepStrictEqual(pipe(RA.make(1, 2), RA.flatMapNonEmptyWithIndex(f)), [1, 4, 3, 4]) + }) + + it("chunksOfNonEmpty", () => { + deepStrictEqual(RA.chunksOfNonEmpty(2)([1, 2, 3, 4, 5]), [ + RA.make(1, 2), + [3, 4], + [5] + ]) + deepStrictEqual(RA.chunksOfNonEmpty(2)([1, 2, 3, 4, 5, 6]), [ + RA.make(1, 2), + [3, 4], + [5, 6] + ]) + deepStrictEqual(RA.chunksOfNonEmpty(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + deepStrictEqual(RA.chunksOfNonEmpty(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) + // out of bounds + deepStrictEqual(RA.chunksOfNonEmpty(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + deepStrictEqual(RA.chunksOfNonEmpty(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + + const assertSingleChunk = ( + input: RA.NonEmptyReadonlyArray, + n: number + ) => { + const chunks = RA.chunksOfNonEmpty(n)(input) + strictEqual(chunks.length, 1) + deepStrictEqual(RA.headNonEmpty(chunks), input) + } + // n = length + assertSingleChunk(RA.make(1, 2), 2) + // n out of bounds + assertSingleChunk(RA.make(1, 2), 3) + }) + + it("mapNonEmpty", () => { + deepStrictEqual( + pipe( + RA.make(RA.make(1, 2), RA.make(3, 4)), + RA.flattenNonEmpty + ), + [1, 2, 3, 4] + ) + }) + + it("sequenceNonEmpty", () => { + const sequence = RA.sequenceNonEmpty(O.Applicative) + deepStrictEqual( + sequence([O.some(1), O.some(2), O.some(3)]), + O.some(RA.make(1, 2, 3)) + ) + deepStrictEqual(sequence([O.none(), O.some(2), O.some(3)]), O.none()) + }) + + it("mapNonEmpty", () => { + deepStrictEqual( + pipe( + RA.make(1, 2), + RA.mapNonEmpty((n) => n * 2) + ), + [2, 4] + ) + }) + + it("mapNonEmptyWithIndex", () => { + const add = (s: string, i: number) => s + i + deepStrictEqual( + pipe(RA.make("a", "b"), RA.mapNonEmptyWithIndex(add)), + ["a0", "b1"] + ) + }) + + it("min", () => { + deepStrictEqual(RA.min(Number.Order)([2, 1, 3]), 1) + deepStrictEqual(RA.min(Number.Order)([3]), 3) + }) + + it("max", () => { + deepStrictEqual( + RA.max(Number.Order)(RA.make(1, 2, 3)), + 3 + ) + deepStrictEqual(RA.max(Number.Order)([1]), 1) + }) + + it("flatten", () => { + deepStrictEqual(RA.flatten([[1], [2], [3]]), [1, 2, 3]) + }) + + it("intercalate", () => { + deepStrictEqual(RA.intercalate(String.Monoid)("-")([]), "") + deepStrictEqual(RA.intercalate(String.Monoid)("-")(["a"]), "a") + deepStrictEqual(RA.intercalate(String.Monoid)("-")(["a", "b", "c"]), "a-b-c") + deepStrictEqual(RA.intercalate(String.Monoid)("-")(["a", "", "c"]), "a--c") + deepStrictEqual(RA.intercalate(String.Monoid)("-")(["a", "b"]), "a-b") + deepStrictEqual(RA.intercalate(String.Monoid)("-")(["a", "b", "c", "d"]), "a-b-c-d") + }) + + it("group", () => { + const group = RA.group(Number.Equivalence) + deepStrictEqual(group([1, 2, 1, 1]), [[1], [2], [1, 1]]) + deepStrictEqual(group([1, 2, 1, 1, 3]), [[1], [2], [1, 1], [3]]) + }) + + it("groupBy", () => { + deepStrictEqual(RA.groupBy((_) => "")([]), {}) + deepStrictEqual(RA.groupBy((a) => `${a}`)([1]), { "1": [1] }) + deepStrictEqual( + RA.groupBy((s: string) => `${s.length}`)(["foo", "bar", "foobar"]), + { + "3": ["foo", "bar"], + "6": ["foobar"] + } + ) + }) + + it("reverseNonEmpty", () => { + deepStrictEqual(RA.reverseNonEmpty([1]), [1]) + deepStrictEqual(RA.reverseNonEmpty(RA.make(1, 2, 3)), [3, 2, 1]) + }) + + it("match", () => { + const len: (as: ReadonlyArray) => number = RA.match( + () => 0, + (_, tail) => 1 + len(tail) + ) + deepStrictEqual(len([1, 2, 3]), 3) + }) + + it("matchRight", () => { + const len: (as: ReadonlyArray) => number = RA.matchRight( + () => 0, + (init, _) => 1 + len(init) + ) + deepStrictEqual(len([1, 2, 3]), 3) + }) + + it("uniqNonEmpty", () => { + const uniqNonEmpty = RA.uniqNonEmpty(String.Equivalence) + deepStrictEqual(uniqNonEmpty(["a", "b", "A"]), ["a", "b", "A"]) + }) + + it("sortBy / sortByNonEmpty", () => { + interface X { + readonly a: string + readonly b: number + readonly c: boolean + } + + const byName = pipe( + String.Order, + Order.contramap((p: { readonly a: string; readonly b: number }) => p.a) + ) + + const byAge = pipe( + Number.Order, + Order.contramap((p: { readonly a: string; readonly b: number }) => p.b) + ) + + const sortByNameByAge = RA.sortBy(byName, byAge) + + const xs: RA.NonEmptyArray = [ + { a: "a", b: 1, c: true }, + { a: "b", b: 3, c: true }, + { a: "c", b: 2, c: true }, + { a: "b", b: 2, c: true } + ] + + deepStrictEqual(RA.sortBy()(xs), xs) + deepStrictEqual(sortByNameByAge([]), []) + deepStrictEqual(sortByNameByAge(xs), [ + { a: "a", b: 1, c: true }, + { a: "b", b: 2, c: true }, + { a: "b", b: 3, c: true }, + { a: "c", b: 2, c: true } + ]) + + deepStrictEqual(RA.sortBy()(new Set(xs)), xs) + deepStrictEqual(sortByNameByAge(new Set([])), []) + deepStrictEqual(sortByNameByAge(new Set(xs)), [ + { a: "a", b: 1, c: true }, + { a: "b", b: 2, c: true }, + { a: "b", b: 3, c: true }, + { a: "c", b: 2, c: true } + ]) + + const sortByAgeByName = RA.sortByNonEmpty(byAge, byName) + deepStrictEqual(sortByAgeByName(xs), [ + { a: "a", b: 1, c: true }, + { a: "b", b: 2, c: true }, + { a: "c", b: 2, c: true }, + { a: "b", b: 3, c: true } + ]) + }) + + it("copy", () => { + expect(pipe([], RA.copy)).toEqual([]) + expect(pipe([1, 2, 3], RA.copy)).toEqual([1, 2, 3]) + }) + + it("intercalateNonEmpty", () => { + expect(pipe(["a"], RA.intercalateNonEmpty(String.Semigroup)("b"))).toEqual("a") + expect(pipe(["a1", "a2"], RA.intercalateNonEmpty(String.Semigroup)("b"))).toEqual("a1ba2") + }) + + it("join", () => { + expect(pipe([], RA.join(", "))).toEqual("") + expect(pipe(["a"], RA.join(", "))).toEqual("a") + expect(pipe(["a", "b"], RA.join(", "))).toEqual("a, b") + }) + + it("chop", () => { + const f = RA.chop((as) => [as[0] * 2, as.slice(1)]) + const empty: ReadonlyArray = [] + deepStrictEqual(f(empty), RA.empty()) + deepStrictEqual(f(RA.empty()), RA.empty()) + deepStrictEqual(f([1, 2, 3]), [2, 4, 6]) + deepStrictEqual(RA.chopNonEmpty((as) => [as[0] * 2, as.slice(1)])([1, 2, 3]), [ + 2, + 4, + 6 + ]) + }) + + describe.concurrent("chunksOf", () => { + it("should split a `ReadonlyArray` into length-n pieces", () => { + deepStrictEqual(RA.chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]]) + deepStrictEqual(RA.chunksOf(2)([1, 2, 3, 4, 5, 6]), [ + [1, 2], + [3, 4], + [5, 6] + ]) + deepStrictEqual(RA.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + deepStrictEqual(RA.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) + // out of bounds + deepStrictEqual(RA.chunksOf(0)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + deepStrictEqual(RA.chunksOf(-1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + + const assertSingleChunk = (input: ReadonlyArray, n: number) => { + const chunks = RA.chunksOf(n)(input) + deepStrictEqual(chunks.length, 1) + deepStrictEqual(chunks[0], input) + } + // n = length + assertSingleChunk([1, 2], 2) + // n out of bounds + assertSingleChunk([1, 2], 3) + }) + + it("returns an empty array if provided an empty array", () => { + const empty: ReadonlyArray = [] + deepStrictEqual(RA.chunksOf(0)(empty), RA.empty()) + deepStrictEqual(RA.chunksOf(0)(RA.empty()), RA.empty()) + deepStrictEqual(RA.chunksOf(1)(empty), RA.empty()) + deepStrictEqual(RA.chunksOf(1)(RA.empty()), RA.empty()) + deepStrictEqual(RA.chunksOf(2)(empty), RA.empty()) + deepStrictEqual(RA.chunksOf(2)(RA.empty()), RA.empty()) + }) + + it("should respect the law: chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys)))", () => { + const xs: ReadonlyArray = [] + const ys: ReadonlyArray = [1, 2] + deepStrictEqual( + RA.chunksOf(2)(xs).concat(RA.chunksOf(2)(ys)), + RA.chunksOf(2)(xs.concat(ys)) + ) + fc.assert( + fc.property( + fc.array(fc.integer()).filter((xs) => xs.length % 2 === 0), // Ensures `xs.length` is even + fc.array(fc.integer()), + fc.integer({ min: 1, max: 1 }).map((x) => x * 2), // Generates `n` to be even so that it evenly divides `xs` + (xs, ys, n) => { + const as = RA.chunksOf(n)(xs).concat(RA.chunksOf(n)(ys)) + const bs = RA.chunksOf(n)(xs.concat(ys)) + deepStrictEqual(as, bs) + } + ) + ) + }) + }) + + it("makeBy", () => { + deepStrictEqual(RA.makeBy(double)(5), [0, 2, 4, 6, 8]) + deepStrictEqual(RA.makeBy(double)(2.2), [0, 2]) + }) + + it("replicate", () => { + deepStrictEqual(RA.replicate("a")(0), ["a"]) + deepStrictEqual(RA.replicate("a")(-1), ["a"]) + deepStrictEqual(RA.replicate("a")(3), ["a", "a", "a"]) + deepStrictEqual(RA.replicate("a")(2.2), ["a", "a"]) + }) + + it("range", () => { + deepStrictEqual(RA.range(0, 0), [0]) + deepStrictEqual(RA.range(0, 1), [0, 1]) + deepStrictEqual(RA.range(1, 5), [1, 2, 3, 4, 5]) + deepStrictEqual(RA.range(10, 15), [10, 11, 12, 13, 14, 15]) + deepStrictEqual(RA.range(-1, 0), [-1, 0]) + deepStrictEqual(RA.range(-5, -1), [-5, -4, -3, -2, -1]) + // out of bound + deepStrictEqual(RA.range(2, 1), [2]) + deepStrictEqual(RA.range(-1, -2), [-1]) + }) + + it("union", () => { + const union = RA.union(Number.Equivalence) + const two: ReadonlyArray = [1, 2] + deepStrictEqual(pipe(two, union([3, 4])), [1, 2, 3, 4]) + deepStrictEqual(pipe(two, union([2, 3])), [1, 2, 3]) + deepStrictEqual(pipe(two, union([1, 2])), [1, 2]) + deepStrictEqual(pipe(two, union(RA.empty())), two) + deepStrictEqual(pipe(RA.empty(), union(two)), two) + deepStrictEqual( + pipe(RA.empty(), union(RA.empty())), + RA.empty() + ) + deepStrictEqual(RA.unionNonEmpty(Number.Equivalence)([3, 4])([1, 2]), [1, 2, 3, 4]) + }) + + it("getSemigroup", () => { + const S = RA.getSemigroup() + expect(pipe([1, 2], S.combine([2, 3]))).toEqual([1, 2, 2, 3]) + }) + + it("getUnionSemigroup", () => { + const S = RA.getUnionSemigroup(Number.Equivalence) + expect(pipe([1, 2], S.combine([2, 3]))).toEqual([1, 2, 3]) + }) + + it("intersection", () => { + const intersection = RA.intersection(Number.Equivalence) + deepStrictEqual(pipe([1, 2], intersection([3, 4])), []) + deepStrictEqual(pipe([1, 2], intersection([2, 3])), [2]) + deepStrictEqual(pipe([1, 2], intersection([1, 2])), [1, 2]) + }) + + it("difference", () => { + const difference = RA.difference(Number.Equivalence) + deepStrictEqual(pipe([1, 2], difference([3, 4])), [1, 2]) + deepStrictEqual(pipe([1, 2], difference([2, 3])), [1]) + deepStrictEqual(pipe([1, 2], difference([1, 2])), []) + }) + + it("getUnionMonoid", () => { + const M = RA.getUnionMonoid(Number.Equivalence) + const two: ReadonlyArray = [1, 2] + deepStrictEqual(M.combine([3, 4])(two), [1, 2, 3, 4]) + deepStrictEqual(M.combine([2, 3])(two), [1, 2, 3]) + deepStrictEqual(M.combine([1, 2])(two), [1, 2]) + + deepStrictEqual(M.combine(two)(M.empty), two) + deepStrictEqual(M.combine(M.empty)(two), two) + deepStrictEqual(M.combine(M.empty)(M.empty), M.empty) + + deepStrictEqual(M.combineAll([[1, 2], [3, 4, 5], [5, 6, 7, 1]]), [1, 2, 3, 4, 5, 6, 7]) + }) + + it("getIntersectionSemigroup", () => { + const S = RA.getIntersectionSemigroup(Number.Equivalence) + deepStrictEqual(S.combine([1, 2])([3, 4]), []) + deepStrictEqual(S.combine([1, 2])([2, 3]), [2]) + deepStrictEqual(S.combine([1, 2])([1, 2]), [1, 2]) + }) + + it("should be safe when calling map with a binary function", () => { + interface Foo { + readonly bar: () => number + } + const f = (a: number, x?: Foo) => (x !== undefined ? `${a}${x.bar()}` : `${a}`) + deepStrictEqual(pipe([1, 2], RA.map(f)), ["1", "2"]) + }) + + it("empty", () => { + deepStrictEqual(RA.empty.length, 0) + }) + + it("do notation", () => { + deepStrictEqual( + pipe( + RA.Do, + RA.bind("a", () => [1, 2, 3]), + RA.map(({ a }) => a * 2) + ), + [2, 4, 6] + ) + + deepStrictEqual( + pipe( + RA.Do, + RA.bind("a", () => [1, 2, 3]), + RA.bind("b", () => ["a", "b"]), + RA.map(({ a, b }) => [a, b] as const) + ), + [ + [1, "a"], + [1, "b"], + [2, "a"], + [2, "b"], + [3, "a"], + [3, "b"] + ] + ) + + deepStrictEqual( + pipe( + RA.Do, + RA.bind("a", () => [1, 2, 3]), + RA.bind("b", () => ["a", "b"]), + RA.map(({ a, b }) => [a, b] as const), + RA.filter(([a, b]) => (a + b.length) % 2 === 0) + ), + [ + [1, "a"], + [1, "b"], + [3, "a"], + [3, "b"] + ] + ) + }) + + it("every", () => { + const isPositive: Predicate = (n) => n > 0 + deepStrictEqual(pipe([1, 2, 3], RA.every(isPositive)), true) + deepStrictEqual(pipe([1, 2, -3], RA.every(isPositive)), false) + }) + + it("foldMapNonEmpty", () => { + deepStrictEqual( + pipe( + RA.make("a", "b", "c"), + RA.foldMapNonEmpty(String.Semigroup)(identity) + ), + "abc" + ) + }) + + it("foldMapNonEmptyWithIndex", () => { + deepStrictEqual( + pipe( + RA.make("a", "b"), + RA.foldMapNonEmptyWithIndex(String.Semigroup)((a, i) => i + a) + ), + "0a1b" + ) + }) + + it("some", () => { + const isPositive: Predicate = (n) => n > 0 + deepStrictEqual(pipe([-1, -2, 3], RA.some(isPositive)), true) + deepStrictEqual(pipe([-1, -2, -3], RA.some(isPositive)), false) + // has is an alias of some + deepStrictEqual(pipe([-1, -2, -3], RA.has(isPositive)), false) + }) + + it("size", () => { + deepStrictEqual(RA.size(RA.empty()), 0) + deepStrictEqual(RA.size([]), 0) + deepStrictEqual(RA.size(["a"]), 1) + }) + + it("fromOption", () => { + deepStrictEqual(RA.fromOption(O.some("hello")), ["hello"]) + deepStrictEqual(RA.fromOption(O.none()), []) + }) + + it("fromResult", () => { + deepStrictEqual(RA.fromEither(E.right(1)), [1]) + deepStrictEqual(RA.fromEither(E.left("a")), RA.empty()) + }) + + test("product", () => { + expect(pipe([], RA.product(["a", "b"]))).toEqual([]) + expect(pipe([1, 2], RA.product([]))).toEqual([]) + expect(pipe([1, 2], RA.product(["a", "b"]))).toEqual([ + [1, "a"], + [1, "b"], + [2, "a"], + [2, "b"] + ]) + }) + + test("productMany", () => { + const productMany = semiProduct.productMany(RA.Covariant, RA.product) + expect(pipe( + [], + RA.productMany([ + [2], + [4, 5], + [8, 9, 10] + ]) + )).toEqual(pipe( + [], + productMany([ + [2], + [4, 5], + [8, 9, 10] + ]) + )) + expect(pipe( + [1, 2, 3], + RA.productMany([]) + )).toEqual(pipe( + [1, 2, 3], + productMany([]) + )) + expect(pipe( + [1, 2, 3], + RA.productMany([ + [2], + [4, 5], + [8, 9, 10] + ]) + )).toEqual(pipe( + [1, 2, 3], + productMany([ + [2], + [4, 5], + [8, 9, 10] + ]) + )) + }) + + test("productAll", () => { + expect(RA.productAll([])).toEqual([]) + expect(RA.productAll([[1, 2, 3]])).toEqual([[1], [2], [3]]) + expect(RA.productAll([[1, 2, 3], [4, 5]])).toEqual([[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [ + 3, + 5 + ]]) + }) +}) diff --git a/test/internal/Iterable.ts b/test/internal/Iterable.ts deleted file mode 100644 index 7ee04a196..000000000 --- a/test/internal/Iterable.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as iterable from "@fp-ts/core/internal/Iterable" - -describe.concurrent("internal/Iterable", () => { - it("fromIterable/Array should return the same reference if the iterable is an Array", () => { - const i = [1, 2, 3] - expect(iterable.fromIterable(i) === i).toEqual(true) - }) - - it("fromIterable/Iterable", () => { - expect(iterable.fromIterable(new Set([1, 2, 3]))).toEqual([1, 2, 3]) - }) -}) From 8a579bbc4ec8d8f0d54c53268daf948e5ac8763b Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 15:05:45 +0100 Subject: [PATCH 15/80] chore: remove @category mutations --- src/Either.ts | 1 - src/ReadonlyArray.ts | 35 +---------------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 421875f7a..1101c2435 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -852,7 +852,6 @@ export const liftThrowable = , B, E>( export const merge: (self: Either) => E | A = match(identity, identity) /** - * @category mutations * @since 1.0.0 */ export const reverse = (self: Either): Either => diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index b299f7ce3..8052add73 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -154,7 +154,6 @@ export const matchRight = ( /** * Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`. * - * @category mutations * @since 1.0.0 */ export const prepend = ( @@ -162,14 +161,12 @@ export const prepend = ( ) => (self: Iterable): NonEmptyArray => [head, ...self] /** - * @category mutations * @since 1.0.0 */ export const prependAll = (that: Iterable) => (self: Iterable): Array => fromIterable(that).concat(fromIterable(self)) /** - * @category mutations * @since 1.0.0 */ export function prependAllNonEmpty( @@ -187,7 +184,6 @@ export function prependAllNonEmpty( /** * Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`. * - * @category mutations * @since 1.0.0 */ export const append = ( @@ -195,14 +191,12 @@ export const append = ( ) => (self: Iterable): NonEmptyArray => [...self, last] as any /** - * @category mutations * @since 1.0.0 */ export const appendAll = (that: Iterable) => (self: Iterable): Array => fromIterable(self).concat(fromIterable(that)) /** - * @category mutations * @since 1.0.0 */ export function appendAllNonEmpty( @@ -624,7 +618,6 @@ export function findLast(predicate: Predicate): (self: Iterable) => Opt * Insert an element at the specified index, creating a new `NonEmptyArray`, * or return `None` if the index is out of bounds. * - * @category mutations * @since 1.0.0 */ export const insertAt = (i: number, b: B) => @@ -642,7 +635,6 @@ export const insertAt = (i: number, b: B) => * Change the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * - * @category mutations * @since 1.0.0 */ export const replace = ( @@ -651,7 +643,6 @@ export const replace = ( ): ((self: Iterable) => Array) => modify(i, () => b) /** - * @category mutations * @since 1.0.0 */ export const replaceOption = ( @@ -663,7 +654,6 @@ export const replaceOption = ( * Apply a function to the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * - * @category mutations * @since 1.0.0 */ export const modify = (i: number, f: (a: A) => B) => @@ -673,8 +663,7 @@ export const modify = (i: number, f: (a: A) => B) => /** * Apply a function to the element at the specified index, creating a new `Array`, * or return `None` if the index is out of bounds. - -* @category mutations + * * @since 1.0.0 */ export const modifyOption = (i: number, f: (a: A) => B) => @@ -693,7 +682,6 @@ export const modifyOption = (i: number, f: (a: A) => B) => * Delete the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * - * @category mutations * @since 1.0.0 */ export const remove = (i: number) => @@ -709,7 +697,6 @@ export const remove = (i: number) => /** * Reverse an `Iterable`, creating a new `Array`. * - * @category mutations * @since 1.0.0 */ export const reverse = ( @@ -808,7 +795,6 @@ export const sortByNonEmpty = ( * If one input `Iterable` is short, excess elements of the * longer `Iterable` are discarded. * - * @category mutations * @since 1.0.0 */ export const zip = ( @@ -819,7 +805,6 @@ export const zip = ( * Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one * input `Iterable` is short, excess elements of the longer `Iterable` are discarded. * - * @category mutations * @since 1.0.0 */ export const zipWith = (that: Iterable, f: (a: A, b: B) => C) => @@ -830,7 +815,6 @@ export const zipWith = (that: Iterable, f: (a: A, b: B) => C) => } /** - * @category mutations * @since 1.0.0 */ export const zipNonEmpty = (that: NonEmptyReadonlyArray) => @@ -841,7 +825,6 @@ export const zipNonEmpty = (that: NonEmptyReadonlyArray) => ) /** - * @category mutations * @since 1.0.0 */ export const zipNonEmptyWith = (that: NonEmptyReadonlyArray, f: (a: A, b: B) => C) => @@ -857,7 +840,6 @@ export const zipNonEmptyWith = (that: NonEmptyReadonlyArray, f: (a: /** * This function is the inverse of `zip`. Takes an `Iterable` of pairs and return two corresponding `Array`s. * - * @category mutations * @since 1.0.0 */ export const unzip = ( @@ -868,7 +850,6 @@ export const unzip = ( } /** - * @category mutations * @since 1.0.0 */ export const unzipNonEmpty = ( @@ -886,7 +867,6 @@ export const unzipNonEmpty = ( /** * Places an element in between members of an `Iterable` * - * @category mutations * @since 1.0.0 */ export const intersperse = (middle: B) => @@ -898,7 +878,6 @@ export const intersperse = (middle: B) => /** * Places an element in between members of a `NonEmptyReadonlyArray` * - * @category mutations * @since 1.0.0 */ export const intersperseNonEmpty = (middle: B) => @@ -917,7 +896,6 @@ export const intersperseNonEmpty = (middle: B) => /** * Apply a function to the head, creating a new `NonEmptyReadonlyArray`. * - * @category mutations * @since 1.0.0 */ export const modifyNonEmptyHead = (f: (a: A) => B) => @@ -928,7 +906,6 @@ export const modifyNonEmptyHead = (f: (a: A) => B) => /** * Change the head, creating a new `NonEmptyReadonlyArray`. * - * @category mutations * @since 1.0.0 */ export const setNonEmptyHead = ( @@ -938,7 +915,6 @@ export const setNonEmptyHead = ( /** * Apply a function to the last element, creating a new `NonEmptyReadonlyArray`. * - * @category mutations * @since 1.0.0 */ export const modifyNonEmptyLast = (f: (a: A) => B) => @@ -948,7 +924,6 @@ export const modifyNonEmptyLast = (f: (a: A) => B) => /** * Change the last element, creating a new `NonEmptyReadonlyArray`. * - * @category mutations * @since 1.0.0 */ export const setNonEmptyLast = ( @@ -958,7 +933,6 @@ export const setNonEmptyLast = ( /** * Rotate an `Iterable` by `n` steps. * - * @category mutations * @since 1.0.0 */ export const rotate = (n: number) => @@ -970,7 +944,6 @@ export const rotate = (n: number) => /** * Rotate a `NonEmptyReadonlyArray` by `n` steps. * - * @category mutations * @since 1.0.0 */ export const rotateNonEmpty = (n: number) => @@ -1008,7 +981,6 @@ export const contains = (equivalence: Equivalence) => /** * Remove duplicates from am `Iterable`, keeping the first occurrence of an element. * - * @category mutations * @since 1.0.0 */ export const uniq = (equivalence: Equivalence) => @@ -1020,7 +992,6 @@ export const uniq = (equivalence: Equivalence) => /** * Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element. * - * @category mutations * @since 1.0.0 */ export const uniqNonEmpty = (equivalence: Equivalence) => @@ -1191,7 +1162,6 @@ export const groupBy = (f: (a: A) => string) => } /** - * @category mutations * @since 1.0.0 */ export const union = (equivalence: Equivalence) => @@ -1207,7 +1177,6 @@ export const union = (equivalence: Equivalence) => } /** - * @category mutations * @since 1.0.0 */ export const unionNonEmpty = (equivalence: Equivalence): { @@ -1223,7 +1192,6 @@ export const unionNonEmpty = (equivalence: Equivalence): { * Creates an `Array` of unique values that are included in all given `Iterable`s. * The order and references of result values are determined by the first `Iterable`. * - * @category mutations * @since 1.0.0 */ export const intersection = (equivalence: Equivalence) => @@ -1235,7 +1203,6 @@ export const intersection = (equivalence: Equivalence) => * Creates a `Array` of values not included in the other given `Iterable`. * The order and references of result values are determined by the first `Iterable`. * - * @category mutations * @since 1.0.0 */ export const difference = (equivalence: Equivalence) => From 1d9fbe4a769106e78bc7715a90b1dfc7af6ce265 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 15:08:00 +0100 Subject: [PATCH 16/80] chore: remove @fp-ts/data --- src/Boolean.ts | 14 +++++----- src/Function.ts | 22 +++++++-------- src/Number.ts | 8 +++--- src/String.ts | 66 ++++++++++++++++++++++----------------------- test/data/Option.ts | 20 +++++++------- 5 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/Boolean.ts b/src/Boolean.ts index f10ed523f..7e9d8b8b1 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -33,9 +33,9 @@ export const or = (that: boolean) => (self: boolean): boolean => self || that * If `value` is `false`, `onFalse()` is returned, otherwise `onTrue()`. * * @example - * import { some, map } from '@fp-ts/data/Option' - * import { pipe } from '@fp-ts/data/Function' - * import { match } from '@fp-ts/data/Boolean' + * import { some, map } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' + * import { match } from '@fp-ts/core/Boolean' * * assert.deepStrictEqual( * pipe( @@ -55,8 +55,8 @@ export const match = (onFalse: LazyArg, onTrue: LazyArg) => * `boolean` semigroup under conjunction. * * @example - * import { SemigroupAll } from '@fp-ts/data/Boolean' - * import { pipe } from '@fp-ts/data/Function' + * import { SemigroupAll } from '@fp-ts/core/Boolean' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(true)), true) * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(false)), false) @@ -84,8 +84,8 @@ export const SemigroupAll: semigroup.Semigroup = { * `boolean` semigroup under disjunction. * * @example - * import { SemigroupAny } from '@fp-ts/data/Boolean' - * import { pipe } from '@fp-ts/data/Function' + * import { SemigroupAny } from '@fp-ts/core/Boolean' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(true)), true) * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(false)), true) diff --git a/src/Function.ts b/src/Function.ts index d93985a8a..6326f46fc 100644 --- a/src/Function.ts +++ b/src/Function.ts @@ -27,9 +27,9 @@ export const compose: (bc: (b: B) => C) => (ab: (a: A) => B) => (a: A) * Unary functions form a semigroup as long as you can provide a semigroup for the codomain. * * @example - * import { Predicate } from '@fp-ts/data/Predicate' - * import { pipe, getSemigroup } from '@fp-ts/data/Function' - * import * as B from '@fp-ts/data/Boolean' + * import { Predicate } from '@fp-ts/core/Predicate' + * import { pipe, getSemigroup } from '@fp-ts/core/Function' + * import * as B from '@fp-ts/core/Boolean' * * const f: Predicate = (n) => n <= 2 * const g: Predicate = (n) => n >= 0 @@ -55,9 +55,9 @@ export const getSemigroup = (Semigroup: semigroup.Semigroup) => * Unary functions form a monoid as long as you can provide a monoid for the codomain. * * @example - * import { Predicate } from '@fp-ts/data/Predicate' - * import { getMonoid, pipe } from '@fp-ts/data/Function' - * import * as B from '@fp-ts/data/Boolean' + * import { Predicate } from '@fp-ts/core/Predicate' + * import { getMonoid, pipe } from '@fp-ts/core/Function' + * import * as B from '@fp-ts/core/Boolean' * * const f: Predicate = (n) => n <= 2 * const g: Predicate = (n) => n >= 0 @@ -102,7 +102,7 @@ export interface LazyArg { /** * @example - * import { FunctionN } from '@fp-ts/data/Function' + * import { FunctionN } from '@fp-ts/core/Function' * * export const sum: FunctionN<[number, number], number> = (a, b) => a + b * @@ -166,7 +166,7 @@ export const constVoid: LazyArg = constUndefined * Flips the arguments of a curried function. * * @example - * import { flip } from '@fp-ts/data/Function' + * import { flip } from '@fp-ts/core/Function' * * const f = (a: number) => (b: string) => a - b.length * @@ -181,7 +181,7 @@ export const flip = (f: (a: A) => (b: B) => C): ((b: B) => (a: A) => C) * Performs left-to-right function composition. The first argument may have any arity, the remaining arguments must be unary. * * @example - * import { flow } from '@fp-ts/data/Function' + * import { flow } from '@fp-ts/core/Function' * * const len = (s: string): number => s.length * const double = (n: number): number => n * 2 @@ -315,7 +315,7 @@ export const absurd = (_: never): A => { * Creates a tupled version of this function: instead of `n` arguments, it accepts a single tuple argument. * * @example - * import { tupled } from '@fp-ts/data/Function' + * import { tupled } from '@fp-ts/core/Function' * * const add = tupled((x: number, y: number): number => x + y) * @@ -338,7 +338,7 @@ export const untupled = , B>(f: (a: A) => B): ( * Pipes the value of an expression into a pipeline of functions. * * @example - * import { pipe } from '@fp-ts/data/Function' + * import { pipe } from '@fp-ts/core/Function' * * const len = (s: string): number => s.length * const double = (n: number): number => n * 2 diff --git a/src/Number.ts b/src/Number.ts index ba33b56db..54067783b 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -69,8 +69,8 @@ export const Bounded: bounded.Bounded = { * `number` semigroup under addition. * * @example - * import { SemigroupSum } from '@fp-ts/data/Number' - * import { pipe } from '@fp-ts/data/Function' + * import { SemigroupSum } from '@fp-ts/core/Number' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(2, SemigroupSum.combine(3)), 5) * @@ -83,8 +83,8 @@ export const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(s * `number` semigroup under multiplication. * * @example - * import { SemigroupMultiply } from '@fp-ts/data/Number' - * import { pipe } from '@fp-ts/data/Function' + * import { SemigroupMultiply } from '@fp-ts/core/Number' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(2, SemigroupMultiply.combine(3)), 6) * diff --git a/src/String.ts b/src/String.ts index 74ecc7a96..950aca447 100644 --- a/src/String.ts +++ b/src/String.ts @@ -19,8 +19,8 @@ export const concat = (that: string) => (self: string): string => self + that * `string` semigroup under concatenation. * * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('a', S.Semigroup.combine('b')), 'ab') * @@ -42,8 +42,8 @@ export const empty: "" = "" as const * The `empty` value is `''`. * * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('a', S.Monoid.combine('b')), 'ab') * assert.deepStrictEqual(pipe('a', S.Monoid.combine(S.Monoid.empty)), 'a') @@ -65,8 +65,8 @@ export const Equivalence: equivalence.Equivalence = equivalence.string /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('a', S.Order.compare('a')), 0) * assert.deepStrictEqual(pipe('a', S.Order.compare('b')), -1) @@ -81,7 +81,7 @@ export const Order: order.Order = { /** * @example - * import * as S from '@fp-ts/data/String' + * import * as S from '@fp-ts/core/String' * * assert.deepStrictEqual(S.isString('a'), true) * assert.deepStrictEqual(S.isString(1), false) @@ -94,8 +94,8 @@ export const isString: Refinement = (u: unknown): u is string = /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('a', S.toUpperCase), 'A') * @@ -105,8 +105,8 @@ export const toUpperCase = (s: string): string => s.toUpperCase() /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('A', S.toLowerCase), 'a') * @@ -116,8 +116,8 @@ export const toLowerCase = (s: string): string => s.toLowerCase() /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.replace('b', 'd')), 'adc') * @@ -128,8 +128,8 @@ export const replace = (searchValue: string | RegExp, replaceValue: string) => /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(' a ', S.trim), 'a') * @@ -139,8 +139,8 @@ export const trim = (s: string): string => s.trim() /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(' a ', S.trimLeft), 'a ') * @@ -150,8 +150,8 @@ export const trimLeft = (s: string): string => s.trimLeft() /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe(' a ', S.trimRight), ' a') * @@ -161,8 +161,8 @@ export const trimRight = (s: string): string => s.trimRight() /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abcd', S.slice(1, 3)), 'bc') * @@ -174,8 +174,8 @@ export const slice = (start: number, end: number) => (s: string): string => s.sl * Test whether a `string` is empty. * * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('', S.isEmpty), true) * assert.deepStrictEqual(pipe('a', S.isEmpty), false) @@ -188,8 +188,8 @@ export const isEmpty = (s: string): s is "" => s.length === 0 * Calculate the number of characters in a `string`. * * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.size), 3) * @@ -199,8 +199,8 @@ export const size = (s: string): number => s.length /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.split('')), ['a', 'b', 'c']) * assert.deepStrictEqual(pipe('', S.split('')), ['']) @@ -215,8 +215,8 @@ export const split = (separator: string | RegExp) => /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.includes('b')), true) * assert.deepStrictEqual(pipe('abc', S.includes('d')), false) @@ -228,8 +228,8 @@ export const includes = (searchString: string, position?: number) => /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.startsWith('a')), true) * assert.deepStrictEqual(pipe('bc', S.startsWith('a')), false) @@ -241,8 +241,8 @@ export const startsWith = (searchString: string, position?: number) => /** * @example - * import * as S from '@fp-ts/data/String' - * import { pipe } from '@fp-ts/data/Function' + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual(pipe('abc', S.endsWith('c')), true) * assert.deepStrictEqual(pipe('ab', S.endsWith('c')), false) diff --git a/test/data/Option.ts b/test/data/Option.ts index e88852b59..95e34df15 100644 --- a/test/data/Option.ts +++ b/test/data/Option.ts @@ -96,8 +96,8 @@ export const toNull: (self: Option) => A | null = getOrElse(null) * Extracts the value out of the structure, if it exists. Otherwise returns `undefined`. * * @exampleTodo - * import { some, none, toUndefined } from '@fp-ts/data/Option' - * import { pipe } from '@fp-ts/data/Function' + * import { some, none, toUndefined } from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' * * assert.strictEqual( * pipe( @@ -233,8 +233,8 @@ export const catchAll = (that: LazyArg>) => * | some(a) | some(b) | some(a) | * * @exampleTodo - * import * as O from '@fp-ts/data/Option' - * import { pipe } from '@fp-ts/data/Function' + * import * as O from '@fp-ts/core/Option' + * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual( * pipe( @@ -320,9 +320,9 @@ export const traverse = ( * `None` is considered to be less than any `Some` value. * * @exampleTodo - * import { none, some, liftOrder } from '@fp-ts/data/Option' - * import * as N from '@fp-ts/data/number' - * import { pipe } from '@fp-ts/data/Function' + * import { none, some, liftOrder } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/number' + * import { pipe } from '@fp-ts/core/Function' * * const O = liftOrder(N.Order) * assert.strictEqual(pipe(none, O.compare(none)), 0) @@ -351,9 +351,9 @@ export const liftOrder = (O: order.Order): order.Order> => * | some(a) | some(b) | some(combine(b)(a)) | * * @exampleTodo - * import { getMonoid, some, none } from '@fp-ts/data/Option' - * import * as N from '@fp-ts/data/number' - * import { pipe } from '@fp-ts/data/Function' + * import { getMonoid, some, none } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/number' + * import { pipe } from '@fp-ts/core/Function' * * const M = getMonoid(N.SemigroupSum) * assert.deepStrictEqual(pipe(none, M.combine(none)), none) From e21d82fb0fb9b23cee910403abc098687d8ff8c1 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 15:26:12 +0100 Subject: [PATCH 17/80] ReadonlyArray: add CovariantWithIndex, FilterableWithIndex instances --- src/ReadonlyArray.ts | 24 ++++++++++++++ test/ReadonlyArray.ts | 2 ++ test/typeclass/Compactable.ts | 25 ++++++++++++++ test/typeclass/CovariantWithIndex.ts | 2 +- test/typeclass/Filterable.ts | 44 +++++++++++++++++++++++++ test/typeclass/TraversableFilterable.ts | 36 ++++++++++++++++++++ 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 test/typeclass/Compactable.ts create mode 100644 test/typeclass/Filterable.ts create mode 100644 test/typeclass/TraversableFilterable.ts diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 8052add73..1a6b90d2c 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -18,8 +18,10 @@ import * as chainable from "@fp-ts/core/typeclass/Chainable" import type * as compactable from "@fp-ts/core/typeclass/Compactable" import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type * as covariantWithIndex from "@fp-ts/core/typeclass/CovariantWithIndex" import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" +import type * as filterableWithIndex from "@fp-ts/core/typeclass/FilterableWithIndex" import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import type * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -1291,6 +1293,17 @@ export const Invariant: invariant.Invariant = { imap } +/** + * @category instances + * @since 1.0.0 + */ +export const CovariantWithIndex: covariantWithIndex.CovariantWithIndex< + ReadonlyArrayTypeLambda, + number +> = { + mapWithIndex +} + /** * @category instances * @since 1.0.0 @@ -1492,6 +1505,17 @@ export const separate = ( return [left, right] } +/** + * @category instances + * @since 1.0.0 + */ +export const FilterableWithIndex: filterableWithIndex.FilterableWithIndex< + ReadonlyArrayTypeLambda, + number +> = { + filterMapWithIndex +} + /** * @category instances * @since 1.0.0 diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 550933a4e..d662c135c 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -16,6 +16,7 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.Invariant).exist expect(RA.imap).exist + expect(RA.CovariantWithIndex).exist expect(RA.Covariant).exist expect(RA.map).exist expect(RA.let).exist @@ -75,6 +76,7 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.compact).exist expect(RA.separate).exist + expect(RA.FilterableWithIndex).exist expect(RA.Filterable).exist expect(RA.filterMap).exist expect(RA.filter).exist diff --git a/test/typeclass/Compactable.ts b/test/typeclass/Compactable.ts new file mode 100644 index 000000000..50fed2746 --- /dev/null +++ b/test/typeclass/Compactable.ts @@ -0,0 +1,25 @@ +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/typeclass/Compactable" +import * as U from "../util" + +describe("Compactable", () => { + it("compactComposition", () => { + const compact = _.compactComposition(RA.Covariant, O.Compactable) + U.deepStrictEqual(pipe([], compact), []) + U.deepStrictEqual(pipe([O.none()], compact), [O.none()]) + U.deepStrictEqual(pipe([O.some(O.none())], compact), [O.none()]) + U.deepStrictEqual(pipe([O.some(O.some(1))], compact), [O.some(1)]) + }) + + it("separate", () => { + const separate = _.separate({ ...RA.Covariant, ...RA.Compactable }) + U.deepStrictEqual(pipe([], separate), [[], []]) + U.deepStrictEqual(pipe([E.right(1), E.left("e"), E.right(2)], separate), [ + ["e"], + [1, 2] + ]) + }) +}) diff --git a/test/typeclass/CovariantWithIndex.ts b/test/typeclass/CovariantWithIndex.ts index a71e23efd..f60fa5511 100644 --- a/test/typeclass/CovariantWithIndex.ts +++ b/test/typeclass/CovariantWithIndex.ts @@ -3,7 +3,7 @@ import * as RA from "../data/ReadonlyArray" import * as _ from "../limbo/CovariantWithIndex" import * as U from "../util" -describe("FunctorWithIndex", () => { +describe("CovariantWithIndex", () => { it("mapWithIndexComposition", () => { const mapWithIndex = _.mapWithIndexComposition(RA.CovariantWithIndex, RA.CovariantWithIndex) const f = (a: string, [i, j]: readonly [number, number]) => a + i + j diff --git a/test/typeclass/Filterable.ts b/test/typeclass/Filterable.ts new file mode 100644 index 000000000..a22477b65 --- /dev/null +++ b/test/typeclass/Filterable.ts @@ -0,0 +1,44 @@ +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/typeclass/Filterable" +import * as U from "../util" + +describe("Filterable", () => { + it("filterMapComposition", () => { + const filterMap = _.filterMapComposition(RA.Covariant, O.Filterable) + const f = filterMap((s: string) => s.length > 1 ? O.some(s.length) : O.none()) + U.deepStrictEqual(pipe([], f), []) + U.deepStrictEqual(pipe([O.none()], f), [O.none()]) + U.deepStrictEqual(pipe([O.some("a")], f), [O.none()]) + U.deepStrictEqual(pipe([O.some("aa")], f), [O.some(2)]) + }) + + it("filter", () => { + const filter = _.filter(RA.Filterable) + const f = filter((n: number) => n > 0) + U.deepStrictEqual(pipe([], f), []) + U.deepStrictEqual(pipe([1], f), [1]) + U.deepStrictEqual(pipe([-1], f), []) + U.deepStrictEqual(pipe([1, -1], f), [1]) + }) + + it("partitionMap", () => { + const partitionMap = _.partitionMap(RA.Filterable) + const f = partitionMap((s: string) => s.length > 1 ? E.right(s.length) : E.left(s)) + U.deepStrictEqual(pipe([], f), [[], []]) + U.deepStrictEqual(pipe(["a"], f), [["a"], []]) + U.deepStrictEqual(pipe(["aa"], f), [[], [2]]) + U.deepStrictEqual(pipe(["aa", "a"], f), [["a"], [2]]) + }) + + it("partition", () => { + const partition = _.partition(RA.Filterable) + const f = partition((n: number) => n > 0) + U.deepStrictEqual(pipe([], f), [[], []]) + U.deepStrictEqual(pipe([1], f), [[], [1]]) + U.deepStrictEqual(pipe([-1], f), [[-1], []]) + U.deepStrictEqual(pipe([1, -1], f), [[-1], [1]]) + }) +}) diff --git a/test/typeclass/TraversableFilterable.ts b/test/typeclass/TraversableFilterable.ts new file mode 100644 index 000000000..4b207fe9c --- /dev/null +++ b/test/typeclass/TraversableFilterable.ts @@ -0,0 +1,36 @@ +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/typeclass/TraversableFilterable" +import * as U from "../util" + +describe("TraversableFilterable", () => { + it("traverseFilter", () => { + const traverseFilter = _.traverseFilter( + RA.TraversableFilterable + )(O.Applicative) + const f = traverseFilter((s: string) => + s.length > 2 ? O.some(false) : s.length > 1 ? O.some(true) : O.none() + ) + U.deepStrictEqual(f([]), O.some([])) + U.deepStrictEqual(f(["a"]), O.none()) + U.deepStrictEqual(f(["a", "aa"]), O.none()) + U.deepStrictEqual(f(["aa"]), O.some(["aa"])) + U.deepStrictEqual(f(["aaa"]), O.some([])) + U.deepStrictEqual(f(["aaa", "aa"]), O.some(["aa"])) + }) + + it("traversePartition", () => { + const traversePartition = _.traversePartition( + RA.TraversableFilterable + )(O.Applicative) + const f = traversePartition((s: string) => + s.length > 2 ? O.some(false) : s.length > 1 ? O.some(true) : O.none() + ) + U.deepStrictEqual(f([]), O.some([[], []] as const)) + U.deepStrictEqual(f(["a"]), O.none()) + U.deepStrictEqual(f(["a", "aa"]), O.none()) + U.deepStrictEqual(f(["aa"]), O.some([[], ["aa"]] as const)) + U.deepStrictEqual(f(["aaa"]), O.some([["aaa"], []] as const)) + U.deepStrictEqual(f(["aaa", "aa"]), O.some([["aaa"], ["aa"]] as const)) + }) +}) From 0db018e85b33a24bbef5724fbef9bcc6770ae8e0 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 15:50:01 +0100 Subject: [PATCH 18/80] chore: remove /test/data --- src/ReadonlyArray.ts | 28 + test/data/Either.ts | 64 --- test/data/NonEmptyArray.ts | 3 - test/data/NonEmptyReadonlyArray.ts | 69 --- test/data/Option.ts | 701 ------------------------- test/data/Predicate.ts | 90 ---- test/data/ReadonlyArray.ts | 150 ------ test/data/boolean.ts | 42 -- test/data/number.ts | 41 -- test/data/string.ts | 13 - test/limbo/CovariantWithIndex.ts | 40 -- test/typeclass/Applicative.ts | 10 +- test/typeclass/Bicovariant.ts | 6 +- test/typeclass/Bounded.ts | 6 +- test/typeclass/Chainable.ts | 16 +- test/typeclass/Contravariant.ts | 8 +- test/typeclass/Coproduct.ts | 10 +- test/typeclass/Covariant.ts | 12 +- test/typeclass/CovariantWithIndex.ts | 4 +- test/typeclass/FlatMap.ts | 20 +- test/typeclass/Foldable.ts | 34 +- test/typeclass/FoldableWithIndex.ts | 104 ++-- test/typeclass/Invariant.ts | 22 +- test/typeclass/Monoid.ts | 18 +- test/typeclass/NonEmptyTraversable.ts | 35 +- test/typeclass/Of.ts | 4 +- test/typeclass/Order.ts | 8 +- test/typeclass/Product.ts | 23 +- test/typeclass/SemiApplicative.ts | 44 +- test/typeclass/SemiCoproduct.ts | 2 +- test/typeclass/SemiProduct.ts | 79 +-- test/typeclass/Semigroup.ts | 38 +- test/typeclass/Traversable.ts | 23 +- test/typeclass/TraversableWithIndex.ts | 68 +-- vitest.config.ts | 4 +- 35 files changed, 335 insertions(+), 1504 deletions(-) delete mode 100644 test/data/Either.ts delete mode 100644 test/data/NonEmptyArray.ts delete mode 100644 test/data/NonEmptyReadonlyArray.ts delete mode 100644 test/data/Option.ts delete mode 100644 test/data/Predicate.ts delete mode 100644 test/data/ReadonlyArray.ts delete mode 100644 test/data/boolean.ts delete mode 100644 test/data/number.ts delete mode 100644 test/data/string.ts delete mode 100644 test/limbo/CovariantWithIndex.ts diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 1a6b90d2c..9d6947353 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -40,6 +40,8 @@ import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" import * as traversableFilterable from "@fp-ts/core/typeclass/TraversableFilterable" +import type * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" + /** * @category type lambdas * @since 1.0.0 @@ -48,6 +50,14 @@ export interface ReadonlyArrayTypeLambda extends TypeLambda { readonly type: ReadonlyArray } +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface NonEmptyReadonlyArrayTypeLambda extends TypeLambda { + readonly type: NonEmptyReadonlyArray +} + /** * @category models * @since 1.0.0 @@ -2254,3 +2264,21 @@ export const liftOrder = (O: Order): Order> => return number.Order.compare(bLen)(aLen) } ) + +/** + * @category instances + * @since 1.0.0 + */ +export const NonEmptyCovariant: covariant.Covariant = covariant + .make(mapNonEmpty) + +/** + * @category instances + * @since 1.0.0 + */ +export const NonEmptyTraversable: nonEmptyTraversable.NonEmptyTraversable< + NonEmptyReadonlyArrayTypeLambda +> = { + traverseNonEmpty, + sequenceNonEmpty: F => self => pipe(self, traverseNonEmpty(F)(identity)) +} diff --git a/test/data/Either.ts b/test/data/Either.ts deleted file mode 100644 index 643702a48..000000000 --- a/test/data/Either.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { TypeLambda } from "@fp-ts/core/HKT" -import * as covariant from "@fp-ts/core/typeclass/Covariant" -import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" -import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" - -export interface Left { - readonly _tag: "Left" - readonly left: E -} - -export interface Right { - readonly _tag: "Right" - readonly right: A -} - -export type Either = Left | Right - -export interface EitherTypeLambda extends TypeLambda { - readonly type: Either -} - -export const left = (e: E): Either => ({ _tag: "Left", left: e }) - -export const right = (a: A): Either => ({ _tag: "Right", right: a }) - -/** - * @since 1.0.0 - */ -export const isLeft = (self: Either): self is Left => self._tag === "Left" - -/** - * @since 1.0.0 - */ -export const isRight = (self: Either): self is Right => self._tag === "Right" - -const map = (f: (a: A) => B) => - (self: Either): Either => isRight(self) ? right(f(self.right)) : self - -const imap = covariant.imap(map) - -const coproduct = ( - that: Either -) => (self: Either): Either => isRight(self) ? self : that - -export const SemiCoproduct: semiCoproduct.SemiCoproduct = { - imap, - coproduct, - coproductMany: collection => - self => { - let out = self - if (isRight(out)) { - return out - } - for (out of collection) { - if (isRight(out)) { - return out - } - } - return out - } -} - -export const getSemigroup: () => Semigroup> = semiCoproduct - .getSemigroup(SemiCoproduct) diff --git a/test/data/NonEmptyArray.ts b/test/data/NonEmptyArray.ts deleted file mode 100644 index 02b536784..000000000 --- a/test/data/NonEmptyArray.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const make = ( - ...as: [A, ...Array] -): [A, ...Array] => as diff --git a/test/data/NonEmptyReadonlyArray.ts b/test/data/NonEmptyReadonlyArray.ts deleted file mode 100644 index 0a53f4b2f..000000000 --- a/test/data/NonEmptyReadonlyArray.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { identity, pipe } from "@fp-ts/core/Function" -import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import * as covariant from "@fp-ts/core/typeclass/Covariant" -import type * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" -import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" - -/** - * @category models - * @since 1.0.0 - */ -export type NonEmptyReadonlyArray = readonly [A, ...ReadonlyArray] - -/** - * @category type lambdas - * @since 1.0.0 - */ -export interface NonEmptyReadonlyArrayTypeLambda extends TypeLambda { - readonly type: NonEmptyReadonlyArray -} - -export const isNonEmpty = (self: ReadonlyArray): self is readonly [A, ...ReadonlyArray] => - self.length > 0 - -export const head = (as: readonly [A, ...ReadonlyArray]): A => as[0] -export const tail = (as: readonly [A, ...ReadonlyArray]): ReadonlyArray => as.slice(1) - -export const mapWithIndex = ( - f: (a: A, i: number) => B -) => - (self: NonEmptyReadonlyArray): NonEmptyReadonlyArray => { - const out: [B, ...Array] = [f(head(self), 0)] - for (let i = 1; i < self.length; i++) { - out.push(f(self[i], i)) - } - return out - } - -export const map = ( - f: (a: A) => B -): (self: NonEmptyReadonlyArray) => NonEmptyReadonlyArray => mapWithIndex(f) - -export const traverseWithIndex = ( - F: SemiApplicative -) => - (f: (a: A, i: number) => Kind) => - (self: NonEmptyReadonlyArray): Kind> => { - const fbs = pipe(self, mapWithIndex(f)) - return pipe( - head(fbs), - F.productMany(tail(fbs)) - ) - } - -export const traverseNonEmpty = ( - F: SemiApplicative -) => - ( - f: (a: A) => Kind - ): ((self: NonEmptyReadonlyArray) => Kind>) => - traverseWithIndex(F)(f) - -export const Covariant: covariant.Covariant = covariant.make(map) - -export const NonEmptyTraversable: nonEmptyTraversable.NonEmptyTraversable< - NonEmptyReadonlyArrayTypeLambda -> = { - traverseNonEmpty, - sequenceNonEmpty: F => self => pipe(self, traverseNonEmpty(F)(identity)) -} diff --git a/test/data/Option.ts b/test/data/Option.ts deleted file mode 100644 index 95e34df15..000000000 --- a/test/data/Option.ts +++ /dev/null @@ -1,701 +0,0 @@ -/** - * ```ts - * type Option = None | Some - * ``` - * - * `Option` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is - * an instance of `Some`, containing the present value of type `A`. If the value is absent, the `Option` is an - * instance of `None`. - * - * An option could be looked at as a collection or foldable structure with either one or zero elements. - * Another way to look at `Option` is: it represents the effect of a possibly failing computation. - * - * @since 1.0.0 - */ -import { identity, pipe } from "@fp-ts/core/Function" -import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import type * as extendable from "@fp-ts/core/test/limbo/Extendable" -import type * as alternative from "@fp-ts/core/typeclass/Alternative" -import type * as applicative from "@fp-ts/core/typeclass/Applicative" -import * as chainable from "@fp-ts/core/typeclass/Chainable" -import * as covariant from "@fp-ts/core/typeclass/Covariant" -import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" -import type * as foldable from "@fp-ts/core/typeclass/Foldable" -import * as invariant from "@fp-ts/core/typeclass/Invariant" -import type * as monad from "@fp-ts/core/typeclass/Monad" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" -import type * as of_ from "@fp-ts/core/typeclass/Of" -import * as order from "@fp-ts/core/typeclass/Order" -import type * as pointed from "@fp-ts/core/typeclass/Pointed" -import type * as product from "@fp-ts/core/typeclass/Product" -import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" -import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" -import type * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" -import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" -import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" -import * as traversable from "@fp-ts/core/typeclass/Traversable" -import type * as foldableWithIndex from "../limbo/FoldableWithIndex" -import * as nonEmptyArray from "./NonEmptyArray" -import * as nonEmptyReadonlyArray from "./NonEmptyReadonlyArray" - -export interface LazyArg { - (): A -} - -export interface None { - readonly _tag: "None" -} - -export interface Some { - readonly _tag: "Some" - readonly value: A -} - -export type Option = None | Some - -export interface OptionTypeLambda extends TypeLambda { - readonly type: Option -} - -export const isNone = (fa: Option): fa is None => fa._tag === "None" - -export const isSome = (fa: Option): fa is Some => fa._tag === "Some" - -export const none: Option = { _tag: "None" } - -export const some = (a: A): Option => ({ _tag: "Some", value: a }) - -export const fromIterable = (collection: Iterable): Option => { - for (const a of collection) { - return some(a) - } - return none -} - -export const match = (onNone: LazyArg, onSome: (a: A) => C) => - (ma: Option): B | C => isNone(ma) ? onNone() : onSome(ma.value) - -export const getOrElse = (onNone: B) => - (ma: Option): A | B => isNone(ma) ? onNone : ma.value - -export const fromThrowable = (f: () => A): Option => { - try { - return some(f()) - } catch (e) { - return none - } -} - -export const liftThrowable = , B>( - f: (...a: A) => B -): ((...a: A) => Option) => (...a) => fromThrowable(() => f(...a)) - -export const toNull: (self: Option) => A | null = getOrElse(null) - -/** - * Extracts the value out of the structure, if it exists. Otherwise returns `undefined`. - * - * @exampleTodo - * import { some, none, toUndefined } from '@fp-ts/core/Option' - * import { pipe } from '@fp-ts/core/Function' - * - * assert.strictEqual( - * pipe( - * some(1), - * toUndefined - * ), - * 1 - * ) - * assert.strictEqual( - * pipe( - * none, - * toUndefined - * ), - * undefined - * ) - * - * @category conversions - * @since 1.0.0 - */ -export const toUndefined: (self: Option) => A | undefined = getOrElse(undefined) - -/** - * Returns an effect whose success is mapped by the specified `f` function. - * - * @category mapping - * @since 1.0.0 - */ -export const map: (f: (a: A) => B) => (fa: Option) => Option = (f) => - (fa) => isNone(fa) ? none : some(f(fa.value)) - -/** - * @category instances - * @since 1.0.0 - */ -export const Covariant: covariant.Covariant = covariant.make(map) - -export const Of: of_.Of = { - of: some -} - -/** - * @category instances - * @since 1.0.0 - */ -export const Pointed: pointed.Pointed = { - ...Covariant, - ...Of -} - -/** - * @category sequencing - * @since 1.0.0 - */ -export const flatMap: (f: (a: A) => Option) => (self: Option) => Option = (f) => - (self) => isNone(self) ? none : f(self.value) - -/** - * @category instances - * @since 1.0.0 - */ -export const FlatMap: flatMap_.FlatMap = { - flatMap -} - -/** - * A variant of `flatMap` that ignores the value produced by this effect. - * - * @category sequencing - * @since 1.0.0 - */ -export const andThen: (that: Option) => (self: Option) => Option = flatMap_ - .andThen(FlatMap) - -/** - * @category instances - * @since 1.0.0 - */ -export const Chainable: chainable.Chainable = { - ...FlatMap, - ...Covariant -} - -/** - * Returns an effect that effectfully "peeks" at the success of this effect. - * - * @since 1.0.0 - */ -export const tap: (f: (a: A) => Option) => (self: Option) => Option = chainable - .tap(Chainable) - -/** - * Sequences the specified effect after this effect, but ignores the value - * produced by the effect. - * - * @category sequencing - * @since 1.0.0 - */ -export const andThenDiscard: (that: Option) => (self: Option) => Option = - chainable - .andThenDiscard(Chainable) - -/** - * @since 1.0.0 - */ -export const ap: (fa: Option) => (fab: Option<(a: A) => B>) => Option = (fa) => - (fab) => pipe(fab, flatMap((ab) => pipe(fa, map((a) => ab(a))))) - -/** - * @since 1.0.0 - */ -export const flatten: (mma: Option>) => Option = flatMap(identity) - -/** - * Lazy version of `orElse`. - * - * @category error handling - * @since 1.0.0 - */ -export const catchAll = (that: LazyArg>) => - (self: Option): Option => isNone(self) ? that() : self - -/** - * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to - * types of kind `* -> *`. - * - * In case of `Option` returns the left-most non-`None` value. - * - * | x | y | pipe(x, orElse(y) | - * | ------- | ------- | ------------------| - * | none | none | none | - * | some(a) | none | some(a) | - * | none | some(b) | some(b) | - * | some(a) | some(b) | some(a) | - * - * @exampleTodo - * import * as O from '@fp-ts/core/Option' - * import { pipe } from '@fp-ts/core/Function' - * - * assert.deepStrictEqual( - * pipe( - * O.none, - * O.orElse(O.none) - * ), - * O.none - * ) - * assert.deepStrictEqual( - * pipe( - * O.some('a'), - * O.orElse(O.none) - * ), - * O.some('a') - * ) - * assert.deepStrictEqual( - * pipe( - * O.none, - * O.orElse(O.some('b')) - * ), - * O.some('b') - * ) - * assert.deepStrictEqual( - * pipe( - * O.some('a'), - * O.orElse(O.some('b')) - * ), - * O.some('a') - * ) - * - * @category instance operations - * @since 1.0.0 - */ -export const orElse = (that: Option): ((self: Option) => Option) => - catchAll(() => that) - -/** - * @since 1.0.0 - */ -export const extend: (f: (wa: Option) => B) => (wa: Option) => Option = (f) => - (wa) => isNone(wa) ? none : some(f(wa)) - -/** - * @since 1.0.0 - */ -export const duplicate: (ma: Option) => Option> = extend(identity) - -/** - * @category filtering - * @since 1.0.0 - */ -export const compact: (foa: Option>) => Option = flatten - -/** - * @category filtering - * @since 1.0.0 - */ -export const filterMap: (f: (a: A) => Option) => (fa: Option) => Option = (f) => - (fa) => isNone(fa) ? none : f(fa.value) - -/** - * @category traversing - * @since 1.0.0 - */ -export const traverse = ( - F: applicative.Applicative -) => - ( - f: (a: A) => Kind - ) => - (ta: Option): Kind> => - isNone(ta) ? F.of>(none) : pipe(f(ta.value), F.map(some)) - -// ------------------------------------------------------------------------------------- -// instances -// ------------------------------------------------------------------------------------- - -/** - * The `Sortable` instance allows `Option` values to be compared with - * `compare`, whenever there is an `Sortable` instance for - * the type the `Option` contains. - * - * `None` is considered to be less than any `Some` value. - * - * @exampleTodo - * import { none, some, liftOrder } from '@fp-ts/core/Option' - * import * as N from '@fp-ts/core/number' - * import { pipe } from '@fp-ts/core/Function' - * - * const O = liftOrder(N.Order) - * assert.strictEqual(pipe(none, O.compare(none)), 0) - * assert.strictEqual(pipe(none, O.compare(some(1))), -1) - * assert.strictEqual(pipe(some(1), O.compare(none)), 1) - * assert.strictEqual(pipe(some(1), O.compare(some(2))), -1) - * assert.strictEqual(pipe(some(1), O.compare(some(1))), 0) - * - * @category instances - * @since 1.0.0 - */ -export const liftOrder = (O: order.Order): order.Order> => - order.fromCompare((that) => - (self) => isSome(self) ? (isSome(that) ? O.compare(that.value)(self.value) : 1) : -1 - ) - -/** - * Monoid returning the left-most non-`None` value. If both operands are `Some`s then the inner values are - * combined using the provided `Semigroup` - * - * | x | y | combine(y)(x) | - * | ------- | ------- | ------------------- | - * | none | none | none | - * | some(a) | none | some(a) | - * | none | some(a) | some(a) | - * | some(a) | some(b) | some(combine(b)(a)) | - * - * @exampleTodo - * import { getMonoid, some, none } from '@fp-ts/core/Option' - * import * as N from '@fp-ts/core/number' - * import { pipe } from '@fp-ts/core/Function' - * - * const M = getMonoid(N.SemigroupSum) - * assert.deepStrictEqual(pipe(none, M.combine(none)), none) - * assert.deepStrictEqual(pipe(some(1), M.combine(none)), some(1)) - * assert.deepStrictEqual(pipe(none, M.combine(some(1))), some(1)) - * assert.deepStrictEqual(pipe(some(1), M.combine(some(2))), some(3)) - * - * @category instances - * @since 1.0.0 - */ -export const getMonoid = ( - S: semigroup.Semigroup -): monoid.Monoid> => { - const combine = (that: Option) => - (self: Option): Option => - isNone(self) ? that : isNone(that) ? self : some(S.combine(that.value)(self.value)) - return ({ - combine, - combineMany: (others) => - (start) => { - let c = start - for (const o of others) { - c = combine(o)(c) - } - return c - }, - combineAll: (collection: Iterable>): Option => { - let c: Option = none - for (const o of collection) { - c = combine(o)(c) - } - return c - }, - empty: none - }) -} - -/** - * @category mapping - * @since 1.0.0 - */ -export const flap: (a: A) => (fab: Option<(a: A) => B>) => Option = covariant.flap( - Covariant -) - -/** - * Maps the success value of this effect to the specified constant value. - * - * @category mapping - * @since 1.0.0 - */ -export const as: (b: B) => (self: Option) => Option = covariant.as(Covariant) - -/** - * Returns the effect resulting from mapping the success of this effect to unit. - * - * @category mapping - * @since 1.0.0 - */ -export const asUnit: (self: Option) => Option = covariant.asUnit(Covariant) - -/** - * @category instances - * @since 1.0.0 - */ -export const Invariant: invariant.Invariant = { - imap: covariant.imap(Covariant.map) -} - -/** - * @category instances - * @since 1.0.0 - */ -export const SemiProduct: semiProduct.SemiProduct = { - imap: Invariant.imap, - product: that => self => isSome(self) && isSome(that) ? some([self.value, that.value]) : none, - productMany: collection => - self => { - if (isNone(self)) { - return none - } - const out = nonEmptyArray.make(self.value) - for (const o of collection) { - if (isNone(o)) { - return none - } - out.push(o.value) - } - return some(out) - } -} - -/** - * @category instances - * @since 1.0.0 - */ -export const SemiApplicative: semiApplicative.SemiApplicative = { - ...Covariant, - ...SemiProduct -} - -const coproduct = ( - that: Option -) => (self: Option): Option => isSome(self) ? self : isSome(that) ? that : none - -export const SemiCoproduct: semiCoproduct.SemiCoproduct = { - imap: Invariant.imap, - coproduct, - coproductMany: collection => - self => { - if (isSome(self)) { - return self - } - for (const o of collection) { - if (isSome(o)) { - return o - } - } - return none - } -} - -export const SemiAlternative: semiAlternative.SemiAlternative = { - ...Covariant, - ...SemiCoproduct -} - -export const Alternative: alternative.Alternative = { - ...SemiAlternative, - zero: () => none, - coproductAll: collection => SemiAlternative.coproductMany(collection)(none) -} - -/** - * Lifts a binary function into `Option`. - * - * @category lifting - * @since 1.0.0 - */ -export const lift2: (f: (a: A, b: B) => C) => (fa: Option, fb: Option) => Option = - semiApplicative.lift2(SemiApplicative) - -/** - * Lifts a ternary function into `Option`. - * - * @category lifting - * @since 1.0.0 - */ -export const lift3: ( - f: (a: A, b: B, c: C) => D -) => (fa: Option, fb: Option, fc: Option) => Option = semiApplicative.lift3( - SemiApplicative -) - -export const Product: product.Product = { - ...SemiProduct, - ...Of, - productAll: collection => { - const as = Array.from(collection) - return nonEmptyReadonlyArray.isNonEmpty(as) ? - SemiApplicative.productMany(nonEmptyReadonlyArray.tail(as))( - nonEmptyReadonlyArray.head(as) - ) : - some([]) - } -} - -/** - * @category instances - * @since 1.0.0 - */ -export const Applicative: applicative.Applicative = { - ...SemiApplicative, - ...Product -} - -/** - * @category instances - * @since 1.0.0 - */ -export const Monad: monad.Monad = { - ...Covariant, - ...Of, - flatMap -} - -/** - * @category conversions - * @since 1.0.0 - */ -export const toReadonlyArray = ( - self: Option -): ReadonlyArray => (isNone(self) ? [] : [self.value]) - -/** - * @category folding - * @since 1.0.0 - */ -export const reduce = (b: B, f: (b: B, a: A) => B) => - (self: Option): B => isNone(self) ? b : f(b, self.value) - -/** - * @category folding - * @since 1.0.0 - */ -export const foldMap = (Monoid: monoid.Monoid) => - (f: (a: A) => M) => (self: Option): M => isNone(self) ? Monoid.empty : f(self.value) - -/** - * @category folding - * @since 1.0.0 - */ -export const reduceRight = (b: B, f: (b: B, a: A) => B) => - (self: Option): B => isNone(self) ? b : f(b, self.value) - -/** - * @category instances - * @since 1.0.0 - */ -export const Foldable: foldable.Foldable = { - reduce -} - -/** - * @category folding - * @since 1.0.0 - */ -export const reduceWithIndex = (b: B, f: (b: B, a: A, i: number) => B) => - (self: Option): B => isNone(self) ? b : f(b, self.value, 0) - -/** - * @category folding - * @since 1.0.0 - */ -export const reduceRightWithIndex = (b: B, f: (b: B, a: A, i: number) => B) => - (self: Option): B => isNone(self) ? b : f(b, self.value, 0) - -/** - * @category instances - * @since 1.0.0 - */ -export const FoldableWithIndex: foldableWithIndex.FoldableWithIndex = { - reduceWithIndex, - reduceRightWithIndex -} - -/** - * @category instances - * @since 1.0.0 - */ -export const Extendable: extendable.Extendable = { - ...Covariant, - extend -} - -/** - * @category traversing - * @since 1.0.0 - */ -export const sequence: ( - Applicative: applicative.Applicative -) => (fas: Option>) => Kind> = traversable - .sequence(traverse) - -/** - * @category instances - * @since 1.0.0 - */ -export const Traversable: traversable.Traversable = { - traverse, - sequence -} - -export const exists = (predicate: (a: A) => boolean) => - (ma: Option): boolean => isNone(ma) ? false : predicate(ma.value) - -/** - * @category do notation - * @since 1.0.0 - */ -export const Do: Option<{}> = some({}) - -/** - * @category do notation - * @since 1.0.0 - */ -export const bindTo: ( - name: N -) => (self: Option) => Option<{ readonly [K in N]: A }> = invariant.bindTo(Invariant) - -const let_: ( - name: Exclude, - f: (a: A) => B -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - covariant.let(Covariant) - -export { let_ as let } - -/** - * @category do notation - * @since 1.0.0 - */ -export const bind: ( - name: Exclude, - f: (a: A) => Option -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - chainable.bind(Chainable) - -/** - * A variant of `bind` that sequentially ignores the scope. - * - * @category do notation - * @since 1.0.0 - */ -export const andThenBind: ( - name: Exclude, - fb: Option -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - semiProduct.andThenBind(SemiApplicative) - -// ------------------------------------------------------------------------------------- -// tuple sequencing -// ------------------------------------------------------------------------------------- - -/** - * @category tuple sequencing - * @since 1.0.0 - */ -export const Zip: Option = some([]) - -/** - * @since 1.0.0 - */ -export const tupled: (self: Option) => Option = invariant.tupled(Invariant) - -/** - * Sequentially zips this effect with the specified effect. - * - * @category tuple sequencing - * @since 1.0.0 - */ -export const productFlatten: ( - fb: Option -) => >(self: Option) => Option = semiProduct - .productFlatten(SemiProduct) diff --git a/test/data/Predicate.ts b/test/data/Predicate.ts deleted file mode 100644 index 872b8bd0a..000000000 --- a/test/data/Predicate.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { TypeLambda } from "@fp-ts/core/HKT" -import * as contravariant from "@fp-ts/core/typeclass/Contravariant" -import * as invariant from "@fp-ts/core/typeclass/Invariant" -import * as of_ from "@fp-ts/core/typeclass/Of" -import * as product from "@fp-ts/core/typeclass/Product" -import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" - -export interface Predicate { - (a: A): boolean -} - -export interface PredicateTypeLambda extends TypeLambda { - readonly type: Predicate -} - -export const contramap = (f: (b: B) => A) => - (self: Predicate): Predicate => b => self(f(b)) - -export const Contravariant: contravariant.Contravariant = contravariant.make( - contramap -) - -export const Invariant: invariant.Invariant = { - imap: Contravariant.imap -} - -export const SemiProduct: semiProduct.SemiProduct = { - imap: Contravariant.imap, - product: that => self => ([a, b]) => self(a) && that(b), - productMany: collection => - self => { - return ([head, ...tail]) => { - if (self(head) === false) { - return false - } - const predicates = Array.from(collection) - for (let i = 0; i < predicates.length; i++) { - if (predicates[i](tail[i]) === false) { - return false - } - } - return true - } - } -} - -export const of = (_: A): Predicate => () => true - -export const Of: of_.Of = { - of -} - -export const Do = of_.Do(Of) - -export const Product: product.Product = { - ...SemiProduct, - of, - productAll: collection => - as => { - const predicates = Array.from(collection) - for (let i = 0; i < predicates.length; i++) { - if (predicates[i](as[i]) === false) { - return false - } - } - return true - } -} - -export const andThenBind: ( - name: Exclude, - fb: Predicate -) => ( - self: Predicate -) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct - .andThenBind( - SemiProduct - ) - -export const tupled: (self: Predicate) => Predicate = invariant.tupled( - Invariant -) - -export const tuple = product.tuple(Product) - -export const isString = (u: unknown): u is string => typeof u === "string" - -export const isNumber = (u: unknown): u is number => typeof u === "number" - -export const isBoolean = (u: unknown): u is boolean => typeof u === "boolean" diff --git a/test/data/ReadonlyArray.ts b/test/data/ReadonlyArray.ts deleted file mode 100644 index 9c3e7cd63..000000000 --- a/test/data/ReadonlyArray.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { semiProduct } from "@fp-ts/core" -import { identity, pipe } from "@fp-ts/core/Function" -import type { Kind, TypeLambda } from "@fp-ts/core/HKT" -import type * as applicative from "@fp-ts/core/typeclass/Applicative" -import * as covariant from "@fp-ts/core/typeclass/Covariant" -import type * as foldable from "@fp-ts/core/typeclass/Foldable" -import type * as of_ from "@fp-ts/core/typeclass/Of" -import type { Order } from "@fp-ts/core/typeclass/Order" -import type * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" -import type * as traverse_ from "@fp-ts/core/typeclass/Traversable" -import type * as covariantWithIndex from "../limbo/CovariantWithIndex" -import * as foldableWithIndex from "../limbo/FoldableWithIndex" -import type * as traversableWithIndex from "../limbo/TraversableWithIndex" -import type { NonEmptyReadonlyArray } from "./NonEmptyReadonlyArray" -import * as nonEmptyReadonlyArray from "./NonEmptyReadonlyArray" -import * as O from "./Option" -import type { Option } from "./Option" - -export interface ReadonlyArrayTypeLambda extends TypeLambda { - readonly type: ReadonlyArray -} - -export const map = (f: (a: A) => B) => - (self: ReadonlyArray): ReadonlyArray => self.map(a => f(a)) - -export const mapWithIndex = (f: (a: A, i: number) => B) => - (self: ReadonlyArray): ReadonlyArray => self.map((a, i) => f(a, i)) - -export const Covariant: covariant.Covariant = covariant.make(map) - -export const CovariantWithIndex: covariantWithIndex.CovariantWithIndex< - ReadonlyArrayTypeLambda, - number -> = { - mapWithIndex: (f) => (self) => self.map((a, i) => f(a, i)) -} - -export const reduceWithIndex = ( - b: B, - f: (b: B, a: A, i: number) => B -) => (self: ReadonlyArray) => self.reduce((b, a, i) => f(b, a, i), b) - -export const reduceRightWithIndex = (b: B, f: (b: B, a: A, i: number) => B) => - (self: ReadonlyArray): B => self.reduceRight((b, a, i) => f(b, a, i), b) - -export const FoldableWithIndex: foldableWithIndex.FoldableWithIndex< - ReadonlyArrayTypeLambda, - number -> = { - reduceWithIndex, - reduceRightWithIndex -} - -export const Foldable: foldable.Foldable = { - reduce: foldableWithIndex.reduce(FoldableWithIndex) -} - -export const isNonEmpty: (self: ReadonlyArray) => self is NonEmptyReadonlyArray = - nonEmptyReadonlyArray.isNonEmpty - -export const head = ( - self: ReadonlyArray -): O.Option => (isNonEmpty(self) ? O.some(self[0]) : O.none) - -export const sort = (Compare: Order) => - (as: ReadonlyArray): ReadonlyArray => - as.length <= 1 ? as : as.slice().sort((a1, a2) => Compare.compare(a2)(a1)) - -export function concat( - that: NonEmptyReadonlyArray -): (self: ReadonlyArray) => NonEmptyReadonlyArray -export function concat( - that: ReadonlyArray -): (self: NonEmptyReadonlyArray) => NonEmptyReadonlyArray -export function concat( - that: ReadonlyArray -): (self: NonEmptyReadonlyArray) => ReadonlyArray { - return (self: NonEmptyReadonlyArray) => self.concat(that) -} - -export const traverseWithIndex = ( - Applicative: applicative.Applicative -) => - (f: (a: A, i: number) => Kind) => - (self: Iterable): Kind> => { - const fbs: Array> = [] - let i = 0 - for (const a of self) { - fbs.push(f(a, i++)) - } - return Applicative.productAll(fbs) - } - -export const traverse = ( - Applicative: applicative.Applicative -) => - ( - f: (a: A) => Kind - ): (self: ReadonlyArray) => Kind> => - traverseWithIndex(Applicative)(f) - -export const Traversable: traverse_.Traversable = { - traverse, - sequence: F => self => pipe(self, traverse(F)(identity)) -} - -export const TraversableWithIndex: traversableWithIndex.TraversableWithIndex< - ReadonlyArrayTypeLambda, - number -> = { - traverseWithIndex -} - -export const product = (that: ReadonlyArray) => - (self: ReadonlyArray): ReadonlyArray => { - const out: Array = [] - for (const a of self) { - for (const b of that) { - out.push([a, b]) - } - } - return out - } - -export const Of: of_.Of = { - of: a => [a] -} - -const SemiProduct: semiProduct.SemiProduct = { - imap: Covariant.imap, - product, - productMany: semiProduct.productMany(Covariant, product) -} - -export const SemiApplicative: semiApplicative.SemiApplicative = { - ...Covariant, - ...SemiProduct -} - -export const filterMapWithIndex = (f: (a: A, i: number) => Option) => - (fa: ReadonlyArray): ReadonlyArray => { - const out: Array = [] - for (let i = 0; i < fa.length; i++) { - const optionB = f(fa[i], i) - if (O.isSome(optionB)) { - out.push(optionB.value) - } - } - return out - } diff --git a/test/data/boolean.ts b/test/data/boolean.ts deleted file mode 100644 index 6145df49a..000000000 --- a/test/data/boolean.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Monoid } from "@fp-ts/core/typeclass/Monoid" -import * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as order from "@fp-ts/core/typeclass/Order" -import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" - -export const Order: order.Order = order.fromCompare((that) => - (self) => self < that ? -1 : self > that ? 1 : 0 -) - -export const SemigroupAll: semigroup.Semigroup = ({ - combine: (that) => (self) => self && that, - combineMany: (collection) => - (self) => { - if (self === false) { - return false - } - for (const bool of collection) { - if (bool === false) { - return false - } - } - return true - } -}) - -export const SemigroupAny: semigroup.Semigroup = { - combine: (that) => (self) => self || that, - combineMany: (collection) => - (self) => { - if (self === true) { - return true - } - for (const bool of collection) { - if (bool === true) { - return true - } - } - return false - } -} - -export const MonoidAll: Monoid = monoid.fromSemigroup(SemigroupAll, true) diff --git a/test/data/number.ts b/test/data/number.ts deleted file mode 100644 index 9b30eae41..000000000 --- a/test/data/number.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type * as bounded from "@fp-ts/core/typeclass/Bounded" -import type { Monoid } from "@fp-ts/core/typeclass/Monoid" -import * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as order from "@fp-ts/core/typeclass/Order" -import * as semigroup from "@fp-ts/core/typeclass/Semigroup" - -const sum = (that: number) => (self: number): number => self + that - -export const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(sum) - -export const MonoidSum: Monoid = monoid.fromSemigroup(SemigroupSum, 0) - -const multiply = (that: number) => (self: number): number => self * that - -export const SemigroupMultiply: semigroup.Semigroup = { - combine: multiply, - combineMany: (collection) => - (self) => { - let multiplication: number = self - if (multiplication === 0) { - return 0 - } - for (const n of collection) { - if (n === 0) { - return 0 - } - multiplication = multiplication * n - } - return multiplication - } -} - -export const Order: order.Order = order.fromCompare((that) => - (self) => self < that ? -1 : self > that ? 1 : 0 -) - -export const Bounded: bounded.Bounded = { - ...Order, - maxBound: Infinity, - minBound: -Infinity -} diff --git a/test/data/string.ts b/test/data/string.ts deleted file mode 100644 index d317c3cc0..000000000 --- a/test/data/string.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as monoid from "@fp-ts/core/typeclass/Monoid" -import type * as order from "@fp-ts/core/typeclass/Order" -import * as semigroup from "@fp-ts/core/typeclass/Semigroup" - -export const Semigroup: semigroup.Semigroup = semigroup.fromCombine((that) => - (self) => self + that -) - -export const Monoid: monoid.Monoid = monoid.fromSemigroup(Semigroup, "") - -export const Order: order.Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 -} diff --git a/test/limbo/CovariantWithIndex.ts b/test/limbo/CovariantWithIndex.ts deleted file mode 100644 index 0ef9c20d9..000000000 --- a/test/limbo/CovariantWithIndex.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @since 1.0.0 - */ -import { pipe } from "@fp-ts/core/Function" -import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import type { Covariant } from "@fp-ts/core/typeclass/Covariant" - -/** - * @category type class - * @since 1.0.0 - */ -export interface CovariantWithIndex extends TypeClass { - readonly mapWithIndex: ( - f: (a: A, i: I) => B - ) => (self: Kind) => Kind -} - -/** - * Returns a default `mapWithIndex` composition. - * - * @since 1.0.0 - */ -export const mapWithIndexComposition = ( - F: CovariantWithIndex, - G: CovariantWithIndex -): (( - f: (a: A, ij: readonly [I, J]) => B -) => ( - self: Kind> -) => Kind>) => - (f) => F.mapWithIndex((ga, i) => pipe(ga, G.mapWithIndex((a, j) => f(a, [i, j])))) - -/** - * Returns a default `map` implementation. - * - * @since 1.0.0 - */ -export const map = ( - F: CovariantWithIndex -): Covariant["map"] => f => F.mapWithIndex(f) diff --git a/test/typeclass/Applicative.ts b/test/typeclass/Applicative.ts index 80d5a7908..c62252223 100644 --- a/test/typeclass/Applicative.ts +++ b/test/typeclass/Applicative.ts @@ -1,16 +1,16 @@ import { pipe } from "@fp-ts/core/Function" +import * as N from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/Applicative" -import * as N from "../data/number" -import * as O from "../data/Option" import * as U from "../util" describe("Applicative", () => { it("liftMonoid", () => { const liftMonoid = _.liftMonoid(O.Applicative) const M = liftMonoid(N.MonoidSum) - U.deepStrictEqual(pipe(O.none, M.combine(O.none)), O.none) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, M.combine(O.some(2))), O.none) + U.deepStrictEqual(pipe(O.none(), M.combine(O.none())), O.none()) + U.deepStrictEqual(pipe(O.some(1), M.combine(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), M.combine(O.some(2))), O.none()) U.deepStrictEqual(pipe(O.some(1), M.combine(O.some(2))), O.some(3)) }) }) diff --git a/test/typeclass/Bicovariant.ts b/test/typeclass/Bicovariant.ts index 6b661cf0d..5bc02c0f5 100644 --- a/test/typeclass/Bicovariant.ts +++ b/test/typeclass/Bicovariant.ts @@ -1,8 +1,8 @@ +import type { EitherTypeLambda } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" import { pipe } from "@fp-ts/core/Function" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/Bicovariant" -import type { EitherTypeLambda } from "../data/Either" -import * as E from "../data/Either" -import * as RA from "../data/ReadonlyArray" import * as U from "../util" export const Bicovariant: _.Bicovariant = { diff --git a/test/typeclass/Bounded.ts b/test/typeclass/Bounded.ts index 7c02285dc..0744c5c9b 100644 --- a/test/typeclass/Bounded.ts +++ b/test/typeclass/Bounded.ts @@ -1,10 +1,10 @@ +import * as Number from "@fp-ts/core/Number" import * as _ from "@fp-ts/core/typeclass/Bounded" -import * as number from "../data/number" import * as U from "../util" describe("Bounded", () => { it("clamp", () => { - const clamp = _.clamp({ ...number.Order, minBound: 1, maxBound: 10 }) + const clamp = _.clamp({ ...Number.Order, minBound: 1, maxBound: 10 }) U.deepStrictEqual(clamp(2), 2) U.deepStrictEqual(clamp(10), 10) U.deepStrictEqual(clamp(20), 10) @@ -13,7 +13,7 @@ describe("Bounded", () => { }) it("reverse", () => { - const B = _.reverse({ ...number.Order, minBound: 10, maxBound: 1 }) + const B = _.reverse({ ...Number.Order, minBound: 10, maxBound: 1 }) U.deepStrictEqual(B.maxBound, 1) U.deepStrictEqual(B.minBound, 10) }) diff --git a/test/typeclass/Chainable.ts b/test/typeclass/Chainable.ts index a608ff5ac..944c8f118 100644 --- a/test/typeclass/Chainable.ts +++ b/test/typeclass/Chainable.ts @@ -1,28 +1,28 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/Chainable" -import * as O from "../data/Option" import * as U from "../util" describe("Chainable", () => { it("andThenDiscard", () => { const andThenDiscard = _.andThenDiscard(O.Chainable) - U.deepStrictEqual(pipe(O.none, andThenDiscard(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, andThenDiscard(O.some(2))), O.none) - U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.some(2))), O.none()) + U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none())), O.none()) U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.some(2))), O.some(1)) }) it("bind", () => { const bind = _.bind(O.Chainable) - U.deepStrictEqual(pipe(O.Do, bind("a", () => O.none)), O.none) + U.deepStrictEqual(pipe(O.Do, bind("a", () => O.none())), O.none()) U.deepStrictEqual(pipe(O.Do, bind("a", () => O.some(1))), O.some({ a: 1 })) }) it("tap", () => { const tap = _.tap(O.Chainable) - U.deepStrictEqual(pipe(O.none, tap(() => O.none)), O.none) - U.deepStrictEqual(pipe(O.none, tap(() => O.some(2))), O.none) - U.deepStrictEqual(pipe(O.some(1), tap(() => O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), tap(() => O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), tap(() => O.some(2))), O.none()) + U.deepStrictEqual(pipe(O.some(1), tap(() => O.none())), O.none()) U.deepStrictEqual(pipe(O.some(1), tap(() => O.some(2))), O.some(1)) }) }) diff --git a/test/typeclass/Contravariant.ts b/test/typeclass/Contravariant.ts index ca8255303..de754eb91 100644 --- a/test/typeclass/Contravariant.ts +++ b/test/typeclass/Contravariant.ts @@ -1,8 +1,8 @@ import { pipe } from "@fp-ts/core/Function" +import * as P from "@fp-ts/core/Predicate" +import * as S from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/Contravariant" import * as order from "@fp-ts/core/typeclass/Order" -import * as P from "../data/Predicate" -import * as string from "../data/string" import * as U from "../util" describe("Contravariant", () => { @@ -10,7 +10,7 @@ describe("Contravariant", () => { const map = _.contramapComposition(P.Contravariant, P.Contravariant) const emptyString: P.Predicate> = p => p("") === true const a = pipe(emptyString, map(s => s.length)) - U.deepStrictEqual(a(P.isString), false) + U.deepStrictEqual(a(S.isString), false) U.deepStrictEqual(a(n => n === 0), true) }) @@ -19,7 +19,7 @@ describe("Contravariant", () => { (s: string) => [s], ([s]) => s )( - string.Order + S.Order ) U.deepStrictEqual(pipe(["a"], O.compare(["b"])), -1) U.deepStrictEqual(pipe(["a"], O.compare(["a"])), 0) diff --git a/test/typeclass/Coproduct.ts b/test/typeclass/Coproduct.ts index 9299dfa1a..0a138b74e 100644 --- a/test/typeclass/Coproduct.ts +++ b/test/typeclass/Coproduct.ts @@ -1,17 +1,17 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/Coproduct" -import * as O from "../data/Option" import * as U from "../util" describe("Coproduct", () => { it("getMonoid", () => { const M = _.getMonoid(O.Alternative)() - U.deepStrictEqual(pipe(O.none, M.combine(O.none)), O.none) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.none)), O.some(1)) - U.deepStrictEqual(pipe(O.none, M.combine(O.some(2))), O.some(2)) + U.deepStrictEqual(pipe(O.none(), M.combine(O.none())), O.none()) + U.deepStrictEqual(pipe(O.some(1), M.combine(O.none())), O.some(1)) + U.deepStrictEqual(pipe(O.none(), M.combine(O.some(2))), O.some(2)) U.deepStrictEqual(pipe(O.some(1), M.combine(O.some(2))), O.some(1)) - U.deepStrictEqual(pipe(M.empty, M.combine(O.none)), O.none) + U.deepStrictEqual(pipe(M.empty, M.combine(O.none())), O.none()) U.deepStrictEqual(pipe(M.empty, M.combine(O.some(2))), O.some(2)) U.deepStrictEqual(pipe(O.some(1), M.combine(M.empty)), O.some(1)) }) diff --git a/test/typeclass/Covariant.ts b/test/typeclass/Covariant.ts index 309380285..5fc4acac0 100644 --- a/test/typeclass/Covariant.ts +++ b/test/typeclass/Covariant.ts @@ -1,7 +1,7 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/Covariant" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" import * as U from "../util" describe("Covariant", () => { @@ -21,19 +21,19 @@ describe("Covariant", () => { it("flap", () => { const flap = _.flap(O.Covariant) - U.deepStrictEqual(pipe(O.none, flap(1)), O.none) + U.deepStrictEqual(pipe(O.none(), flap(1)), O.none()) U.deepStrictEqual(pipe(O.some(U.double), flap(1)), O.some(2)) }) it("as", () => { const as = _.as(O.Covariant) - U.deepStrictEqual(pipe(O.none, as(1)), O.none) + U.deepStrictEqual(pipe(O.none(), as(1)), O.none()) U.deepStrictEqual(pipe(O.some(1), as(2)), O.some(2)) }) it("asUnit", () => { const asUnit = _.asUnit(O.Covariant) - U.deepStrictEqual(pipe(O.none, asUnit), O.none) + U.deepStrictEqual(pipe(O.none(), asUnit), O.none()) U.deepStrictEqual(pipe(O.some(1), asUnit), O.some(undefined)) }) @@ -47,7 +47,7 @@ describe("Covariant", () => { it("imap", () => { const f = _.imap(O.map)((s: string) => [s], ([s]) => s) - U.deepStrictEqual(pipe(O.none, f), O.none) + U.deepStrictEqual(pipe(O.none(), f), O.none()) U.deepStrictEqual(pipe(O.some("a"), f), O.some(["a"])) }) }) diff --git a/test/typeclass/CovariantWithIndex.ts b/test/typeclass/CovariantWithIndex.ts index f60fa5511..d2a92dd15 100644 --- a/test/typeclass/CovariantWithIndex.ts +++ b/test/typeclass/CovariantWithIndex.ts @@ -1,6 +1,6 @@ import { pipe } from "@fp-ts/core/Function" -import * as RA from "../data/ReadonlyArray" -import * as _ from "../limbo/CovariantWithIndex" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/typeclass/CovariantWithIndex" import * as U from "../util" describe("CovariantWithIndex", () => { diff --git a/test/typeclass/FlatMap.ts b/test/typeclass/FlatMap.ts index 3ee00c623..4ce685825 100644 --- a/test/typeclass/FlatMap.ts +++ b/test/typeclass/FlatMap.ts @@ -1,31 +1,31 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/FlatMap" -import * as O from "../data/Option" import * as U from "../util" describe("FlatMap", () => { it("flatten", () => { const flatten = _.flatten(O.FlatMap) - U.deepStrictEqual(pipe(O.none, flatten), O.none) - U.deepStrictEqual(pipe(O.some(O.none), flatten), O.none) + U.deepStrictEqual(pipe(O.none(), flatten), O.none()) + U.deepStrictEqual(pipe(O.some(O.none()), flatten), O.none()) U.deepStrictEqual(pipe(O.some(O.some(1)), flatten), O.some(1)) }) it("andThen", () => { const andThen = _.andThen(O.FlatMap) - U.deepStrictEqual(pipe(O.none, andThen(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, andThen(O.some(2))), O.none) - U.deepStrictEqual(pipe(O.some(1), andThen(O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), andThen(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), andThen(O.some(2))), O.none()) + U.deepStrictEqual(pipe(O.some(1), andThen(O.none())), O.none()) U.deepStrictEqual(pipe(O.some(1), andThen(O.some(2))), O.some(2)) }) it("composeKleisliArrow", () => { const composeKleisliArrow = _.composeKleisliArrow(O.FlatMap) - const f = (s: string): O.Option => s.length > 0 ? O.some(s.length) : O.none - const g = (n: number): O.Option => n > 1 ? O.some(n) : O.none + const f = (s: string): O.Option => s.length > 0 ? O.some(s.length) : O.none() + const g = (n: number): O.Option => n > 1 ? O.some(n) : O.none() const h = pipe(f, composeKleisliArrow(g)) - U.deepStrictEqual(h(""), O.none) - U.deepStrictEqual(h("a"), O.none) + U.deepStrictEqual(h(""), O.none()) + U.deepStrictEqual(h("a"), O.none()) U.deepStrictEqual(h("aa"), O.some(2)) }) }) diff --git a/test/typeclass/Foldable.ts b/test/typeclass/Foldable.ts index dec700db0..a2569c4a3 100644 --- a/test/typeclass/Foldable.ts +++ b/test/typeclass/Foldable.ts @@ -1,8 +1,8 @@ import { pipe } from "@fp-ts/core/Function" +import * as N from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/Foldable" -import * as number from "../data/number" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" import * as U from "../util" describe("Foldable", () => { @@ -16,19 +16,19 @@ describe("Foldable", () => { it("toArray", () => { const toArray = _.toArray(O.Foldable) - U.deepStrictEqual(toArray(O.none), []) + U.deepStrictEqual(toArray(O.none()), []) U.deepStrictEqual(toArray(O.some(2)), [2]) }) it("toArrayWith", () => { const toArrayWith = _.toArrayWith(O.Foldable) - U.deepStrictEqual(pipe(O.none, toArrayWith(U.double)), []) + U.deepStrictEqual(pipe(O.none(), toArrayWith(U.double)), []) U.deepStrictEqual(pipe(O.some(2), toArrayWith(U.double)), [4]) }) it("foldMap", () => { const foldMap = _.foldMap(RA.Foldable) - U.deepStrictEqual(pipe([1, 2, 3], foldMap(number.MonoidSum)(U.double)), 12) + U.deepStrictEqual(pipe([1, 2, 3], foldMap(N.MonoidSum)(U.double)), 12) }) it("reduceRight", () => { @@ -38,39 +38,39 @@ describe("Foldable", () => { it("reduceKind", () => { const reduceKind = _.reduceKind(RA.Foldable)(O.Monad) - U.deepStrictEqual(pipe([], reduceKind("-", () => O.none)), O.some("-")) - U.deepStrictEqual(pipe(["a"], reduceKind("-", () => O.none)), O.none) + U.deepStrictEqual(pipe([], reduceKind("-", () => O.none())), O.some("-")) + U.deepStrictEqual(pipe(["a"], reduceKind("-", () => O.none())), O.none()) U.deepStrictEqual( pipe(["a", "b", "c"], reduceKind("-", (b, a) => O.some(b + a))), O.some("-abc") ) U.deepStrictEqual( - pipe(["a", "b", "c"], reduceKind("-", (b, a) => a === "b" ? O.none : O.some(b + a))), - O.none + pipe(["a", "b", "c"], reduceKind("-", (b, a) => a === "b" ? O.none() : O.some(b + a))), + O.none() ) }) it("reduceRightKind", () => { const reduceRightKind = _.reduceRightKind(RA.Foldable)(O.Monad) - U.deepStrictEqual(pipe([], reduceRightKind("-", () => O.none)), O.some("-")) - U.deepStrictEqual(pipe(["a"], reduceRightKind("-", () => O.none)), O.none) + U.deepStrictEqual(pipe([], reduceRightKind("-", () => O.none())), O.some("-")) + U.deepStrictEqual(pipe(["a"], reduceRightKind("-", () => O.none())), O.none()) U.deepStrictEqual( pipe(["a", "b", "c"], reduceRightKind("-", (b, a) => O.some(b + a))), O.some("-cba") ) U.deepStrictEqual( - pipe(["a", "b", "c"], reduceRightKind("-", (b, a) => a === "b" ? O.none : O.some(b + a))), - O.none + pipe(["a", "b", "c"], reduceRightKind("-", (b, a) => a === "b" ? O.none() : O.some(b + a))), + O.none() ) }) it("foldMapKind", () => { const foldMapKind = _.foldMapKind(RA.Foldable)(O.Alternative) - U.deepStrictEqual(pipe([], foldMapKind(() => O.none)), O.none) - U.deepStrictEqual(pipe(["a"], foldMapKind(() => O.none)), O.none) + U.deepStrictEqual(pipe([], foldMapKind(() => O.none())), O.none()) + U.deepStrictEqual(pipe(["a"], foldMapKind(() => O.none())), O.none()) U.deepStrictEqual(pipe(["a", "b", "c"], foldMapKind((a) => O.some(a))), O.some("a")) U.deepStrictEqual( - pipe(["a", "b", "c"], foldMapKind((a) => a === "b" ? O.none : O.some(a))), + pipe(["a", "b", "c"], foldMapKind((a) => a === "b" ? O.none() : O.some(a))), O.some("a") ) }) diff --git a/test/typeclass/FoldableWithIndex.ts b/test/typeclass/FoldableWithIndex.ts index cdcffd5ee..7cadae671 100644 --- a/test/typeclass/FoldableWithIndex.ts +++ b/test/typeclass/FoldableWithIndex.ts @@ -1,57 +1,57 @@ -import { pipe } from "@fp-ts/core/Function" -import * as number from "../data/number" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" -import * as foldableWithIndex from "../limbo/FoldableWithIndex" -import * as U from "../util" +// import { pipe } from "@fp-ts/core/Function" +// import * as N from "@fp-ts/core/Number" +// import * as O from "@fp-ts/core/Option" +// import * as RA from "@fp-ts/core/ReadonlyArray" +// import * as foldableWithIndex from "../limbo/FoldableWithIndex" +// import * as U from "../util" -describe("FoldableWithIndex", () => { - it("reduceWithIndexComposition", () => { - const reduceWithIndex = foldableWithIndex.reduceWithIndexComposition( - RA.FoldableWithIndex, - RA.FoldableWithIndex - ) - const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j - U.deepStrictEqual(pipe([], reduceWithIndex("-", f)), "-") - U.deepStrictEqual(pipe([[]], reduceWithIndex("-", f)), "-") - U.deepStrictEqual( - pipe([["a", "c"], ["b", "d"]], reduceWithIndex("-", f)), - "-a00c01b10d11" - ) - }) +// describe("FoldableWithIndex", () => { +// it("reduceWithIndexComposition", () => { +// const reduceWithIndex = foldableWithIndex.reduceWithIndexComposition( +// RA.FoldableWithIndex, +// RA.FoldableWithIndex +// ) +// const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j +// U.deepStrictEqual(pipe([], reduceWithIndex("-", f)), "-") +// U.deepStrictEqual(pipe([[]], reduceWithIndex("-", f)), "-") +// U.deepStrictEqual( +// pipe([["a", "c"], ["b", "d"]], reduceWithIndex("-", f)), +// "-a00c01b10d11" +// ) +// }) - it("reduceRightWithIndexComposition", () => { - const reduceRightWithIndex = foldableWithIndex.reduceRightWithIndexComposition( - RA.FoldableWithIndex, - RA.FoldableWithIndex - ) - const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j - U.deepStrictEqual(pipe([], reduceRightWithIndex("-", f)), "-") - U.deepStrictEqual(pipe([[]], reduceRightWithIndex("-", f)), "-") - U.deepStrictEqual( - pipe([["a", "c"], ["b", "d"]], reduceRightWithIndex("-", f)), - "-d11b10c01a00" - ) - }) +// it("reduceRightWithIndexComposition", () => { +// const reduceRightWithIndex = foldableWithIndex.reduceRightWithIndexComposition( +// RA.FoldableWithIndex, +// RA.FoldableWithIndex +// ) +// const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j +// U.deepStrictEqual(pipe([], reduceRightWithIndex("-", f)), "-") +// U.deepStrictEqual(pipe([[]], reduceRightWithIndex("-", f)), "-") +// U.deepStrictEqual( +// pipe([["a", "c"], ["b", "d"]], reduceRightWithIndex("-", f)), +// "-d11b10c01a00" +// ) +// }) - it("toArray", () => { - const toReadonlyArray = foldableWithIndex.toArray(O.FoldableWithIndex) - U.deepStrictEqual(toReadonlyArray(O.none), []) - U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) - }) +// it("toArray", () => { +// const toReadonlyArray = foldableWithIndex.toArray(O.FoldableWithIndex) +// U.deepStrictEqual(toReadonlyArray(O.none()), []) +// U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) +// }) - it("toArrayWith", () => { - const toReadonlyArrayWith = foldableWithIndex.toArrayWith(O.FoldableWithIndex) - U.deepStrictEqual(pipe(O.none, toReadonlyArrayWith(U.double)), []) - U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith(U.double)), [4]) - U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith((a, i) => U.double(a) * i)), [0]) - }) +// it("toArrayWith", () => { +// const toReadonlyArrayWith = foldableWithIndex.toArrayWith(O.FoldableWithIndex) +// U.deepStrictEqual(pipe(O.none(), toReadonlyArrayWith(U.double)), []) +// U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith(U.double)), [4]) +// U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith((a, i) => U.double(a) * i)), [0]) +// }) - it("foldMapWithIndex", () => { - const foldMapWithIndex = foldableWithIndex.foldMapWithIndex(RA.FoldableWithIndex) - U.deepStrictEqual( - pipe([1, 2, 3], foldMapWithIndex(number.MonoidSum)((n, i) => (n * 2) + i)), - 15 - ) - }) -}) +// it("foldMapWithIndex", () => { +// const foldMapWithIndex = foldableWithIndex.foldMapWithIndex(RA.FoldableWithIndex) +// U.deepStrictEqual( +// pipe([1, 2, 3], foldMapWithIndex(N.MonoidSum)((n, i) => (n * 2) + i)), +// 15 +// ) +// }) +// }) diff --git a/test/typeclass/Invariant.ts b/test/typeclass/Invariant.ts index 808c746ea..c12bfc3b5 100644 --- a/test/typeclass/Invariant.ts +++ b/test/typeclass/Invariant.ts @@ -1,18 +1,18 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as P from "@fp-ts/core/Predicate" +import * as String from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/Invariant" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" -import * as O from "../data/Option" -import * as P from "../data/Predicate" -import * as string from "../data/string" import * as U from "../util" describe("Invariant", () => { it("imapComposition", () => { const imap = _.imapComposition(semigroup.Invariant, O.Invariant) - const S = pipe(O.getMonoid(string.Semigroup), imap(s => [s] as const, ([s]) => s)) - U.deepStrictEqual(pipe(O.none, S.combine(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, S.combine(O.some(["b"]))), O.some(["b"] as const)) - U.deepStrictEqual(pipe(O.some(["a"] as const), S.combine(O.none)), O.some(["a"] as const)) + const S = pipe(O.getMonoid(String.Semigroup), imap(s => [s] as const, ([s]) => s)) + U.deepStrictEqual(pipe(O.none(), S.combine(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), S.combine(O.some(["b"]))), O.some(["b"] as const)) + U.deepStrictEqual(pipe(O.some(["a"] as const), S.combine(O.none())), O.some(["a"] as const)) U.deepStrictEqual( pipe(O.some(["a"] as const), S.combine(O.some(["b"]))), O.some(["ab"] as const) @@ -22,13 +22,13 @@ describe("Invariant", () => { describe("bindTo", () => { it("Covariant (Option)", () => { const bindTo = _.bindTo(O.Invariant) - U.deepStrictEqual(pipe(O.none, bindTo("a")), O.none) + U.deepStrictEqual(pipe(O.none(), bindTo("a")), O.none()) U.deepStrictEqual(pipe(O.some(1), bindTo("a")), O.some({ a: 1 })) }) it("Contravariant (Predicate)", () => { const bindTo = _.bindTo(P.Invariant) - const p = pipe(P.isString, bindTo("a")) + const p = pipe(String.isString, bindTo("a")) U.deepStrictEqual(p({ a: "a" }), true) U.deepStrictEqual(p({ a: 1 }), false) }) @@ -37,13 +37,13 @@ describe("Invariant", () => { describe("tupled", () => { it("Covariant (Option)", () => { const tupled = _.tupled(O.Invariant) - U.deepStrictEqual(pipe(O.none, tupled), O.none) + U.deepStrictEqual(pipe(O.none(), tupled), O.none()) U.deepStrictEqual(pipe(O.some(1), tupled), O.some([1] as const)) }) it("Contravariant (Predicate)", () => { const tupled = _.tupled(P.Invariant) - const p = pipe(P.isString, tupled) + const p = pipe(String.isString, tupled) U.deepStrictEqual(p(["a"]), true) U.deepStrictEqual(p([1]), false) }) diff --git a/test/typeclass/Monoid.ts b/test/typeclass/Monoid.ts index fd46f6808..4e9dbae21 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -1,26 +1,26 @@ import { pipe } from "@fp-ts/core/Function" +import * as N from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" import * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as number from "../data/number" -import * as string from "../data/string" import * as U from "../util" describe("Monoid", () => { it("min", () => { - const M = monoid.min(number.Bounded) + const M = monoid.min(N.Bounded) U.deepStrictEqual(M.combineAll([]), +Infinity) U.deepStrictEqual(M.combineAll([1]), 1) U.deepStrictEqual(M.combineAll([1, -1]), -1) }) it("max", () => { - const M = monoid.max(number.Bounded) + const M = monoid.max(N.Bounded) U.deepStrictEqual(M.combineAll([]), -Infinity) U.deepStrictEqual(M.combineAll([1]), 1) U.deepStrictEqual(M.combineAll([1, -1]), 1) }) it("reverse", () => { - const M = monoid.reverse(string.Monoid) + const M = monoid.reverse(String.Monoid) U.deepStrictEqual(pipe("a", M.combine("b")), "ba") U.deepStrictEqual(pipe("a", M.combine(M.empty)), "a") U.deepStrictEqual(pipe(M.empty, M.combine("a")), "a") @@ -33,8 +33,8 @@ describe("Monoid", () => { describe("struct", () => { it("baseline", () => { const M = monoid.struct({ - name: string.Monoid, - age: number.MonoidSum + name: String.Monoid, + age: N.MonoidSum }) U.deepStrictEqual(M.empty, { name: "", age: 0 }) U.deepStrictEqual(pipe({ name: "a", age: 10 }, M.combine({ name: "b", age: 20 })), { @@ -52,8 +52,8 @@ describe("Monoid", () => { it("tuple", () => { const M = monoid.tuple( - string.Monoid, - number.MonoidSum + String.Monoid, + N.MonoidSum ) U.deepStrictEqual(M.empty, ["", 0]) U.deepStrictEqual(pipe(["a", 10], M.combine(["b", 20])), ["ab", 30]) diff --git a/test/typeclass/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index ce901b412..4fbb4de6b 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -1,42 +1,45 @@ +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/NonEmptyTraversable" -import * as NERA from "../data/NonEmptyReadonlyArray" -import * as O from "../data/Option" import * as U from "../util" describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const traverseNonEmpty = _.traverseNonEmptyComposition( - NERA.NonEmptyTraversable, - NERA.NonEmptyTraversable - )(O.SemiApplicative)((n: number) => (n > 0 ? O.some(n) : O.none)) + RA.NonEmptyTraversable, + RA.NonEmptyTraversable + )(O.SemiApplicative)((n: number) => (n > 0 ? O.some(n) : O.none())) U.deepStrictEqual(traverseNonEmpty([[1]]), O.some([[1]] as const)) - U.deepStrictEqual(traverseNonEmpty([[1, -1]]), O.none) + U.deepStrictEqual(traverseNonEmpty([[1, -1]]), O.none()) U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, 5]]), O.some([[1, 2, 3], [4, 5]] as const)) - U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, -1]]), O.none) + U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, -1]]), O.none()) }) it("traverseNonEmptyComposition", () => { const sequence = _.sequenceNonEmptyComposition( - { ...NERA.NonEmptyTraversable, ...NERA.Covariant }, - NERA.NonEmptyTraversable + { ...RA.NonEmptyTraversable, ...RA.NonEmptyCovariant }, + RA.NonEmptyTraversable )(O.SemiApplicative) 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.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) + 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.traverseNonEmpty + const sequenceNonEmpty = _.sequenceNonEmpty( + RA.NonEmptyTraversable.traverseNonEmpty )(O.SemiApplicative) - U.deepStrictEqual(sequenceNonEmpty([O.none]), O.none) + 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) - U.deepStrictEqual(sequenceNonEmpty([O.some(1), O.none]), O.none) + U.deepStrictEqual(sequenceNonEmpty([O.none()]), O.none()) + U.deepStrictEqual(sequenceNonEmpty([O.some(1), O.none()]), O.none()) U.deepStrictEqual(sequenceNonEmpty([O.some(1), O.some(2)]), O.some([1, 2] as const)) }) }) diff --git a/test/typeclass/Of.ts b/test/typeclass/Of.ts index 8fb357fdd..487f8e39a 100644 --- a/test/typeclass/Of.ts +++ b/test/typeclass/Of.ts @@ -1,6 +1,6 @@ +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/Of" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" import * as U from "../util" describe("Of", () => { diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 4fccb6243..7910bbc90 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -1,9 +1,9 @@ +import * as boolean from "@fp-ts/core/Boolean" import { pipe } from "@fp-ts/core/Function" +import * as number from "@fp-ts/core/Number" +import { sort } from "@fp-ts/core/ReadonlyArray" +import * as string from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/Order" -import * as boolean from "../data/boolean" -import * as number from "../data/number" -import { sort } from "../data/ReadonlyArray" -import * as string from "../data/string" import * as U from "../util" describe("Order", () => { diff --git a/test/typeclass/Product.ts b/test/typeclass/Product.ts index 16a74eb35..3ae0590ff 100644 --- a/test/typeclass/Product.ts +++ b/test/typeclass/Product.ts @@ -1,10 +1,11 @@ +import * as Boolean from "@fp-ts/core/Boolean" import { pipe } from "@fp-ts/core/Function" +import * as Number from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import * as P from "@fp-ts/core/Predicate" +import * as String from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" -import * as number from "../data/number" -import * as O from "../data/Option" -import * as P from "../data/Predicate" -import * as string from "../data/string" import * as U from "../util" describe("Product", () => { @@ -17,20 +18,20 @@ describe("Product", () => { tuple(O.some("a"), O.some(1), O.some(true)), O.some(["a", 1, true] as const) ) - U.deepStrictEqual(tuple(O.some("a"), O.some(1), O.none), O.none) + U.deepStrictEqual(tuple(O.some("a"), O.some(1), O.none()), O.none()) }) it("Invariant (Semigroup)", () => { const tuple = _.tuple(semigroup.Product) U.deepStrictEqual(pipe([], tuple().combine([])), []) - const S = tuple(string.Semigroup, number.SemigroupSum) + const S = tuple(String.Semigroup, Number.SemigroupSum) U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) }) it("Contravariant (Predicate)", () => { const tuple = _.tuple(P.Product) U.deepStrictEqual(tuple()([]), true) - const p = tuple(P.isString, P.isNumber, P.isBoolean) + const p = tuple(String.isString, Number.isNumber, Boolean.isBoolean) U.deepStrictEqual(p(["a", 1, true]), true) U.deepStrictEqual(p(["a", 1, "b"]), false) }) @@ -46,22 +47,22 @@ describe("Product", () => { O.some({ a: "a", b: 1, c: true }) ) U.deepStrictEqual( - struct({ a: O.some("a"), b: O.some(1), c: O.none }), - O.none + struct({ a: O.some("a"), b: O.some(1), c: O.none() }), + O.none() ) }) it("Invariant (Semigroup)", () => { const struct = _.struct(semigroup.Product) U.deepStrictEqual(pipe({}, struct({}).combine({})), {}) - const S = struct({ x: string.Semigroup, y: number.SemigroupSum }) + const S = struct({ x: String.Semigroup, y: Number.SemigroupSum }) U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) }) it("Contravariant (Predicate)", () => { const struct = _.struct(P.Product) U.deepStrictEqual(struct({})({}), true) - const p = struct({ x: P.isString, y: P.isNumber, z: P.isBoolean }) + const p = struct({ x: String.isString, y: Number.isNumber, z: Boolean.isBoolean }) U.deepStrictEqual(p({ x: "a", y: 1, z: true }), true) U.deepStrictEqual(p({ x: "a", y: 1, z: "b" }), false) }) diff --git a/test/typeclass/SemiApplicative.ts b/test/typeclass/SemiApplicative.ts index a1a81f87d..fad3732da 100644 --- a/test/typeclass/SemiApplicative.ts +++ b/test/typeclass/SemiApplicative.ts @@ -1,41 +1,41 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as String from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/SemiApplicative" -import * as O from "../data/Option" -import * as string from "../data/string" import * as U from "../util" describe("SemiApplicative", () => { it("ap", () => { const ap = _.ap(O.SemiApplicative) const double = (n: number) => n * 2 - U.deepStrictEqual(pipe(O.none, ap(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, ap(O.some(1))), O.none) - U.deepStrictEqual(pipe(O.some(double), ap(O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), ap(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), ap(O.some(1))), O.none()) + U.deepStrictEqual(pipe(O.some(double), ap(O.none())), O.none()) U.deepStrictEqual(pipe(O.some(double), ap(O.some(1))), O.some(2)) }) it("andThenDiscard", () => { const andThenDiscard = _.andThenDiscard(O.SemiApplicative) - U.deepStrictEqual(pipe(O.none, andThenDiscard(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, andThenDiscard(O.some(2))), O.none) - U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.some(2))), O.none()) + U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none())), O.none()) U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.some(2))), O.some(1)) }) it("andThen", () => { const andThen = _.andThen(O.SemiApplicative) - U.deepStrictEqual(pipe(O.none, andThen(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, andThen(O.some(2))), O.none) - U.deepStrictEqual(pipe(O.some(1), andThen(O.none)), O.none) + U.deepStrictEqual(pipe(O.none(), andThen(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), andThen(O.some(2))), O.none()) + U.deepStrictEqual(pipe(O.some(1), andThen(O.none())), O.none()) U.deepStrictEqual(pipe(O.some(1), andThen(O.some(2))), O.some(2)) }) it("liftSemigroup", () => { const liftSemigroup = _.liftSemigroup(O.SemiApplicative) - const S = liftSemigroup(string.Semigroup) - U.deepStrictEqual(pipe(O.none, S.combine(O.none)), O.none) - U.deepStrictEqual(pipe(O.none, S.combine(O.some("b"))), O.none) - U.deepStrictEqual(pipe(O.some("a"), S.combine(O.none)), O.none) + const S = liftSemigroup(String.Semigroup) + U.deepStrictEqual(pipe(O.none(), S.combine(O.none())), O.none()) + U.deepStrictEqual(pipe(O.none(), S.combine(O.some("b"))), O.none()) + U.deepStrictEqual(pipe(O.some("a"), S.combine(O.none())), O.none()) U.deepStrictEqual(pipe(O.some("a"), S.combine(O.some("b"))), O.some("ab")) U.deepStrictEqual(pipe(O.some("a"), S.combineMany([O.some("b"), O.some("c")])), O.some("abc")) @@ -43,18 +43,18 @@ describe("SemiApplicative", () => { it("lift2", () => { const sum = _.lift2(O.SemiApplicative)((a: number, b: number) => a + b) - U.deepStrictEqual(sum(O.none, O.none), O.none) - U.deepStrictEqual(sum(O.some(1), O.none), O.none) - U.deepStrictEqual(sum(O.none, O.some(2)), O.none) + U.deepStrictEqual(sum(O.none(), O.none()), O.none()) + U.deepStrictEqual(sum(O.some(1), O.none()), O.none()) + U.deepStrictEqual(sum(O.none(), O.some(2)), O.none()) U.deepStrictEqual(sum(O.some(1), O.some(2)), O.some(3)) }) it("lift3", () => { const sum = _.lift3(O.SemiApplicative)((a: number, b: number, c: number) => a + b + c) - U.deepStrictEqual(sum(O.none, O.none, O.none), O.none) - U.deepStrictEqual(sum(O.some(1), O.none, O.none), O.none) - U.deepStrictEqual(sum(O.none, O.some(2), O.none), O.none) - U.deepStrictEqual(sum(O.none, O.none, O.some(3)), O.none) + U.deepStrictEqual(sum(O.none(), O.none(), O.none()), O.none()) + U.deepStrictEqual(sum(O.some(1), O.none(), O.none()), O.none()) + U.deepStrictEqual(sum(O.none(), O.some(2), O.none()), O.none()) + U.deepStrictEqual(sum(O.none(), O.none(), O.some(3)), O.none()) U.deepStrictEqual(sum(O.some(1), O.some(2), O.some(3)), O.some(6)) }) }) diff --git a/test/typeclass/SemiCoproduct.ts b/test/typeclass/SemiCoproduct.ts index 55eec17be..1049fd717 100644 --- a/test/typeclass/SemiCoproduct.ts +++ b/test/typeclass/SemiCoproduct.ts @@ -1,6 +1,6 @@ +import * as E from "@fp-ts/core/Either" import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/SemiCoproduct" -import * as E from "../data/Either" import * as U from "../util" describe("SemiCoproduct", () => { diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index a822c89bf..aacea2833 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -1,13 +1,14 @@ +import * as Boolean from "@fp-ts/core/Boolean" import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as Number from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import * as P from "@fp-ts/core/Predicate" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as String from "@fp-ts/core/String" import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as _ from "@fp-ts/core/typeclass/SemiProduct" -import * as number from "../data/number" -import * as O from "../data/Option" -import * as P from "../data/Predicate" -import * as RA from "../data/ReadonlyArray" -import * as string from "../data/string" import * as U from "../util" describe("SemiProduct", () => { @@ -81,19 +82,19 @@ describe("SemiProduct", () => { describe("productComposition", () => { it("ReadonlyArray", () => { const product = _.productComposition(RA.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe([], product([O.none])), []) - U.deepStrictEqual(pipe([O.none], product([])), []) - U.deepStrictEqual(pipe([O.none], product([O.none])), [O.none]) + U.deepStrictEqual(pipe([], product([O.none()])), []) + U.deepStrictEqual(pipe([O.none()], product([])), []) + U.deepStrictEqual(pipe([O.none()], product([O.none()])), [O.none()]) U.deepStrictEqual(pipe([O.some(1)], product([O.some(2)])), [O.some([1, 2] as const)]) }) it("Option", () => { const product = _.productComposition(O.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe(O.none, product(O.none)), O.none) - U.deepStrictEqual(pipe(O.some(O.none), product(O.none)), O.none) - U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.none)), O.none) - U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.some(O.none))), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.none), product(O.some(O.some(2)))), O.some(O.none)) + U.deepStrictEqual(pipe(O.none(), product(O.none())), O.none()) + U.deepStrictEqual(pipe(O.some(O.none()), product(O.none())), O.none()) + U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.none())), O.none()) + U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.some(O.none()))), O.some(O.none())) + U.deepStrictEqual(pipe(O.some(O.none()), product(O.some(O.some(2)))), O.some(O.none())) U.deepStrictEqual( pipe(O.some(O.some(1)), product(O.some(O.some(2)))), O.some(O.some([1, 2] as const)) @@ -104,12 +105,15 @@ describe("SemiProduct", () => { describe("productManyComposition", () => { it("ReadonlyArray", () => { const productMany = _.productManyComposition(RA.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe([O.some(1), O.none], productMany([])), [O.some([1] as const), O.none]) - U.deepStrictEqual(pipe([O.some(1), O.none], productMany([[O.some(2), O.none]])), [ + U.deepStrictEqual(pipe([O.some(1), O.none()], productMany([])), [ + O.some([1] as const), + O.none() + ]) + U.deepStrictEqual(pipe([O.some(1), O.none()], productMany([[O.some(2), O.none()]])), [ O.some([1, 2] as const), - O.none, - O.none, - O.none + O.none(), + O.none(), + O.none() ]) U.deepStrictEqual( pipe([O.some(1), O.some(2)], productMany([[O.some(3), O.some(4)], [O.some(5)]])), @@ -124,13 +128,16 @@ describe("SemiProduct", () => { it("Option", () => { const productMany = _.productManyComposition(O.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe(O.none, productMany([])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), productMany([])), O.some(O.none)) + U.deepStrictEqual(pipe(O.none(), productMany([])), O.none()) + U.deepStrictEqual(pipe(O.some(O.none()), productMany([])), O.some(O.none())) U.deepStrictEqual(pipe(O.some(O.some(1)), productMany([])), O.some(O.some([1] as const))) - U.deepStrictEqual(pipe(O.none, productMany([O.none])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), productMany([O.none])), O.none) - U.deepStrictEqual(pipe(O.some(O.none), productMany([O.some(O.none)])), O.some(O.none)) - U.deepStrictEqual(pipe(O.some(O.none), productMany([O.some(O.some("a"))])), O.some(O.none)) + U.deepStrictEqual(pipe(O.none(), productMany([O.none()])), O.none()) + U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.none()])), O.none()) + U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.some(O.none())])), O.some(O.none())) + U.deepStrictEqual( + pipe(O.some(O.none()), productMany([O.some(O.some("a"))])), + O.some(O.none()) + ) U.deepStrictEqual( pipe(O.some(O.some(1)), productMany([O.some(O.some(2))])), O.some(O.some([1, 2] as const)) @@ -141,15 +148,15 @@ describe("SemiProduct", () => { describe("andThenBind", () => { it("Covariant (Option)", () => { const andThenBind = _.andThenBind(O.Applicative) - U.deepStrictEqual(pipe(O.some({ a: 1 }), andThenBind("b", O.none)), O.none) + U.deepStrictEqual(pipe(O.some({ a: 1 }), andThenBind("b", O.none())), O.none()) U.deepStrictEqual(pipe(O.some({ a: 1 }), andThenBind("b", O.some(2))), O.some({ a: 1, b: 2 })) }) it("Contravariant (Predicate)", () => { const p = pipe( P.Do, - P.andThenBind("x", P.isString), - P.andThenBind("y", P.isNumber) + P.andThenBind("x", String.isString), + P.andThenBind("y", Number.isNumber) ) U.deepStrictEqual(p({ x: "a", y: 1 }), true) U.deepStrictEqual(p({ x: "a", y: "x" }), false) @@ -159,13 +166,13 @@ describe("SemiProduct", () => { describe("productFlatten", () => { it("Covariant (Option)", () => { const productFlatten = _.productFlatten(O.SemiProduct) - U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.none)), O.none) + U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.none())), O.none()) U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.some(3))), O.some([1, 2, 3] as const)) }) it("Contravariant (Predicate)", () => { const productFlatten = _.productFlatten(P.SemiProduct) - const p = pipe(P.tuple(P.isString, P.isString), productFlatten(P.isNumber)) + const p = pipe(P.tuple(String.isString, String.isString), productFlatten(Number.isNumber)) U.deepStrictEqual(p(["a", "b", 3]), true) U.deepStrictEqual(p(["a", "b", "c"]), false) U.deepStrictEqual(p([1, "b", 1]), false) @@ -180,18 +187,18 @@ describe("SemiProduct", () => { nonEmptyTuple(O.some("a"), O.some(1), O.some(true)), O.some(["a", 1, true] as const) ) - U.deepStrictEqual(nonEmptyTuple(O.some("a"), O.some(1), O.none), O.none) + U.deepStrictEqual(nonEmptyTuple(O.some("a"), O.some(1), O.none()), O.none()) }) it("Invariant (Semigroup)", () => { const nonEmptyTuple = _.nonEmptyTuple(semigroup.SemiProduct) - const S = nonEmptyTuple(string.Semigroup, number.SemigroupSum) + const S = nonEmptyTuple(String.Semigroup, Number.SemigroupSum) U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) }) it("Contravariant (Predicate)", () => { const nonEmptyTuple = _.nonEmptyTuple(P.SemiProduct) - const p = nonEmptyTuple(P.isString, P.isNumber, P.isBoolean) + const p = nonEmptyTuple(String.isString, Number.isNumber, Boolean.isBoolean) U.deepStrictEqual(p(["a", 1, true]), true) U.deepStrictEqual(p(["a", 1, "b"]), false) }) @@ -206,20 +213,20 @@ describe("SemiProduct", () => { O.some({ a: "a", b: 1, c: true }) ) U.deepStrictEqual( - nonEmptyStruct({ a: O.some("a"), b: O.some(1), c: O.none }), - O.none + nonEmptyStruct({ a: O.some("a"), b: O.some(1), c: O.none() }), + O.none() ) }) it("Invariant (Semigroup)", () => { const nonEmptyStruct = _.nonEmptyStruct(semigroup.Product) - const S = nonEmptyStruct({ x: string.Semigroup, y: number.SemigroupSum }) + const S = nonEmptyStruct({ x: String.Semigroup, y: Number.SemigroupSum }) U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) }) it("Contravariant (Predicate)", () => { const nonEmptyStruct = _.nonEmptyStruct(P.Product) - const p = nonEmptyStruct({ x: P.isString, y: P.isNumber, z: P.isBoolean }) + const p = nonEmptyStruct({ x: String.isString, y: Number.isNumber, z: Boolean.isBoolean }) U.deepStrictEqual(p({ x: "a", y: 1, z: true }), true) U.deepStrictEqual(p({ x: "a", y: 1, z: "b" }), false) }) diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index 56d55d05c..b7b3d3999 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -1,13 +1,13 @@ import { pipe } from "@fp-ts/core/Function" +import * as Number from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" import * as order from "@fp-ts/core/typeclass/Order" import * as _ from "@fp-ts/core/typeclass/Semigroup" -import * as number from "../data/number" -import * as string from "../data/string" import * as U from "../util" describe("Semigroup", () => { it("reverse", () => { - const A = _.reverse(string.Semigroup) + const A = _.reverse(String.Semigroup) U.deepStrictEqual(pipe("a", A.combine("b")), "ba") U.deepStrictEqual(pipe("a", A.combineMany([])), "a") U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "ba") @@ -22,7 +22,7 @@ describe("Semigroup", () => { }) it("intercalate", () => { - const A = pipe(string.Semigroup, _.intercalate("|")) + const A = pipe(String.Semigroup, _.intercalate("|")) U.deepStrictEqual(pipe("a", A.combine("b")), "a|b") U.deepStrictEqual(pipe("a", A.combineMany([])), "a") U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "a|b") @@ -31,14 +31,14 @@ describe("Semigroup", () => { describe("min", () => { it("should return the minimum", () => { - const A = _.min(number.Order) + const A = _.min(Number.Order) U.deepStrictEqual(pipe(1, A.combineMany([])), 1) U.deepStrictEqual(pipe(1, A.combineMany([3, 2])), 1) }) it("should return the last minimum", () => { type Item = { a: number } - const A = _.min(pipe(number.Order, order.contramap((_: Item) => _.a))) + const A = _.min(pipe(Number.Order, order.contramap((_: Item) => _.a))) const item: Item = { a: 1 } U.strictEqual(pipe({ a: 2 }, A.combineMany([{ a: 1 }, item])), item) U.strictEqual(pipe(item, A.combineMany([])), item) @@ -47,14 +47,14 @@ describe("Semigroup", () => { describe("max", () => { it("should return the maximum", () => { - const A = _.max(number.Order) + const A = _.max(Number.Order) U.deepStrictEqual(pipe(1, A.combineMany([])), 1) U.deepStrictEqual(pipe(1, A.combineMany([3, 2])), 3) }) it("should return the last minimum", () => { type Item = { a: number } - const A = _.max(pipe(number.Order, order.contramap((_: Item) => _.a))) + const A = _.max(pipe(Number.Order, order.contramap((_: Item) => _.a))) const item: Item = { a: 2 } U.strictEqual(pipe({ a: 1 }, A.combineMany([{ a: 2 }, item])), item) U.strictEqual(pipe(item, A.combineMany([])), item) @@ -63,8 +63,8 @@ describe("Semigroup", () => { it("struct", () => { const A = _.struct({ - name: string.Semigroup, - age: number.SemigroupSum + name: String.Semigroup, + age: Number.SemigroupSum }) U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combine({ name: "b", age: 20 })), { name: "ab", @@ -89,8 +89,8 @@ describe("Semigroup", () => { it("tuple", () => { const A = _.tuple( - string.Semigroup, - number.SemigroupSum + String.Semigroup, + Number.SemigroupSum ) U.deepStrictEqual(pipe(["a", 10], A.combine(["b", 20])), ["ab", 30]) U.deepStrictEqual(pipe(["a", 10], A.combineMany([])), ["a", 10]) @@ -114,7 +114,7 @@ describe("Semigroup", () => { it("imap", () => { const imap = _.imap - const To = imap((s: string) => [s], ([s]) => s)(string.Semigroup) + const To = imap((s: string) => [s], ([s]) => s)(String.Semigroup) U.deepStrictEqual(pipe(["a"], To.combine(["b"])), ["ab"]) U.deepStrictEqual(pipe(["a"], To.combineMany([])), ["a"]) U.deepStrictEqual(pipe(["a"], To.combineMany([["b"]])), ["ab"]) @@ -123,7 +123,7 @@ describe("Semigroup", () => { U.deepStrictEqual( pipe( ["a"], - _.Invariant.imap((s: string) => [s], ([s]) => s)(string.Semigroup).combineMany([["b"], [ + _.Invariant.imap((s: string) => [s], ([s]) => s)(String.Semigroup).combineMany([["b"], [ "c" ]]) ), @@ -135,9 +135,9 @@ describe("Semigroup", () => { it("product", () => { const A = pipe( - string.Semigroup, - _.SemiProduct.product(number.SemigroupSum), - _.SemiProduct.product(number.SemigroupMultiply), + String.Semigroup, + _.SemiProduct.product(Number.SemigroupSum), + _.SemiProduct.product(Number.SemigroupMultiply), _.imap(([[a, b], c]) => [a, b, c] as const, ([a, b, c]) => [[a, b], c] as const) ) U.deepStrictEqual(pipe(["a", 2, 3], A.combine(["b", 3, 4])), ["ab", 5, 12]) @@ -145,8 +145,8 @@ describe("Semigroup", () => { it("productMany", () => { const A = pipe( - string.Semigroup, - _.SemiProduct.productMany([string.Semigroup, string.Semigroup]) + String.Semigroup, + _.SemiProduct.productMany([String.Semigroup, String.Semigroup]) ) U.deepStrictEqual(pipe(["a", "b", "c"], A.combine(["d", "e", "f"])), ["ad", "be", "cf"]) }) diff --git a/test/typeclass/Traversable.ts b/test/typeclass/Traversable.ts index 794205dbd..79386bae9 100644 --- a/test/typeclass/Traversable.ts +++ b/test/typeclass/Traversable.ts @@ -1,17 +1,20 @@ import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/typeclass/Traversable" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" import * as U from "../util" describe("Traversable", () => { it("traverseComposition", () => { const traverse = _.traverseComposition(RA.Traversable, RA.Traversable)(O.Applicative) U.deepStrictEqual( - pipe([[1, 2], [3]], traverse((a) => (a > 0 ? O.some(a) : O.none))), + pipe([[1, 2], [3]], traverse((a) => (a > 0 ? O.some(a) : O.none()))), O.some([[1, 2], [3]]) ) - U.deepStrictEqual(pipe([[1, -2], [3]], traverse((a) => (a > 0 ? O.some(a) : O.none))), O.none) + U.deepStrictEqual( + pipe([[1, -2], [3]], traverse((a) => (a > 0 ? O.some(a) : O.none()))), + O.none() + ) }) it("sequenceComposition", () => { @@ -22,28 +25,28 @@ describe("Traversable", () => { pipe([[O.some(1), O.some(2)], [O.some(3)]], sequence), O.some([[1, 2], [3]]) ) - U.deepStrictEqual(pipe([[O.some(1), O.none], [O.some(3)]], sequence), O.none) + U.deepStrictEqual(pipe([[O.some(1), O.none()], [O.some(3)]], sequence), O.none()) }) it("sequence", () => { const sequence = _.sequence(RA.Traversable.traverse)(O.Applicative) - U.deepStrictEqual(pipe([O.none, O.some(2)], sequence), O.none) + U.deepStrictEqual(pipe([O.none(), O.some(2)], sequence), O.none()) U.deepStrictEqual(pipe([O.some(1), O.some(2)], sequence), O.some([1, 2])) }) it("traverseTap", () => { const traverseTap = _.traverseTap(RA.Traversable)(O.Applicative) U.deepStrictEqual( - pipe([], traverseTap(n => n > 0 ? O.some(n) : O.none)), + pipe([], traverseTap(n => n > 0 ? O.some(n) : O.none())), O.some([]) ) U.deepStrictEqual( - pipe(["a", "b", "c"], traverseTap(s => s.length > 0 ? O.some(s.length) : O.none)), + pipe(["a", "b", "c"], traverseTap(s => s.length > 0 ? O.some(s.length) : O.none())), O.some(["a", "b", "c"]) ) U.deepStrictEqual( - pipe(["a", "", "c"], traverseTap(s => s.length > 0 ? O.some(s) : O.none)), - O.none + pipe(["a", "", "c"], traverseTap(s => s.length > 0 ? O.some(s) : O.none())), + O.none() ) }) }) diff --git a/test/typeclass/TraversableWithIndex.ts b/test/typeclass/TraversableWithIndex.ts index b35b3a699..fad0b7b1a 100644 --- a/test/typeclass/TraversableWithIndex.ts +++ b/test/typeclass/TraversableWithIndex.ts @@ -1,36 +1,36 @@ -import { pipe } from "@fp-ts/core/Function" -import * as O from "../data/Option" -import * as RA from "../data/ReadonlyArray" -import * as _ from "../limbo/TraversableWithIndex" -import * as U from "../util" +// import { pipe } from "@fp-ts/core/Function" +// import * as O from "@fp-ts/core/Option" +// import * as RA from "@fp-ts/core/ReadonlyArray" +// import * as _ from "../limbo/TraversableWithIndex" +// import * as U from "../util" -describe("TraversableWithIndex", () => { - it("traverseWithIndexComposition", () => { - const traverseWithIndex = _.traverseWithIndexComposition( - RA.TraversableWithIndex, - RA.TraversableWithIndex - )(O.Applicative) - U.deepStrictEqual( - pipe( - [["a"], ["bb"]], - traverseWithIndex((s, [i, j]) => (s.length >= 1 ? O.some(s + i + j) : O.none)) - ), - O.some([["a00"], ["bb10"]]) - ) - U.deepStrictEqual( - pipe( - [["a"], ["bb"]], - traverseWithIndex((s, [i, j]) => (s.length > 1 ? O.some(s + i + j) : O.none)) - ), - O.none - ) - }) +// describe("TraversableWithIndex", () => { +// it("traverseWithIndexComposition", () => { +// const traverseWithIndex = _.traverseWithIndexComposition( +// RA.TraversableWithIndex, +// RA.TraversableWithIndex +// )(O.Applicative) +// U.deepStrictEqual( +// pipe( +// [["a"], ["bb"]], +// traverseWithIndex((s, [i, j]) => (s.length >= 1 ? O.some(s + i + j) : O.none())) +// ), +// O.some([["a00"], ["bb10"]]) +// ) +// U.deepStrictEqual( +// pipe( +// [["a"], ["bb"]], +// traverseWithIndex((s, [i, j]) => (s.length > 1 ? O.some(s + i + j) : O.none())) +// ), +// O.none() +// ) +// }) - it("traverse", () => { - const traverse = _.traverse(RA.TraversableWithIndex)(O.Applicative) - const f = (n: number) => n > 0 ? O.some(n) : O.none - U.deepStrictEqual(pipe([], traverse(f)), O.some([])) - U.deepStrictEqual(pipe([1, 2, 3], traverse(f)), O.some([1, 2, 3])) - U.deepStrictEqual(pipe([1, -2, 3], traverse(f)), O.none) - }) -}) +// it("traverse", () => { +// const traverse = _.traverse(RA.TraversableWithIndex)(O.Applicative) +// const f = (n: number) => n > 0 ? O.some(n) : O.none() +// U.deepStrictEqual(pipe([], traverse(f)), O.some([])) +// U.deepStrictEqual(pipe([1, 2, 3], traverse(f)), O.some([1, 2, 3])) +// U.deepStrictEqual(pipe([1, -2, 3], traverse(f)), O.none()) +// }) +// }) diff --git a/vitest.config.ts b/vitest.config.ts index 9469eb3d4..2baea9ee6 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -8,7 +8,9 @@ export default defineConfig({ exclude: [ "./test/util.ts", "./test/data/*.ts", - "./test/limbo/*.ts" + "./test/limbo/*.ts", + "./test/typeclass/FoldableWithIndex.ts", + "./test/typeclass/TraversableWithIndex.ts" ], globals: true }, From d7889667d151107b680d8351c5eee2e5a93e46a4 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 16:45:53 +0100 Subject: [PATCH 19/80] FilterableWithIndex: 100% coverage --- test/typeclass/FilterableWithIndex.ts | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/typeclass/FilterableWithIndex.ts diff --git a/test/typeclass/FilterableWithIndex.ts b/test/typeclass/FilterableWithIndex.ts new file mode 100644 index 000000000..64bb94e27 --- /dev/null +++ b/test/typeclass/FilterableWithIndex.ts @@ -0,0 +1,69 @@ +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/typeclass/FilterableWithIndex" +import * as U from "../util" + +describe("FilterableWithIndex", () => { + it("filterMapComposition", () => { + const filterMapWithIndex: ( + f: (a: A, i: number) => O.Option + ) => ( + self: ReadonlyArray> + ) => ReadonlyArray> = _ + .filterMapWithIndexComposition(RA.Covariant, RA.FilterableWithIndex) + const f = filterMapWithIndex((s: string, i: number) => + s.length + i > 1 ? O.some(s.length) : O.none() + ) + U.deepStrictEqual(pipe([], f), []) + U.deepStrictEqual(pipe([[]], f), [[]]) + U.deepStrictEqual(pipe([["a"]], f), [[]]) + U.deepStrictEqual(pipe([["aa", "a"]], f), [[2, 1]]) + }) + + it("filterMap", () => { + const filterMap: ( + f: (a: A) => O.Option + ) => (self: ReadonlyArray) => ReadonlyArray = _.filterMap(RA.FilterableWithIndex) + const f = (n: number) => (n % 2 === 0 ? O.none() : O.some(n)) + U.deepStrictEqual(pipe([1, 2, 3], filterMap(f)), [1, 3]) + U.deepStrictEqual(pipe([], filterMap(f)), []) + }) + + it("filterWithIndex", () => { + const filterWithIndex = _.filterWithIndex(RA.FilterableWithIndex) + const f = (n: number) => n % 2 === 0 + U.deepStrictEqual(pipe(["a", "b", "c"], filterWithIndex((_, i) => f(i))), [ + "a", + "c" + ]) + }) + + it("partitionMapWithIndex", () => { + const partitionMapWithIndex = _.partitionMapWithIndex(RA.FilterableWithIndex) + U.deepStrictEqual( + pipe([], partitionMapWithIndex((a) => a)), + [[], []] + ) + U.deepStrictEqual( + pipe( + [E.right(1), E.left("foo"), E.right(2)], + partitionMapWithIndex((a, i) => pipe(a, E.filter((n) => n > i, () => "err"))) + ), + [["foo", "err"], [1]] + ) + }) + + it("partitionWithIndex", () => { + const partitionWithIndex = _.partitionWithIndex(RA.FilterableWithIndex) + U.deepStrictEqual( + pipe([], partitionWithIndex((i, n) => i + n > 2)), + [[], []] + ) + U.deepStrictEqual( + pipe([1, 2], partitionWithIndex((i, n) => i + n > 2)), + [[1], [2]] + ) + }) +}) From de4f635e92369484ed878808d00d200354322ac7 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 17:43:16 +0100 Subject: [PATCH 20/80] remove NonEmptyTraversable typeclass --- .changeset/nervous-schools-knock.md | 5 + Overview.md | 15 --- src/ReadonlyArray.ts | 37 ------ src/index.ts | 18 --- test/ReadonlyArray.ts | 2 - .../limbo}/CovariantWithIndex.ts | 0 .../limbo}/FilterableWithIndex.ts | 0 .../limbo}/NonEmptyTraversable.ts | 0 test/typeclass/CovariantWithIndex.ts | 10 +- test/typeclass/FilterableWithIndex.ts | 16 ++- test/typeclass/FoldableWithIndex.ts | 114 ++++++++++-------- test/typeclass/NonEmptyTraversable.ts | 18 ++- test/typeclass/TraversableWithIndex.ts | 72 +++++------ vitest.config.ts | 4 +- 14 files changed, 135 insertions(+), 176 deletions(-) create mode 100644 .changeset/nervous-schools-knock.md rename {src/typeclass => test/limbo}/CovariantWithIndex.ts (100%) rename {src/typeclass => test/limbo}/FilterableWithIndex.ts (100%) rename {src/typeclass => test/limbo}/NonEmptyTraversable.ts (100%) diff --git a/.changeset/nervous-schools-knock.md b/.changeset/nervous-schools-knock.md new file mode 100644 index 000000000..1defb228e --- /dev/null +++ b/.changeset/nervous-schools-knock.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +remove NonEmptyTraversable typeclass diff --git a/Overview.md b/Overview.md index 0ced453dc..df6fdd915 100644 --- a/Overview.md +++ b/Overview.md @@ -311,21 +311,6 @@ Extends: - `FlatMap` - `Pointed` -### NonEmptyTraversable - -`NonEmptyTraversable`, also known as `Traversable1`. - -`NonEmptyTraversable` is like a non-empty `Traversable`. Unlike the `traverse` and `sequence` -methods of `Traversable` it provides `traverseNonEmpty` and `sequenceNonEmpty` methods which require a `SemiApplicative` -instance instead of `Applicative`. - -| Name | Given | To | -| --------------------------- | -------------------------------------------- | ------------ | -| **traverseNonEmpty** | `SemiApplicative`, `T`, `A => F` | `F>` | -| **sequenceNonEmpty** | `SemiApplicative`, `T>` | `F>` | -| traverseNonEmptyComposition | `SemiApplicative`, `T>`, `A => F` | `F>>` | -| sequenceNonEmptyComposition | `SemiApplicative`, `T>>` | `F>>` | - ### Of | Name | Given | To | diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 9d6947353..2509e2860 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -18,10 +18,8 @@ import * as chainable from "@fp-ts/core/typeclass/Chainable" import type * as compactable from "@fp-ts/core/typeclass/Compactable" import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" -import type * as covariantWithIndex from "@fp-ts/core/typeclass/CovariantWithIndex" import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" -import type * as filterableWithIndex from "@fp-ts/core/typeclass/FilterableWithIndex" import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import type * as invariant from "@fp-ts/core/typeclass/Invariant" @@ -40,8 +38,6 @@ import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" import * as traversableFilterable from "@fp-ts/core/typeclass/TraversableFilterable" -import type * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" - /** * @category type lambdas * @since 1.0.0 @@ -1303,17 +1299,6 @@ export const Invariant: invariant.Invariant = { imap } -/** - * @category instances - * @since 1.0.0 - */ -export const CovariantWithIndex: covariantWithIndex.CovariantWithIndex< - ReadonlyArrayTypeLambda, - number -> = { - mapWithIndex -} - /** * @category instances * @since 1.0.0 @@ -1515,17 +1500,6 @@ export const separate = ( return [left, right] } -/** - * @category instances - * @since 1.0.0 - */ -export const FilterableWithIndex: filterableWithIndex.FilterableWithIndex< - ReadonlyArrayTypeLambda, - number -> = { - filterMapWithIndex -} - /** * @category instances * @since 1.0.0 @@ -2271,14 +2245,3 @@ export const liftOrder = (O: Order): Order> => */ export const NonEmptyCovariant: covariant.Covariant = covariant .make(mapNonEmpty) - -/** - * @category instances - * @since 1.0.0 - */ -export const NonEmptyTraversable: nonEmptyTraversable.NonEmptyTraversable< - NonEmptyReadonlyArrayTypeLambda -> = { - traverseNonEmpty, - sequenceNonEmpty: F => self => pipe(self, traverseNonEmpty(F)(identity)) -} diff --git a/src/index.ts b/src/index.ts index 97ddb8daf..651a2e6f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,16 +32,13 @@ import * as compactable from "@fp-ts/core/typeclass/Compactable" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as coproduct from "@fp-ts/core/typeclass/Coproduct" import * as covariant from "@fp-ts/core/typeclass/Covariant" -import * as covariantWithIndex from "@fp-ts/core/typeclass/CovariantWithIndex" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" -import * as filterableWithIndex from "@fp-ts/core/typeclass/FilterableWithIndex" import * as flatMap from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" import * as monad from "@fp-ts/core/typeclass/Monad" import * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as nonEmptyTraversable from "@fp-ts/core/typeclass/NonEmptyTraversable" import * as of from "@fp-ts/core/typeclass/Of" import * as order from "@fp-ts/core/typeclass/Order" import * as pointed from "@fp-ts/core/typeclass/Pointed" @@ -108,11 +105,6 @@ export { * @since 1.0.0 */ covariant, - /** - * @category typeclass - * @since 1.0.0 - */ - covariantWithIndex, /** * @since 1.0.0 */ @@ -127,11 +119,6 @@ export { * @since 1.0.0 */ filterable, - /** - * @category typeclass - * @since 1.0.0 - */ - filterableWithIndex, /** * @category typeclass * @since 1.0.0 @@ -165,11 +152,6 @@ export { * @since 1.0.0 */ monoid, - /** - * @category typeclass - * @since 1.0.0 - */ - nonEmptyTraversable, /** * @since 1.0.0 */ diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index d662c135c..550933a4e 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -16,7 +16,6 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.Invariant).exist expect(RA.imap).exist - expect(RA.CovariantWithIndex).exist expect(RA.Covariant).exist expect(RA.map).exist expect(RA.let).exist @@ -76,7 +75,6 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.compact).exist expect(RA.separate).exist - expect(RA.FilterableWithIndex).exist expect(RA.Filterable).exist expect(RA.filterMap).exist expect(RA.filter).exist diff --git a/src/typeclass/CovariantWithIndex.ts b/test/limbo/CovariantWithIndex.ts similarity index 100% rename from src/typeclass/CovariantWithIndex.ts rename to test/limbo/CovariantWithIndex.ts diff --git a/src/typeclass/FilterableWithIndex.ts b/test/limbo/FilterableWithIndex.ts similarity index 100% rename from src/typeclass/FilterableWithIndex.ts rename to test/limbo/FilterableWithIndex.ts diff --git a/src/typeclass/NonEmptyTraversable.ts b/test/limbo/NonEmptyTraversable.ts similarity index 100% rename from src/typeclass/NonEmptyTraversable.ts rename to test/limbo/NonEmptyTraversable.ts diff --git a/test/typeclass/CovariantWithIndex.ts b/test/typeclass/CovariantWithIndex.ts index d2a92dd15..0bbe11bc8 100644 --- a/test/typeclass/CovariantWithIndex.ts +++ b/test/typeclass/CovariantWithIndex.ts @@ -1,11 +1,15 @@ import { pipe } from "@fp-ts/core/Function" import * as RA from "@fp-ts/core/ReadonlyArray" -import * as _ from "@fp-ts/core/typeclass/CovariantWithIndex" +import * as _ from "@fp-ts/core/test/limbo/CovariantWithIndex" import * as U from "../util" +const CovariantWithIndex: _.CovariantWithIndex = { + mapWithIndex: RA.mapWithIndex +} + describe("CovariantWithIndex", () => { it("mapWithIndexComposition", () => { - const mapWithIndex = _.mapWithIndexComposition(RA.CovariantWithIndex, RA.CovariantWithIndex) + const mapWithIndex = _.mapWithIndexComposition(CovariantWithIndex, CovariantWithIndex) const f = (a: string, [i, j]: readonly [number, number]) => a + i + j U.deepStrictEqual(pipe([], mapWithIndex(f)), []) U.deepStrictEqual(pipe([[]], mapWithIndex(f)), [[]]) @@ -19,7 +23,7 @@ describe("CovariantWithIndex", () => { }) it("map", () => { - const map = _.map(RA.CovariantWithIndex) + const map = _.map(CovariantWithIndex) const f = (a: string) => a + "!" U.deepStrictEqual(pipe([], map(f)), []) U.deepStrictEqual(pipe(["a", "b", "c"], map(f)), ["a!", "b!", "c!"]) diff --git a/test/typeclass/FilterableWithIndex.ts b/test/typeclass/FilterableWithIndex.ts index 64bb94e27..64ec04ade 100644 --- a/test/typeclass/FilterableWithIndex.ts +++ b/test/typeclass/FilterableWithIndex.ts @@ -2,9 +2,13 @@ import * as E from "@fp-ts/core/Either" import { pipe } from "@fp-ts/core/Function" import * as O from "@fp-ts/core/Option" import * as RA from "@fp-ts/core/ReadonlyArray" -import * as _ from "@fp-ts/core/typeclass/FilterableWithIndex" +import * as _ from "@fp-ts/core/test/limbo/FilterableWithIndex" import * as U from "../util" +const FilterableWithIndex: _.FilterableWithIndex = { + filterMapWithIndex: RA.filterMapWithIndex +} + describe("FilterableWithIndex", () => { it("filterMapComposition", () => { const filterMapWithIndex: ( @@ -12,7 +16,7 @@ describe("FilterableWithIndex", () => { ) => ( self: ReadonlyArray> ) => ReadonlyArray> = _ - .filterMapWithIndexComposition(RA.Covariant, RA.FilterableWithIndex) + .filterMapWithIndexComposition(RA.Covariant, FilterableWithIndex) const f = filterMapWithIndex((s: string, i: number) => s.length + i > 1 ? O.some(s.length) : O.none() ) @@ -25,14 +29,14 @@ describe("FilterableWithIndex", () => { it("filterMap", () => { const filterMap: ( f: (a: A) => O.Option - ) => (self: ReadonlyArray) => ReadonlyArray = _.filterMap(RA.FilterableWithIndex) + ) => (self: ReadonlyArray) => ReadonlyArray = _.filterMap(FilterableWithIndex) const f = (n: number) => (n % 2 === 0 ? O.none() : O.some(n)) U.deepStrictEqual(pipe([1, 2, 3], filterMap(f)), [1, 3]) U.deepStrictEqual(pipe([], filterMap(f)), []) }) it("filterWithIndex", () => { - const filterWithIndex = _.filterWithIndex(RA.FilterableWithIndex) + const filterWithIndex = _.filterWithIndex(FilterableWithIndex) const f = (n: number) => n % 2 === 0 U.deepStrictEqual(pipe(["a", "b", "c"], filterWithIndex((_, i) => f(i))), [ "a", @@ -41,7 +45,7 @@ describe("FilterableWithIndex", () => { }) it("partitionMapWithIndex", () => { - const partitionMapWithIndex = _.partitionMapWithIndex(RA.FilterableWithIndex) + const partitionMapWithIndex = _.partitionMapWithIndex(FilterableWithIndex) U.deepStrictEqual( pipe([], partitionMapWithIndex((a) => a)), [[], []] @@ -56,7 +60,7 @@ describe("FilterableWithIndex", () => { }) it("partitionWithIndex", () => { - const partitionWithIndex = _.partitionWithIndex(RA.FilterableWithIndex) + const partitionWithIndex = _.partitionWithIndex(FilterableWithIndex) U.deepStrictEqual( pipe([], partitionWithIndex((i, n) => i + n > 2)), [[], []] diff --git a/test/typeclass/FoldableWithIndex.ts b/test/typeclass/FoldableWithIndex.ts index 7cadae671..1fc9c8f4d 100644 --- a/test/typeclass/FoldableWithIndex.ts +++ b/test/typeclass/FoldableWithIndex.ts @@ -1,57 +1,67 @@ -// import { pipe } from "@fp-ts/core/Function" -// import * as N from "@fp-ts/core/Number" -// import * as O from "@fp-ts/core/Option" -// import * as RA from "@fp-ts/core/ReadonlyArray" -// import * as foldableWithIndex from "../limbo/FoldableWithIndex" -// import * as U from "../util" +import { pipe } from "@fp-ts/core/Function" +import * as N from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "../limbo/FoldableWithIndex" +import * as U from "../util" -// describe("FoldableWithIndex", () => { -// it("reduceWithIndexComposition", () => { -// const reduceWithIndex = foldableWithIndex.reduceWithIndexComposition( -// RA.FoldableWithIndex, -// RA.FoldableWithIndex -// ) -// const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j -// U.deepStrictEqual(pipe([], reduceWithIndex("-", f)), "-") -// U.deepStrictEqual(pipe([[]], reduceWithIndex("-", f)), "-") -// U.deepStrictEqual( -// pipe([["a", "c"], ["b", "d"]], reduceWithIndex("-", f)), -// "-a00c01b10d11" -// ) -// }) +const FoldableWithIndex: _.FoldableWithIndex = { + reduceWithIndex: RA.reduceWithIndex, + reduceRightWithIndex: RA.reduceRightWithIndex +} -// it("reduceRightWithIndexComposition", () => { -// const reduceRightWithIndex = foldableWithIndex.reduceRightWithIndexComposition( -// RA.FoldableWithIndex, -// RA.FoldableWithIndex -// ) -// const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j -// U.deepStrictEqual(pipe([], reduceRightWithIndex("-", f)), "-") -// U.deepStrictEqual(pipe([[]], reduceRightWithIndex("-", f)), "-") -// U.deepStrictEqual( -// pipe([["a", "c"], ["b", "d"]], reduceRightWithIndex("-", f)), -// "-d11b10c01a00" -// ) -// }) +const FoldableWithIndexO: _.FoldableWithIndex = { + reduceWithIndex: (b, f) => (self) => O.isNone(self) ? b : f(b, self.value, 0), + reduceRightWithIndex: (b, f) => (self) => O.isNone(self) ? b : f(b, self.value, 0) +} -// it("toArray", () => { -// const toReadonlyArray = foldableWithIndex.toArray(O.FoldableWithIndex) -// U.deepStrictEqual(toReadonlyArray(O.none()), []) -// U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) -// }) +describe("FoldableWithIndex", () => { + it("reduceWithIndexComposition", () => { + const reduceWithIndex = _.reduceWithIndexComposition( + FoldableWithIndex, + FoldableWithIndex + ) + const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j + U.deepStrictEqual(pipe([], reduceWithIndex("-", f)), "-") + U.deepStrictEqual(pipe([[]], reduceWithIndex("-", f)), "-") + U.deepStrictEqual( + pipe([["a", "c"], ["b", "d"]], reduceWithIndex("-", f)), + "-a00c01b10d11" + ) + }) -// it("toArrayWith", () => { -// const toReadonlyArrayWith = foldableWithIndex.toArrayWith(O.FoldableWithIndex) -// U.deepStrictEqual(pipe(O.none(), toReadonlyArrayWith(U.double)), []) -// U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith(U.double)), [4]) -// U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith((a, i) => U.double(a) * i)), [0]) -// }) + it("reduceRightWithIndexComposition", () => { + const reduceRightWithIndex = _.reduceRightWithIndexComposition( + FoldableWithIndex, + FoldableWithIndex + ) + const f = (b: string, a: string, [i, j]: readonly [number, number]) => b + a + i + j + U.deepStrictEqual(pipe([], reduceRightWithIndex("-", f)), "-") + U.deepStrictEqual(pipe([[]], reduceRightWithIndex("-", f)), "-") + U.deepStrictEqual( + pipe([["a", "c"], ["b", "d"]], reduceRightWithIndex("-", f)), + "-d11b10c01a00" + ) + }) -// it("foldMapWithIndex", () => { -// const foldMapWithIndex = foldableWithIndex.foldMapWithIndex(RA.FoldableWithIndex) -// U.deepStrictEqual( -// pipe([1, 2, 3], foldMapWithIndex(N.MonoidSum)((n, i) => (n * 2) + i)), -// 15 -// ) -// }) -// }) + it("toArray", () => { + const toReadonlyArray = _.toArray(FoldableWithIndexO) + U.deepStrictEqual(toReadonlyArray(O.none()), []) + U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) + }) + + it("toArrayWith", () => { + const toReadonlyArrayWith = _.toArrayWith(FoldableWithIndexO) + U.deepStrictEqual(pipe(O.none(), toReadonlyArrayWith(U.double)), []) + U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith(U.double)), [4]) + U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith((a, i) => U.double(a) * i)), [0]) + }) + + it("foldMapWithIndex", () => { + const foldMapWithIndex = _.foldMapWithIndex(FoldableWithIndex) + U.deepStrictEqual( + pipe([1, 2, 3], foldMapWithIndex(N.MonoidSum)((n, i) => (n * 2) + i)), + 15 + ) + }) +}) diff --git a/test/typeclass/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index 4fbb4de6b..666f074c6 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -1,13 +1,19 @@ +import { identity, pipe } from "@fp-ts/core/Function" import * as O from "@fp-ts/core/Option" import * as RA from "@fp-ts/core/ReadonlyArray" -import * as _ from "@fp-ts/core/typeclass/NonEmptyTraversable" +import * as _ from "@fp-ts/core/test/limbo/NonEmptyTraversable" import * as U from "../util" +const NonEmptyTraversable: _.NonEmptyTraversable = { + traverseNonEmpty: RA.traverseNonEmpty, + sequenceNonEmpty: F => self => pipe(self, RA.traverseNonEmpty(F)(identity)) +} + describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const traverseNonEmpty = _.traverseNonEmptyComposition( - RA.NonEmptyTraversable, - RA.NonEmptyTraversable + NonEmptyTraversable, + NonEmptyTraversable )(O.SemiApplicative)((n: number) => (n > 0 ? O.some(n) : O.none())) U.deepStrictEqual(traverseNonEmpty([[1]]), O.some([[1]] as const)) U.deepStrictEqual(traverseNonEmpty([[1, -1]]), O.none()) @@ -17,8 +23,8 @@ describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const sequence = _.sequenceNonEmptyComposition( - { ...RA.NonEmptyTraversable, ...RA.NonEmptyCovariant }, - RA.NonEmptyTraversable + { ...NonEmptyTraversable, ...RA.NonEmptyCovariant }, + NonEmptyTraversable )(O.SemiApplicative) U.deepStrictEqual(sequence([[O.some(1)]]), O.some([[1]] as const)) U.deepStrictEqual(sequence([[O.some(1), O.none()]]), O.none()) @@ -34,7 +40,7 @@ describe("NonEmptyTraversable", () => { it("sequenceNonEmpty", () => { const sequenceNonEmpty = _.sequenceNonEmpty( - RA.NonEmptyTraversable.traverseNonEmpty + NonEmptyTraversable.traverseNonEmpty )(O.SemiApplicative) U.deepStrictEqual(sequenceNonEmpty([O.none()]), O.none()) U.deepStrictEqual(sequenceNonEmpty([O.some(1)]), O.some([1] as const)) diff --git a/test/typeclass/TraversableWithIndex.ts b/test/typeclass/TraversableWithIndex.ts index fad0b7b1a..0b7e83638 100644 --- a/test/typeclass/TraversableWithIndex.ts +++ b/test/typeclass/TraversableWithIndex.ts @@ -1,36 +1,40 @@ -// import { pipe } from "@fp-ts/core/Function" -// import * as O from "@fp-ts/core/Option" -// import * as RA from "@fp-ts/core/ReadonlyArray" -// import * as _ from "../limbo/TraversableWithIndex" -// import * as U from "../util" +import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as _ from "@fp-ts/core/test/limbo/TraversableWithIndex" +import * as U from "../util" -// describe("TraversableWithIndex", () => { -// it("traverseWithIndexComposition", () => { -// const traverseWithIndex = _.traverseWithIndexComposition( -// RA.TraversableWithIndex, -// RA.TraversableWithIndex -// )(O.Applicative) -// U.deepStrictEqual( -// pipe( -// [["a"], ["bb"]], -// traverseWithIndex((s, [i, j]) => (s.length >= 1 ? O.some(s + i + j) : O.none())) -// ), -// O.some([["a00"], ["bb10"]]) -// ) -// U.deepStrictEqual( -// pipe( -// [["a"], ["bb"]], -// traverseWithIndex((s, [i, j]) => (s.length > 1 ? O.some(s + i + j) : O.none())) -// ), -// O.none() -// ) -// }) +const TraversableWithIndex: _.TraversableWithIndex = { + traverseWithIndex: RA.traverseWithIndex +} -// it("traverse", () => { -// const traverse = _.traverse(RA.TraversableWithIndex)(O.Applicative) -// const f = (n: number) => n > 0 ? O.some(n) : O.none() -// U.deepStrictEqual(pipe([], traverse(f)), O.some([])) -// U.deepStrictEqual(pipe([1, 2, 3], traverse(f)), O.some([1, 2, 3])) -// U.deepStrictEqual(pipe([1, -2, 3], traverse(f)), O.none()) -// }) -// }) +describe("TraversableWithIndex", () => { + it("traverseWithIndexComposition", () => { + const traverseWithIndex = _.traverseWithIndexComposition( + TraversableWithIndex, + TraversableWithIndex + )(O.Applicative) + U.deepStrictEqual( + pipe( + [["a"], ["bb"]], + traverseWithIndex((s, [i, j]) => (s.length >= 1 ? O.some(s + i + j) : O.none())) + ), + O.some([["a00"], ["bb10"]]) + ) + U.deepStrictEqual( + pipe( + [["a"], ["bb"]], + traverseWithIndex((s, [i, j]) => (s.length > 1 ? O.some(s + i + j) : O.none())) + ), + O.none() + ) + }) + + it("traverse", () => { + const traverse = _.traverse(TraversableWithIndex)(O.Applicative) + const f = (n: number) => n > 0 ? O.some(n) : O.none() + U.deepStrictEqual(pipe([], traverse(f)), O.some([])) + U.deepStrictEqual(pipe([1, 2, 3], traverse(f)), O.some([1, 2, 3])) + U.deepStrictEqual(pipe([1, -2, 3], traverse(f)), O.none()) + }) +}) diff --git a/vitest.config.ts b/vitest.config.ts index 2baea9ee6..9469eb3d4 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -8,9 +8,7 @@ export default defineConfig({ exclude: [ "./test/util.ts", "./test/data/*.ts", - "./test/limbo/*.ts", - "./test/typeclass/FoldableWithIndex.ts", - "./test/typeclass/TraversableWithIndex.ts" + "./test/limbo/*.ts" ], globals: true }, From 8165742d7e8ace8096b1798b78b578bbfa702907 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 18:10:19 +0100 Subject: [PATCH 21/80] docs: add Equivalence to Overview.md --- Overview.md | 23 ++++++++++++++++++++--- src/Option.ts | 9 +++++---- src/These.ts | 16 ++++++---------- src/typeclass/Equivalence.ts | 5 +++-- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/Overview.md b/Overview.md index df6fdd915..04cb78a75 100644 --- a/Overview.md +++ b/Overview.md @@ -26,10 +26,27 @@ Extends: | reverse | `Bounded` | `Bounded` | | clamp | `A` | `A` | +### Equivalence + +`Equivalence` defines a binary relation that is reflexive, symmetric, and transitive. +In other words, it defines a notion of equivalence between values of a certain type. +These properties are also known in mathematics as an "equivalence relation". + +| Name | Given | To | +| ------------ | ----------------------------------------------- | ------------------------------------------ | +| strict | `A` | `Equivalence` | +| tuple | `[Equivalence, Equivalence, ...]` | `Equivalence<[A, B, ...]>` | +| array | `Equivalence` | `Equivalence` | +| struct | `{ a: Equivalence, b: Equivalence, ... }` | `Equivalence<{ a: A, b: B, ... }>` | +| record | `Equivalence` | `Equivalence<{ readonly [x: string]: A }>` | +| getSemigroup | `A` | `Semigroup>` | +| getMonoid | `A` | `Monoid>` | +| contramap | `Equivalence`, `B => A` | `Equivalence` | + ### Monoid -A monoid is a semigroup with an identity. A monoid is a specialization of a -semigroup, so its operation must be associative. Additionally, +A `Monoid` is a `Semigroup` with an identity. A `Monoid` is a specialization of a +`Semigroup`, so its operation must be associative. Additionally, `x |> combine(empty) == empty |> combine(x) == x`. For example, if we have `Monoid`, with `combine` as string concatenation, then `empty = ""`. @@ -85,7 +102,7 @@ By the totality law, `x <= y` and `y <= x` cannot be both `false`. ### Semigroup -A semigroup is any set `A` with an associative operation (`combine`): +A `Semigroup` is any set `A` with an associative operation (`combine`): `x |> combine(y) |> combine(z) == x |> combine(y |> combine(z))` diff --git a/src/Option.ts b/src/Option.ts index 819f08f3a..7ab3091e2 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -3,12 +3,13 @@ * type Option = None | Some * ``` * - * `Option` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is + * The `Option` type can be interpreted in a few ways: + * + * 1) `Option` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is * an instance of `Some`, containing the present value of type `A`. If the value is absent, the `Option` is an * instance of `None`. - * - * An option could be looked at as a collection or foldable structure with either one or zero elements. - * Another way to look at `Option` is: it represents the effect of a possibly failing computation. + * 2) Another way to view `Option` is as a representation of a possibly failing computation. + * 3) An option can also be thought of as a collection or foldable structure with either one or zero elements. * * @since 1.0.0 */ diff --git a/src/These.ts b/src/These.ts index 61a16d21d..0784b7cd9 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1,22 +1,18 @@ /** * A data structure providing "inclusive-or" as opposed to `Either`'s "exclusive-or". * - * If you interpret `Either` as suggesting the computation may either fail or of (exclusively), then - * `These` may fail, of, or do both at the same time. + * If you interpret `Either` as suggesting the computation may either fail or succeed (exclusively), then + * `These` may fail, succeed, or do both at the same time. * - * There are a few ways to interpret the both case: + * There are a few ways to interpret the `Both` case: * - * - You can think of a computation that has a non-fatal error. - * - You can think of a computation that went as far as it could before erroring. - * - You can think of a computation that keeps track of errors as it completes. + * 1) You can think of a computation that has a non-fatal error. + * 2) You can think of a computation that went as far as it could before erroring. + * 3) You can think of a computation that keeps track of errors as it completes. * * Another way you can think of `These` is saying that we want to handle `E` kind of data, `A` kind of data, or * both `E` and `A` kind of data at the same time. This is particularly useful when it comes to displaying UI's. * - * (description adapted from https://package.elm-lang.org/packages/joneshf/elm-these) - * - * Adapted from https://github.com/purescript-contrib/purescript-these - * * @since 1.0.0 */ import type { Either, Left, Right } from "@fp-ts/core/Either" diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index 762f7790c..ee2ee8fcf 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -1,6 +1,7 @@ /** - * This module provides an implementation of the `Equivalence` type class. - * An `Equivalence` is a binary relation that is reflexive, symmetric and transitive. + * This module provides an implementation of the `Equivalence` type class, which defines a binary relation + * that is reflexive, symmetric, and transitive. In other words, it defines a notion of equivalence between values of a certain type. + * These properties are also known in mathematics as an "equivalence relation". * * @since 1.0.0 */ From 76dd577b9749aabef5fa8bd1e9dbb9b18f2d113e Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 18:50:29 +0100 Subject: [PATCH 22/80] update README --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index aed36b388..364c07560 100644 --- a/README.md +++ b/README.md @@ -16,30 +16,29 @@ Functional programming in TypeScript # Typed functional programming in TypeScript -This project represents the next major iteration of [`fp-ts`](https://github.com/gcanti/fp-ts) and it's objective is a reconciliation with [`Effect`](https://github.com/Effect-TS) in order to unify the ecosystems. +This project represents the next major iteration of [`fp-ts`](https://github.com/gcanti/fp-ts) and it's objective is a reconciliation with [`@effect`](https://github.com/Effect-TS) in order to unify the ecosystems. -The [`Effect`](https://github.com/Effect-TS) project will reduce it's scope to simply being an effect system and will delegate to `fp-ts org` all the lower level abstractions such as typeclasses and common data structures. +The [`@effect`](https://github.com/Effect-TS) project will reduce it's scope to simply being an effect system and will delegate to `fp-ts org` all the lower level abstractions such as typeclasses and common data structures. The objective of the `fp-ts org` in github and in npm (`@fp-ts`) is to simplify structure and management of the project, have smaller and better scoped packages. Our "current" idea (that is well open for changes) is for `fp-ts org` to have: -- `@fp-ts/core` with the new `HKT` implementation and the most common typeclasses such as `Monad` -- `@fp-ts/data` with `Option`, `Either`, `ReadonlyArray`, `List` and the most common data structures together with data related typeclasses (i.e. `Compactable`, etc) -- `@fp-ts/optics` with an optic implementation that will provide also optics for structures in `@fp-ts/data` -- `@fp-ts/schema` with a concrete codec such as `io-ts` again for all the structures in `@fp-ts/data` +- The [`@fp-ts/core`](https://github.com/fp-ts/core) library features a new implementation of the Higher Kinded Type (HKT) pattern, including common typeclasses such as `Monad` and widely-used data types like `Option`, `Either`, and `ReadonlyArray` +- [`@fp-ts/schema`](https://github.com/fp-ts/schema) offers schema validation with static type inference, including decoders for data structures in `@fp-ts/core` and `@effect/data` +- [`@fp-ts/optic`](https://github.com/fp-ts/optic) provides optics for structures in both `@fp-ts/core` and `@effect/data` -And for [`Effect`](https://github.com/Effect-TS) to have: +For those using [`fp-ts`](https://github.com/gcanti/fp-ts) v2 and its ecosystem, roughly these are the equivalents: -- `@effect/core` with the effect system -- `@effect/query` with the query impl -- `@effect/*` every other effect based impl +- [`fp-ts`](https://github.com/gcanti/fp-ts) -> [`@fp-ts/core`](https://github.com/fp-ts/core) + [`@effect/*` packages](https://github.com/Effect-TS) +- [`io-ts`](https://github.com/gcanti/io-ts) -> [`@fp-ts/schema`](https://github.com/fp-ts/schema) +- [`monocle-ts`](https://github.com/gcanti/monocle-ts) -> [`@fp-ts/optic`](https://github.com/fp-ts/optic) -Note that [`Effect`](https://github.com/Effect-TS) will not have base structures like `Option` / `Either` / `List` and typeclasses like `Monad` / `Functor` and [`fp-ts`](https://github.com/fp-ts) will not have effect execution modules like `Task` / `IO` as both projects are made to be the same ecosystem and each answer a specific set of needs in the best way possible. +Note that `@fp-ts/core` will not contain any effect system (e.g. `Task`, `TaskEither`, `ReaderTaskEither`) since the handling of effects is entirely delegated to the packages contained in [`@effect/*`](https://github.com/Effect-TS)." # Installation -To install the **pre-alpha** version: +To install the **alpha** version: ``` npm install @fp-ts/core @@ -47,7 +46,7 @@ npm install @fp-ts/core # Documentation -- [Overview](./Overview.md) +- [Typeclass overview](./Overview.md) - [API Reference](https://fp-ts.github.io/core/) # License From 36c80fcfd14d19a759c0cbfe1b322451787f4b38 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 18:57:30 +0100 Subject: [PATCH 23/80] add the prefix @fp-ts/core to the _tag of the data types --- .changeset/olive-pumas-trade.md | 5 +++++ src/Either.ts | 4 ++-- src/Option.ts | 4 ++-- src/These.ts | 30 +++++++++++++++++------------- src/internal/Either.ts | 12 +++++++----- src/internal/Option.ts | 10 +++++----- 6 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 .changeset/olive-pumas-trade.md diff --git a/.changeset/olive-pumas-trade.md b/.changeset/olive-pumas-trade.md new file mode 100644 index 000000000..6a5413982 --- /dev/null +++ b/.changeset/olive-pumas-trade.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add the prefix @fp-ts/core to the \_tag of the data types diff --git a/src/Either.ts b/src/Either.ts index 1101c2435..42194e2c5 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -45,7 +45,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type Left = { - readonly _tag: "Left" + readonly _tag: "@fp-ts/core/Either/Left" readonly left: E } @@ -54,7 +54,7 @@ export type Left = { * @since 1.0.0 */ export type Right = { - readonly _tag: "Right" + readonly _tag: "@fp-ts/core/Either/Right" readonly right: A } diff --git a/src/Option.ts b/src/Option.ts index 7ab3091e2..aa76952bc 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -51,7 +51,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type None = { - readonly _tag: "None" + readonly _tag: "@fp-ts/core/Option/None" } /** @@ -59,7 +59,7 @@ export type None = { * @since 1.0.0 */ export type Some = { - readonly _tag: "Some" + readonly _tag: "@fp-ts/core/Option/Some" readonly value: A } diff --git a/src/These.ts b/src/These.ts index 0784b7cd9..5ed4e4e80 100644 --- a/src/These.ts +++ b/src/These.ts @@ -50,7 +50,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type Both = { - readonly _tag: "Both" + readonly _tag: "@fp-ts/core/These/Both" readonly left: E readonly right: A } @@ -87,13 +87,13 @@ export interface ValidatedTypeLambda extends TypeLambda { * @category constructors * @since 1.0.0 */ -export const left = (left: E): These => ({ _tag: "Left", left }) +export const left = (left: E): These => ({ _tag: "@fp-ts/core/Either/Left", left }) /** * @category constructors * @since 1.0.0 */ -export const right = (right: A): These => ({ _tag: "Right", right }) +export const right = (right: A): These => ({ _tag: "@fp-ts/core/Either/Right", right }) /** * Alias of `right`. @@ -108,7 +108,7 @@ export const of = right * @since 1.0.0 */ export const both = (left: E, right: A): These => ({ - _tag: "Both", + _tag: "@fp-ts/core/These/Both", left, right }) @@ -158,11 +158,11 @@ export const match = ( ) => (self: These): B | C | D => { switch (self._tag) { - case "Left": + case "@fp-ts/core/Either/Left": return onLeft(self.left) - case "Right": + case "@fp-ts/core/Either/Right": return onRight(self.right) - case "Both": + case "@fp-ts/core/These/Both": return onBoth(self.left, self.right) } } @@ -182,14 +182,15 @@ export const reverse: (self: These) => These = match( * @category guards * @since 1.0.0 */ -export const isLeft = (self: These): self is Left => self._tag === "Left" +export const isLeft = (self: These): self is Left => + self._tag === "@fp-ts/core/Either/Left" /** * @category guards * @since 1.0.0 */ export const isLeftOrBoth = (self: These): self is Left | Both => - self._tag !== "Right" + self._tag !== "@fp-ts/core/Either/Right" /** * Returns `true` if the these is an instance of `Right`, `false` otherwise @@ -197,14 +198,15 @@ export const isLeftOrBoth = (self: These): self is Left | Both(self: These): self is Right => self._tag === "Right" +export const isRight = (self: These): self is Right => + self._tag === "@fp-ts/core/Either/Right" /** * @category guards * @since 1.0.0 */ export const isRightOrBoth = (self: These): self is Right | Both => - self._tag !== "Left" + self._tag !== "@fp-ts/core/Either/Left" /** * Returns `true` if the these is an instance of `Both`, `false` otherwise @@ -212,7 +214,8 @@ export const isRightOrBoth = (self: These): self is Right | Both< * @category guards * @since 1.0.0 */ -export const isBoth = (self: These): self is Both => self._tag === "Both" +export const isBoth = (self: These): self is Both => + self._tag === "@fp-ts/core/These/Both" /** * Returns `true` if the specified value is an instance of `These`, `false` @@ -224,7 +227,8 @@ export const isBoth = (self: These): self is Both => self._tag export const isThese = (u: unknown): u is These => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "Left" || u["_tag"] === "Right" || u["_tag"] === "Both") + (u["_tag"] === "@fp-ts/core/Either/Left" || u["_tag"] === "@fp-ts/core/Either/Right" || + u["_tag"] === "@fp-ts/core/These/Both") /** * Constructs a new `These` from a function that might throw. diff --git a/src/internal/Either.ts b/src/internal/Either.ts index fbc5bce28..c8d2867d8 100644 --- a/src/internal/Either.ts +++ b/src/internal/Either.ts @@ -10,19 +10,21 @@ import type { Option } from "@fp-ts/core/Option" /** @internal */ export const isEither = (u: unknown): u is Either => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "Left" || u["_tag"] === "Right") + (u["_tag"] === "@fp-ts/core/Either/Left" || u["_tag"] === "@fp-ts/core/Either/Right") /** @internal */ -export const isLeft = (ma: Either): ma is Left => ma._tag === "Left" +export const isLeft = (ma: Either): ma is Left => + ma._tag === "@fp-ts/core/Either/Left" /** @internal */ -export const isRight = (ma: Either): ma is Right => ma._tag === "Right" +export const isRight = (ma: Either): ma is Right => + ma._tag === "@fp-ts/core/Either/Right" /** @internal */ -export const left = (e: E): Either => ({ _tag: "Left", left: e }) +export const left = (e: E): Either => ({ _tag: "@fp-ts/core/Either/Left", left: e }) /** @internal */ -export const right = (a: A): Either => ({ _tag: "Right", right: a }) +export const right = (a: A): Either => ({ _tag: "@fp-ts/core/Either/Right", right: a }) /** @internal */ export const getLeft = ( diff --git a/src/internal/Option.ts b/src/internal/Option.ts index c3a101ea6..04e3586ac 100644 --- a/src/internal/Option.ts +++ b/src/internal/Option.ts @@ -7,19 +7,19 @@ import type { None, Option, Some } from "@fp-ts/core/Option" /** @internal */ export const isOption = (u: unknown): u is Option => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "None" || u["_tag"] === "Some") + (u["_tag"] === "@fp-ts/core/Option/None" || u["_tag"] === "@fp-ts/core/Option/Some") /** @internal */ -export const isNone = (fa: Option): fa is None => fa._tag === "None" +export const isNone = (fa: Option): fa is None => fa._tag === "@fp-ts/core/Option/None" /** @internal */ -export const isSome = (fa: Option): fa is Some => fa._tag === "Some" +export const isSome = (fa: Option): fa is Some => fa._tag === "@fp-ts/core/Option/Some" /** @internal */ -export const none: Option = { _tag: "None" } +export const none: Option = { _tag: "@fp-ts/core/Option/None" } /** @internal */ -export const some = (a: A): Option => ({ _tag: "Some", value: a }) +export const some = (a: A): Option => ({ _tag: "@fp-ts/core/Option/Some", value: a }) /** @internal */ export const fromNullable = ( From df3cb49b9db3e057a8fa8d0d8daa3768abec8d5b Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 19:00:38 +0100 Subject: [PATCH 24/80] chore --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 2 +- src/Predicate.ts | 2 +- src/ReadonlyArray.ts | 2 +- src/These.ts | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 42194e2c5..ebfb94b33 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -407,7 +407,7 @@ export const SemiProduct: semiProduct.SemiProduct = { */ export const andThenBind: ( name: Exclude, - fb: Either + that: Either ) => ( self: Either ) => Either = semiProduct diff --git a/src/Identity.ts b/src/Identity.ts index 41ba1275d..95b890460 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -282,7 +282,7 @@ export const SemiProduct: semiProduct.SemiProduct = { */ export const andThenBind: ( name: Exclude, - fb: Identity + that: Identity ) => ( self: Identity ) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct diff --git a/src/Option.ts b/src/Option.ts index aa76952bc..d279adfb2 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -451,7 +451,7 @@ export const SemiProduct: semiProduct.SemiProduct = { */ export const andThenBind: ( name: Exclude, - fb: Option + that: Option ) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct.andThenBind(SemiProduct) diff --git a/src/Predicate.ts b/src/Predicate.ts index dce414810..17d73a020 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -185,7 +185,7 @@ export const Product: product_.Product = { */ export const andThenBind: ( name: Exclude, - self: Predicate + that: Predicate ) => ( self: Predicate ) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 2509e2860..8b7fa5a0b 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1736,7 +1736,7 @@ export const SemiProduct: semiProduct.SemiProduct = { */ export const andThenBind: ( name: Exclude, - fb: ReadonlyArray + that: ReadonlyArray ) => ( self: ReadonlyArray ) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct diff --git a/src/These.ts b/src/These.ts index 5ed4e4e80..88819b8a5 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1090,7 +1090,7 @@ export const productAll = ( */ export const andThenBind: ( name: Exclude, - fb: Validated + that: Validated ) => ( self: Validated ) => Validated = semiProduct @@ -1102,11 +1102,11 @@ export const andThenBind: ( */ export const andThenBindEither = ( name: Exclude, - fb: Either + that: Either ): ( self: Validated ) => Validated => - andThenBind(name, fromEither(fb)) + andThenBind(name, fromEither(that)) /** * @category do notation @@ -1114,11 +1114,11 @@ export const andThenBindEither = ( */ export const andThenBindThese = ( name: Exclude, - fb: These + that: These ): ( self: Validated ) => Validated => - andThenBind(name, fromThese(fb)) + andThenBind(name, fromThese(that)) /** * @since 1.0.0 From 44d74917caae1bd4382406ea5dac42d219c433a4 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 16 Jan 2023 19:04:51 +0100 Subject: [PATCH 25/80] Boolean: add not combinator --- .changeset/purple-cooks-destroy.md | 5 +++++ src/Boolean.ts | 6 ++++++ test/Boolean.ts | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 .changeset/purple-cooks-destroy.md diff --git a/.changeset/purple-cooks-destroy.md b/.changeset/purple-cooks-destroy.md new file mode 100644 index 000000000..3623a0b0c --- /dev/null +++ b/.changeset/purple-cooks-destroy.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Boolean: add not combinator diff --git a/src/Boolean.ts b/src/Boolean.ts index 7e9d8b8b1..da22ddc9e 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -27,6 +27,12 @@ export const and = (that: boolean) => (self: boolean): boolean => self && that */ export const or = (that: boolean) => (self: boolean): boolean => self || that +/** + * @category combinators + * @since 1.0.0 + */ +export const not = (self: boolean): boolean => !self + /** * Defines the match over a boolean value. * Takes two thunks `onTrue`, `onFalse` and a `boolean` value. diff --git a/test/Boolean.ts b/test/Boolean.ts index d9d7a3aae..ad34f13d9 100644 --- a/test/Boolean.ts +++ b/test/Boolean.ts @@ -31,6 +31,11 @@ describe.concurrent("Boolean", () => { deepStrictEqual(pipe(false, Boolean.or(false)), false) }) + it("not", () => { + deepStrictEqual(pipe(true, Boolean.not), false) + deepStrictEqual(pipe(false, Boolean.not), true) + }) + describe.concurrent("MonoidAll", () => { it("baseline", () => { deepStrictEqual(Boolean.MonoidAll.combineMany([true, true])(true), true) From 0b3b3df2c03427e258c46d93cba36c8d78da3b51 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:35:03 +0100 Subject: [PATCH 26/80] Chainable: remove readonly from bind return type --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 4 ++-- src/These.ts | 6 +++--- src/typeclass/Chainable.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index ebfb94b33..52b890345 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -335,7 +335,7 @@ export const bind: ( f: (a: A) => Either ) => ( self: Either -) => Either = chainable +) => Either = chainable .bind(Chainable) /** diff --git a/src/Identity.ts b/src/Identity.ts index 95b890460..744922601 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -217,7 +217,7 @@ export const bind: ( f: (a: A) => Identity ) => ( self: Identity -) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind( +) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind( Chainable ) diff --git a/src/Option.ts b/src/Option.ts index d279adfb2..1e24144db 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -347,8 +347,8 @@ export const Chainable: chainable.Chainable = { export const bind: ( name: Exclude, f: (a: A) => Option -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - chainable.bind(Chainable) +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable + .bind(Chainable) /** * Returns an effect that effectfully "peeks" at the success of this effect. diff --git a/src/These.ts b/src/These.ts index 88819b8a5..4cb30338c 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1259,7 +1259,7 @@ export const bind: ( f: (a: A) => Validated ) => ( self: Validated -) => Validated = chainable +) => Validated = chainable .bind(Chainable) /** @@ -1271,7 +1271,7 @@ export const bindEither = ( f: (a: A) => Either ): ( self: Validated -) => Validated => +) => Validated => bind(name, (a) => fromEither(f(a))) /** @@ -1283,7 +1283,7 @@ export const bindThese = ( f: (a: A) => These ): ( self: Validated -) => Validated => +) => Validated => bind(name, (a) => fromThese(f(a))) /** diff --git a/src/typeclass/Chainable.ts b/src/typeclass/Chainable.ts index 697a8fa7a..86ce4df76 100644 --- a/src/typeclass/Chainable.ts +++ b/src/typeclass/Chainable.ts @@ -56,7 +56,7 @@ export const bind = (F: Chainable) => R1 & R2, O1 | O2, E1 | E2, - { readonly [K in keyof A | N]: K extends keyof A ? A[K] : B } + { [K in keyof A | N]: K extends keyof A ? A[K] : B } > => F.flatMap(a => pipe( From b4a991d9e1e2e6abce4eb058009c471f8c36b7b0 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:37:32 +0100 Subject: [PATCH 27/80] Compactable: remove readonly from separate --- src/Option.ts | 4 ++-- src/typeclass/Compactable.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Option.ts b/src/Option.ts index 1e24144db..149d63675 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -749,8 +749,8 @@ export const Compactable: compactable.Compactable = { * @category filtering * @since 1.0.0 */ -export const separate: (self: Option>) => readonly [Option, Option] = - compactable.separate({ ...Covariant, ...Compactable }) +export const separate: (self: Option>) => [Option, Option] = compactable + .separate({ ...Covariant, ...Compactable }) /** * @category filtering diff --git a/src/typeclass/Compactable.ts b/src/typeclass/Compactable.ts index ef0c79de7..80de788d4 100644 --- a/src/typeclass/Compactable.ts +++ b/src/typeclass/Compactable.ts @@ -38,7 +38,7 @@ export const separate = ( ) => ( self: Kind> - ): readonly [Kind, Kind] => { + ): [Kind, Kind] => { return [ pipe(self, F.map(either.getLeft), F.compact), pipe(self, F.map(either.getRight), F.compact) From a1826aeec4c6594de323533e18ffb5eec89ec0b1 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:41:58 +0100 Subject: [PATCH 28/80] Filterable: remove readonly from partitionMap --- src/typeclass/Filterable.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typeclass/Filterable.ts b/src/typeclass/Filterable.ts index 03c5befeb..e749c2298 100644 --- a/src/typeclass/Filterable.ts +++ b/src/typeclass/Filterable.ts @@ -61,7 +61,7 @@ export const partitionMap = (F: Filterable) => (f: (a: A) => Either) => ( self: Kind - ): readonly [Kind, Kind] => { + ): [Kind, Kind] => { return [ pipe(self, F.filterMap((a) => either.getLeft(f(a)))), pipe(self, F.filterMap((a) => either.getRight(f(a)))) From 2e704c5222e9d01c17d548686c0079d95208a127 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:44:59 +0100 Subject: [PATCH 29/80] Filterable: remove readonly from partition --- src/ReadonlyArray.ts | 4 ++-- src/typeclass/Filterable.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 8b7fa5a0b..6e6b3a496 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1542,10 +1542,10 @@ export const filterWithIndex: { export const partition: { (refinement: Refinement): ( self: ReadonlyArray - ) => readonly [ReadonlyArray, ReadonlyArray] + ) => [ReadonlyArray, ReadonlyArray] ( predicate: Predicate - ): (self: ReadonlyArray) => readonly [ReadonlyArray, ReadonlyArray] + ): (self: ReadonlyArray) => [ReadonlyArray, ReadonlyArray] } = filterable.partition(Filterable) /** diff --git a/src/typeclass/Filterable.ts b/src/typeclass/Filterable.ts index e749c2298..61f209229 100644 --- a/src/typeclass/Filterable.ts +++ b/src/typeclass/Filterable.ts @@ -76,14 +76,14 @@ export const partition: ( ) => { (refinement: (a: A) => a is B): ( self: Kind - ) => readonly [Kind, Kind] + ) => [Kind, Kind] (predicate: (a: A) => boolean): ( self: Kind - ) => readonly [Kind, Kind] + ) => [Kind, Kind] } = (Filterable: Filterable) => ( predicate: (a: A) => boolean ): (( self: Kind - ) => readonly [Kind, Kind]) => + ) => [Kind, Kind]) => partitionMap(Filterable)((b) => (predicate(b) ? either.right(b) : either.left(b))) From debc39d6d0cdb16cc55d1bea393c75ccde339018 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:50:37 +0100 Subject: [PATCH 30/80] TraversableFilterable: remove readonly from traversePartitionMap, traversePartition --- src/ReadonlyArray.ts | 4 ++-- src/typeclass/TraversableFilterable.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 6e6b3a496..4006e7351 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1958,7 +1958,7 @@ export const traversePartitionMap: ( F: applicative.Applicative ) => ( f: (a: A) => Kind> -) => (self: ReadonlyArray) => Kind, ReadonlyArray]> = +) => (self: ReadonlyArray) => Kind, ReadonlyArray]> = traversableFilterable .traversePartitionMap({ ...Traversable, ...Covariant, ...Compactable }) @@ -1994,7 +1994,7 @@ export const traversePartition: ( predicate: (a: A) => Kind ) => ( self: ReadonlyArray -) => Kind, ReadonlyArray]> = traversableFilterable +) => Kind, ReadonlyArray]> = traversableFilterable .traversePartition(TraversableFilterable) /** diff --git a/src/typeclass/TraversableFilterable.ts b/src/typeclass/TraversableFilterable.ts index 2afc083c7..9c2b4198a 100644 --- a/src/typeclass/TraversableFilterable.ts +++ b/src/typeclass/TraversableFilterable.ts @@ -26,7 +26,7 @@ export interface TraversableFilterable extends TypeClass Kind> ) => ( self: Kind - ) => Kind, Kind]> + ) => Kind, Kind]> readonly traverseFilterMap: ( F: Applicative @@ -97,7 +97,7 @@ export const traversePartition = ( predicate: (a: A) => Kind ) => ( self: Kind - ) => Kind, Kind]>) => + ) => Kind, Kind]>) => (predicate) => T.traversePartitionMap(F)((b) => pipe( From 5892aae83419ea59d1a62899907fd873fae0fbf3 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 08:56:19 +0100 Subject: [PATCH 31/80] Covariant: remove readonly from let --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 4 ++-- src/These.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 52b890345..64c78fc85 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -208,7 +208,7 @@ const let_: ( f: (a: A) => B ) => ( self: Either -) => Either = covariant.let( +) => Either = covariant.let( Covariant ) diff --git a/src/Identity.ts b/src/Identity.ts index 744922601..4a0fa2ef8 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -94,7 +94,7 @@ const let_: ( f: (a: A) => B ) => ( self: Identity -) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let( +) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let( Covariant ) diff --git a/src/Option.ts b/src/Option.ts index 149d63675..d0fc017bf 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -227,8 +227,8 @@ export const Covariant: covariant.Covariant = { const let_: ( name: Exclude, f: (a: A) => B -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - covariant.let(Covariant) +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant + .let(Covariant) export { /** diff --git a/src/These.ts b/src/These.ts index 4cb30338c..87ca2e258 100644 --- a/src/These.ts +++ b/src/These.ts @@ -687,7 +687,7 @@ const let_: ( f: (a: A) => B ) => ( self: These -) => These = covariant.let( +) => These = covariant.let( Covariant ) From 6b075996eb6faf3066d4f3bfff8c523dc4aa6d23 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 14:17:17 +0100 Subject: [PATCH 32/80] bindTo: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 2 +- src/ReadonlyArray.ts | 13 ++++++++++++- src/These.ts | 2 +- test/typeclass/TraversableFilterable.ts | 12 ++++++------ 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 64c78fc85..268d3d2b9 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -163,7 +163,7 @@ export const tupled: (self: Either) => Either = inv */ export const bindTo: ( name: N -) => (self: Either) => Either = invariant.bindTo(Invariant) +) => (self: Either) => Either = invariant.bindTo(Invariant) /** * @category instances diff --git a/src/Identity.ts b/src/Identity.ts index 4a0fa2ef8..80bc10f1f 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -78,7 +78,7 @@ export const tupled: (self: Identity) => Identity = invarian */ export const bindTo: ( name: N -) => (self: Identity) => Identity<{ readonly [K in N]: A }> = invariant.bindTo(Invariant) +) => (self: Identity) => Identity<{ [K in N]: A }> = invariant.bindTo(Invariant) /** * @category instances diff --git a/src/Option.ts b/src/Option.ts index d0fc017bf..dce1aea2e 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -213,7 +213,7 @@ export const tupled: (self: Option) => Option = invariant.tu */ export const bindTo: ( name: N -) => (self: Option) => Option<{ readonly [K in N]: A }> = invariant.bindTo(Invariant) +) => (self: Option) => Option<{ [K in N]: A }> = invariant.bindTo(Invariant) /** * @category instances diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 4006e7351..96ad210fb 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -22,7 +22,7 @@ import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" import * as filterable from "@fp-ts/core/typeclass/Filterable" import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" -import type * as invariant from "@fp-ts/core/typeclass/Invariant" +import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monad from "@fp-ts/core/typeclass/Monad" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" @@ -1299,6 +1299,17 @@ export const Invariant: invariant.Invariant = { imap } +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: ( + name: N +) => (self: ReadonlyArray) => ReadonlyArray<{ [K in N]: A }> = invariant + .bindTo( + Invariant + ) + /** * @category instances * @since 1.0.0 diff --git a/src/These.ts b/src/These.ts index 87ca2e258..70e1dbed7 100644 --- a/src/These.ts +++ b/src/These.ts @@ -653,7 +653,7 @@ export const tupled: (self: These) => These = invar */ export const bindTo: ( name: N -) => (self: These) => These = invariant.bindTo(Invariant) +) => (self: These) => These = invariant.bindTo(Invariant) /** * @category mapping diff --git a/test/typeclass/TraversableFilterable.ts b/test/typeclass/TraversableFilterable.ts index 4b207fe9c..649bea03d 100644 --- a/test/typeclass/TraversableFilterable.ts +++ b/test/typeclass/TraversableFilterable.ts @@ -26,11 +26,11 @@ describe("TraversableFilterable", () => { const f = traversePartition((s: string) => s.length > 2 ? O.some(false) : s.length > 1 ? O.some(true) : O.none() ) - U.deepStrictEqual(f([]), O.some([[], []] as const)) - U.deepStrictEqual(f(["a"]), O.none()) - U.deepStrictEqual(f(["a", "aa"]), O.none()) - U.deepStrictEqual(f(["aa"]), O.some([[], ["aa"]] as const)) - U.deepStrictEqual(f(["aaa"]), O.some([["aaa"], []] as const)) - U.deepStrictEqual(f(["aaa", "aa"]), O.some([["aaa"], ["aa"]] as const)) + expect(f([])).toEqual(O.some([[], []])) + expect(f(["a"])).toEqual(O.none()) + expect(f(["a", "aa"])).toEqual(O.none()) + expect(f(["aa"])).toEqual(O.some([[], ["aa"]])) + expect(f(["aaa"])).toEqual(O.some([["aaa"], []])) + expect(f(["aaa", "aa"])).toEqual(O.some([["aaa"], ["aa"]])) }) }) From bd1fbcbd06bf22878329e7ae0bdee93237028c73 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 14:31:41 +0100 Subject: [PATCH 33/80] tupled: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 2 +- src/Predicate.ts | 1 + src/ReadonlyArray.ts | 15 +++++++++++---- src/These.ts | 2 +- src/typeclass/Invariant.ts | 4 ++-- test/typeclass/Invariant.ts | 2 +- 8 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 268d3d2b9..2e714f857 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -153,7 +153,7 @@ export const Invariant: invariant.Invariant = { * @category mapping * @since 1.0.0 */ -export const tupled: (self: Either) => Either = invariant.tupled( +export const tupled: (self: Either) => Either = invariant.tupled( Invariant ) diff --git a/src/Identity.ts b/src/Identity.ts index 80bc10f1f..86fd42078 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -70,7 +70,7 @@ export const Invariant: invariant.Invariant = { /** * @since 1.0.0 */ -export const tupled: (self: Identity) => Identity = invariant.tupled(Invariant) +export const tupled: (self: Identity) => Identity<[A]> = invariant.tupled(Invariant) /** * @category do notation diff --git a/src/Option.ts b/src/Option.ts index dce1aea2e..98c886318 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -205,7 +205,7 @@ export const Invariant: invariant.Invariant = { /** * @since 1.0.0 */ -export const tupled: (self: Option) => Option = invariant.tupled(Invariant) +export const tupled: (self: Option) => Option<[A]> = invariant.tupled(Invariant) /** * @category do notation diff --git a/src/Predicate.ts b/src/Predicate.ts index 17d73a020..aac750baf 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -80,6 +80,7 @@ export const Invariant: invariant.Invariant = { /** * @since 1.0.0 */ +// @ts-expect-error export const tupled: (self: Predicate) => Predicate = invariant.tupled( Invariant ) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 96ad210fb..1c24855c1 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1299,16 +1299,23 @@ export const Invariant: invariant.Invariant = { imap } +/** + * @category mapping + * @since 1.0.0 + */ +// @ts-expect-error +export const tupled: (self: ReadonlyArray) => Array<[A]> = invariant + .tupled(Invariant) + /** * @category do notation * @since 1.0.0 */ +// @ts-expect-error export const bindTo: ( name: N -) => (self: ReadonlyArray) => ReadonlyArray<{ [K in N]: A }> = invariant - .bindTo( - Invariant - ) +) => (self: ReadonlyArray) => Array<{ [K in N]: A }> = invariant + .bindTo(Invariant) /** * @category instances diff --git a/src/These.ts b/src/These.ts index 70e1dbed7..67b7e52ed 100644 --- a/src/These.ts +++ b/src/These.ts @@ -643,7 +643,7 @@ export const Invariant: invariant.Invariant = { * @category mapping * @since 1.0.0 */ -export const tupled: (self: These) => These = invariant.tupled( +export const tupled: (self: These) => These = invariant.tupled( Invariant ) diff --git a/src/typeclass/Invariant.ts b/src/typeclass/Invariant.ts index c0c41dfba..2b7c19265 100644 --- a/src/typeclass/Invariant.ts +++ b/src/typeclass/Invariant.ts @@ -46,5 +46,5 @@ export const bindTo = (F: Invariant) => */ export const tupled = ( F: Invariant -): ((self: Kind) => Kind) => - F.imap(a => [a] as const, ([a]) => a) +): ((self: Kind) => Kind) => + F.imap(a => [a], ([a]) => a) diff --git a/test/typeclass/Invariant.ts b/test/typeclass/Invariant.ts index c12bfc3b5..e10460ea9 100644 --- a/test/typeclass/Invariant.ts +++ b/test/typeclass/Invariant.ts @@ -38,7 +38,7 @@ describe("Invariant", () => { it("Covariant (Option)", () => { const tupled = _.tupled(O.Invariant) U.deepStrictEqual(pipe(O.none(), tupled), O.none()) - U.deepStrictEqual(pipe(O.some(1), tupled), O.some([1] as const)) + U.deepStrictEqual(pipe(O.some(1), tupled), O.some([1])) }) it("Contravariant (Predicate)", () => { From 1f41108df582e0363b4f44d250699c3eb73f6100 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 15:06:05 +0100 Subject: [PATCH 34/80] Product, SemiProduct: remove readonly from typeclass members --- src/Either.ts | 6 ++-- src/Identity.ts | 6 ++-- src/Option.ts | 6 ++-- src/ReadonlyArray.ts | 21 +++++++----- src/These.ts | 6 ++-- src/typeclass/Monoid.ts | 2 +- src/typeclass/Product.ts | 2 +- src/typeclass/SemiProduct.ts | 12 +++---- src/typeclass/Semigroup.ts | 5 ++- test/Either.ts | 11 +++--- test/Option.ts | 11 +++--- test/These.ts | 43 +++++++++--------------- test/typeclass/NonEmptyTraversable.ts | 2 ++ test/typeclass/SemiProduct.ts | 46 ++++++++++++++------------ test/typeclass/Semigroup.ts | 5 ++- test/typeclass/TraversableWithIndex.ts | 1 + 16 files changed, 94 insertions(+), 91 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 2e714f857..0ab20f611 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -365,7 +365,7 @@ export const Monad: monad.Monad = { export const product = ( that: Either ) => - (self: Either): Either => + (self: Either): Either => isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self /** @@ -375,7 +375,7 @@ export const product = ( export const productMany = ( collection: Iterable> ) => - (self: Either): Either]> => { + (self: Either): Either]> => { if (isLeft(self)) { return self } @@ -428,7 +428,7 @@ export const productFlatten: ( */ export const productAll = ( collection: Iterable> -): Either> => { +): Either> => { const out: Array = [] for (const e of collection) { if (isLeft(e)) { diff --git a/src/Identity.ts b/src/Identity.ts index 86fd42078..8369aaf8b 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -256,13 +256,13 @@ export const Monad: monad.Monad = { */ export const product = ( that: Identity -) => (self: Identity): Identity => [self, that] +) => (self: Identity): Identity<[A, B]> => [self, that] /** * @since 1.0.0 */ export const productMany = (collection: Iterable>) => - (self: Identity): Identity]> => [self, ...collection] + (self: Identity): Identity<[A, ...Array]> => [self, ...collection] /** * @category instances @@ -300,7 +300,7 @@ export const productFlatten: ( /** * @since 1.0.0 */ -export const productAll = (collection: Iterable>): Identity> => +export const productAll = (collection: Iterable>): Identity> => readonlyArray.fromIterable(collection) /** diff --git a/src/Option.ts b/src/Option.ts index 98c886318..f0bd3a20e 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -412,14 +412,14 @@ export const Monad: monad.Monad = { export const product = ( that: Option ) => - (self: Option): Option => + (self: Option): Option<[A, B]> => isSome(self) && isSome(that) ? some([self.value, that.value]) : option.none /** * @since 1.0.0 */ export const productMany = (collection: Iterable>) => - (self: Option): Option]> => { + (self: Option): Option<[A, ...Array]> => { if (isNone(self)) { return option.none } @@ -466,7 +466,7 @@ export const productFlatten: ( /** * @since 1.0.0 */ -export const productAll = (collection: Iterable>): Option> => { +export const productAll = (collection: Iterable>): Option> => { const out: Array = [] for (const o of collection) { if (isNone(o)) { diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 1c24855c1..110b17af1 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1616,7 +1616,7 @@ export const partitionMapWithIndex = (f: (a: A, i: number) => Either(F: applicative.Applicative) => ( f: (a: A) => Kind - ): ((self: ReadonlyArray) => Kind>) => traverseWithIndex(F)(f) + ): ((self: ReadonlyArray) => Kind>) => traverseWithIndex(F)(f) /** * @category traversing @@ -1624,7 +1624,7 @@ export const traverse = (F: applicative.Applicative) => */ export const traverseWithIndex = (F: applicative.Applicative) => (f: (a: A, i: number) => Kind) => - (self: ReadonlyArray): Kind> => F.productAll(self.map(f)) + (self: ReadonlyArray): Kind> => F.productAll(self.map(f)) /** * @category traversing @@ -1635,7 +1635,7 @@ export const traverseNonEmpty = ( ) => ( f: (a: A) => Kind - ): ((self: NonEmptyReadonlyArray) => Kind>) => + ): ((self: NonEmptyReadonlyArray) => Kind>) => traverseNonEmptyWithIndex(F)(f) /** @@ -1646,7 +1646,7 @@ export const traverseNonEmptyWithIndex = ( F: semiApplicative.SemiApplicative ) => (f: (a: A, i: number) => Kind) => - (self: NonEmptyReadonlyArray): Kind> => { + (self: NonEmptyReadonlyArray): Kind> => { const fbs = pipe(self, mapNonEmptyWithIndex(f)) return pipe(headNonEmpty(fbs), F.productMany(tailNonEmpty(fbs))) } @@ -1655,18 +1655,23 @@ export const traverseNonEmptyWithIndex = ( * @category traversing * @since 1.0.0 */ +// @ts-expect-error export const sequence: ( F: applicative.Applicative ) => ( self: ReadonlyArray> -) => Kind> = traversable.sequence(traverse) +) => Kind> = + // @ts-expect-error + traversable.sequence(traverse) /** * @category instances * @since 1.0.0 */ export const Traversable: traversable.Traversable = { + // @ts-expect-error traverse, + // @ts-expect-error sequence } @@ -1691,7 +1696,7 @@ export const sequenceNonEmpty = ( F: semiApplicative.SemiApplicative ): (( self: NonEmptyReadonlyArray> -) => Kind>) => traverseNonEmpty(F)(identity) +) => Kind>) => traverseNonEmpty(F)(identity) /** * @since 1.0.0 @@ -1717,7 +1722,7 @@ export const product = ( */ export const productMany: ( collection: Iterable> -) => (self: ReadonlyArray) => ReadonlyArray> = semiProduct +) => (self: ReadonlyArray) => ReadonlyArray<[A, ...Array]> = semiProduct .productMany( Covariant, product @@ -1728,7 +1733,7 @@ export const productMany: ( */ export const productAll = ( collection: Iterable> -): ReadonlyArray> => { +): ReadonlyArray> => { const arrays = Array.from(collection) if (isEmpty(arrays)) { return empty() diff --git a/src/These.ts b/src/These.ts index 67b7e52ed..2d8de3069 100644 --- a/src/These.ts +++ b/src/These.ts @@ -961,7 +961,7 @@ export const filterMap = ( export const product = (that: Validated) => ( self: Validated - ): Validated => { + ): Validated => { if (isLeft(self)) { return self } @@ -991,7 +991,7 @@ export const productMany = ( ) => ( self: Validated - ): Validated]> => + ): Validated]> => pipe(self, product(productAll(collection)), map(([a, as]) => [a, ...as])) /** @@ -1062,7 +1062,7 @@ export const getFirstLeftSemigroup: ( */ export const productAll = ( collection: Iterable> -): Validated> => { +): Validated> => { const rights: Array = [] const lefts: Array = [] let isFatal = false diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index 160d0a8a9..a5ff1ecd1 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -79,5 +79,5 @@ export const tuple = >( ...monoids: { [K in keyof A]: Monoid } ): Monoid> => { const empty: A = monoids.map((m) => m.empty) as any - return fromSemigroup(semigroup.tuple(...monoids), empty) + return fromSemigroup(semigroup.tuple>(...monoids), empty) } diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index e6129ea94..9e849f17f 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -13,7 +13,7 @@ import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" export interface Product extends SemiProduct, Of { readonly productAll: ( collection: Iterable> - ) => Kind> + ) => Kind> } /** diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 833aa743c..2234c9b4e 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -16,11 +16,11 @@ export interface SemiProduct extends Invariant { that: Kind ) => ( self: Kind - ) => Kind + ) => Kind readonly productMany: ( collection: Iterable> - ) => (self: Kind) => Kind]> + ) => (self: Kind) => Kind]> } /** @@ -42,7 +42,7 @@ export const productComposition = ( FR1 & FR2, FO1 | FO2, FE1 | FE2, - Kind + Kind > => pipe(self, F.product(that), F.map(([ga, gb]) => pipe(ga, G.product(gb)))) /** @@ -59,7 +59,7 @@ export const productManyComposition = ( self: Kind> - ): Kind]>> => + ): Kind]>> => pipe( self, F.productMany(collection), @@ -82,13 +82,13 @@ export const productMany = ( (self: Kind) => { let out = pipe( self, - Covariant.map((a): readonly [A, ...Array] => [a]) + Covariant.map((a): [A, ...Array] => [a]) ) for (const fa of collection) { out = pipe( out, product(fa), - Covariant.map(([[head, ...tail], a]): readonly [A, ...Array] => [head, ...tail, a]) + Covariant.map(([[head, ...tail], a]): [A, ...Array] => [head, ...tail, a]) ) } return out diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index b49e76b28..35b442adf 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -205,7 +205,9 @@ export const Invariant: invariant.Invariant = { */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, + // @ts-expect-error product: that => self => tuple(self, that), + // @ts-expect-error productMany: collection => self => tuple(self, ...collection) } @@ -216,5 +218,6 @@ export const SemiProduct: semiProduct.SemiProduct = { export const Product: product.Product = { ...SemiProduct, of: constant, - productAll: (collection: Iterable>) => tuple>(...collection) + // @ts-expect-error + productAll: (collection: Iterable>) => tuple(...collection) } diff --git a/test/Either.ts b/test/Either.ts index 83e429746..8b53175a5 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -381,17 +381,17 @@ describe.concurrent("Either", () => { }) it("product", () => { - deepStrictEqual(pipe(_.right(1), _.product(_.right("a"))), _.right([1, "a"] as const)) + deepStrictEqual(pipe(_.right(1), _.product(_.right("a"))), _.right([1, "a"])) deepStrictEqual(pipe(_.right(1), _.product(_.left("e2"))), _.left("e2")) deepStrictEqual(pipe(_.left("e1"), _.product(_.right("a"))), _.left("e1")) deepStrictEqual(pipe(_.left("e1"), _.product(_.left("2"))), _.left("e1")) }) it("productMany", () => { - deepStrictEqual(pipe(_.right(1), _.productMany([])), _.right([1] as const)) + deepStrictEqual(pipe(_.right(1), _.productMany([])), _.right([1])) deepStrictEqual( pipe(_.right(1), _.productMany([_.right(2), _.right(3)])), - _.right([1, 2, 3] as const) + _.right([1, 2, 3]) ) deepStrictEqual( pipe(_.right(1), _.productMany([_.left("e"), _.right(3)])), @@ -445,9 +445,8 @@ describe.concurrent("Either", () => { }) it("productFlatten", () => { - deepStrictEqual( - pipe(_.right(1), _.tupled, _.productFlatten(_.right("b"))), - _.right([1, "b"] as const) + expect(pipe(_.right(1), _.tupled, _.productFlatten(_.right("b")))).toEqual( + _.right([1, "b"]) ) }) diff --git a/test/Option.ts b/test/Option.ts index f0c9f641a..bb6d2283e 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -216,20 +216,20 @@ describe.concurrent("Option", () => { deepStrictEqual(pipe(_.none(), _.product(_.some("a"))), _.none()) deepStrictEqual( pipe(_.some(1), _.product(_.some("a"))), - _.some([1, "a"] as const) + _.some([1, "a"]) ) }) it("productMany", () => { deepStrictEqual(pipe(_.none(), _.SemiProduct.productMany([])), _.none()) - deepStrictEqual(pipe(_.some(1), _.SemiProduct.productMany([])), _.some([1] as const)) + deepStrictEqual(pipe(_.some(1), _.SemiProduct.productMany([])), _.some([1])) deepStrictEqual( pipe(_.some(1), _.SemiProduct.productMany([_.none() as _.Option])), _.none() ) deepStrictEqual( pipe(_.some(1), _.SemiProduct.productMany([_.some(2)])), - _.some([1, 2] as const) + _.some([1, 2]) ) }) @@ -503,9 +503,8 @@ describe.concurrent("Option", () => { }) it("productFlatten", () => { - deepStrictEqual( - pipe(_.some(1), _.tupled, _.productFlatten(_.some("b"))), - _.some([1, "b"] as const) + expect(pipe(_.some(1), _.tupled, _.productFlatten(_.some("b")))).toEqual( + _.some([1, "b"]) ) }) diff --git a/test/These.ts b/test/These.ts index 5dcd2eddb..79e276c98 100644 --- a/test/These.ts +++ b/test/These.ts @@ -274,15 +274,15 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - U.deepStrictEqual(pipe(_.right(1), _.product(_.right(2))), _.right([1, 2] as const)) + expect(pipe(_.right(1), _.product(_.right(2)))).toEqual(_.right([1, 2])) U.deepStrictEqual(pipe(_.right(1), _.product(_.left(b))), _.left(b)) - U.deepStrictEqual(pipe(_.right(1), _.product(_.both(b, 2))), _.both(b, [1, 2] as const)) + expect(pipe(_.right(1), _.product(_.both(b, 2)))).toEqual(_.both(b, [1, 2])) U.deepStrictEqual(pipe(_.left(a), _.product(_.right(2))), _.left(a)) U.deepStrictEqual(pipe(_.left(a), _.product(_.left(b))), _.left(a)) U.deepStrictEqual(pipe(_.left(a), _.product(_.both(b, 2))), _.left(a)) - U.deepStrictEqual(pipe(_.both(a, 1), _.product(_.right(2))), _.both(a, [1, 2] as const)) + expect(pipe(_.both(a, 1), _.product(_.right(2)))).toEqual(_.both(a, [1, 2])) expect(pipe(_.both(a, 1), _.product(_.left(b)))).toEqual(_.left(ab)) expect(pipe(_.both(a, 1), _.product(_.both(b, 2)))).toEqual(_.both(ab, [1, 2])) }) @@ -292,14 +292,15 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - U.deepStrictEqual(pipe(_.right(1), _.productMany([_.right(2)])), _.right([1, 2] as const)) + expect(pipe(_.right(1), _.productMany([_.right(2)]))).toEqual(_.right([1, 2])) U.deepStrictEqual( pipe(_.right(1), _.productMany([_.left(b)])), _.left(b) ) - U.deepStrictEqual( - pipe(_.right(1), _.productMany([_.both(b, 2)])), - _.both(b, [1, 2] as const) + expect( + pipe(_.right(1), _.productMany([_.both(b, 2)])) + ).toEqual( + _.both(b, [1, 2]) ) U.deepStrictEqual(pipe(_.left(a), _.productMany([_.right(2)])), _.left(a)) @@ -309,7 +310,7 @@ describe("These", () => { _.left(a) ) - U.deepStrictEqual(pipe(_.both(a, 1), _.productMany([_.right(2)])), _.both(a, [1, 2] as const)) + expect(pipe(_.both(a, 1), _.productMany([_.right(2)]))).toEqual(_.both(a, [1, 2])) expect(pipe(_.both(a, 1), _.productMany([_.left(b)]))).toEqual(_.left(ab)) expect(pipe(_.both(a, 1), _.productMany([_.both(b, 2)]))).toEqual( _.both(ab, [1, 2]) @@ -321,9 +322,9 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - U.deepStrictEqual(_.productAll([_.right(1), _.right(2)]), _.right([1, 2] as const)) + U.deepStrictEqual(_.productAll([_.right(1), _.right(2)]), _.right([1, 2])) U.deepStrictEqual(_.productAll([_.right(1), _.left(b)]), _.left(b)) - U.deepStrictEqual(_.productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2] as const)) + U.deepStrictEqual(_.productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2])) U.deepStrictEqual(_.productAll([_.left(a), _.right(2)]), _.left(a)) U.deepStrictEqual(_.productAll([_.left(a), _.left(b)]), _.left(a)) @@ -351,20 +352,8 @@ describe("These", () => { pipe(_.warn("e1", 2), _.flatMap(f)), _.warn("e1", 4) ) - U.deepStrictEqual( - pipe( - _.warn("e1", 1), - _.flatMap(f) - ), - _.left(["e1", "e3"] as const) - ) - U.deepStrictEqual( - pipe( - _.warn("e1", 6), - _.flatMap(f) - ), - _.both(["e1", "e2"] as const, 6) - ) + expect(pipe(_.warn("e1", 1), _.flatMap(f))).toEqual(_.left(["e1", "e3"])) + expect(pipe(_.warn("e1", 6), _.flatMap(f))).toEqual(_.both(["e1", "e2"], 6)) }) it("flatMapNullable", () => { @@ -435,7 +424,7 @@ describe("These", () => { it("getBoth", () => { U.deepStrictEqual(pipe(_.left("e"), _.getBoth), O.none()) U.deepStrictEqual(pipe(_.right(1), _.getBoth), O.none()) - U.deepStrictEqual(pipe(_.both("e", 1), _.getBoth), O.some(["e", 1] as const)) + expect(pipe(_.both("e", 1), _.getBoth)).toEqual(O.some(["e", 1])) }) it("getLeft", () => { @@ -627,7 +616,7 @@ describe("These", () => { it("fromEither", () => { U.deepStrictEqual(_.fromEither(E.right(1)), _.right(1)) - U.deepStrictEqual(_.fromEither(E.left("e")), _.left(["e"] as const)) + expect(_.fromEither(E.left("e"))).toEqual(_.left(["e"])) }) it("fromThese", () => { @@ -671,7 +660,7 @@ describe("These", () => { }) it("fromTuple", () => { - U.deepStrictEqual(pipe(["e", 1] as const, _.fromTuple), _.both("e", 1)) + expect(pipe(["e", 1] as [string, number], _.fromTuple)).toEqual(_.both("e", 1)) }) it("reverse", () => { diff --git a/test/typeclass/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index 666f074c6..73ddb23d9 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -5,7 +5,9 @@ import * as _ from "@fp-ts/core/test/limbo/NonEmptyTraversable" import * as U from "../util" const NonEmptyTraversable: _.NonEmptyTraversable = { + // @ts-expect-error traverseNonEmpty: RA.traverseNonEmpty, + // @ts-expect-error sequenceNonEmpty: F => self => pipe(self, RA.traverseNonEmpty(F)(identity)) } diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index aacea2833..29566f2e2 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -35,7 +35,7 @@ describe("SemiProduct", () => { const productManyFromAp = (collection: Iterable>) => ( self: Kind - ): Kind]> => { + ): Kind]> => { const args = [self, ...Array.from(collection)] const len = args.length const f = getCurriedTupleConstructor(len) @@ -52,8 +52,8 @@ describe("SemiProduct", () => { } const product = (that: ReadonlyArray) => - (self: ReadonlyArray): ReadonlyArray => { - const out: Array = [] + (self: ReadonlyArray): ReadonlyArray<[A, B]> => { + const out: Array<[A, B]> = [] for (const a of self) { for (const b of that) { out.push([a, b]) @@ -85,7 +85,7 @@ describe("SemiProduct", () => { U.deepStrictEqual(pipe([], product([O.none()])), []) U.deepStrictEqual(pipe([O.none()], product([])), []) U.deepStrictEqual(pipe([O.none()], product([O.none()])), [O.none()]) - U.deepStrictEqual(pipe([O.some(1)], product([O.some(2)])), [O.some([1, 2] as const)]) + expect(pipe([O.some(1)], product([O.some(2)]))).toEqual([O.some([1, 2])]) }) it("Option", () => { @@ -97,7 +97,7 @@ describe("SemiProduct", () => { U.deepStrictEqual(pipe(O.some(O.none()), product(O.some(O.some(2)))), O.some(O.none())) U.deepStrictEqual( pipe(O.some(O.some(1)), product(O.some(O.some(2)))), - O.some(O.some([1, 2] as const)) + O.some(O.some([1, 2])) ) }) }) @@ -105,23 +105,24 @@ describe("SemiProduct", () => { describe("productManyComposition", () => { it("ReadonlyArray", () => { const productMany = _.productManyComposition(RA.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe([O.some(1), O.none()], productMany([])), [ - O.some([1] as const), + expect(pipe([O.some(1), O.none()], productMany([]))).toEqual([ + O.some([1]), O.none() ]) - U.deepStrictEqual(pipe([O.some(1), O.none()], productMany([[O.some(2), O.none()]])), [ - O.some([1, 2] as const), + expect(pipe([O.some(1), O.none()], productMany([[O.some(2), O.none()]]))).toEqual([ + O.some([1, 2]), O.none(), O.none(), O.none() ]) - U.deepStrictEqual( - pipe([O.some(1), O.some(2)], productMany([[O.some(3), O.some(4)], [O.some(5)]])), + expect( + pipe([O.some(1), O.some(2)], productMany([[O.some(3), O.some(4)], [O.some(5)]])) + ).toEqual( [ - 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) + O.some([1, 3, 5]), + O.some([1, 4, 5]), + O.some([2, 3, 5]), + O.some([2, 4, 5]) ] ) }) @@ -130,7 +131,7 @@ describe("SemiProduct", () => { const productMany = _.productManyComposition(O.SemiApplicative, O.SemiProduct) U.deepStrictEqual(pipe(O.none(), productMany([])), O.none()) U.deepStrictEqual(pipe(O.some(O.none()), productMany([])), O.some(O.none())) - U.deepStrictEqual(pipe(O.some(O.some(1)), productMany([])), O.some(O.some([1] as const))) + U.deepStrictEqual(pipe(O.some(O.some(1)), productMany([])), O.some(O.some([1]))) U.deepStrictEqual(pipe(O.none(), productMany([O.none()])), O.none()) U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.none()])), O.none()) U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.some(O.none())])), O.some(O.none())) @@ -140,7 +141,7 @@ describe("SemiProduct", () => { ) U.deepStrictEqual( pipe(O.some(O.some(1)), productMany([O.some(O.some(2))])), - O.some(O.some([1, 2] as const)) + O.some(O.some([1, 2])) ) }) }) @@ -167,7 +168,7 @@ describe("SemiProduct", () => { it("Covariant (Option)", () => { const productFlatten = _.productFlatten(O.SemiProduct) U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.some(3))), O.some([1, 2, 3] as const)) + expect(pipe(O.some([1, 2]), productFlatten(O.some(3)))).toEqual(O.some([1, 2, 3])) }) it("Contravariant (Predicate)", () => { @@ -182,10 +183,11 @@ describe("SemiProduct", () => { describe("nonEmptyTuple", () => { it("Covariant (Option)", () => { const nonEmptyTuple = _.nonEmptyTuple(O.SemiProduct) - U.deepStrictEqual(nonEmptyTuple(O.some("a")), O.some(["a"] as const)) - U.deepStrictEqual( - nonEmptyTuple(O.some("a"), O.some(1), O.some(true)), - O.some(["a", 1, true] as const) + expect(nonEmptyTuple(O.some("a"))).toEqual(O.some(["a"])) + expect( + nonEmptyTuple(O.some("a"), O.some(1), O.some(true)) + ).toEqual( + O.some(["a", 1, true]) ) U.deepStrictEqual(nonEmptyTuple(O.some("a"), O.some(1), O.none()), O.none()) }) diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index b7b3d3999..be9986c23 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -138,7 +138,10 @@ describe("Semigroup", () => { String.Semigroup, _.SemiProduct.product(Number.SemigroupSum), _.SemiProduct.product(Number.SemigroupMultiply), - _.imap(([[a, b], c]) => [a, b, c] as const, ([a, b, c]) => [[a, b], c] as const) + _.imap( + ([[a, b], c]): [string, number, number] => [a, b, c], + ([a, b, c]): [[string, number], number] => [[a, b], c] + ) ) U.deepStrictEqual(pipe(["a", 2, 3], A.combine(["b", 3, 4])), ["ab", 5, 12]) }) diff --git a/test/typeclass/TraversableWithIndex.ts b/test/typeclass/TraversableWithIndex.ts index 0b7e83638..338e6d75a 100644 --- a/test/typeclass/TraversableWithIndex.ts +++ b/test/typeclass/TraversableWithIndex.ts @@ -5,6 +5,7 @@ import * as _ from "@fp-ts/core/test/limbo/TraversableWithIndex" import * as U from "../util" const TraversableWithIndex: _.TraversableWithIndex = { + // @ts-expect-error traverseWithIndex: RA.traverseWithIndex } From 2676b88631340a12179f23333e2d167582b81bd0 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 16:58:10 +0100 Subject: [PATCH 35/80] andThenBind: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 4 ++-- src/These.ts | 6 +++--- src/typeclass/SemiProduct.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 0ab20f611..3ad8d237b 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -410,7 +410,7 @@ export const andThenBind: ( that: Either ) => ( self: Either -) => Either = semiProduct +) => Either = semiProduct .andThenBind(SemiProduct) /** diff --git a/src/Identity.ts b/src/Identity.ts index 8369aaf8b..5a43015b3 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -285,7 +285,7 @@ export const andThenBind: ( that: Identity ) => ( self: Identity -) => Identity<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct +) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct .andThenBind(SemiProduct) /** diff --git a/src/Option.ts b/src/Option.ts index f0bd3a20e..e24a0702c 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -452,8 +452,8 @@ export const SemiProduct: semiProduct.SemiProduct = { export const andThenBind: ( name: Exclude, that: Option -) => (self: Option) => Option<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> = - semiProduct.andThenBind(SemiProduct) +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct + .andThenBind(SemiProduct) /** * @since 1.0.0 diff --git a/src/These.ts b/src/These.ts index 2d8de3069..2d4bd7de1 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1093,7 +1093,7 @@ export const andThenBind: ( that: Validated ) => ( self: Validated -) => Validated = semiProduct +) => Validated = semiProduct .andThenBind(SemiProduct) /** @@ -1105,7 +1105,7 @@ export const andThenBindEither = ( that: Either ): ( self: Validated -) => Validated => +) => Validated => andThenBind(name, fromEither(that)) /** @@ -1117,7 +1117,7 @@ export const andThenBindThese = ( that: These ): ( self: Validated -) => Validated => +) => Validated => andThenBind(name, fromThese(that)) /** diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 2234c9b4e..0faaaefee 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -109,7 +109,7 @@ export const andThenBind = (F: SemiProduct) => R1 & R2, O1 | O2, E1 | E2, - { readonly [K in keyof A | N]: K extends keyof A ? A[K] : B } + { [K in keyof A | N]: K extends keyof A ? A[K] : B } > => pipe( self, From 08120d47cfbf3bb291905770370a3dcbc8391f58 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:07:45 +0100 Subject: [PATCH 36/80] productFlatten: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 5 ++--- src/Option.ts | 2 +- src/Predicate.ts | 8 ++++++++ src/ReadonlyArray.ts | 7 +++---- src/These.ts | 2 +- src/typeclass/SemiProduct.ts | 4 ++-- test/Predicate.ts | 1 + test/ReadonlyArray.ts | 2 ++ 9 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 3ad8d237b..d42e1ec62 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -420,7 +420,7 @@ export const productFlatten: ( that: Either ) => >( self: Either -) => Either = semiProduct +) => Either = semiProduct .productFlatten(SemiProduct) /** diff --git a/src/Identity.ts b/src/Identity.ts index 5a43015b3..62d1b9656 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -293,9 +293,8 @@ export const andThenBind: ( */ export const productFlatten: ( fb: Identity -) => >(self: Identity) => Identity = - semiProduct - .productFlatten(SemiProduct) +) => >(self: Identity) => Identity<[...A, B]> = semiProduct + .productFlatten(SemiProduct) /** * @since 1.0.0 diff --git a/src/Option.ts b/src/Option.ts index e24a0702c..e173c6a93 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -460,7 +460,7 @@ export const andThenBind: ( */ export const productFlatten: ( fb: Option -) => >(self: Option) => Option = semiProduct +) => >(self: Option) => Option<[...A, B]> = semiProduct .productFlatten(SemiProduct) /** diff --git a/src/Predicate.ts b/src/Predicate.ts index aac750baf..8d2934ffe 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -194,6 +194,14 @@ export const andThenBind: ( SemiProduct ) +/** + * @since 1.0.0 + */ +// @ts-expect-error +export const productFlatten: (that: Predicate) => >( + self: Predicate +) => Predicate = semiProduct.productFlatten(SemiProduct) + /** * @since 1.0.0 */ diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 110b17af1..d8e78af31 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2136,11 +2136,10 @@ export const join: (sep: string) => (self: ReadonlyArray) => string = in /** * @since 1.0.0 */ -export const productFlatten: ( - that: ReadonlyArray -) => >( +// @ts-expect-error +export const productFlatten: (that: ReadonlyArray) => >( self: ReadonlyArray -) => ReadonlyArray = semiProduct.productFlatten(SemiProduct) +) => Array<[...A, B]> = semiProduct.productFlatten(SemiProduct) /** * @since 1.0.0 diff --git a/src/These.ts b/src/These.ts index 2d4bd7de1..b05c58f54 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1127,7 +1127,7 @@ export const productFlatten: ( that: Validated ) => >( self: Validated -) => Validated = semiProduct +) => Validated = semiProduct .productFlatten(SemiProduct) /** diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 0faaaefee..0b2d5b79b 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -129,11 +129,11 @@ export const productFlatten = (F: SemiProduct) => ) => >( self: Kind - ): Kind => + ): Kind => pipe( self, F.product(that), - F.imap(([a, b]) => [...a, b] as const, ab => [ab.slice(0, -1), ab[ab.length - 1]] as any) + F.imap(([a, b]) => [...a, b], ab => [ab.slice(0, -1), ab[ab.length - 1]] as any) ) /** diff --git a/test/Predicate.ts b/test/Predicate.ts index 7b272faa4..d727fa653 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -35,6 +35,7 @@ describe.concurrent("Predicate", () => { expect(_.product).exist expect(_.productMany).exist expect(_.andThenBind).exist + expect(_.productFlatten).exist expect(_.Product).exist expect(_.productAll).exist diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 550933a4e..a3e4022d3 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -15,6 +15,8 @@ describe.concurrent("ReadonlyArray", () => { it("instances and derived exports", () => { expect(RA.Invariant).exist expect(RA.imap).exist + expect(RA.tupled).exist + expect(RA.bindTo).exist expect(RA.Covariant).exist expect(RA.map).exist From f0db9b26afc3b9fc8d253a33ab6f19db51008159 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:12:06 +0100 Subject: [PATCH 37/80] nonEmptyStruct: remove readonly --- src/typeclass/SemiProduct.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 0b2d5b79b..5af134638 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -163,7 +163,7 @@ export const nonEmptyStruct = (F: SemiProduct) => ([R[keyof R]] extends [Kind] ? R : never), ([R[keyof R]] extends [Kind] ? O : never), ([R[keyof R]] extends [Kind] ? E : never), - { readonly [K in keyof R]: [R[K]] extends [Kind] ? A : never } + { [K in keyof R]: [R[K]] extends [Kind] ? A : never } > => { const keys = Object.keys(fields) return pipe( From 48295e9fe413cba24a5cdb5c2e7fef5bdc73dccc Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:15:32 +0100 Subject: [PATCH 38/80] struct: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 5 ++--- src/Option.ts | 2 +- src/These.ts | 2 +- src/typeclass/Product.ts | 2 +- src/typeclass/Semigroup.ts | 4 +--- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index d42e1ec62..0ae1c58e9 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -467,7 +467,7 @@ export const struct: >>( r: R ) => Either< [R[keyof R]] extends [Either] ? E : never, - { readonly [K in keyof R]: [R[K]] extends [Either] ? A : never } + { [K in keyof R]: [R[K]] extends [Either] ? A : never } > = product_ .struct(Product) diff --git a/src/Identity.ts b/src/Identity.ts index 62d1b9656..dfe99be9c 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -326,9 +326,8 @@ export const tuple: >>( */ export const struct: >>( r: R -) => Identity<{ readonly [K in keyof R]: [R[K]] extends [Identity] ? A : never }> = - product_ - .struct(Product) +) => Identity<{ [K in keyof R]: [R[K]] extends [Identity] ? A : never }> = product_ + .struct(Product) /** * @category instances diff --git a/src/Option.ts b/src/Option.ts index e173c6a93..b48a4491b 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -500,7 +500,7 @@ export const tuple: >>( */ export const struct: >>( r: R -) => Option<{ readonly [K in keyof R]: [R[K]] extends [Option] ? A : never }> = product_ +) => Option<{ [K in keyof R]: [R[K]] extends [Option] ? A : never }> = product_ .struct(Product) /** diff --git a/src/These.ts b/src/These.ts index b05c58f54..8de9bb833 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1158,7 +1158,7 @@ export const struct: >>( r: R ) => Validated< [R[keyof R]] extends [Validated] ? E : never, - { readonly [K in keyof R]: [R[K]] extends [Validated] ? A : never } + { [K in keyof R]: [R[K]] extends [Validated] ? A : never } > = product_ .struct(Product) diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index 9e849f17f..bba168114 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -37,7 +37,7 @@ export const struct = (F: Product) => ([R[keyof R]] extends [Kind] ? R : never), ([R[keyof R]] extends [Kind] ? O : never), ([R[keyof R]] extends [Kind] ? E : never), - { readonly [K in keyof R]: [R[K]] extends [Kind] ? A : never } + { [K in keyof R]: [R[K]] extends [Kind] ? A : never } > => { const keys = Object.keys(fields) return pipe( diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 35b442adf..927bc76fb 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -110,9 +110,7 @@ export const reverse = (S: Semigroup): Semigroup => ({ * @since 1.0.0 */ export const struct = (semigroups: { readonly [K in keyof A]: Semigroup }): Semigroup< - { - readonly [K in keyof A]: A[K] - } + { readonly [K in keyof A]: A[K] } > => fromCombine((that) => (self) => { From d0d5dc417188fc808da26814e30cdbb8ff120651 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:19:52 +0100 Subject: [PATCH 39/80] struct: remove readonly --- src/Either.ts | 2 +- src/Identity.ts | 6 +++--- src/Option.ts | 5 +++-- src/Predicate.ts | 3 +-- src/These.ts | 9 +++------ src/typeclass/Product.ts | 2 +- test/typeclass/Product.ts | 6 +++--- 7 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 0ae1c58e9..02861283c 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -456,7 +456,7 @@ export const tuple: >>( ...tuple: T ) => Either< [T[number]] extends [Either] ? E : never, - Readonly<{ [I in keyof T]: [T[I]] extends [Either] ? A : never }> + { [I in keyof T]: [T[I]] extends [Either] ? A : never } > = product_ .tuple(Product) diff --git a/src/Identity.ts b/src/Identity.ts index dfe99be9c..829d64c24 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -317,9 +317,9 @@ export const Product: product_.Product = { */ export const tuple: >>( ...tuple: T -) => Identity] ? A : never }>> = - product_ - .tuple(Product) +) => Identity<{ [I in keyof T]: [T[I]] extends [Identity] ? A : never }> = product_.tuple( + Product +) /** * @since 1.0.0 diff --git a/src/Option.ts b/src/Option.ts index b48a4491b..ad278b120 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -492,8 +492,9 @@ export const Product: product_.Product = { */ export const tuple: >>( ...tuple: T -) => Option] ? A : never }>> = product_ - .tuple(Product) +) => Option<{ [I in keyof T]: [T[I]] extends [Option] ? A : never }> = product_.tuple( + Product +) /** * @since 1.0.0 diff --git a/src/Predicate.ts b/src/Predicate.ts index 8d2934ffe..1477c59f9 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -208,8 +208,7 @@ export const productFlatten: (that: Predicate) => >>( ...predicates: T ) => Predicate] ? A : never }>> = - product_ - .tuple(Product) + product_.tuple(Product) /** * @since 1.0.0 diff --git a/src/These.ts b/src/These.ts index 8de9bb833..f87a565a2 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1143,13 +1143,10 @@ export const Product: product_.Product = { /** * @since 1.0.0 */ -export const tuple: >>( - ...tuple: T -) => Validated< +export const tuple: >>(...tuple: T) => Validated< [T[number]] extends [Validated] ? E : never, - Readonly<{ [I in keyof T]: [T[I]] extends [Validated] ? A : never }> -> = product_ - .tuple(Product) + { [I in keyof T]: [T[I]] extends [Validated] ? A : never } +> = product_.tuple(Product) /** * @since 1.0.0 diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index bba168114..bbbc4b640 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -25,7 +25,7 @@ export const tuple = (F: Product) => ([T[number]] extends [Kind] ? R : never), ([T[number]] extends [Kind] ? O : never), ([T[number]] extends [Kind] ? E : never), - Readonly<{ [I in keyof T]: [T[I]] extends [Kind] ? A : never }> + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } > => F.productAll(components) as any /** diff --git a/test/typeclass/Product.ts b/test/typeclass/Product.ts index 3ae0590ff..76cc0e81c 100644 --- a/test/typeclass/Product.ts +++ b/test/typeclass/Product.ts @@ -12,11 +12,11 @@ describe("Product", () => { describe("tuple", () => { it("Covariant (Option)", () => { const tuple = _.tuple(O.Product) - U.deepStrictEqual(tuple(), O.some([] as const)) - U.deepStrictEqual(tuple(O.some("a")), O.some(["a"] as const)) + U.deepStrictEqual(tuple(), O.some([])) + U.deepStrictEqual(tuple(O.some("a")), O.some(["a"])) U.deepStrictEqual( tuple(O.some("a"), O.some(1), O.some(true)), - O.some(["a", 1, true] as const) + O.some(["a", 1, true]) ) U.deepStrictEqual(tuple(O.some("a"), O.some(1), O.none()), O.none()) }) From 82a003212a540379d52e7061aac1391f180e1bfd Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:21:43 +0100 Subject: [PATCH 40/80] nonEmptyTuple: remove readonly --- src/typeclass/SemiProduct.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 5af134638..80c8a0efc 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -147,7 +147,7 @@ export const nonEmptyTuple = (F: SemiProduct) => ([T[number]] extends [Kind] ? R : never), ([T[number]] extends [Kind] ? O : never), ([T[number]] extends [Kind] ? E : never), - Readonly<{ [I in keyof T]: [T[I]] extends [Kind] ? A : never }> + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } > => F.productMany(components.slice(1))(components[0]) as any type EnforceNonEmptyRecord = keyof R extends never ? never : R From 17b54470ca0370c66527232a33b6e129916315f7 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 17:51:41 +0100 Subject: [PATCH 41/80] Semigroup, Monoid: tuple: remove readonly --- dtslint/ts4.7/FlatMap.ts | 2 +- dtslint/ts4.7/Monoid.ts | 19 +++++++++++++++++++ dtslint/ts4.7/Product.ts | 4 ++-- dtslint/ts4.7/SemiAlternative.ts | 2 +- dtslint/ts4.7/SemiProduct.ts | 2 +- dtslint/ts4.7/Semigroup.ts | 19 +++++++++++++++++++ dtslint/ts4.7/tsconfig.json | 2 +- dtslint/ts4.7/tslint.json | 3 ++- src/typeclass/Monoid.ts | 26 +++++++++++++------------- src/typeclass/Semigroup.ts | 27 ++++++++++++--------------- 10 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 dtslint/ts4.7/Monoid.ts create mode 100644 dtslint/ts4.7/Semigroup.ts diff --git a/dtslint/ts4.7/FlatMap.ts b/dtslint/ts4.7/FlatMap.ts index b3890d50b..fe241dd32 100644 --- a/dtslint/ts4.7/FlatMap.ts +++ b/dtslint/ts4.7/FlatMap.ts @@ -1,6 +1,6 @@ import * as _ from "@fp-ts/core/typeclass/FlatMap" import type { TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" interface RAW { (r: R): () => Promise diff --git a/dtslint/ts4.7/Monoid.ts b/dtslint/ts4.7/Monoid.ts new file mode 100644 index 000000000..a8e830cdc --- /dev/null +++ b/dtslint/ts4.7/Monoid.ts @@ -0,0 +1,19 @@ +import * as _ from "@fp-ts/core/typeclass/Monoid" +import * as Number from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" + +// +// tuple +// + +// $ExpectType Monoid<[string, number]> +_.tuple( + String.Monoid, + Number.MonoidSum +) + +// $ExpectType Monoid +_.tuple( + String.Monoid, + Number.MonoidSum +) diff --git a/dtslint/ts4.7/Product.ts b/dtslint/ts4.7/Product.ts index 8287e5f69..68ee95b7f 100644 --- a/dtslint/ts4.7/Product.ts +++ b/dtslint/ts4.7/Product.ts @@ -15,10 +15,10 @@ declare const fc: RAW<{ c: boolean }, "c", boolean> export declare const Product: _.Product -// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", readonly [string, number, boolean]> +// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", [string, number, boolean]> _.tuple(Product)(fa, fb, fc) -// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", { readonly fa: string; readonly fb: number; readonly fc: boolean; }> +// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", { fa: string; fb: number; fc: boolean; }> _.struct(Product)({ fa, fb, fc }) _.tuple(Product)() // should allow empty tuple diff --git a/dtslint/ts4.7/SemiAlternative.ts b/dtslint/ts4.7/SemiAlternative.ts index 8ea619f71..e59f824d0 100644 --- a/dtslint/ts4.7/SemiAlternative.ts +++ b/dtslint/ts4.7/SemiAlternative.ts @@ -1,6 +1,6 @@ import * as _ from "@fp-ts/core/typeclass/SemiAlternative" import type { TypeLambda } from "@fp-ts/core/HKT" -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" interface RAW { (r: R): () => Promise diff --git a/dtslint/ts4.7/SemiProduct.ts b/dtslint/ts4.7/SemiProduct.ts index e4024d816..ea422d2d1 100644 --- a/dtslint/ts4.7/SemiProduct.ts +++ b/dtslint/ts4.7/SemiProduct.ts @@ -1,4 +1,4 @@ -import { OptionTypeLambda } from "@fp-ts/core/test/data/Option" +import { OptionTypeLambda } from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/SemiProduct" export declare const SemiProduct: _.SemiProduct diff --git a/dtslint/ts4.7/Semigroup.ts b/dtslint/ts4.7/Semigroup.ts new file mode 100644 index 000000000..854f4d468 --- /dev/null +++ b/dtslint/ts4.7/Semigroup.ts @@ -0,0 +1,19 @@ +import * as _ from "@fp-ts/core/typeclass/Semigroup" +import * as Number from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" + +// +// tuple +// + +// $ExpectType Semigroup<[string, number]> +_.tuple( + String.Semigroup, + Number.SemigroupSum +) + +// $ExpectType Semigroup +_.tuple( + String.Semigroup, + Number.SemigroupSum +) diff --git a/dtslint/ts4.7/tsconfig.json b/dtslint/ts4.7/tsconfig.json index 109760a8d..0696bacfb 100644 --- a/dtslint/ts4.7/tsconfig.json +++ b/dtslint/ts4.7/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "noEmit": true, "strict": true, - "noImplicitAny": true, + "noImplicitAny": false, "noImplicitThis": true, "strictNullChecks": true, "strictFunctionTypes": true, diff --git a/dtslint/ts4.7/tslint.json b/dtslint/ts4.7/tslint.json index 222a6c998..1457aa8de 100644 --- a/dtslint/ts4.7/tslint.json +++ b/dtslint/ts4.7/tslint.json @@ -19,6 +19,7 @@ "no-relative-import-in-test": false, "no-null-undefined-union": false, "invalid-void": false, - "max-line-length": false + "max-line-length": false, + "no-useless-files": false } } diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index a5ff1ecd1..071447afe 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -54,7 +54,19 @@ export const max = (B: Bounded): Monoid => fromSemigroup(semigroup.max( export const reverse = (M: Monoid): Monoid => fromSemigroup(semigroup.reverse(M), M.empty) /** - * Given a struct of monoids returns a monoid for the struct. + * Given a tuple of `Monoid`s returns a `Monoid` for the tuple. + * + * @since 1.0.0 + */ +export const tuple = >( + ...monoids: { [K in keyof A]: Monoid } +): Monoid => { + const empty: A = monoids.map((m) => m.empty) as any + return fromSemigroup(semigroup.tuple(...monoids), empty) +} + +/** + * Given a struct of `Monoid`s returns a `Monoid` for the struct. * * @since 1.0.0 */ @@ -69,15 +81,3 @@ export const struct = ( } return fromSemigroup(semigroup.struct(monoids), empty) } - -/** - * Given a tuple of monoids returns a monoid for the tuple. - * - * @since 1.0.0 - */ -export const tuple = >( - ...monoids: { [K in keyof A]: Monoid } -): Monoid> => { - const empty: A = monoids.map((m) => m.empty) as any - return fromSemigroup(semigroup.tuple>(...monoids), empty) -} diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 927bc76fb..adaa273c2 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -105,7 +105,17 @@ export const reverse = (S: Semigroup): Semigroup => ({ }) /** - * Given a struct of associatives returns an associative for the struct. + * Given a tuple of `Semigroup`s returns a `Semigroup` for the tuple. + * + * @since 1.0.0 + */ +export const tuple = >( + ...semigroups: { readonly [K in keyof A]: Semigroup } +): Semigroup => + fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) + +/** + * Given a struct of `Semigroup`s returns a `Semigroup` for the struct. * * @since 1.0.0 */ @@ -124,16 +134,6 @@ export const struct = (semigroups: { readonly [K in keyof A]: Semigroup } ) -/** - * Given a tuple of associatives returns an associative for the tuple. - * - * @since 1.0.0 - */ -export const tuple = >( - ...semigroups: { readonly [K in keyof A]: Semigroup } -): Semigroup> => - fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) - /** * @since 1.0.0 */ @@ -203,9 +203,7 @@ export const Invariant: invariant.Invariant = { */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, - // @ts-expect-error product: that => self => tuple(self, that), - // @ts-expect-error productMany: collection => self => tuple(self, ...collection) } @@ -216,6 +214,5 @@ export const SemiProduct: semiProduct.SemiProduct = { export const Product: product.Product = { ...SemiProduct, of: constant, - // @ts-expect-error - productAll: (collection: Iterable>) => tuple(...collection) + productAll: (collection: Iterable>) => tuple>(...collection) } From 7b5de2a28231a69bbdeca928323defd41235009b Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 18:31:23 +0100 Subject: [PATCH 42/80] ReadonlyArray: remove useless instance --- src/ReadonlyArray.ts | 37 +++++---------------------- test/typeclass/NonEmptyTraversable.ts | 23 ++++++++++++++--- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index d8e78af31..b8931d03c 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -46,14 +46,6 @@ export interface ReadonlyArrayTypeLambda extends TypeLambda { readonly type: ReadonlyArray } -/** - * @category type lambdas - * @since 1.0.0 - */ -export interface NonEmptyReadonlyArrayTypeLambda extends TypeLambda { - readonly type: NonEmptyReadonlyArray -} - /** * @category models * @since 1.0.0 @@ -779,12 +771,8 @@ export const sortNonEmpty = (O: Order) => * @category sorting * @since 1.0.0 */ -export const sortBy = ( - ...orders: ReadonlyArray> -) => - ( - self: Iterable - ): Array => { +export const sortBy = (...orders: ReadonlyArray>) => + (self: Iterable): Array => { const input = fromIterable(self) return (isNonEmpty(input) ? sortByNonEmpty(...orders)(input) : []) } @@ -805,9 +793,8 @@ export const sortByNonEmpty = ( * * @since 1.0.0 */ -export const zip = ( - that: Iterable -): (self: Iterable) => Array<[A, B]> => zipWith(that, (a, b) => [a, b]) +export const zip = (that: Iterable): (self: Iterable) => Array<[A, B]> => + zipWith(that, (a, b) => [a, b]) /** * Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one @@ -827,10 +814,7 @@ export const zipWith = (that: Iterable, f: (a: A, b: B) => C) => */ export const zipNonEmpty = (that: NonEmptyReadonlyArray) => (self: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> => - pipe( - self, - zipNonEmptyWith(that, (a, b) => [a, b]) - ) + pipe(self, zipNonEmptyWith(that, (a, b) => [a, b])) /** * @since 1.0.0 @@ -850,9 +834,7 @@ export const zipNonEmptyWith = (that: NonEmptyReadonlyArray, f: (a: * * @since 1.0.0 */ -export const unzip = ( - self: Iterable<[A, B]> -): [Array, Array] => { +export const unzip = (self: Iterable<[A, B]>): [Array, Array] => { const input = fromIterable(self) return isNonEmpty(input) ? unzipNonEmpty(input) : [[], []] } @@ -2260,10 +2242,3 @@ export const liftOrder = (O: Order): Order> => return number.Order.compare(bLen)(aLen) } ) - -/** - * @category instances - * @since 1.0.0 - */ -export const NonEmptyCovariant: covariant.Covariant = covariant - .make(mapNonEmpty) diff --git a/test/typeclass/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index 73ddb23d9..050a96db9 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -1,16 +1,33 @@ import { identity, pipe } from "@fp-ts/core/Function" +import type { TypeLambda } from "@fp-ts/core/HKT" import * as O from "@fp-ts/core/Option" import * as RA from "@fp-ts/core/ReadonlyArray" import * as _ from "@fp-ts/core/test/limbo/NonEmptyTraversable" +import * as covariant from "@fp-ts/core/typeclass/Covariant" import * as U from "../util" -const NonEmptyTraversable: _.NonEmptyTraversable = { +const NonEmptyTraversable: _.NonEmptyTraversable = { // @ts-expect-error traverseNonEmpty: RA.traverseNonEmpty, // @ts-expect-error sequenceNonEmpty: F => self => pipe(self, RA.traverseNonEmpty(F)(identity)) } +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface NonEmptyReadonlyArrayTypeLambda extends TypeLambda { + readonly type: RA.NonEmptyReadonlyArray +} + +/** + * @category instances + * @since 1.0.0 + */ +export const NonEmptyCovariant: covariant.Covariant = covariant + .make(RA.mapNonEmpty) + describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const traverseNonEmpty = _.traverseNonEmptyComposition( @@ -25,7 +42,7 @@ describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const sequence = _.sequenceNonEmptyComposition( - { ...NonEmptyTraversable, ...RA.NonEmptyCovariant }, + { ...NonEmptyTraversable, ...NonEmptyCovariant }, NonEmptyTraversable )(O.SemiApplicative) U.deepStrictEqual(sequence([[O.some(1)]]), O.some([[1]] as const)) @@ -41,7 +58,7 @@ describe("NonEmptyTraversable", () => { }) it("sequenceNonEmpty", () => { - const sequenceNonEmpty = _.sequenceNonEmpty( + const sequenceNonEmpty = _.sequenceNonEmpty( NonEmptyTraversable.traverseNonEmpty )(O.SemiApplicative) U.deepStrictEqual(sequenceNonEmpty([O.none()]), O.none()) From 5849b60752942a1ea0e77c18a4a5ff1262aee555 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 17 Jan 2023 19:49:46 +0100 Subject: [PATCH 43/80] ReadonlyArray: remove readonly from imap, let, flap, as, flatten, bind, filter, partition, traverseTap, productMany, andThenBind, ap, lift2, lift3, traverseFilterMap, traversePartitionMap, traverseFilter, traversePartition --- src/ReadonlyArray.ts | 143 ++++++++++++++++-------------------------- test/ReadonlyArray.ts | 2 - 2 files changed, 55 insertions(+), 90 deletions(-) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index b8931d03c..582ec3ec6 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1271,7 +1271,7 @@ export const Do: ReadonlyArray<{}> = of_.Do(Of) export const imap: ( to: (a: A) => B, from: (b: B) => A -) => (self: ReadonlyArray) => ReadonlyArray = covariant.imap(map) +) => (self: ReadonlyArray) => Array = covariant.imap(map) as any /** * @category instances @@ -1285,19 +1285,17 @@ export const Invariant: invariant.Invariant = { * @category mapping * @since 1.0.0 */ -// @ts-expect-error export const tupled: (self: ReadonlyArray) => Array<[A]> = invariant - .tupled(Invariant) + .tupled(Invariant) as any /** * @category do notation * @since 1.0.0 */ -// @ts-expect-error export const bindTo: ( name: N ) => (self: ReadonlyArray) => Array<{ [K in N]: A }> = invariant - .bindTo(Invariant) + .bindTo(Invariant) as any /** * @category instances @@ -1310,7 +1308,7 @@ const let_: ( f: (a: A) => B ) => ( self: ReadonlyArray -) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let(Covariant) +) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = covariant.let(Covariant) as any export { /** @@ -1326,7 +1324,7 @@ export { */ export const flap: (a: A) => ( self: ReadonlyArray<(a: A) => B> -) => ReadonlyArray = covariant.flap(Covariant) +) => Array = covariant.flap(Covariant) as any /** * Maps the success value of this effect to the specified constant value. @@ -1334,9 +1332,9 @@ export const flap: (a: A) => ( * @category mapping * @since 1.0.0 */ -export const as: (b: B) => (self: ReadonlyArray) => ReadonlyArray = covariant.as( +export const as: (b: B) => (self: ReadonlyArray) => Array = covariant.as( Covariant -) +) as any /** * @category instances @@ -1404,8 +1402,8 @@ export const FlatMap: flatMap_.FlatMap = { * @category sequencing * @since 1.0.0 */ -export const flatten: (self: ReadonlyArray>) => ReadonlyArray = flatMap_ - .flatten(FlatMap) +export const flatten: (self: ReadonlyArray>) => Array = flatMap_ + .flatten(FlatMap) as any /** * @category sequencing @@ -1441,7 +1439,7 @@ export const bind: ( f: (a: A) => ReadonlyArray ) => ( self: ReadonlyArray -) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind(Chainable) +) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = chainable.bind(Chainable) as any /** * @category filtering @@ -1515,9 +1513,9 @@ export const Filterable: filterable.Filterable = { export const filter: { ( refinement: Refinement - ): (self: ReadonlyArray) => ReadonlyArray - (predicate: Predicate): (self: ReadonlyArray) => ReadonlyArray -} = filterable.filter(Filterable) + ): (self: ReadonlyArray) => Array + (predicate: Predicate): (self: ReadonlyArray) => Array +} = filterable.filter(Filterable) as any /** * @category filtering @@ -1542,11 +1540,11 @@ export const filterWithIndex: { export const partition: { (refinement: Refinement): ( self: ReadonlyArray - ) => [ReadonlyArray, ReadonlyArray] + ) => [Array, Array] ( predicate: Predicate - ): (self: ReadonlyArray) => [ReadonlyArray, ReadonlyArray] -} = filterable.partition(Filterable) + ): (self: ReadonlyArray) => [Array, Array] +} = filterable.partition(Filterable) as any /** * @category filtering @@ -1637,14 +1635,13 @@ export const traverseNonEmptyWithIndex = ( * @category traversing * @since 1.0.0 */ -// @ts-expect-error export const sequence: ( F: applicative.Applicative ) => ( self: ReadonlyArray> -) => Kind> = - // @ts-expect-error - traversable.sequence(traverse) +) => Kind> = traversable.sequence( + traverse as any +) as any /** * @category instances @@ -1665,10 +1662,8 @@ export const traverseTap: ( F: applicative.Applicative ) => ( f: (a: A) => Kind -) => (self: ReadonlyArray) => Kind> = traversable - .traverseTap( - Traversable - ) +) => (self: ReadonlyArray) => Kind> = traversable + .traverseTap(Traversable) as any /** * @category traversing @@ -1704,18 +1699,15 @@ export const product = ( */ export const productMany: ( collection: Iterable> -) => (self: ReadonlyArray) => ReadonlyArray<[A, ...Array]> = semiProduct - .productMany( - Covariant, - product - ) +) => (self: ReadonlyArray) => Array<[A, ...Array]> = semiProduct + .productMany(Covariant, product) as any /** * @since 1.0.0 */ export const productAll = ( collection: Iterable> -): ReadonlyArray> => { +): Array> => { const arrays = Array.from(collection) if (isEmpty(arrays)) { return empty() @@ -1744,8 +1736,8 @@ export const andThenBind: ( that: ReadonlyArray ) => ( self: ReadonlyArray -) => ReadonlyArray<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct - .andThenBind(SemiProduct) +) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> = semiProduct + .andThenBind(SemiProduct) as any /** * @category instances @@ -1761,9 +1753,9 @@ export const SemiApplicative: semiApplicative.SemiApplicative( fa: ReadonlyArray -) => (self: ReadonlyArray<(a: A) => B>) => ReadonlyArray = semiApplicative.ap( +) => (self: ReadonlyArray<(a: A) => B>) => Array = semiApplicative.ap( SemiApplicative -) +) as any /** * Lifts a binary function into `ReadonlyArray`. @@ -1773,9 +1765,9 @@ export const ap: ( */ export const lift2: ( f: (a: A, b: B) => C -) => (fa: ReadonlyArray, fb: ReadonlyArray) => ReadonlyArray = semiApplicative.lift2( +) => (fa: ReadonlyArray, fb: ReadonlyArray) => Array = semiApplicative.lift2( SemiApplicative -) +) as any /** * Lifts a ternary function into `ReadonlyArray`. @@ -1785,8 +1777,8 @@ export const lift2: ( */ export const lift3: ( f: (a: A, b: B, c: C) => D -) => (fa: ReadonlyArray, fb: ReadonlyArray, fc: ReadonlyArray) => ReadonlyArray = - semiApplicative.lift3(SemiApplicative) +) => (fa: ReadonlyArray, fb: ReadonlyArray, fc: ReadonlyArray) => Array = + semiApplicative.lift3(SemiApplicative) as any /** * @category lifting @@ -1874,11 +1866,8 @@ export const Foldable: foldable.Foldable = { * @category folding * @since 1.0.0 */ -export const foldMap: ( - M: Monoid -) => (f: (a: A) => M) => (self: ReadonlyArray) => M = foldable.foldMap( - Foldable -) +export const foldMap: (M: Monoid) => (f: (a: A) => M) => (self: ReadonlyArray) => M = + foldable.foldMap(Foldable) /** * @category folding @@ -1909,40 +1898,28 @@ export const foldMapNonEmptyWithIndex = (S: Semigroup) => * @category folding * @since 1.0.0 */ -export const reduceKind: ( - F: monad.Monad -) => ( +export const reduceKind: (F: monad.Monad) => ( b: B, f: (b: B, a: A) => Kind -) => (self: ReadonlyArray) => Kind = foldable.reduceKind( - Foldable -) +) => (self: ReadonlyArray) => Kind = foldable.reduceKind(Foldable) /** * @category folding * @since 1.0.0 */ -export const reduceRightKind: ( - F: monad.Monad -) => ( +export const reduceRightKind: (F: monad.Monad) => ( b: B, f: (b: B, a: A) => Kind ) => (self: ReadonlyArray) => Kind = foldable - .reduceRightKind( - Foldable - ) + .reduceRightKind(Foldable) /** * @category folding * @since 1.0.0 */ -export const foldMapKind: ( - F: Coproduct -) => ( +export const foldMapKind: (F: Coproduct) => ( f: (a: A) => Kind -) => (self: ReadonlyArray) => Kind = foldable.foldMapKind( - Foldable -) +) => (self: ReadonlyArray) => Kind = foldable.foldMapKind(Foldable) /** * @category filtering @@ -1952,8 +1929,8 @@ export const traverseFilterMap: ( F: applicative.Applicative ) => ( f: (a: A) => Kind> -) => (ta: ReadonlyArray) => Kind> = traversableFilterable - .traverseFilterMap({ ...Traversable, ...Compactable }) +) => (ta: ReadonlyArray) => Kind> = traversableFilterable + .traverseFilterMap({ ...Traversable, ...Compactable }) as any /** * @category filtering @@ -1963,9 +1940,8 @@ export const traversePartitionMap: ( F: applicative.Applicative ) => ( f: (a: A) => Kind> -) => (self: ReadonlyArray) => Kind, ReadonlyArray]> = - traversableFilterable - .traversePartitionMap({ ...Traversable, ...Covariant, ...Compactable }) +) => (self: ReadonlyArray) => Kind, Array]> = traversableFilterable + .traversePartitionMap({ ...Traversable, ...Covariant, ...Compactable }) as any /** * @category instances @@ -1974,7 +1950,9 @@ export const traversePartitionMap: ( export const TraversableFilterable: traversableFilterable.TraversableFilterable< ReadonlyArrayTypeLambda > = { + // @ts-expect-error traverseFilterMap, + // @ts-expect-error traversePartitionMap } @@ -1987,8 +1965,8 @@ export const traverseFilter: ( F: applicative.Applicative ) => ( predicate: (a: A) => Kind -) => (self: ReadonlyArray) => Kind> = traversableFilterable - .traverseFilter(TraversableFilterable) +) => (self: ReadonlyArray) => Kind> = traversableFilterable + .traverseFilter(TraversableFilterable) as any /** * @since 1.0.0 @@ -1999,8 +1977,8 @@ export const traversePartition: ( predicate: (a: A) => Kind ) => ( self: ReadonlyArray -) => Kind, ReadonlyArray]> = traversableFilterable - .traversePartition(TraversableFilterable) +) => Kind, Array]> = traversableFilterable + .traversePartition(TraversableFilterable) as any /** * @category lifting @@ -2064,7 +2042,7 @@ export const liftEither = , E, B>( */ export function every( refinement: Refinement -): Refinement, Array> +): Refinement, ReadonlyArray> export function every(predicate: Predicate): Predicate> export function every(predicate: Predicate): Predicate> { return (self) => self.every(predicate) @@ -2077,14 +2055,7 @@ export function every(predicate: Predicate): Predicate> { * @since 1.0.0 */ export const some = (predicate: Predicate) => - (self: ReadonlyArray): self is NonEmptyArray => self.some(predicate) - -/** - * Alias of [`some`](#some) - * - * @since 1.0.0 - */ -export const has = some + (self: ReadonlyArray): self is NonEmptyReadonlyArray => self.some(predicate) /** * Fold a data structure, accumulating values in some `Monoid`, combining adjacent elements @@ -2118,10 +2089,9 @@ export const join: (sep: string) => (self: ReadonlyArray) => string = in /** * @since 1.0.0 */ -// @ts-expect-error export const productFlatten: (that: ReadonlyArray) => >( self: ReadonlyArray -) => Array<[...A, B]> = semiProduct.productFlatten(SemiProduct) +) => Array<[...A, B]> = semiProduct.productFlatten(SemiProduct) as any /** * @since 1.0.0 @@ -2167,8 +2137,7 @@ export const unfold = (b: B, f: (b: B) => Option): Array< * @since 1.0.0 */ export const getUnionSemigroup = (equivalence: Equivalence): Semigroup> => - // @ts-expect-error - fromCombine(union(equivalence)) + fromCombine(union(equivalence)) as any /** * @category instances @@ -2190,9 +2159,7 @@ export const getUnionMonoid = (equivalence: Equivalence): Monoid( equivalence: Equivalence -): Semigroup> => - // @ts-expect-error - fromCombine(intersection(equivalence)) +): Semigroup> => fromCombine(intersection(equivalence)) as any /** * Returns a `Semigroup` for `ReadonlyArray`. diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index a3e4022d3..83f16cec9 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -1589,8 +1589,6 @@ describe.concurrent("ReadonlyArray", () => { const isPositive: Predicate = (n) => n > 0 deepStrictEqual(pipe([-1, -2, 3], RA.some(isPositive)), true) deepStrictEqual(pipe([-1, -2, -3], RA.some(isPositive)), false) - // has is an alias of some - deepStrictEqual(pipe([-1, -2, -3], RA.has(isPositive)), false) }) it("size", () => { From bd12b4c76c1481b6fb0eb4828adf94d6c11e2677 Mon Sep 17 00:00:00 2001 From: gcanti Date: Wed, 18 Jan 2023 16:13:23 +0100 Subject: [PATCH 44/80] rename `productFlatten` to `element` --- .changeset/blue-books-breathe.md | 5 +++++ src/Either.ts | 6 ++++-- src/Identity.ts | 6 ++++-- src/Option.ts | 6 ++++-- src/Predicate.ts | 7 ++++--- src/ReadonlyArray.ts | 6 ++++-- src/These.ts | 6 ++++-- src/typeclass/SemiProduct.ts | 4 +++- test/Either.ts | 4 ++-- test/Identity.ts | 2 +- test/Option.ts | 4 ++-- test/Predicate.ts | 2 +- test/ReadonlyArray.ts | 2 +- test/These.ts | 2 +- test/typeclass/SemiProduct.ts | 12 ++++++------ 15 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 .changeset/blue-books-breathe.md diff --git a/.changeset/blue-books-breathe.md b/.changeset/blue-books-breathe.md new file mode 100644 index 000000000..fe1864618 --- /dev/null +++ b/.changeset/blue-books-breathe.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +rename `productFlatten` to `element` diff --git a/src/Either.ts b/src/Either.ts index 02861283c..98e084414 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -414,14 +414,16 @@ export const andThenBind: ( .andThenBind(SemiProduct) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten: ( +export const element: ( that: Either ) => >( self: Either ) => Either = semiProduct - .productFlatten(SemiProduct) + .element(SemiProduct) /** * @since 1.0.0 diff --git a/src/Identity.ts b/src/Identity.ts index 829d64c24..8e88771fd 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -289,12 +289,14 @@ export const andThenBind: ( .andThenBind(SemiProduct) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten: ( +export const element: ( fb: Identity ) => >(self: Identity) => Identity<[...A, B]> = semiProduct - .productFlatten(SemiProduct) + .element(SemiProduct) /** * @since 1.0.0 diff --git a/src/Option.ts b/src/Option.ts index ad278b120..1dc3b3724 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -456,12 +456,14 @@ export const andThenBind: ( .andThenBind(SemiProduct) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten: ( +export const element: ( fb: Option ) => >(self: Option) => Option<[...A, B]> = semiProduct - .productFlatten(SemiProduct) + .element(SemiProduct) /** * @since 1.0.0 diff --git a/src/Predicate.ts b/src/Predicate.ts index 1477c59f9..c21fb8d27 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -195,12 +195,13 @@ export const andThenBind: ( ) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -// @ts-expect-error -export const productFlatten: (that: Predicate) => >( +export const element: (that: Predicate) => >( self: Predicate -) => Predicate = semiProduct.productFlatten(SemiProduct) +) => Predicate = semiProduct.element(SemiProduct) as any /** * @since 1.0.0 diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 582ec3ec6..0d6a44b02 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2087,11 +2087,13 @@ export const join: (sep: string) => (self: ReadonlyArray) => string = in ) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten: (that: ReadonlyArray) => >( +export const element: (that: ReadonlyArray) => >( self: ReadonlyArray -) => Array<[...A, B]> = semiProduct.productFlatten(SemiProduct) as any +) => Array<[...A, B]> = semiProduct.element(SemiProduct) as any /** * @since 1.0.0 diff --git a/src/These.ts b/src/These.ts index f87a565a2..e9b1c9e71 100644 --- a/src/These.ts +++ b/src/These.ts @@ -1121,14 +1121,16 @@ export const andThenBindThese = ( andThenBind(name, fromThese(that)) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten: ( +export const element: ( that: Validated ) => >( self: Validated ) => Validated = semiProduct - .productFlatten(SemiProduct) + .element(SemiProduct) /** * @category instances diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 80c8a0efc..2bcf5ddac 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -121,9 +121,11 @@ export const andThenBind = (F: SemiProduct) => ) /** + * Adds an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten = (F: SemiProduct) => +export const element = (F: SemiProduct) => ( that: Kind ) => diff --git a/test/Either.ts b/test/Either.ts index 8b53175a5..fe9844b2a 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -444,8 +444,8 @@ describe.concurrent("Either", () => { ) }) - it("productFlatten", () => { - expect(pipe(_.right(1), _.tupled, _.productFlatten(_.right("b")))).toEqual( + it("element", () => { + expect(pipe(_.right(1), _.tupled, _.element(_.right("b")))).toEqual( _.right([1, "b"]) ) }) diff --git a/test/Identity.ts b/test/Identity.ts index f840be294..fbc74fe38 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -41,7 +41,7 @@ describe.concurrent("Identity", () => { expect(_.product).exist expect(_.productMany).exist expect(_.andThenBind).exist - expect(_.productFlatten).exist + expect(_.element).exist expect(_.Product).exist expect(_.productAll).exist diff --git a/test/Option.ts b/test/Option.ts index bb6d2283e..74912d033 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -502,8 +502,8 @@ describe.concurrent("Option", () => { ) }) - it("productFlatten", () => { - expect(pipe(_.some(1), _.tupled, _.productFlatten(_.some("b")))).toEqual( + it("element", () => { + expect(pipe(_.some(1), _.tupled, _.element(_.some("b")))).toEqual( _.some([1, "b"]) ) }) diff --git a/test/Predicate.ts b/test/Predicate.ts index d727fa653..b675bf30f 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -35,7 +35,7 @@ describe.concurrent("Predicate", () => { expect(_.product).exist expect(_.productMany).exist expect(_.andThenBind).exist - expect(_.productFlatten).exist + expect(_.element).exist expect(_.Product).exist expect(_.productAll).exist diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 83f16cec9..66ac4a973 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -44,7 +44,7 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.product).exist expect(RA.productMany).exist expect(RA.andThenBind).exist - expect(RA.productFlatten).exist + expect(RA.element).exist expect(RA.Product).exist expect(RA.productAll).exist diff --git a/test/These.ts b/test/These.ts index 79e276c98..680bc8efd 100644 --- a/test/These.ts +++ b/test/These.ts @@ -47,7 +47,7 @@ describe("These", () => { expect(_.product).exist expect(_.productMany).exist expect(_.andThenBind).exist - expect(_.productFlatten).exist + expect(_.element).exist expect(_.Product).exist expect(_.productAll).exist diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 29566f2e2..0befff78a 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -164,16 +164,16 @@ describe("SemiProduct", () => { }) }) - describe("productFlatten", () => { + describe("element", () => { it("Covariant (Option)", () => { - const productFlatten = _.productFlatten(O.SemiProduct) - U.deepStrictEqual(pipe(O.some([1, 2]), productFlatten(O.none())), O.none()) - expect(pipe(O.some([1, 2]), productFlatten(O.some(3)))).toEqual(O.some([1, 2, 3])) + const element = _.element(O.SemiProduct) + U.deepStrictEqual(pipe(O.some([1, 2]), element(O.none())), O.none()) + expect(pipe(O.some([1, 2]), element(O.some(3)))).toEqual(O.some([1, 2, 3])) }) it("Contravariant (Predicate)", () => { - const productFlatten = _.productFlatten(P.SemiProduct) - const p = pipe(P.tuple(String.isString, String.isString), productFlatten(Number.isNumber)) + const element = _.element(P.SemiProduct) + const p = pipe(P.tuple(String.isString, String.isString), element(Number.isNumber)) U.deepStrictEqual(p(["a", "b", 3]), true) U.deepStrictEqual(p(["a", "b", "c"]), false) U.deepStrictEqual(p([1, "b", 1]), false) From c1f1c5d0ca4b248966685858cd09a3222d97c29a Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 16:32:52 +0100 Subject: [PATCH 45/80] Order: add array combinator --- .changeset/green-kangaroos-report.md | 5 +++++ src/ReadonlyArray.ts | 25 +++++----------------- src/typeclass/Order.ts | 32 +++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 .changeset/green-kangaroos-report.md diff --git a/.changeset/green-kangaroos-report.md b/.changeset/green-kangaroos-report.md new file mode 100644 index 000000000..ef0285a57 --- /dev/null +++ b/.changeset/green-kangaroos-report.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Order: add array combinator diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 0d6a44b02..24a226902 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -8,7 +8,6 @@ import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import * as either from "@fp-ts/core/internal/Either" import * as option from "@fp-ts/core/internal/Option" import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" -import * as number from "@fp-ts/core/Number" import type { Option } from "@fp-ts/core/Option" import * as O from "@fp-ts/core/Option" import type { Predicate, Refinement } from "@fp-ts/core/Predicate" @@ -2188,26 +2187,12 @@ export const getMonoid = (): Monoid> => { } /** - * Derives an `Order` over the `ReadonlyArray` of a given element type from the `Order` of that type. The ordering between two such - * `ReadonlyArray`s is equal to: the first non equal comparison of each `ReadonlyArray`s elements taken pairwise in increasing order, in - * case of equality over all the pairwise elements; the longest `ReadonlyArray` is considered the greatest, if both `ReadonlyArray`s have - * the same length, the result is equality. + * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. + * The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. + * If all elements are equal, the arrays are then compared based on their length. + * It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. * * @category lifting * @since 1.0.0 */ -export const liftOrder = (O: Order): Order> => - order.fromCompare((that) => - (self) => { - const aLen = self.length - const bLen = that.length - const len = Math.min(aLen, bLen) - for (let i = 0; i < len; i++) { - const o = O.compare(that[i])(self[i]) - if (o !== 0) { - return o - } - } - return number.Order.compare(bLen)(aLen) - } - ) +export const liftOrder: (O: Order) => Order> = order.array diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 571a872f6..c2623058a 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -2,6 +2,7 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" +import * as number from "@fp-ts/core/Number" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import type * as invariant from "@fp-ts/core/typeclass/Invariant" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" @@ -37,8 +38,12 @@ export const fromCompare = (compare: Order["compare"]): Order => ({ }) /** - * Given a tuple of `Compare`s returns a `Compare` for the tuple. + * This function creates and returns a new `Order` for a tuple of values based on the given `Order`s for each element in the tuple. + * The returned `Order` compares two tuples of the same type by applying the corresponding `Order` to each element in the tuple. + * It is useful when you need to compare two tuples of the same type and you have a specific way of comparing each element + * of the tuple. * + * @category combinators * @since 1.0.0 */ export const tuple = >( @@ -57,6 +62,31 @@ export const tuple = >( } ) +/** + * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. + * The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. + * If all elements are equal, the arrays are then compared based on their length. + * It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. + * + * @category combinators + * @since 1.0.0 + */ +export const array = (O: Order): Order> => + fromCompare((that) => + (self) => { + const aLen = self.length + const bLen = that.length + const len = Math.min(aLen, bLen) + for (let i = 0; i < len; i++) { + const o = O.compare(that[i])(self[i]) + if (o !== 0) { + return o + } + } + return number.Order.compare(bLen)(aLen) + } + ) + /** * @since 1.0.0 */ From 79bb968cec1b00f23784f1473f2435e36d89a83e Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 17:19:03 +0100 Subject: [PATCH 46/80] Semigroup: add array, readonlyArray combinator --- .changeset/big-weeks-kiss.md | 5 +++ README.md | 3 +- data.md | 29 +++++++++++++ src/ReadonlyArray.ts | 2 +- src/typeclass/Semigroup.ts | 84 +++++++++++++++++++++++------------- test/typeclass/Semigroup.ts | 4 ++ Overview.md => typeclass.md | 0 7 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 .changeset/big-weeks-kiss.md create mode 100644 data.md rename Overview.md => typeclass.md (100%) diff --git a/.changeset/big-weeks-kiss.md b/.changeset/big-weeks-kiss.md new file mode 100644 index 000000000..931923006 --- /dev/null +++ b/.changeset/big-weeks-kiss.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Semigroup: add array combinator diff --git a/README.md b/README.md index 364c07560..3de06454e 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ npm install @fp-ts/core # Documentation -- [Typeclass overview](./Overview.md) +- [Typeclass overview](./typeclass.md) +- [Data overview](./data.md) - [API Reference](https://fp-ts.github.io/core/) # License diff --git a/data.md b/data.md new file mode 100644 index 000000000..e8eae4e1e --- /dev/null +++ b/data.md @@ -0,0 +1,29 @@ +# Data overview + +## tuples + +This section covers the various modules and combinators that work with tuples. + +| Module | Name | Given | To | +| ----------- | ------------- | --------------------------------------- | -------------------------------------- | +| Equivalence | tuple | `[Equivalence, Equivalence, ...]` | `Equivalence` | +| Order | tuple | `[Order, Order, ...]` | `Order` | +| Semigroup | tuple | `[Semigroup, Semigroup, ...]` | `Semigroup<[A, B, ...]>` | +| Monoid | tuple | `[Monoid, Monoid, ...]` | `Monoid<[A, B, ...]>` | +| SemiProduct | nonEmptyTuple | `[F, F, ...]` | `F<[A, B, ...]>` | +| Product | tuple | `[F, F, ...]` (can be empty) | `F<[A, B, ...]>` | +| Either | tuple | `[Either, Either, ...]` | `Either` | +| Option | tuple | `[Option, Option, ...]` | `Option<[A, B, ...]>` | +| Predicate | tuple | `[Predicate, Predicate, ...]` | `Predicate` | +| These | tuple | `[These, These, ...]` | `These` | + +## arrays + +This section covers the various modules and combinators that work with arrays. + +| Module | Name | Given | To | +| ----------- | ------------- | ---------------- | ------------------------------- | +| Equivalence | array | `Equivalence` | `Equivalence>` | +| Order | array | `Order` | `Order>` | +| Semigroup | array | `A` | `Semigroup>` | +| Semigroup | readonlyArray | `A` | `Semigroup>` | diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 24a226902..0083c5e82 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2168,7 +2168,7 @@ export const getIntersectionSemigroup = ( * @category instances * @since 1.0.0 */ -export const getSemigroup = (): Semigroup> => fromCombine(appendAll) +export const getSemigroup: () => Semigroup> = semigroup.readonlyArray /** * Returns a `Monoid` for `ReadonlyArray`. diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index adaa273c2..fe3bcca48 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -61,6 +61,60 @@ export const fromCombine = (combine: Semigroup["combine"]): Semigroup = } }) +/** + * This function creates and returns a new `Semigroup` for a tuple of values based on the given `Semigroup`s for each element in the tuple. + * The returned `Semigroup` combines two tuples of the same type by applying the corresponding `Semigroup` passed as arguments to each element in the tuple. + * It is useful when you need to combine two tuples of the same type and you have a specific way of combining each element of the tuple. + * + * @category combinators + * @since 1.0.0 + */ +export const tuple = >( + ...semigroups: { readonly [K in keyof A]: Semigroup } +): Semigroup => + fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) + +/** + * Given a type `A`, this function creates and returns a `Semigroup` for `Array`. + * The returned `Semigroup` combines two arrays by concatenating them. + * + * @category combinators + * @since 1.0.0 + */ +export const array = (): Semigroup> => fromCombine(that => self => self.concat(that)) + +/** + * Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. + * The returned `Semigroup` combines two arrays by concatenating them. + * + * @category combinators + * @since 1.0.0 + */ +export const readonlyArray: () => Semigroup> = array as any + +/** + * This function creates and returns a new `Semigroup` for a struct of values based on the given `Semigroup`s for each property in the struct. + * The returned `Semigroup` combines two structs of the same type by applying the corresponding `Semigroup` passed as arguments to each property in the struct. + * It is useful when you need to combine two structs of the same type and you have a specific way of combining each property of the struct. + * + * @category combinators + * @since 1.0.0 + */ +export const struct = (semigroups: { readonly [K in keyof A]: Semigroup }): Semigroup< + { readonly [K in keyof A]: A[K] } +> => + fromCombine((that) => + (self) => { + const r = {} as any + for (const k in semigroups) { + if (Object.prototype.hasOwnProperty.call(semigroups, k)) { + r[k] = semigroups[k].combine(that[k])(self[k]) + } + } + return r + } + ) + /** * `Semigroup` that returns last minimum of elements. * @@ -104,36 +158,6 @@ export const reverse = (S: Semigroup): Semigroup => ({ } }) -/** - * Given a tuple of `Semigroup`s returns a `Semigroup` for the tuple. - * - * @since 1.0.0 - */ -export const tuple = >( - ...semigroups: { readonly [K in keyof A]: Semigroup } -): Semigroup => - fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) - -/** - * Given a struct of `Semigroup`s returns a `Semigroup` for the struct. - * - * @since 1.0.0 - */ -export const struct = (semigroups: { readonly [K in keyof A]: Semigroup }): Semigroup< - { readonly [K in keyof A]: A[K] } -> => - fromCombine((that) => - (self) => { - const r = {} as any - for (const k in semigroups) { - if (Object.prototype.hasOwnProperty.call(semigroups, k)) { - r[k] = semigroups[k].combine(that[k])(self[k]) - } - } - return r - } - ) - /** * @since 1.0.0 */ diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index be9986c23..537d82ce9 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -6,6 +6,10 @@ import * as _ from "@fp-ts/core/typeclass/Semigroup" import * as U from "../util" describe("Semigroup", () => { + it("exports", () => { + expect(_.array).exists + }) + it("reverse", () => { const A = _.reverse(String.Semigroup) U.deepStrictEqual(pipe("a", A.combine("b")), "ba") diff --git a/Overview.md b/typeclass.md similarity index 100% rename from Overview.md rename to typeclass.md From fb293fce3549368cfa43096121116bc661324ce4 Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 17:28:22 +0100 Subject: [PATCH 47/80] Monoid: add array, readonlyArray combinators --- .changeset/popular-baboons-promise.md | 5 +++++ data.md | 2 ++ src/ReadonlyArray.ts | 11 ++--------- src/typeclass/Monoid.ts | 26 ++++++++++++++++++++++++++ test/typeclass/Monoid.ts | 4 ++++ 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 .changeset/popular-baboons-promise.md diff --git a/.changeset/popular-baboons-promise.md b/.changeset/popular-baboons-promise.md new file mode 100644 index 000000000..9446ae176 --- /dev/null +++ b/.changeset/popular-baboons-promise.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Monoid: add array, readonlyArray combinators diff --git a/data.md b/data.md index e8eae4e1e..7188d8485 100644 --- a/data.md +++ b/data.md @@ -27,3 +27,5 @@ This section covers the various modules and combinators that work with arrays. | Order | array | `Order` | `Order>` | | Semigroup | array | `A` | `Semigroup>` | | Semigroup | readonlyArray | `A` | `Semigroup>` | +| Monoid | array | `A` | `Monoid>` | +| Monoid | readonlyArray | `A` | `Monoid>` | diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 0083c5e82..cfdb335a8 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -24,6 +24,7 @@ import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monad from "@fp-ts/core/typeclass/Monad" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import * as order from "@fp-ts/core/typeclass/Order" import type { Order } from "@fp-ts/core/typeclass/Order" @@ -2176,15 +2177,7 @@ export const getSemigroup: () => Semigroup> = semigroup.read * @category instances * @since 1.0.0 */ -export const getMonoid = (): Monoid> => { - const S = getSemigroup() - return ({ - combine: S.combine, - combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(collection)(empty()), - empty: empty() - }) -} +export const getMonoid: () => Monoid> = monoid.readonlyArray /** * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index 071447afe..d574a2352 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -65,6 +65,32 @@ export const tuple = >( return fromSemigroup(semigroup.tuple(...monoids), empty) } +/** + * Given a type `A`, this function creates and returns a `Monoid` for `Array`. + * The returned `Monoid`'s empty value is the empty array. + * + * @category combinators + * @since 1.0.0 + */ +export const array = (): Monoid> => { + const S = semigroup.array() + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany(collection)([]), + empty: [] + }) +} + +/** + * Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. + * The returned `Monoid`'s empty value is the empty array. + * + * @category combinators + * @since 1.0.0 + */ +export const readonlyArray: () => Monoid> = array as any + /** * Given a struct of `Monoid`s returns a `Monoid` for the struct. * diff --git a/test/typeclass/Monoid.ts b/test/typeclass/Monoid.ts index 4e9dbae21..1ce38fb06 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -5,6 +5,10 @@ import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as U from "../util" describe("Monoid", () => { + it("exports", () => { + expect(monoid.array).exists + }) + it("min", () => { const M = monoid.min(N.Bounded) U.deepStrictEqual(M.combineAll([]), +Infinity) From e04d2d6fa52538d0f462e1ecfc2c67388dee5650 Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 17:40:14 +0100 Subject: [PATCH 48/80] data.md: add struct section --- data.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/data.md b/data.md index 7188d8485..4decf1b9f 100644 --- a/data.md +++ b/data.md @@ -10,8 +10,8 @@ This section covers the various modules and combinators that work with tuples. | Order | tuple | `[Order, Order, ...]` | `Order` | | Semigroup | tuple | `[Semigroup, Semigroup, ...]` | `Semigroup<[A, B, ...]>` | | Monoid | tuple | `[Monoid, Monoid, ...]` | `Monoid<[A, B, ...]>` | -| SemiProduct | nonEmptyTuple | `[F, F, ...]` | `F<[A, B, ...]>` | -| Product | tuple | `[F, F, ...]` (can be empty) | `F<[A, B, ...]>` | +| SemiProduct | nonEmptyTuple | `[F, F, ...]` (cannot be empty) | `F<[A, B, ...]>` | +| Product | tuple | `[F, F, ...]` | `F<[A, B, ...]>` | | Either | tuple | `[Either, Either, ...]` | `Either` | | Option | tuple | `[Option, Option, ...]` | `Option<[A, B, ...]>` | | Predicate | tuple | `[Predicate, Predicate, ...]` | `Predicate` | @@ -21,11 +21,30 @@ This section covers the various modules and combinators that work with tuples. This section covers the various modules and combinators that work with arrays. -| Module | Name | Given | To | -| ----------- | ------------- | ---------------- | ------------------------------- | -| Equivalence | array | `Equivalence` | `Equivalence>` | -| Order | array | `Order` | `Order>` | -| Semigroup | array | `A` | `Semigroup>` | -| Semigroup | readonlyArray | `A` | `Semigroup>` | -| Monoid | array | `A` | `Monoid>` | -| Monoid | readonlyArray | `A` | `Monoid>` | +| Module | Name | Given | To | +| ------------- | ------------- | ---------------- | ------------------------------- | +| Equivalence | array | `Equivalence` | `Equivalence>` | +| Order | array | `Order` | `Order>` | +| Semigroup | array | `A` | `Semigroup>` | +| Semigroup | readonlyArray | `A` | `Semigroup>` | +| Monoid | array | `A` | `Monoid>` | +| Monoid | readonlyArray | `A` | `Monoid>` | +| ReadonlyArray | getSemigroup | `A` | `Semigroup>` | +| ReadonlyArray | getMonoid | `A` | `Monoid>` | + +## structs + +This section covers the various modules and combinators that work with structs. + +| Module | Name | Given | To | +| ----------- | -------------- | ----------------------------------------------- | ----------------------------------------- | +| Equivalence | struct | `{ a: Equivalence, b: Equivalence, ... }` | `Equivalence<{ a: A, b: B, ... }>` | +| Order | NA | NA | NA | +| Semigroup | struct | `{ a: Semigroup, b: Semigroup, ... }` | `Semigroup<{ a: A, b: B, ... }>` | +| Monoid | struct | `{ a: Monoid, b: Monoid, ... }` | `Monoid<{ a: A, b: B, ... }>` | +| SemiProduct | nonEmptyStruct | `{ a: F, b: F, ... }` (cannot be empty) | `F<{ a: A, b: B, ... }>` | +| Product | struct | `{ a: F, b: F, ... }` | `F<{ a: A, b: B, ... }>` | +| Either | struct | `{ a: Either, b: Either, ... }` | `Either` | +| Option | struct | `{ a: Option, b: Option, ... }` | `Option<{ a: A, b: B }>` | +| Predicate | struct | `{ a: Predicate, b: Predicate, ... }` | `Predicate>` | +| These | struct | `{ a: These, b: These, ... }` | `These` | From 804e7270dc0ca94ea0ad4bb09c0ee190b41a1022 Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 17:58:04 +0100 Subject: [PATCH 49/80] data.md: add string section --- data.md | 15 +++++++++ src/Predicate.ts | 7 ++++ src/String.ts | 69 ++++++++++---------------------------- src/typeclass/Monoid.ts | 15 +++++++-- src/typeclass/Order.ts | 8 +++++ src/typeclass/Semigroup.ts | 8 +++++ 6 files changed, 68 insertions(+), 54 deletions(-) diff --git a/data.md b/data.md index 4decf1b9f..3f2321bb9 100644 --- a/data.md +++ b/data.md @@ -48,3 +48,18 @@ This section covers the various modules and combinators that work with structs. | Option | struct | `{ a: Option, b: Option, ... }` | `Option<{ a: A, b: B }>` | | Predicate | struct | `{ a: Predicate, b: Predicate, ... }` | `Predicate>` | | These | struct | `{ a: These, b: These, ... }` | `These` | + +## strings + +| Module | Name | Given | To | +| ----------- | ----------- | ----- | ----------------------------- | +| Equivalence | string | | `Equivalence` | +| Order | string | | `Order` | +| Semigroup | string | | `Semigroup` | +| Monoid | string | | `Monoid` | +| Predicate | isString | | `Refinement` | +| String | Equivalence | | `Equivalence` | +| String | Order | | `Order` | +| String | Semigroup | | `Semigroup` | +| String | Monoid | | `Monoid` | +| String | isString | | `Refinement` | diff --git a/src/Predicate.ts b/src/Predicate.ts index c21fb8d27..70a326370 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -35,6 +35,13 @@ export interface Refinement { (a: A): a is B } +/** + * @category guards + * @since 1.0.0 + */ +export const isString: Refinement = (u: unknown): u is string => + typeof u === "string" + /** * @category constructors * @since 1.0.0 diff --git a/src/String.ts b/src/String.ts index 950aca447..747df0de3 100644 --- a/src/String.ts +++ b/src/String.ts @@ -4,93 +4,58 @@ import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import type { Refinement } from "@fp-ts/core/Predicate" +import * as predicate from "@fp-ts/core/Predicate" import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" -import type * as order from "@fp-ts/core/typeclass/Order" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** + * @category guards * @since 1.0.0 */ -export const concat = (that: string) => (self: string): string => self + that +export const isString: Refinement = predicate.isString /** - * `string` semigroup under concatenation. - * - * @example - * import * as S from '@fp-ts/core/String' - * import { pipe } from '@fp-ts/core/Function' - * - * assert.deepStrictEqual(pipe('a', S.Semigroup.combine('b')), 'ab') - * * @category instances * @since 1.0.0 */ -export const Semigroup: semigroup.Semigroup = semigroup.fromCombine(concat) +export const Equivalence: equivalence.Equivalence = equivalence.string /** - * An empty `string`. - * + * @category instances * @since 1.0.0 */ -export const empty: "" = "" as const +export const Order: order.Order = order.string /** - * `string` monoid under concatenation. - * - * The `empty` value is `''`. - * - * @example - * import * as S from '@fp-ts/core/String' - * import { pipe } from '@fp-ts/core/Function' - * - * assert.deepStrictEqual(pipe('a', S.Monoid.combine('b')), 'ab') - * assert.deepStrictEqual(pipe('a', S.Monoid.combine(S.Monoid.empty)), 'a') + * `string` semigroup under concatenation. * * @category instances * @since 1.0.0 */ -export const Monoid: monoid.Monoid = { - ...Semigroup, - combineAll: (collection) => Semigroup.combineMany(collection)(empty), - empty -} +export const Semigroup: semigroup.Semigroup = semigroup.string /** + * `string` monoid under concatenation. + * + * The `empty` value is `''`. + * * @category instances * @since 1.0.0 */ -export const Equivalence: equivalence.Equivalence = equivalence.string +export const Monoid: monoid.Monoid = monoid.string /** - * @example - * import * as S from '@fp-ts/core/String' - * import { pipe } from '@fp-ts/core/Function' - * - * assert.deepStrictEqual(pipe('a', S.Order.compare('a')), 0) - * assert.deepStrictEqual(pipe('a', S.Order.compare('b')), -1) - * assert.deepStrictEqual(pipe('b', S.Order.compare('a')), 1) - * - * @category instances * @since 1.0.0 */ -export const Order: order.Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 -} +export const empty: "" = "" as const /** - * @example - * import * as S from '@fp-ts/core/String' - * - * assert.deepStrictEqual(S.isString('a'), true) - * assert.deepStrictEqual(S.isString(1), false) - * - * @category guards * @since 1.0.0 */ -export const isString: Refinement = (u: unknown): u is string => - typeof u === "string" +export const concat: (that: string) => (self: string) => string = semigroup.string.combine /** * @example diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index d574a2352..be0bde9dd 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -15,8 +15,6 @@ export interface Monoid extends Semigroup { } /** - * Optimised. - * * @category constructors * @since 1.0.0 */ @@ -49,13 +47,25 @@ export const max = (B: Bounded): Monoid => fromSemigroup(semigroup.max( /** * The dual of a `Monoid`, obtained by swapping the arguments of `combine`. * + * @category combinators * @since 1.0.0 */ export const reverse = (M: Monoid): Monoid => fromSemigroup(semigroup.reverse(M), M.empty) +/** + * @category instances + * @since 1.0.0 + */ +export const string: Monoid = { + ...semigroup.string, + combineAll: (collection) => semigroup.string.combineMany(collection)(""), + empty: "" +} + /** * Given a tuple of `Monoid`s returns a `Monoid` for the tuple. * + * @category combinators * @since 1.0.0 */ export const tuple = >( @@ -94,6 +104,7 @@ export const readonlyArray: () => Monoid> = array as any /** * Given a struct of `Monoid`s returns a `Monoid` for the struct. * + * @category combinators * @since 1.0.0 */ export const struct = ( diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index c2623058a..40fa3c303 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -27,6 +27,14 @@ export interface OrderTypeLambda extends TypeLambda { readonly type: Order } +/** + * @category instances + * @since 1.0.0 + */ +export const string: Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + /** * Main constructor. * diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index fe3bcca48..b9bd739cf 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -61,6 +61,14 @@ export const fromCombine = (combine: Semigroup["combine"]): Semigroup = } }) +/** + * @category instances + * @since 1.0.0 + */ +export const string: Semigroup = fromCombine((that: string) => + (self: string): string => self + that +) + /** * This function creates and returns a new `Semigroup` for a tuple of values based on the given `Semigroup`s for each element in the tuple. * The returned `Semigroup` combines two tuples of the same type by applying the corresponding `Semigroup` passed as arguments to each element in the tuple. From b0d2d117b0bb3930b9b5203e4ba95b06cf68b76f Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 18:24:53 +0100 Subject: [PATCH 50/80] data.md: add numbers section --- data.md | 20 ++++++++++++++ src/Number.ts | 56 +++++++++----------------------------- src/Predicate.ts | 7 +++++ src/typeclass/Bounded.ts | 10 +++++++ src/typeclass/Monoid.ts | 28 +++++++++++++++++++ src/typeclass/Order.ts | 11 ++++++-- src/typeclass/Semigroup.ts | 34 +++++++++++++++++++++++ 7 files changed, 121 insertions(+), 45 deletions(-) diff --git a/data.md b/data.md index 3f2321bb9..88fd116c1 100644 --- a/data.md +++ b/data.md @@ -63,3 +63,23 @@ This section covers the various modules and combinators that work with structs. | String | Semigroup | | `Semigroup` | | String | Monoid | | `Monoid` | | String | isString | | `Refinement` | + +## numbers + +| Module | Name | Given | To | +| ----------- | ----------------- | ----- | ----------------------------- | +| Equivalence | number | | `Equivalence` | +| Order | number | | `Order` | +| Bounded | number | | `Bounded` | +| Semigroup | numberSum | | `Semigroup` | +| Semigroup | numberMultiply | | `Semigroup` | +| Monoid | numberSum | | `Monoid` | +| Monoid | numberMultiply | | `Monoid` | +| Predicate | isNumber | | `Refinement` | +| Number | Equivalence | | `Equivalence` | +| Number | Order | | `Order` | +| Number | SemigroupSum | | `Semigroup` | +| Number | SemigroupMultiply | | `Semigroup` | +| Number | MonoidSum | | `Monoid` | +| Number | MonoidMultiply | | `Monoid` | +| Number | isNumber | | `Refinement` | diff --git a/src/Number.ts b/src/Number.ts index 54067783b..8aabd0ff6 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -3,28 +3,28 @@ */ import type { Ordering } from "@fp-ts/core/Ordering" import type { Refinement } from "@fp-ts/core/Predicate" -import type * as bounded from "@fp-ts/core/typeclass/Bounded" +import * as predicate from "@fp-ts/core/Predicate" +import * as bounded from "@fp-ts/core/typeclass/Bounded" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" -import type * as order from "@fp-ts/core/typeclass/Order" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** * @category guards * @since 1.0.0 */ -export const isNumber: Refinement = (u: unknown): u is number => - typeof u === "number" +export const isNumber: Refinement = predicate.isNumber /** * @since 1.0.0 */ -export const sum = (that: number) => (self: number): number => self + that +export const sum: (that: number) => (self: number) => number = semigroup.numberSum.combine /** * @since 1.0.0 */ -export const multiply = (that: number) => (self: number): number => self * that +export const multiply: (that: number) => (self: number) => number = semigroup.numberMultiply.combine /** * @since 1.0.0 @@ -51,19 +51,13 @@ export const Equivalence: equivalence.Equivalence = equivalence.number * @category instances * @since 1.0.0 */ -export const Order: order.Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 -} +export const Order: order.Order = order.number /** * @category instances * @since 1.0.0 */ -export const Bounded: bounded.Bounded = { - compare: Order.compare, - maxBound: Infinity, - minBound: -Infinity -} +export const Bounded: bounded.Bounded = bounded.number /** * `number` semigroup under addition. @@ -77,7 +71,7 @@ export const Bounded: bounded.Bounded = { * @category instances * @since 1.0.0 */ -export const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(sum) +export const SemigroupSum: semigroup.Semigroup = semigroup.numberSum /** * `number` semigroup under multiplication. @@ -91,23 +85,7 @@ export const SemigroupSum: semigroup.Semigroup = semigroup.fromCombine(s * @category instances * @since 1.0.0 */ -export const SemigroupMultiply: semigroup.Semigroup = { - combine: multiply, - combineMany: (collection) => - (self) => { - if (self === 0) { - return 0 - } - let out = self - for (const n of collection) { - if (n === 0) { - return 0 - } - out = out * n - } - return out - } -} +export const SemigroupMultiply: semigroup.Semigroup = semigroup.numberMultiply /** * `number` monoid under addition. @@ -117,11 +95,7 @@ export const SemigroupMultiply: semigroup.Semigroup = { * @category instances * @since 1.0.0 */ -export const MonoidSum: monoid.Monoid = { - ...SemigroupSum, - combineAll: (collection) => SemigroupSum.combineMany(collection)(0), - empty: 0 -} +export const MonoidSum: monoid.Monoid = monoid.numberSum /** * `number` monoid under multiplication. @@ -131,11 +105,7 @@ export const MonoidSum: monoid.Monoid = { * @category instances * @since 1.0.0 */ -export const MonoidMultiply: monoid.Monoid = { - ...SemigroupMultiply, - combineAll: (collection) => SemigroupMultiply.combineMany(collection)(1), - empty: 1 -} +export const MonoidMultiply: monoid.Monoid = monoid.numberMultiply /** * @since 1.0.0 diff --git a/src/Predicate.ts b/src/Predicate.ts index 70a326370..a4d24caa8 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -42,6 +42,13 @@ export interface Refinement { export const isString: Refinement = (u: unknown): u is string => typeof u === "string" +/** + * @category guards + * @since 1.0.0 + */ +export const isNumber: Refinement = (u: unknown): u is number => + typeof u === "number" + /** * @category constructors * @since 1.0.0 diff --git a/src/typeclass/Bounded.ts b/src/typeclass/Bounded.ts index 2459fa434..6d59f747f 100644 --- a/src/typeclass/Bounded.ts +++ b/src/typeclass/Bounded.ts @@ -22,6 +22,16 @@ export interface BoundedTypeLambda extends TypeLambda { readonly type: Bounded } +/** + * @category instances + * @since 1.0.0 + */ +export const number: Bounded = { + compare: order.number.compare, + maxBound: Infinity, + minBound: -Infinity +} + /** * Clamp a value between `minBound` and `maxBound` values. * diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index be0bde9dd..227b5e1b4 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -62,6 +62,34 @@ export const string: Monoid = { empty: "" } +/** + * `number` monoid under addition. + * + * The `empty` value is `0`. + * + * @category instances + * @since 1.0.0 + */ +export const numberSum: Monoid = { + ...semigroup.numberSum, + combineAll: (collection) => semigroup.numberSum.combineMany(collection)(0), + empty: 0 +} + +/** + * `number` monoid under multiplication. + * + * The `empty` value is `1`. + * + * @category instances + * @since 1.0.0 + */ +export const numberMultiply: Monoid = { + ...semigroup.numberMultiply, + combineAll: (collection) => semigroup.numberMultiply.combineMany(collection)(1), + empty: 1 +} + /** * Given a tuple of `Monoid`s returns a `Monoid` for the tuple. * diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 40fa3c303..89714a20c 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -2,7 +2,6 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" -import * as number from "@fp-ts/core/Number" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import type * as invariant from "@fp-ts/core/typeclass/Invariant" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" @@ -35,6 +34,14 @@ export const string: Order = { compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 } +/** + * @category instances + * @since 1.0.0 + */ +export const number: Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + /** * Main constructor. * @@ -91,7 +98,7 @@ export const array = (O: Order): Order> => return o } } - return number.Order.compare(bLen)(aLen) + return number.compare(bLen)(aLen) } ) diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index b9bd739cf..f71897e9d 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -69,6 +69,40 @@ export const string: Semigroup = fromCombine((that: string) => (self: string): string => self + that ) +/** + * `number` semigroup under addition. + * + * @category instances + * @since 1.0.0 + */ +export const numberSum: Semigroup = fromCombine((that: number) => + (self: number): number => self + that +) + +/** + * `number` semigroup under multiplication. + * + * @category instances + * @since 1.0.0 + */ +export const numberMultiply: Semigroup = { + combine: (that: number) => (self: number): number => self * that, + combineMany: (collection) => + (self) => { + if (self === 0) { + return 0 + } + let out = self + for (const n of collection) { + if (n === 0) { + return 0 + } + out = out * n + } + return out + } +} + /** * This function creates and returns a new `Semigroup` for a tuple of values based on the given `Semigroup`s for each element in the tuple. * The returned `Semigroup` combines two tuples of the same type by applying the corresponding `Semigroup` passed as arguments to each element in the tuple. From d6dc0e1c220fdb253f74a77fd33fa2668a40f89e Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 18:58:59 +0100 Subject: [PATCH 51/80] data.md: add booleans section --- data.md | 19 +++++++ src/Boolean.ts | 100 ++++++++++++------------------------- src/Predicate.ts | 7 +++ src/typeclass/Monoid.ts | 28 +++++++++++ src/typeclass/Order.ts | 8 +++ src/typeclass/Semigroup.ts | 44 ++++++++++++++++ 6 files changed, 137 insertions(+), 69 deletions(-) diff --git a/data.md b/data.md index 88fd116c1..861e547c7 100644 --- a/data.md +++ b/data.md @@ -83,3 +83,22 @@ This section covers the various modules and combinators that work with structs. | Number | MonoidSum | | `Monoid` | | Number | MonoidMultiply | | `Monoid` | | Number | isNumber | | `Refinement` | + +## booleans + +| Module | Name | Given | To | +| ----------- | ----------- | ----- | ------------------------------ | +| Equivalence | boolean | | `Equivalence` | +| Order | boolean | | `Order` | +| Semigroup | booleanAny | | `Semigroup` | +| Semigroup | booleanAll | | `Semigroup` | +| Monoid | booleanAny | | `Monoid` | +| Monoid | booleanAll | | `Monoid` | +| Predicate | isBoolean | | `Refinement` | +| Boolean | Equivalence | | `Equivalence` | +| Boolean | Order | | `Order` | +| Boolean | booleanAny | | `Semigroup` | +| Boolean | booleanAll | | `Semigroup` | +| Boolean | booleanAny | | `Monoid` | +| Boolean | booleanAll | | `Monoid` | +| Boolean | isBoolean | | `Refinement` | diff --git a/src/Boolean.ts b/src/Boolean.ts index da22ddc9e..1b2b4e0c1 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -3,35 +3,17 @@ */ import type { LazyArg } from "@fp-ts/core/Function" import type { Refinement } from "@fp-ts/core/Predicate" +import * as predicate from "@fp-ts/core/Predicate" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" -import type * as order from "@fp-ts/core/typeclass/Order" -import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** * @category guards * @since 1.0.0 */ -export const isBoolean: Refinement = (u: unknown): u is boolean => - typeof u === "boolean" - -/** - * @category combinators - * @since 1.0.0 - */ -export const and = (that: boolean) => (self: boolean): boolean => self && that - -/** - * @category combinators - * @since 1.0.0 - */ -export const or = (that: boolean) => (self: boolean): boolean => self || that - -/** - * @category combinators - * @since 1.0.0 - */ -export const not = (self: boolean): boolean => !self +export const isBoolean: Refinement = predicate.isBoolean /** * Defines the match over a boolean value. @@ -57,6 +39,18 @@ export const not = (self: boolean): boolean => !self export const match = (onFalse: LazyArg, onTrue: LazyArg) => (value: boolean): A | B => value ? onTrue() : onFalse() +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.boolean + +/** + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = order.boolean + /** * `boolean` semigroup under conjunction. * @@ -70,21 +64,7 @@ export const match = (onFalse: LazyArg, onTrue: LazyArg) => * @category instances * @since 1.0.0 */ -export const SemigroupAll: semigroup.Semigroup = { - combine: and, - combineMany: (collection) => - (self) => { - if (self === false) { - return false - } - for (const b of collection) { - if (b === false) { - return false - } - } - return true - } -} +export const SemigroupAll: semigroup.Semigroup = semigroup.booleanAll /** * `boolean` semigroup under disjunction. @@ -100,21 +80,7 @@ export const SemigroupAll: semigroup.Semigroup = { * @category instances * @since 1.0.0 */ -export const SemigroupAny: semigroup.Semigroup = { - combine: or, - combineMany: (collection) => - (self) => { - if (self === true) { - return true - } - for (const b of collection) { - if (b === true) { - return true - } - } - return false - } -} +export const SemigroupAny: semigroup.Semigroup = semigroup.booleanAny /** * `boolean` monoid under conjunction. @@ -124,11 +90,7 @@ export const SemigroupAny: semigroup.Semigroup = { * @category instances * @since 1.0.0 */ -export const MonoidAll: monoid.Monoid = { - ...SemigroupAll, - combineAll: (all) => SemigroupAll.combineMany(all)(true), - empty: true -} +export const MonoidAll: monoid.Monoid = monoid.booleanAll /** * `boolean` monoid under disjunction. @@ -138,22 +100,22 @@ export const MonoidAll: monoid.Monoid = { * @category instances * @since 1.0.0 */ -export const MonoidAny: monoid.Monoid = { - ...SemigroupAny, - combineAll: (all) => SemigroupAny.combineMany(all)(false), - empty: false -} +export const MonoidAny: monoid.Monoid = monoid.booleanAny /** - * @category instances + * @category combinators * @since 1.0.0 */ -export const Equivalence: equivalence.Equivalence = equivalence.boolean +export const and: (that: boolean) => (self: boolean) => boolean = semigroup.booleanAll.combine /** - * @category instances + * @category combinators * @since 1.0.0 */ -export const Order: order.Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 -} +export const or: (that: boolean) => (self: boolean) => boolean = semigroup.booleanAny.combine + +/** + * @category combinators + * @since 1.0.0 + */ +export const not = (self: boolean): boolean => !self diff --git a/src/Predicate.ts b/src/Predicate.ts index a4d24caa8..4dd6a901f 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -49,6 +49,13 @@ export const isString: Refinement = (u: unknown): u is string = export const isNumber: Refinement = (u: unknown): u is number => typeof u === "number" +/** + * @category guards + * @since 1.0.0 + */ +export const isBoolean: Refinement = (u: unknown): u is boolean => + typeof u === "boolean" + /** * @category constructors * @since 1.0.0 diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index 227b5e1b4..79f953ac9 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -90,6 +90,34 @@ export const numberMultiply: Monoid = { empty: 1 } +/** + * `boolean` monoid under conjunction. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAll: Monoid = { + ...semigroup.booleanAll, + combineAll: (all) => semigroup.booleanAll.combineMany(all)(true), + empty: true +} + +/** + * `boolean` monoid under disjunction. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAny: Monoid = { + ...semigroup.booleanAny, + combineAll: (all) => semigroup.booleanAny.combineMany(all)(false), + empty: false +} + /** * Given a tuple of `Monoid`s returns a `Monoid` for the tuple. * diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 89714a20c..ed00a3556 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -42,6 +42,14 @@ export const number: Order = { compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 } +/** + * @category instances + * @since 1.0.0 + */ +export const boolean: Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + /** * Main constructor. * diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index f71897e9d..6139abb1b 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -103,6 +103,50 @@ export const numberMultiply: Semigroup = { } } +/** + * `boolean` semigroup under conjunction. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAll: Semigroup = { + combine: (that: boolean) => (self: boolean): boolean => self && that, + combineMany: (collection) => + (self) => { + if (self === false) { + return false + } + for (const b of collection) { + if (b === false) { + return false + } + } + return true + } +} + +/** + * `boolean` semigroup under disjunction. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAny: Semigroup = { + combine: (that: boolean) => (self: boolean): boolean => self || that, + combineMany: (collection) => + (self) => { + if (self === true) { + return true + } + for (const b of collection) { + if (b === true) { + return true + } + } + return false + } +} + /** * This function creates and returns a new `Semigroup` for a tuple of values based on the given `Semigroup`s for each element in the tuple. * The returned `Semigroup` combines two tuples of the same type by applying the corresponding `Semigroup` passed as arguments to each element in the tuple. From 9ee83b0c060ef5418f3aa4919315b6a169951ec3 Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 19:03:51 +0100 Subject: [PATCH 52/80] Order: add bigint instance --- .changeset/nine-bags-end.md | 5 +++++ data.md | 13 +++++++++++++ src/typeclass/Order.ts | 8 ++++++++ test/typeclass/Order.ts | 8 ++++++++ 4 files changed, 34 insertions(+) create mode 100644 .changeset/nine-bags-end.md diff --git a/.changeset/nine-bags-end.md b/.changeset/nine-bags-end.md new file mode 100644 index 000000000..5ff72f442 --- /dev/null +++ b/.changeset/nine-bags-end.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Order: add bigint instance diff --git a/data.md b/data.md index 861e547c7..0dac7a028 100644 --- a/data.md +++ b/data.md @@ -102,3 +102,16 @@ This section covers the various modules and combinators that work with structs. | Boolean | booleanAny | | `Monoid` | | Boolean | booleanAll | | `Monoid` | | Boolean | isBoolean | | `Refinement` | + +## bigints + +| Module | Name | Given | To | +| ----------- | ------ | ----- | --------------------- | +| Equivalence | bigint | | `Equivalence` | +| Order | bigint | | `Order` | + +## symbols + +| Module | Name | Given | To | +| ----------- | ------ | ----- | --------------------- | +| Equivalence | symbol | | `Equivalence` | diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index ed00a3556..335929de6 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -50,6 +50,14 @@ export const boolean: Order = { compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 } +/** + * @category instances + * @since 1.0.0 + */ +export const bigint: Order = { + compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 +} + /** * Main constructor. * diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 7910bbc90..3333f602f 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -7,6 +7,14 @@ import * as _ from "@fp-ts/core/typeclass/Order" import * as U from "../util" describe("Order", () => { + it("bigint", () => { + const O = _.bigint + expect(pipe(1n, _.lessThanOrEqualTo(O)(2n))).toBe(true) + expect(pipe(1n, _.lessThanOrEqualTo(O)(1n))).toBe(true) + expect(pipe(1n, _.lessThan(O)(1n))).toBe(false) + expect(pipe(1n, _.lessThanOrEqualTo(O)(0n))).toBe(false) + }) + it("tuple", () => { const O = _.tuple(string.Order, number.Order, boolean.Order) U.deepStrictEqual(pipe(["a", 1, true], O.compare(["b", 2, true])), -1) From 5eca4d03a5fa1002e3a865c94b837aa3322f591c Mon Sep 17 00:00:00 2001 From: gcanti Date: Thu, 19 Jan 2023 19:17:00 +0100 Subject: [PATCH 53/80] add ReadonlyRecord module --- .changeset/gold-teachers-admire.md | 5 ++++ data.md | 11 +++++++ dtslint/ts4.7/ReadonlyRecord.ts | 31 +++++++++++++++++++ src/ReadonlyRecord.ts | 48 ++++++++++++++++++++++++++++++ src/index.ts | 12 +++++++- src/typeclass/Equivalence.ts | 3 +- test/ReadonlyRecord.ts | 24 +++++++++++++++ 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 .changeset/gold-teachers-admire.md create mode 100644 dtslint/ts4.7/ReadonlyRecord.ts create mode 100644 src/ReadonlyRecord.ts create mode 100644 test/ReadonlyRecord.ts diff --git a/.changeset/gold-teachers-admire.md b/.changeset/gold-teachers-admire.md new file mode 100644 index 000000000..9ca396e17 --- /dev/null +++ b/.changeset/gold-teachers-admire.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add ReadonlyRecord module diff --git a/data.md b/data.md index 0dac7a028..f6d6cd587 100644 --- a/data.md +++ b/data.md @@ -49,6 +49,17 @@ This section covers the various modules and combinators that work with structs. | Predicate | struct | `{ a: Predicate, b: Predicate, ... }` | `Predicate>` | | These | struct | `{ a: These, b: These, ... }` | `These` | +## records + +This section covers the various modules and combinators that work with records. + +| Module | Name | Given | To | +| -------------- | ------------- | -------------------------------------------- | -------------------------------- | +| Equivalence | record | `Equivalence` | `Equivalence>` | +| ReadonlyRecord | get | `key: string`, `ReadonlyRecord` | `Option` | +| ReadonlyRecord | replaceOption | `key: string`, `B`, `ReadonlyRecord` | `Option>` | +| ReadonlyRecord | modifyOption | `key: string`, `A => B`, `ReadonlyRecord` | `Option>` | + ## strings | Module | Name | Given | To | diff --git a/dtslint/ts4.7/ReadonlyRecord.ts b/dtslint/ts4.7/ReadonlyRecord.ts new file mode 100644 index 000000000..2dd5d3362 --- /dev/null +++ b/dtslint/ts4.7/ReadonlyRecord.ts @@ -0,0 +1,31 @@ +import { pipe } from '@fp-ts/core/Function' +import * as _ from '@fp-ts/core/ReadonlyRecord' + +declare const r: Record + +// +// get +// + +// $ExpectType Option +pipe(r, _.get('a')) + +// +// replaceOption +// + +// $ExpectType Option> +pipe(r, _.replaceOption('a', 2)) + +// $ExpectType Option> +pipe(r, _.replaceOption('a', true)) + +// +// modifyOption +// + +// $ExpectType Option> +pipe(r, _.modifyOption('a', () => 2)) + +// $ExpectType Option> +pipe(r, _.modifyOption('a', () => true)) diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts new file mode 100644 index 000000000..a3f49e039 --- /dev/null +++ b/src/ReadonlyRecord.ts @@ -0,0 +1,48 @@ +/** + * @since 1.0.0 + */ + +import type { Option } from "@fp-ts/core/Option" +import * as O from "@fp-ts/core/Option" + +/** + * @category models + * @since 1.0.0 + */ +export interface ReadonlyRecord { + readonly [x: string]: A +} + +/** + * This function provides a safe way to read a value at a particular key from a record. + * + * @category getters + * @since 1.0.0 + */ +export const get = (key: string) => + (self: ReadonlyRecord): Option => + Object.prototype.hasOwnProperty.call(self, key) ? O.some(self[key]) : O.none() + +/** + * @since 1.0.0 + */ +export const replaceOption = ( + key: string, + b: B +): (self: ReadonlyRecord) => Option> => modifyOption(key, () => b) + +/** + * Apply a function to the element at the specified key, creating a new record, + * or return `None` if the key doesn't exist. + * + * @since 1.0.0 + */ +export const modifyOption = (key: string, f: (a: A) => B) => + (self: ReadonlyRecord): Option> => { + if (!Object.prototype.hasOwnProperty.call(self, key)) { + return O.none() + } + const out: Record = { ...self } + out[key] = f(self[key]) + return O.some(out) + } diff --git a/src/index.ts b/src/index.ts index 651a2e6f8..8fc085374 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import * as hkt from "@fp-ts/core/HKT" // ------------------------------------------------------------------------------------- -// typeclasses +// data types // ------------------------------------------------------------------------------------- import * as boolean from "@fp-ts/core/Boolean" @@ -21,8 +21,14 @@ import * as option from "@fp-ts/core/Option" import * as ordering from "@fp-ts/core/Ordering" import * as predicate from "@fp-ts/core/Predicate" import * as readonlyArray from "@fp-ts/core/ReadonlyArray" +import * as readonlyRecord from "@fp-ts/core/ReadonlyRecord" import * as string from "@fp-ts/core/String" import * as these from "@fp-ts/core/These" + +// ------------------------------------------------------------------------------------- +// typeclasses +// ------------------------------------------------------------------------------------- + import * as alternative from "@fp-ts/core/typeclass/Alternative" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" @@ -192,6 +198,10 @@ export { * @since 1.0.0 */ readonlyArray, + /** + * @since 1.0.0 + */ + readonlyRecord, /** * @category typeclass * @since 1.0.0 diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index ee2ee8fcf..6e6f5d38e 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -6,6 +6,7 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" +import type { ReadonlyRecord } from "@fp-ts/core/ReadonlyRecord" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import type * as invariant from "@fp-ts/core/typeclass/Invariant" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" @@ -122,7 +123,7 @@ export const struct = ( */ export const record = ( equivalence: Equivalence -): Equivalence<{ readonly [x: string]: A }> => +): Equivalence> => (x, y) => { const keys = Object.keys(x) if (Object.keys(y).length !== keys.length) { diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts new file mode 100644 index 000000000..c8127e047 --- /dev/null +++ b/test/ReadonlyRecord.ts @@ -0,0 +1,24 @@ +import { pipe } from "@fp-ts/core/Function" +import * as O from "@fp-ts/core/Option" +import * as RR from "@fp-ts/core/ReadonlyRecord" + +describe.concurrent("ReadonlyRecord", () => { + it("get", () => { + expect(pipe({}, RR.get("a"))).toEqual(O.none()) + expect(pipe({ a: 1 }, RR.get("a"))).toEqual(O.some(1)) + }) + + it("modifyOption", () => { + expect(pipe({}, RR.replaceOption("a", 2))).toEqual(O.none()) + expect(pipe({ a: 1 }, RR.replaceOption("a", 2))).toEqual(O.some({ a: 2 })) + expect(pipe({ a: 1 }, RR.replaceOption("a", true))).toEqual(O.some({ a: true })) + }) + + it("modifyOption", () => { + expect(pipe({}, RR.modifyOption("a", (n: number) => n + 1))).toEqual(O.none()) + expect(pipe({ a: 1 }, RR.modifyOption("a", (n: number) => n + 1))).toEqual(O.some({ a: 2 })) + expect(pipe({ a: 1 }, RR.modifyOption("a", (n: number) => String(n)))).toEqual( + O.some({ a: "1" }) + ) + }) +}) From f2ffe2a5c394612155c252cd769f4db75dcefbb5 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 08:52:41 +0100 Subject: [PATCH 54/80] revert _tag changes --- src/Either.ts | 4 ++-- src/Option.ts | 4 ++-- src/These.ts | 31 ++++++++++++++----------------- src/internal/Either.ts | 12 +++++------- src/internal/Option.ts | 10 +++++----- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/Either.ts b/src/Either.ts index 98e084414..a11d9e99a 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -45,7 +45,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type Left = { - readonly _tag: "@fp-ts/core/Either/Left" + readonly _tag: "Left" readonly left: E } @@ -54,7 +54,7 @@ export type Left = { * @since 1.0.0 */ export type Right = { - readonly _tag: "@fp-ts/core/Either/Right" + readonly _tag: "Right" readonly right: A } diff --git a/src/Option.ts b/src/Option.ts index 1dc3b3724..0d471a218 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -51,7 +51,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type None = { - readonly _tag: "@fp-ts/core/Option/None" + readonly _tag: "None" } /** @@ -59,7 +59,7 @@ export type None = { * @since 1.0.0 */ export type Some = { - readonly _tag: "@fp-ts/core/Option/Some" + readonly _tag: "Some" readonly value: A } diff --git a/src/These.ts b/src/These.ts index e9b1c9e71..b4b076a36 100644 --- a/src/These.ts +++ b/src/These.ts @@ -50,7 +50,7 @@ import * as traversable from "@fp-ts/core/typeclass/Traversable" * @since 1.0.0 */ export type Both = { - readonly _tag: "@fp-ts/core/These/Both" + readonly _tag: "Both" readonly left: E readonly right: A } @@ -87,13 +87,13 @@ export interface ValidatedTypeLambda extends TypeLambda { * @category constructors * @since 1.0.0 */ -export const left = (left: E): These => ({ _tag: "@fp-ts/core/Either/Left", left }) +export const left = (left: E): These => ({ _tag: "Left", left }) /** * @category constructors * @since 1.0.0 */ -export const right = (right: A): These => ({ _tag: "@fp-ts/core/Either/Right", right }) +export const right = (right: A): These => ({ _tag: "Right", right }) /** * Alias of `right`. @@ -108,7 +108,7 @@ export const of = right * @since 1.0.0 */ export const both = (left: E, right: A): These => ({ - _tag: "@fp-ts/core/These/Both", + _tag: "Both", left, right }) @@ -158,11 +158,11 @@ export const match = ( ) => (self: These): B | C | D => { switch (self._tag) { - case "@fp-ts/core/Either/Left": + case "Left": return onLeft(self.left) - case "@fp-ts/core/Either/Right": + case "Right": return onRight(self.right) - case "@fp-ts/core/These/Both": + case "Both": return onBoth(self.left, self.right) } } @@ -182,15 +182,14 @@ export const reverse: (self: These) => These = match( * @category guards * @since 1.0.0 */ -export const isLeft = (self: These): self is Left => - self._tag === "@fp-ts/core/Either/Left" +export const isLeft = (self: These): self is Left => self._tag === "Left" /** * @category guards * @since 1.0.0 */ export const isLeftOrBoth = (self: These): self is Left | Both => - self._tag !== "@fp-ts/core/Either/Right" + self._tag !== "Right" /** * Returns `true` if the these is an instance of `Right`, `false` otherwise @@ -198,15 +197,14 @@ export const isLeftOrBoth = (self: These): self is Left | Both(self: These): self is Right => - self._tag === "@fp-ts/core/Either/Right" +export const isRight = (self: These): self is Right => self._tag === "Right" /** * @category guards * @since 1.0.0 */ export const isRightOrBoth = (self: These): self is Right | Both => - self._tag !== "@fp-ts/core/Either/Left" + self._tag !== "Left" /** * Returns `true` if the these is an instance of `Both`, `false` otherwise @@ -214,8 +212,7 @@ export const isRightOrBoth = (self: These): self is Right | Both< * @category guards * @since 1.0.0 */ -export const isBoth = (self: These): self is Both => - self._tag === "@fp-ts/core/These/Both" +export const isBoth = (self: These): self is Both => self._tag === "Both" /** * Returns `true` if the specified value is an instance of `These`, `false` @@ -227,8 +224,8 @@ export const isBoth = (self: These): self is Both => export const isThese = (u: unknown): u is These => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "@fp-ts/core/Either/Left" || u["_tag"] === "@fp-ts/core/Either/Right" || - u["_tag"] === "@fp-ts/core/These/Both") + (u["_tag"] === "Left" || u["_tag"] === "Right" || + u["_tag"] === "Both") /** * Constructs a new `These` from a function that might throw. diff --git a/src/internal/Either.ts b/src/internal/Either.ts index c8d2867d8..fbc5bce28 100644 --- a/src/internal/Either.ts +++ b/src/internal/Either.ts @@ -10,21 +10,19 @@ import type { Option } from "@fp-ts/core/Option" /** @internal */ export const isEither = (u: unknown): u is Either => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "@fp-ts/core/Either/Left" || u["_tag"] === "@fp-ts/core/Either/Right") + (u["_tag"] === "Left" || u["_tag"] === "Right") /** @internal */ -export const isLeft = (ma: Either): ma is Left => - ma._tag === "@fp-ts/core/Either/Left" +export const isLeft = (ma: Either): ma is Left => ma._tag === "Left" /** @internal */ -export const isRight = (ma: Either): ma is Right => - ma._tag === "@fp-ts/core/Either/Right" +export const isRight = (ma: Either): ma is Right => ma._tag === "Right" /** @internal */ -export const left = (e: E): Either => ({ _tag: "@fp-ts/core/Either/Left", left: e }) +export const left = (e: E): Either => ({ _tag: "Left", left: e }) /** @internal */ -export const right = (a: A): Either => ({ _tag: "@fp-ts/core/Either/Right", right: a }) +export const right = (a: A): Either => ({ _tag: "Right", right: a }) /** @internal */ export const getLeft = ( diff --git a/src/internal/Option.ts b/src/internal/Option.ts index 04e3586ac..c3a101ea6 100644 --- a/src/internal/Option.ts +++ b/src/internal/Option.ts @@ -7,19 +7,19 @@ import type { None, Option, Some } from "@fp-ts/core/Option" /** @internal */ export const isOption = (u: unknown): u is Option => typeof u === "object" && u != null && "_tag" in u && - (u["_tag"] === "@fp-ts/core/Option/None" || u["_tag"] === "@fp-ts/core/Option/Some") + (u["_tag"] === "None" || u["_tag"] === "Some") /** @internal */ -export const isNone = (fa: Option): fa is None => fa._tag === "@fp-ts/core/Option/None" +export const isNone = (fa: Option): fa is None => fa._tag === "None" /** @internal */ -export const isSome = (fa: Option): fa is Some => fa._tag === "@fp-ts/core/Option/Some" +export const isSome = (fa: Option): fa is Some => fa._tag === "Some" /** @internal */ -export const none: Option = { _tag: "@fp-ts/core/Option/None" } +export const none: Option = { _tag: "None" } /** @internal */ -export const some = (a: A): Option => ({ _tag: "@fp-ts/core/Option/Some", value: a }) +export const some = (a: A): Option => ({ _tag: "Some", value: a }) /** @internal */ export const fromNullable = ( From 50bec74a717f12e0351dcbc68297797d039f04ad Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 09:23:32 +0100 Subject: [PATCH 55/80] add Bigint module --- data.md | 19 ++++++++ src/Bigint.ts | 88 ++++++++++++++++++++++++++++++++++++++ src/Predicate.ts | 6 +++ src/index.ts | 5 +++ src/typeclass/Monoid.ts | 28 ++++++++++++ src/typeclass/Semigroup.ts | 34 +++++++++++++++ test/Bigint.ts | 61 ++++++++++++++++++++++++++ 7 files changed, 241 insertions(+) create mode 100644 src/Bigint.ts create mode 100644 test/Bigint.ts diff --git a/data.md b/data.md index f6d6cd587..59e946ffe 100644 --- a/data.md +++ b/data.md @@ -126,3 +126,22 @@ This section covers the various modules and combinators that work with records. | Module | Name | Given | To | | ----------- | ------ | ----- | --------------------- | | Equivalence | symbol | | `Equivalence` | + +## bigints + +| Module | Name | Given | To | +| ----------- | ----------------- | ----- | ----------------------------- | +| Equivalence | bigint | | `Equivalence` | +| Order | bigint | | `Order` | +| Semigroup | bigintSum | | `Semigroup` | +| Semigroup | bigintMultiply | | `Semigroup` | +| Monoid | bigintSum | | `Monoid` | +| Monoid | bigintMultiply | | `Monoid` | +| Predicate | isBigint | | `Refinement` | +| Bigint | Equivalence | | `Equivalence` | +| Bigint | Order | | `Order` | +| Bigint | SemigroupSum | | `Semigroup` | +| Bigint | SemigroupMultiply | | `Semigroup` | +| Bigint | MonoidSum | | `Monoid` | +| Bigint | MonoidMultiply | | `Monoid` | +| Bigint | isBigint | | `Refinement` | diff --git a/src/Bigint.ts b/src/Bigint.ts new file mode 100644 index 000000000..4c3095dce --- /dev/null +++ b/src/Bigint.ts @@ -0,0 +1,88 @@ +/** + * @since 1.0.0 + */ + +import * as predicate from "@fp-ts/core/Predicate" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @category guards + * @since 1.0.0 + */ +export const isBigint: (u: unknown) => u is bigint = predicate.isBigInt + +/** + * @since 1.0.0 + */ +export const sum: (that: bigint) => (self: bigint) => bigint = semigroup.bigintSum.combine + +/** + * @since 1.0.0 + */ +export const multiply: (that: bigint) => (self: bigint) => bigint = semigroup.bigintMultiply.combine + +/** + * @since 1.0.0 + */ +export const sub = (that: bigint) => (self: bigint): bigint => self - that + +/** + * @since 1.0.0 + */ +export const increment = (n: bigint): bigint => n + 1n + +/** + * @since 1.0.0 + */ +export const decrement = (n: bigint): bigint => n - 1n + +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.bigint + +/** + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = order.bigint + +/** + * `bigint` semigroup under addition. + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupSum: semigroup.Semigroup = semigroup.bigintSum + +/** + * `bigint` semigroup under multiplication. + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMultiply: semigroup.Semigroup = semigroup.bigintMultiply + +/** + * `bigint` monoid under addition. + * + * The `empty` value is `0n`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidSum: monoid.Monoid = monoid.bigintSum + +/** + * `bigint` monoid under multiplication. + * + * The `empty` value is `1n`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMultiply: monoid.Monoid = monoid.bigintMultiply diff --git a/src/Predicate.ts b/src/Predicate.ts index 4dd6a901f..9a34b85fc 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -56,6 +56,12 @@ export const isNumber: Refinement = (u: unknown): u is number = export const isBoolean: Refinement = (u: unknown): u is boolean => typeof u === "boolean" +/** + * @category guards + * @since 1.0.0 + */ +export const isBigInt = (u: unknown): u is bigint => typeof u === "bigint" + /** * @category constructors * @since 1.0.0 diff --git a/src/index.ts b/src/index.ts index 8fc085374..fc7d251ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import * as hkt from "@fp-ts/core/HKT" // data types // ------------------------------------------------------------------------------------- +import * as bigint from "@fp-ts/core/Bigint" import * as boolean from "@fp-ts/core/Boolean" import * as either from "@fp-ts/core/Either" import * as _function from "@fp-ts/core/Function" @@ -77,6 +78,10 @@ export { * @since 1.0.0 */ bicovariant, + /** + * @since 1.0.0 + */ + bigint, /** * @since 1.0.0 */ diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index 79f953ac9..55081696b 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -90,6 +90,34 @@ export const numberMultiply: Monoid = { empty: 1 } +/** + * `number` monoid under addition. + * + * The `bigint` value is `0n`. + * + * @category instances + * @since 1.0.0 + */ +export const bigintSum: Monoid = { + ...semigroup.bigintSum, + combineAll: (collection) => semigroup.bigintSum.combineMany(collection)(0n), + empty: 0n +} + +/** + * `bigint` monoid under multiplication. + * + * The `empty` value is `1n`. + * + * @category instances + * @since 1.0.0 + */ +export const bigintMultiply: Monoid = { + ...semigroup.bigintMultiply, + combineAll: (collection) => semigroup.bigintMultiply.combineMany(collection)(1n), + empty: 1n +} + /** * `boolean` monoid under conjunction. * diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 6139abb1b..6673755bb 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -103,6 +103,40 @@ export const numberMultiply: Semigroup = { } } +/** + * `bigint` semigroup under addition. + * + * @category instances + * @since 1.0.0 + */ +export const bigintSum: Semigroup = fromCombine((that: bigint) => + (self: bigint): bigint => self + that +) + +/** + * `bigint` semigroup under multiplication. + * + * @category instances + * @since 1.0.0 + */ +export const bigintMultiply: Semigroup = { + combine: (that: bigint) => (self: bigint): bigint => self * that, + combineMany: (collection) => + (self) => { + if (self === 0n) { + return 0n + } + let out = self + for (const n of collection) { + if (n === 0n) { + return 0n + } + out = out * n + } + return out + } +} + /** * `boolean` semigroup under conjunction. * diff --git a/test/Bigint.ts b/test/Bigint.ts new file mode 100644 index 000000000..cccc0bd48 --- /dev/null +++ b/test/Bigint.ts @@ -0,0 +1,61 @@ +import * as Bigint from "@fp-ts/core/Bigint" +import { pipe } from "@fp-ts/core/Function" +import { deepStrictEqual } from "@fp-ts/core/test/util" + +describe.concurrent("Bigint", () => { + it("isNumber", () => { + expect(Bigint.isBigint(1n)).toEqual(true) + expect(Bigint.isBigint(1)).toEqual(false) + expect(Bigint.isBigint("a")).toEqual(false) + expect(Bigint.isBigint(true)).toEqual(false) + }) + + it("sum", () => { + deepStrictEqual(Bigint.sum(1n)(2n), 3n) + }) + + it("sub", () => { + deepStrictEqual(Bigint.sub(1n)(2n), 1n) + }) + + it("multiply", () => { + deepStrictEqual(Bigint.multiply(3n)(2n), 6n) + }) + + it("increment", () => { + deepStrictEqual(Bigint.increment(2n), 3n) + }) + + it("decrement", () => { + deepStrictEqual(Bigint.decrement(2n), 1n) + }) + + it("Equivalence", () => { + expect(Bigint.Equivalence(1n, 1n)).toBe(true) + expect(Bigint.Equivalence(1n, 2n)).toBe(false) + }) + + it("Order", () => { + deepStrictEqual(pipe(1n, Bigint.Order.compare(2n)), -1) + deepStrictEqual(pipe(2n, Bigint.Order.compare(1n)), 1) + deepStrictEqual(pipe(2n, Bigint.Order.compare(2n)), 0) + }) + + it("SemigroupSum", () => { + deepStrictEqual(pipe(2n, Bigint.SemigroupSum.combine(3n)), 5n) + }) + + it("MonoidSum", () => { + deepStrictEqual(Bigint.MonoidSum.combineAll([1n, 2n, 3n]), 6n) + }) + + it("SemigroupMultiply", () => { + deepStrictEqual(pipe(2n, Bigint.SemigroupMultiply.combine(3n)), 6n) + deepStrictEqual(pipe(0n, Bigint.SemigroupMultiply.combineMany([1n, 2n, 3n])), 0n) + deepStrictEqual(pipe(2n, Bigint.SemigroupMultiply.combineMany([1n, 0n, 3n])), 0n) + }) + + it("MonoidMultiply", () => { + deepStrictEqual(Bigint.MonoidMultiply.combineAll([2n, 3n, 4n]), 24n) + }) +}) From 6ced8da72f9939ee17f7240e30cb21ca1ba83582 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 09:29:32 +0100 Subject: [PATCH 56/80] add Symbol module --- data.md | 8 +++++--- src/Predicate.ts | 6 ++++++ src/Symbol.ts | 11 +++++++++++ src/index.ts | 5 +++++ test/Bigint.ts | 2 +- test/Symbol.ts | 11 +++++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/Symbol.ts create mode 100644 test/Symbol.ts diff --git a/data.md b/data.md index 59e946ffe..64e22438d 100644 --- a/data.md +++ b/data.md @@ -123,9 +123,11 @@ This section covers the various modules and combinators that work with records. ## symbols -| Module | Name | Given | To | -| ----------- | ------ | ----- | --------------------- | -| Equivalence | symbol | | `Equivalence` | +| Module | Name | Given | To | +| ----------- | -------- | ----- | ----------------------------- | +| Equivalence | symbol | | `Equivalence` | +| Predicate | isSymbol | | `Refinement` | +| Symbol | isSymbol | | `Refinement` | ## bigints diff --git a/src/Predicate.ts b/src/Predicate.ts index 9a34b85fc..755bd42c3 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -62,6 +62,12 @@ export const isBoolean: Refinement = (u: unknown): u is boolea */ export const isBigInt = (u: unknown): u is bigint => typeof u === "bigint" +/** + * @category guards + * @since 1.0.0 + */ +export const isSymbol = (u: unknown): u is symbol => typeof u === "symbol" + /** * @category constructors * @since 1.0.0 diff --git a/src/Symbol.ts b/src/Symbol.ts new file mode 100644 index 000000000..9abf22a9e --- /dev/null +++ b/src/Symbol.ts @@ -0,0 +1,11 @@ +/** + * @since 1.0.0 + */ + +import * as predicate from "@fp-ts/core/Predicate" + +/** + * @category guards + * @since 1.0.0 + */ +export const isSymbol: (u: unknown) => u is symbol = predicate.isSymbol diff --git a/src/index.ts b/src/index.ts index fc7d251ae..e4948f9e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ import * as predicate from "@fp-ts/core/Predicate" import * as readonlyArray from "@fp-ts/core/ReadonlyArray" import * as readonlyRecord from "@fp-ts/core/ReadonlyRecord" import * as string from "@fp-ts/core/String" +import * as symbol from "@fp-ts/core/Symbol" import * as these from "@fp-ts/core/These" // ------------------------------------------------------------------------------------- @@ -236,6 +237,10 @@ export { * @since 1.0.0 */ string, + /** + * @since 1.0.0 + */ + symbol, /** * @since 1.0.0 */ diff --git a/test/Bigint.ts b/test/Bigint.ts index cccc0bd48..77a0fbb11 100644 --- a/test/Bigint.ts +++ b/test/Bigint.ts @@ -3,7 +3,7 @@ import { pipe } from "@fp-ts/core/Function" import { deepStrictEqual } from "@fp-ts/core/test/util" describe.concurrent("Bigint", () => { - it("isNumber", () => { + it("isBigint", () => { expect(Bigint.isBigint(1n)).toEqual(true) expect(Bigint.isBigint(1)).toEqual(false) expect(Bigint.isBigint("a")).toEqual(false) diff --git a/test/Symbol.ts b/test/Symbol.ts new file mode 100644 index 000000000..85b8cecd2 --- /dev/null +++ b/test/Symbol.ts @@ -0,0 +1,11 @@ +import * as S from "@fp-ts/core/Symbol" + +describe.concurrent("Symbol", () => { + it("isSymbol", () => { + expect(S.isSymbol(Symbol.for("@fp-ts/core/test/a"))).toEqual(true) + expect(S.isSymbol(1n)).toEqual(false) + expect(S.isSymbol(1)).toEqual(false) + expect(S.isSymbol("a")).toEqual(false) + expect(S.isSymbol(true)).toEqual(false) + }) +}) From d5e30f588a7cc2e0900b22d89c250238b5fefb6e Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 09:38:22 +0100 Subject: [PATCH 57/80] add Struct module --- src/Struct.ts | 31 +++++++++++++++++++++++++++++++ src/index.ts | 5 +++++ test/Struct.ts | 12 ++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/Struct.ts create mode 100644 test/Struct.ts diff --git a/src/Struct.ts b/src/Struct.ts new file mode 100644 index 000000000..25cbea7f1 --- /dev/null +++ b/src/Struct.ts @@ -0,0 +1,31 @@ +/** + * @since 1.0.0 + */ + +/** + * @since 1.0.0 + */ +export const pick = ]>( + ...keys: Keys +) => + (s: S): { [K in Keys[number]]: S[K] } => { + const out: any = {} + for (const k of keys) { + out[k] = s[k] + } + return out + } + +/** + * @since 1.0.0 + */ +export const omit = ]>( + ...keys: Keys +) => + (s: S): { [K in Exclude]: S[K] } => { + const out: any = { ...s } + for (const k of keys) { + delete out[k] + } + return out + } diff --git a/src/index.ts b/src/index.ts index e4948f9e0..4deb9f9c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ import * as predicate from "@fp-ts/core/Predicate" import * as readonlyArray from "@fp-ts/core/ReadonlyArray" import * as readonlyRecord from "@fp-ts/core/ReadonlyRecord" import * as string from "@fp-ts/core/String" +import * as struct from "@fp-ts/core/Struct" import * as symbol from "@fp-ts/core/Symbol" import * as these from "@fp-ts/core/These" @@ -237,6 +238,10 @@ export { * @since 1.0.0 */ string, + /** + * @since 1.0.0 + */ + struct, /** * @since 1.0.0 */ diff --git a/test/Struct.ts b/test/Struct.ts new file mode 100644 index 000000000..66e2a3735 --- /dev/null +++ b/test/Struct.ts @@ -0,0 +1,12 @@ +import { pipe } from "@fp-ts/core/Function" +import * as S from "@fp-ts/core/Struct" + +describe.concurrent("Struct", () => { + it("pick", () => { + expect(pipe({ a: "a", b: 1, c: true }, S.pick("a", "b"))).toEqual({ a: "a", b: 1 }) + }) + + it("omit", () => { + expect(pipe({ a: "a", b: 1, c: true }, S.omit("c"))).toEqual({ a: "a", b: 1 }) + }) +}) From 92d4c5dad0992a0efcedea8f5630bada60335e59 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 12:18:58 +0100 Subject: [PATCH 58/80] add modules from @fp-ts/data --- .changeset/afraid-cars-lay.md | 5 ---- .changeset/big-weeks-kiss.md | 5 ---- .changeset/blue-books-breathe.md | 5 ---- .changeset/brave-poems-appear.md | 5 ---- .changeset/clean-goats-learn.md | 5 ---- .changeset/cyan-melons-own.md | 5 ---- .changeset/famous-parrots-shake.md | 5 ---- .changeset/four-garlics-wonder.md | 5 ---- .changeset/gold-teachers-admire.md | 5 ---- .changeset/green-kangaroos-report.md | 5 ---- .changeset/heavy-suns-buy.md | 5 ---- .changeset/modern-tomatoes-promise.md | 5 ++++ .changeset/nervous-schools-knock.md | 5 ---- .changeset/nine-bags-end.md | 5 ---- .changeset/olive-apes-suffer.md | 5 ---- .changeset/olive-pumas-trade.md | 5 ---- .changeset/pink-gorillas-sneeze.md | 5 ---- .changeset/polite-mice-begin.md | 5 ---- .changeset/popular-baboons-promise.md | 5 ---- .changeset/purple-cooks-destroy.md | 5 ---- .changeset/purple-meals-explode.md | 5 ---- .changeset/silver-experts-punch.md | 5 ---- .changeset/unlucky-chicken-try.md | 5 ---- .changeset/unlucky-knives-exercise.md | 5 ---- CHANGELOG.md | 38 +++++++++++++++++++++++++++ src/Struct.ts | 4 +++ 26 files changed, 47 insertions(+), 115 deletions(-) delete mode 100644 .changeset/afraid-cars-lay.md delete mode 100644 .changeset/big-weeks-kiss.md delete mode 100644 .changeset/blue-books-breathe.md delete mode 100644 .changeset/brave-poems-appear.md delete mode 100644 .changeset/clean-goats-learn.md delete mode 100644 .changeset/cyan-melons-own.md delete mode 100644 .changeset/famous-parrots-shake.md delete mode 100644 .changeset/four-garlics-wonder.md delete mode 100644 .changeset/gold-teachers-admire.md delete mode 100644 .changeset/green-kangaroos-report.md delete mode 100644 .changeset/heavy-suns-buy.md create mode 100644 .changeset/modern-tomatoes-promise.md delete mode 100644 .changeset/nervous-schools-knock.md delete mode 100644 .changeset/nine-bags-end.md delete mode 100644 .changeset/olive-apes-suffer.md delete mode 100644 .changeset/olive-pumas-trade.md delete mode 100644 .changeset/pink-gorillas-sneeze.md delete mode 100644 .changeset/polite-mice-begin.md delete mode 100644 .changeset/popular-baboons-promise.md delete mode 100644 .changeset/purple-cooks-destroy.md delete mode 100644 .changeset/purple-meals-explode.md delete mode 100644 .changeset/silver-experts-punch.md delete mode 100644 .changeset/unlucky-chicken-try.md delete mode 100644 .changeset/unlucky-knives-exercise.md diff --git a/.changeset/afraid-cars-lay.md b/.changeset/afraid-cars-lay.md deleted file mode 100644 index ea1e3c0ad..000000000 --- a/.changeset/afraid-cars-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Either module diff --git a/.changeset/big-weeks-kiss.md b/.changeset/big-weeks-kiss.md deleted file mode 100644 index 931923006..000000000 --- a/.changeset/big-weeks-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -Semigroup: add array combinator diff --git a/.changeset/blue-books-breathe.md b/.changeset/blue-books-breathe.md deleted file mode 100644 index fe1864618..000000000 --- a/.changeset/blue-books-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -rename `productFlatten` to `element` diff --git a/.changeset/brave-poems-appear.md b/.changeset/brave-poems-appear.md deleted file mode 100644 index e0dd1b389..000000000 --- a/.changeset/brave-poems-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Option module diff --git a/.changeset/clean-goats-learn.md b/.changeset/clean-goats-learn.md deleted file mode 100644 index 2f4981c7f..000000000 --- a/.changeset/clean-goats-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add ReadonlyArray module diff --git a/.changeset/cyan-melons-own.md b/.changeset/cyan-melons-own.md deleted file mode 100644 index 7665266f3..000000000 --- a/.changeset/cyan-melons-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Identity module diff --git a/.changeset/famous-parrots-shake.md b/.changeset/famous-parrots-shake.md deleted file mode 100644 index b73d5bcf9..000000000 --- a/.changeset/famous-parrots-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add missing readonly modifiers diff --git a/.changeset/four-garlics-wonder.md b/.changeset/four-garlics-wonder.md deleted file mode 100644 index 75dd1000b..000000000 --- a/.changeset/four-garlics-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Predicate module diff --git a/.changeset/gold-teachers-admire.md b/.changeset/gold-teachers-admire.md deleted file mode 100644 index 9ca396e17..000000000 --- a/.changeset/gold-teachers-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add ReadonlyRecord module diff --git a/.changeset/green-kangaroos-report.md b/.changeset/green-kangaroos-report.md deleted file mode 100644 index ef0285a57..000000000 --- a/.changeset/green-kangaroos-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -Order: add array combinator diff --git a/.changeset/heavy-suns-buy.md b/.changeset/heavy-suns-buy.md deleted file mode 100644 index 8c5199636..000000000 --- a/.changeset/heavy-suns-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add These module diff --git a/.changeset/modern-tomatoes-promise.md b/.changeset/modern-tomatoes-promise.md new file mode 100644 index 000000000..d40355a05 --- /dev/null +++ b/.changeset/modern-tomatoes-promise.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": minor +--- + +add modules from @fp-ts/data diff --git a/.changeset/nervous-schools-knock.md b/.changeset/nervous-schools-knock.md deleted file mode 100644 index 1defb228e..000000000 --- a/.changeset/nervous-schools-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -remove NonEmptyTraversable typeclass diff --git a/.changeset/nine-bags-end.md b/.changeset/nine-bags-end.md deleted file mode 100644 index 5ff72f442..000000000 --- a/.changeset/nine-bags-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -Order: add bigint instance diff --git a/.changeset/olive-apes-suffer.md b/.changeset/olive-apes-suffer.md deleted file mode 100644 index 54975c6ab..000000000 --- a/.changeset/olive-apes-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add String module diff --git a/.changeset/olive-pumas-trade.md b/.changeset/olive-pumas-trade.md deleted file mode 100644 index 6a5413982..000000000 --- a/.changeset/olive-pumas-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add the prefix @fp-ts/core to the \_tag of the data types diff --git a/.changeset/pink-gorillas-sneeze.md b/.changeset/pink-gorillas-sneeze.md deleted file mode 100644 index 3b01876d4..000000000 --- a/.changeset/pink-gorillas-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Number module diff --git a/.changeset/polite-mice-begin.md b/.changeset/polite-mice-begin.md deleted file mode 100644 index a4b66a2e5..000000000 --- a/.changeset/polite-mice-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Equivalence module diff --git a/.changeset/popular-baboons-promise.md b/.changeset/popular-baboons-promise.md deleted file mode 100644 index 9446ae176..000000000 --- a/.changeset/popular-baboons-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -Monoid: add array, readonlyArray combinators diff --git a/.changeset/purple-cooks-destroy.md b/.changeset/purple-cooks-destroy.md deleted file mode 100644 index 3623a0b0c..000000000 --- a/.changeset/purple-cooks-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -Boolean: add not combinator diff --git a/.changeset/purple-meals-explode.md b/.changeset/purple-meals-explode.md deleted file mode 100644 index 3af08023c..000000000 --- a/.changeset/purple-meals-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add CovariantWithIndex, FilterableWithIndex, TraversableFilterable modules diff --git a/.changeset/silver-experts-punch.md b/.changeset/silver-experts-punch.md deleted file mode 100644 index ec7026dc6..000000000 --- a/.changeset/silver-experts-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Ordering module diff --git a/.changeset/unlucky-chicken-try.md b/.changeset/unlucky-chicken-try.md deleted file mode 100644 index 930cdd2ae..000000000 --- a/.changeset/unlucky-chicken-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Function module diff --git a/.changeset/unlucky-knives-exercise.md b/.changeset/unlucky-knives-exercise.md deleted file mode 100644 index 7ef64bbad..000000000 --- a/.changeset/unlucky-knives-exercise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@fp-ts/core": patch ---- - -add Boolean module diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1f2d4d9..e0c793b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # @fp-ts/core +## next + +**Breaking changes** + +- `typeclass/Semiproduct` + - rename `productFlatten` to `element` + +**New Features** + +- `Boolean` + - add `not` combinator +- `Order` + - add `array` combinator + - add `bigint` instance +- `Monoid` + - add `array`, `readonlyArray` combinators +- `Semigroup` + - add `array`, `readonlyArray` combinators +- modules + - `typeclass/Equivalence` + - `typeclass/Filterable` + - `typeclass/TraversableFilterable` + - `Bigint` + - `Boolean` + - `Either` + - `Function` + - `Identity` + - `Number` + - `Option` + - `Ordering` + - `Predicate` + - `ReadonlyArray` + - `ReadonyRecord` + - `String` + - `Struct` + - `Symbol` + - `These` + ## 0.0.11 ### Patch Changes diff --git a/src/Struct.ts b/src/Struct.ts index 25cbea7f1..1efea549e 100644 --- a/src/Struct.ts +++ b/src/Struct.ts @@ -3,6 +3,8 @@ */ /** + * Create a new object by picking properties of an existing object. + * * @since 1.0.0 */ export const pick = ]>( @@ -17,6 +19,8 @@ export const pick = ]>( } /** + * Create a new object by omitting properties of an existing object. + * * @since 1.0.0 */ export const omit = ]>( From 89e28683d36b7667744e9bfe924875c81faefb80 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 12:55:05 +0100 Subject: [PATCH 59/80] Order: make compare binary --- CHANGELOG.md | 4 +- src/Option.ts | 4 +- src/ReadonlyArray.ts | 2 +- src/typeclass/Order.ts | 104 +++++++++++++++----------------- src/typeclass/Semigroup.ts | 4 +- test/Bigint.ts | 6 +- test/Boolean.ts | 6 +- test/Number.ts | 6 +- test/Option.ts | 12 ++-- test/ReadonlyArray.ts | 34 +++++------ test/typeclass/Contravariant.ts | 6 +- test/typeclass/Order.ts | 54 ++++++++--------- 12 files changed, 118 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c793b09..809df28dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ **Breaking changes** -- `typeclass/Semiproduct` +- `Semiproduct` - rename `productFlatten` to `element` +- `Order` + - make `compare` binary **New Features** diff --git a/src/Option.ts b/src/Option.ts index 0d471a218..0a5a352e3 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -1139,8 +1139,8 @@ export const orElseSucceed = ( * @since 1.0.0 */ export const liftOrder = (O: Order): Order> => - order.fromCompare((that) => - (self) => isSome(self) ? (isSome(that) ? O.compare(that.value)(self.value) : 1) : -1 + order.fromCompare((self, that) => + isSome(self) ? (isSome(that) ? O.compare(self.value, that.value) : 1) : -1 ) /** diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index cfdb335a8..2fbcdee0b 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -751,7 +751,7 @@ export const lefts = (self: Iterable>): Array => { export const sort = (O: Order) => (self: Iterable): Array => { const out = Array.from(self) - out.sort((self, that) => O.compare(that)(self)) + out.sort(O.compare) return out } diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 335929de6..dc8321dff 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -15,7 +15,7 @@ import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" * @since 1.0.0 */ export interface Order { - readonly compare: (that: A) => (self: A) => -1 | 0 | 1 + readonly compare: (self: A, that: A) => -1 | 0 | 1 } /** @@ -31,7 +31,7 @@ export interface OrderTypeLambda extends TypeLambda { * @since 1.0.0 */ export const string: Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 + compare: (self, that) => self < that ? -1 : self > that ? 1 : 0 } /** @@ -39,7 +39,7 @@ export const string: Order = { * @since 1.0.0 */ export const number: Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 + compare: (self, that) => self < that ? -1 : self > that ? 1 : 0 } /** @@ -47,7 +47,7 @@ export const number: Order = { * @since 1.0.0 */ export const boolean: Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 + compare: (self, that) => self < that ? -1 : self > that ? 1 : 0 } /** @@ -55,7 +55,7 @@ export const boolean: Order = { * @since 1.0.0 */ export const bigint: Order = { - compare: (that) => (self) => self < that ? -1 : self > that ? 1 : 0 + compare: (self, that) => self < that ? -1 : self > that ? 1 : 0 } /** @@ -65,7 +65,7 @@ export const bigint: Order = { * @since 1.0.0 */ export const fromCompare = (compare: Order["compare"]): Order => ({ - compare: that => self => self === that ? 0 : compare(that)(self) + compare: (self, that) => self === that ? 0 : compare(self, that) }) /** @@ -80,18 +80,16 @@ export const fromCompare = (compare: Order["compare"]): Order => ({ export const tuple = >( ...orders: { readonly [K in keyof A]: Order } ): Order> => - fromCompare(that => - self => { - let i = 0 - for (; i < orders.length - 1; i++) { - const r = orders[i].compare(that[i])(self[i]) - if (r !== 0) { - return r - } + fromCompare((self, that) => { + let i = 0 + for (; i < orders.length - 1; i++) { + const r = orders[i].compare(self[i], that[i]) + if (r !== 0) { + return r } - return orders[i].compare(that[i])(self[i]) } - ) + return orders[i].compare(self[i], that[i]) + }) /** * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. @@ -103,32 +101,30 @@ export const tuple = >( * @since 1.0.0 */ export const array = (O: Order): Order> => - fromCompare((that) => - (self) => { - const aLen = self.length - const bLen = that.length - const len = Math.min(aLen, bLen) - for (let i = 0; i < len; i++) { - const o = O.compare(that[i])(self[i]) - if (o !== 0) { - return o - } + fromCompare((self, that) => { + const aLen = self.length + const bLen = that.length + const len = Math.min(aLen, bLen) + for (let i = 0; i < len; i++) { + const o = O.compare(self[i], that[i]) + if (o !== 0) { + return o } - return number.compare(bLen)(aLen) } - ) + return number.compare(aLen, bLen) + }) /** * @since 1.0.0 */ export const reverse = (O: Order): Order => - fromCompare(that => self => O.compare(self)(that)) + fromCompare((self, that) => O.compare(that, self)) /** * @since 1.0.0 */ export const contramap = (f: (b: B) => A) => - (self: Order): Order => fromCompare((b2) => (b1) => self.compare(f(b2))(f(b1))) + (self: Order): Order => fromCompare((b1, b2) => self.compare(f(b1), f(b2))) /** * @category instances @@ -137,35 +133,31 @@ export const contramap = (f: (b: B) => A) => export const getSemigroup = (): Semigroup> => ({ combine: (O2) => (O1) => - fromCompare(that => - self => { - const out = O1.compare(that)(self) - if (out !== 0) { - return out - } - return O2.compare(that)(self) + fromCompare((self, that) => { + const out = O1.compare(self, that) + if (out !== 0) { + return out } - ), + return O2.compare(self, that) + }), combineMany: (collection) => (self) => - fromCompare(a2 => - a1 => { - let out = self.compare(a2)(a1) + fromCompare((a1, a2) => { + let out = self.compare(a1, a2) + if (out !== 0) { + return out + } + for (const O of collection) { + out = O.compare(a1, a2) if (out !== 0) { return out } - for (const O of collection) { - out = O.compare(a2)(a1) - if (out !== 0) { - return out - } - } - return out } - ) + return out + }) }) -const empty: Order = fromCompare(() => () => 0) +const empty: Order = fromCompare(() => 0) /** * @category instances @@ -214,14 +206,14 @@ export const Product: product.Product = { * * @since 1.0.0 */ -export const lessThan = (O: Order) => (that: A) => (self: A) => O.compare(that)(self) === -1 +export const lessThan = (O: Order) => (that: A) => (self: A) => O.compare(self, that) === -1 /** * Test whether one value is _strictly greater than_ another. * * @since 1.0.0 */ -export const greaterThan = (O: Order) => (that: A) => (self: A) => O.compare(that)(self) === 1 +export const greaterThan = (O: Order) => (that: A) => (self: A) => O.compare(self, that) === 1 /** * Test whether one value is _non-strictly less than_ another. @@ -229,7 +221,7 @@ export const greaterThan = (O: Order) => (that: A) => (self: A) => O.compa * @since 1.0.0 */ export const lessThanOrEqualTo = (O: Order) => - (that: A) => (self: A) => O.compare(that)(self) !== 1 + (that: A) => (self: A) => O.compare(self, that) !== 1 /** * Test whether one value is _non-strictly greater than_ another. @@ -237,7 +229,7 @@ export const lessThanOrEqualTo = (O: Order) => * @since 1.0.0 */ export const greaterThanOrEqualTo = (O: Order) => - (that: A) => (self: A) => O.compare(that)(self) !== -1 + (that: A) => (self: A) => O.compare(self, that) !== -1 /** * Take the minimum of two values. If they are considered equal, the first argument is chosen. @@ -245,7 +237,7 @@ export const greaterThanOrEqualTo = (O: Order) => * @since 1.0.0 */ export const min = (O: Order) => - (that: A) => (self: A): A => self === that || O.compare(that)(self) < 1 ? self : that + (that: A) => (self: A): A => self === that || O.compare(self, that) < 1 ? self : that /** * Take the maximum of two values. If they are considered equal, the first argument is chosen. @@ -253,7 +245,7 @@ export const min = (O: Order) => * @since 1.0.0 */ export const max = (O: Order) => - (that: A) => (self: A): A => self === that || O.compare(that)(self) > -1 ? self : that + (that: A) => (self: A): A => self === that || O.compare(self, that) > -1 ? self : that /** * Clamp a value between a minimum and a maximum. diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 6673755bb..ec36c5743 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -242,7 +242,7 @@ export const struct = (semigroups: { readonly [K in keyof A]: Semigroup * @since 1.0.0 */ export const min = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(that)(self) === -1 ? self : that) + fromCombine((that) => (self) => O.compare(self, that) === -1 ? self : that) /** * `Semigroup` that returns last maximum of elements. @@ -251,7 +251,7 @@ export const min = (O: Order): Semigroup => * @since 1.0.0 */ export const max = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(that)(self) === 1 ? self : that) + fromCombine((that) => (self) => O.compare(self, that) === 1 ? self : that) /** * @category constructors diff --git a/test/Bigint.ts b/test/Bigint.ts index 77a0fbb11..164731dc9 100644 --- a/test/Bigint.ts +++ b/test/Bigint.ts @@ -36,9 +36,9 @@ describe.concurrent("Bigint", () => { }) it("Order", () => { - deepStrictEqual(pipe(1n, Bigint.Order.compare(2n)), -1) - deepStrictEqual(pipe(2n, Bigint.Order.compare(1n)), 1) - deepStrictEqual(pipe(2n, Bigint.Order.compare(2n)), 0) + deepStrictEqual(Bigint.Order.compare(1n, 2n), -1) + deepStrictEqual(Bigint.Order.compare(2n, 1n), 1) + deepStrictEqual(Bigint.Order.compare(2n, 2n), 0) }) it("SemigroupSum", () => { diff --git a/test/Boolean.ts b/test/Boolean.ts index ad34f13d9..8367ba376 100644 --- a/test/Boolean.ts +++ b/test/Boolean.ts @@ -83,8 +83,8 @@ describe.concurrent("Boolean", () => { }) it("Order", () => { - deepStrictEqual(pipe(false, Boolean.Order.compare(true)), -1) - deepStrictEqual(pipe(true, Boolean.Order.compare(false)), 1) - deepStrictEqual(pipe(true, Boolean.Order.compare(true)), 0) + deepStrictEqual(Boolean.Order.compare(false, true), -1) + deepStrictEqual(Boolean.Order.compare(true, false), 1) + deepStrictEqual(Boolean.Order.compare(true, true), 0) }) }) diff --git a/test/Number.ts b/test/Number.ts index a68aa5692..85a945f04 100644 --- a/test/Number.ts +++ b/test/Number.ts @@ -35,9 +35,9 @@ describe.concurrent("Number", () => { }) it("Order", () => { - deepStrictEqual(pipe(1, Number.Order.compare(2)), -1) - deepStrictEqual(pipe(2, Number.Order.compare(1)), 1) - deepStrictEqual(pipe(2, Number.Order.compare(2)), 0) + deepStrictEqual(Number.Order.compare(1, 2), -1) + deepStrictEqual(Number.Order.compare(2, 1), 1) + deepStrictEqual(Number.Order.compare(2, 2), 0) }) it("Bounded", () => { diff --git a/test/Option.ts b/test/Option.ts index 74912d033..3cde1acdf 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -357,12 +357,12 @@ describe.concurrent("Option", () => { it("liftOrder", () => { const OS = _.liftOrder(S.Order) - deepStrictEqual(pipe(_.none(), OS.compare(_.none())), 0) - deepStrictEqual(pipe(_.some("a"), OS.compare(_.none())), 1) - deepStrictEqual(pipe(_.none(), OS.compare(_.some("a"))), -1) - deepStrictEqual(pipe(_.some("a"), OS.compare(_.some("a"))), 0) - deepStrictEqual(pipe(_.some("a"), OS.compare(_.some("b"))), -1) - deepStrictEqual(pipe(_.some("b"), OS.compare(_.some("a"))), 1) + deepStrictEqual(OS.compare(_.none(), _.none()), 0) + deepStrictEqual(OS.compare(_.some("a"), _.none()), 1) + deepStrictEqual(OS.compare(_.none(), _.some("a")), -1) + deepStrictEqual(OS.compare(_.some("a"), _.some("a")), 0) + deepStrictEqual(OS.compare(_.some("a"), _.some("b")), -1) + deepStrictEqual(OS.compare(_.some("b"), _.some("a")), 1) }) it("flatMapNullable", () => { diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 66ac4a973..ba9cbc34c 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -1055,28 +1055,28 @@ describe.concurrent("ReadonlyArray", () => { it("liftOrder", () => { const O = RA.liftOrder(String.Order) - deepStrictEqual(O.compare([])([]), 0) - deepStrictEqual(O.compare(["a"])(["a"]), 0) + deepStrictEqual(O.compare([], []), 0) + deepStrictEqual(O.compare(["a"], ["a"]), 0) - deepStrictEqual(O.compare(["b"])(["a"]), -1) - deepStrictEqual(O.compare(["a"])(["b"]), 1) + deepStrictEqual(O.compare(["a"], ["b"]), -1) + deepStrictEqual(O.compare(["b"], ["a"]), 1) - deepStrictEqual(O.compare(["a"])([]), -1) - deepStrictEqual(O.compare([])(["a"]), 1) - deepStrictEqual(O.compare(["a", "a"])(["a"]), -1) - deepStrictEqual(O.compare(["a", "a"])(["b"]), 1) + deepStrictEqual(O.compare([], ["a"]), -1) + deepStrictEqual(O.compare(["a"], []), 1) + deepStrictEqual(O.compare(["a"], ["a", "a"]), -1) + deepStrictEqual(O.compare(["b"], ["a", "a"]), 1) - deepStrictEqual(O.compare(["a", "a"])(["a", "a"]), 0) - deepStrictEqual(O.compare(["a", "b"])(["a", "b"]), 0) + deepStrictEqual(O.compare(["a", "a"], ["a", "a"]), 0) + deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) - deepStrictEqual(O.compare(["a", "a"])(["a", "b"]), 1) - deepStrictEqual(O.compare(["a", "b"])(["a", "a"]), -1) + deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) + deepStrictEqual(O.compare(["a", "a"], ["a", "b"]), -1) - deepStrictEqual(O.compare(["a", "b"])(["b", "a"]), 1) - deepStrictEqual(O.compare(["b", "a"])(["a", "a"]), -1) - deepStrictEqual(O.compare(["b", "a"])(["a", "b"]), -1) - deepStrictEqual(O.compare(["b", "b"])(["b", "a"]), -1) - deepStrictEqual(O.compare(["b", "a"])(["b", "b"]), 1) + deepStrictEqual(O.compare(["b", "a"], ["a", "b"]), 1) + deepStrictEqual(O.compare(["a", "a"], ["b", "a"]), -1) + deepStrictEqual(O.compare(["a", "b"], ["b", "a"]), -1) + deepStrictEqual(O.compare(["b", "a"], ["b", "b"]), -1) + deepStrictEqual(O.compare(["b", "b"], ["b", "a"]), 1) }) it("isEmpty", () => { diff --git a/test/typeclass/Contravariant.ts b/test/typeclass/Contravariant.ts index de754eb91..5f8e50fd5 100644 --- a/test/typeclass/Contravariant.ts +++ b/test/typeclass/Contravariant.ts @@ -21,8 +21,8 @@ describe("Contravariant", () => { )( S.Order ) - U.deepStrictEqual(pipe(["a"], O.compare(["b"])), -1) - U.deepStrictEqual(pipe(["a"], O.compare(["a"])), 0) - U.deepStrictEqual(pipe(["b"], O.compare(["a"])), 1) + U.deepStrictEqual(O.compare(["a"], ["b"]), -1) + U.deepStrictEqual(O.compare(["a"], ["a"]), 0) + U.deepStrictEqual(O.compare(["b"], ["a"]), 1) }) }) diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 3333f602f..aca17cdde 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -17,25 +17,25 @@ describe("Order", () => { it("tuple", () => { const O = _.tuple(string.Order, number.Order, boolean.Order) - U.deepStrictEqual(pipe(["a", 1, true], O.compare(["b", 2, true])), -1) - U.deepStrictEqual(pipe(["a", 1, true], O.compare(["a", 2, true])), -1) - U.deepStrictEqual(pipe(["a", 1, true], O.compare(["a", 1, false])), 1) + U.deepStrictEqual(O.compare(["a", 1, true], ["b", 2, true]), -1) + U.deepStrictEqual(O.compare(["a", 1, true], ["a", 2, true]), -1) + U.deepStrictEqual(O.compare(["a", 1, true], ["a", 1, false]), 1) }) it("Contravariant", () => { const O = pipe(number.Order, _.Contravariant.contramap((s: string) => s.length)) - U.deepStrictEqual(pipe("a", O.compare("b")), 0) - U.deepStrictEqual(pipe("a", O.compare("bb")), -1) - U.deepStrictEqual(pipe("aa", O.compare("b")), 1) + U.deepStrictEqual(O.compare("a", "b"), 0) + U.deepStrictEqual(O.compare("a", "bb"), -1) + U.deepStrictEqual(O.compare("aa", "b"), 1) }) it("Invariant", () => { const O = _.Invariant.imap((s: string) => [s], ([s]) => s)( string.Order ) - U.deepStrictEqual(pipe(["a"], O.compare(["b"])), -1) - U.deepStrictEqual(pipe(["a"], O.compare(["a"])), 0) - U.deepStrictEqual(pipe(["b"], O.compare(["a"])), 1) + U.deepStrictEqual(O.compare(["a"], ["b"]), -1) + U.deepStrictEqual(O.compare(["a"], ["a"]), 0) + U.deepStrictEqual(O.compare(["b"], ["a"]), 1) }) it("getSemigroup", () => { @@ -132,9 +132,9 @@ describe("Order", () => { it("reverse", () => { const O = _.reverse(number.Order) - U.deepStrictEqual(pipe(1, O.compare(2)), 1) - U.deepStrictEqual(pipe(2, O.compare(1)), -1) - U.deepStrictEqual(pipe(2, O.compare(2)), 0) + U.deepStrictEqual(O.compare(1, 2), 1) + U.deepStrictEqual(O.compare(2, 1), -1) + U.deepStrictEqual(O.compare(2, 2), 0) }) it("lessThan", () => { @@ -201,10 +201,10 @@ describe("Order", () => { string.Order, _.SemiProduct.product(number.Order) ) - U.deepStrictEqual(pipe(["a", 1], O.compare(["a", 2])), -1) - U.deepStrictEqual(pipe(["a", 1], O.compare(["a", 1])), 0) - U.deepStrictEqual(pipe(["a", 1], O.compare(["a", 0])), 1) - U.deepStrictEqual(pipe(["a", 1], O.compare(["b", 1])), -1) + U.deepStrictEqual(O.compare(["a", 1], ["a", 2]), -1) + U.deepStrictEqual(O.compare(["a", 1], ["a", 1]), 0) + U.deepStrictEqual(O.compare(["a", 1], ["a", 0]), 1) + U.deepStrictEqual(O.compare(["a", 1], ["b", 1]), -1) }) it("productMany", () => { @@ -212,29 +212,29 @@ describe("Order", () => { string.Order, _.SemiProduct.productMany([string.Order, string.Order]) ) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "c"])), -1) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "b"])), 0) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "a"])), 1) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["b", "a"])), -1) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "c"]), -1) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) + U.deepStrictEqual(O.compare(["a", "b"], ["b", "a"]), -1) }) }) describe("Product", () => { it("of", () => { const O = _.Product.of("a") - U.deepStrictEqual(pipe("b", O.compare("a")), 0) - U.deepStrictEqual(pipe("a", O.compare("a")), 0) - U.deepStrictEqual(pipe("a", O.compare("b")), 0) + U.deepStrictEqual(O.compare("b", "a"), 0) + U.deepStrictEqual(O.compare("a", "a"), 0) + U.deepStrictEqual(O.compare("a", "b"), 0) }) it("productAll", () => { const O = pipe( _.Product.productAll([string.Order, string.Order, string.Order]) ) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "c"])), -1) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "b"])), 0) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["a", "a"])), 1) - U.deepStrictEqual(pipe(["a", "b"], O.compare(["b", "a"])), -1) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "c"]), -1) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) + U.deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) + U.deepStrictEqual(O.compare(["a", "b"], ["b", "a"]), -1) }) }) }) From ae7ec25a688d584f20a755e45d18e45be945956b Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 14:28:18 +0100 Subject: [PATCH 60/80] Semigroup: make combine binary --- CHANGELOG.md | 2 + src/Bigint.ts | 6 ++- src/Boolean.ts | 6 ++- src/Function.ts | 2 +- src/Identity.ts | 2 +- src/Number.ts | 6 ++- src/Option.ts | 9 ++--- src/Ordering.ts | 2 +- src/Predicate.ts | 7 ++-- src/ReadonlyArray.ts | 13 ++++--- src/String.ts | 3 +- src/typeclass/Equivalence.ts | 2 +- src/typeclass/Order.ts | 17 ++++---- src/typeclass/SemiApplicative.ts | 2 +- src/typeclass/SemiCoproduct.ts | 3 +- src/typeclass/Semigroup.ts | 64 +++++++++++++------------------ test/Bigint.ts | 4 +- test/Function.ts | 8 ++-- test/Number.ts | 4 +- test/Option.ts | 10 ++--- test/Ordering.ts | 20 +++++----- test/Predicate.ts | 8 ++-- test/ReadonlyArray.ts | 28 +++++++------- test/String.ts | 2 +- test/typeclass/Applicative.ts | 9 ++--- test/typeclass/Coproduct.ts | 15 ++++---- test/typeclass/Equivalence.ts | 2 +- test/typeclass/Invariant.ts | 12 +++--- test/typeclass/Monoid.ts | 10 ++--- test/typeclass/Order.ts | 4 +- test/typeclass/Product.ts | 9 ++--- test/typeclass/SemiApplicative.ts | 8 ++-- test/typeclass/SemiCoproduct.ts | 9 ++--- test/typeclass/SemiProduct.ts | 4 +- test/typeclass/Semigroup.ts | 20 +++++----- 35 files changed, 164 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 809df28dd..44cd53a15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ **Breaking changes** +- `Semigroup` + - make `combine` binary - `Semiproduct` - rename `productFlatten` to `element` - `Order` diff --git a/src/Bigint.ts b/src/Bigint.ts index 4c3095dce..9860f19a3 100644 --- a/src/Bigint.ts +++ b/src/Bigint.ts @@ -17,12 +17,14 @@ export const isBigint: (u: unknown) => u is bigint = predicate.isBigInt /** * @since 1.0.0 */ -export const sum: (that: bigint) => (self: bigint) => bigint = semigroup.bigintSum.combine +export const sum = (that: bigint) => + (self: bigint): bigint => semigroup.bigintSum.combine(self, that) /** * @since 1.0.0 */ -export const multiply: (that: bigint) => (self: bigint) => bigint = semigroup.bigintMultiply.combine +export const multiply = (that: bigint) => + (self: bigint): bigint => semigroup.bigintMultiply.combine(self, that) /** * @since 1.0.0 diff --git a/src/Boolean.ts b/src/Boolean.ts index 1b2b4e0c1..bd07985dd 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -106,13 +106,15 @@ export const MonoidAny: monoid.Monoid = monoid.booleanAny * @category combinators * @since 1.0.0 */ -export const and: (that: boolean) => (self: boolean) => boolean = semigroup.booleanAll.combine +export const and = (that: boolean) => + (self: boolean): boolean => semigroup.booleanAll.combine(self, that) /** * @category combinators * @since 1.0.0 */ -export const or: (that: boolean) => (self: boolean) => boolean = semigroup.booleanAny.combine +export const or = (that: boolean) => + (self: boolean): boolean => semigroup.booleanAny.combine(self, that) /** * @category combinators diff --git a/src/Function.ts b/src/Function.ts index 6326f46fc..109a6c259 100644 --- a/src/Function.ts +++ b/src/Function.ts @@ -49,7 +49,7 @@ export const compose: (bc: (b: B) => C) => (ab: (a: A) => B) => (a: A) */ export const getSemigroup = (Semigroup: semigroup.Semigroup) => (): semigroup.Semigroup<(a: A) => S> => - semigroup.fromCombine((that) => (self) => (a) => Semigroup.combine(that(a))(self(a))) + semigroup.fromCombine((self, that) => (a) => Semigroup.combine(self(a), that(a))) /** * Unary functions form a monoid as long as you can provide a monoid for the codomain. diff --git a/src/Identity.ts b/src/Identity.ts index 8e88771fd..59cd59f0b 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -404,7 +404,7 @@ export const getSemiCoproduct = ( S: Semigroup ): semiCoproduct.SemiCoproduct> => ({ imap: Invariant.imap, - coproduct: S.combine, + coproduct: (that) => (self) => S.combine(self, that), coproductMany: S.combineMany }) diff --git a/src/Number.ts b/src/Number.ts index 8aabd0ff6..49ddd27eb 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -19,12 +19,14 @@ export const isNumber: Refinement = predicate.isNumber /** * @since 1.0.0 */ -export const sum: (that: number) => (self: number) => number = semigroup.numberSum.combine +export const sum = (that: number) => + (self: number): number => semigroup.numberSum.combine(self, that) /** * @since 1.0.0 */ -export const multiply: (that: number) => (self: number) => number = semigroup.numberMultiply.combine +export const multiply = (that: number) => + (self: number): number => semigroup.numberMultiply.combine(self, that) /** * @since 1.0.0 diff --git a/src/Option.ts b/src/Option.ts index 0a5a352e3..9c4477cfa 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -543,23 +543,22 @@ export const SemiApplicative: semiApplicative.SemiApplicative export const getMonoid = ( Semigroup: Semigroup ): Monoid> => { - const combine = (that: Option) => - (self: Option): Option => - isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(that.value)(self.value)) + const combine = (self: Option, that: Option): Option => + isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(self.value, that.value)) return ({ combine, combineMany: (others) => (start) => { let c = start for (const o of others) { - c = combine(o)(c) + c = combine(c, o) } return c }, combineAll: (collection: Iterable>): Option => { let c: Option = option.none for (const o of collection) { - c = combine(o)(c) + c = combine(c, o) } return c }, diff --git a/src/Ordering.ts b/src/Ordering.ts index 9e9cc526a..67ccf3917 100644 --- a/src/Ordering.ts +++ b/src/Ordering.ts @@ -31,7 +31,7 @@ export const match = ( * @since 1.0.0 */ export const Semigroup: semigroup.Semigroup = { - combine: (that) => (self) => self !== 0 ? self : that, + combine: (self, that) => self !== 0 ? self : that, combineMany: (collection) => (self) => { let ordering = self diff --git a/src/Predicate.ts b/src/Predicate.ts index 755bd42c3..fb3fe7e63 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import { constFalse, constTrue } from "@fp-ts/core/Function" +import { constFalse, constTrue, pipe } from "@fp-ts/core/Function" import type { TypeLambda } from "@fp-ts/core/HKT" import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" @@ -274,7 +274,8 @@ export const and = (that: Predicate) => * @category instances * @since 1.0.0 */ -export const getSemigroupAny = (): semigroup.Semigroup> => semigroup.fromCombine(or) +export const getSemigroupAny = (): semigroup.Semigroup> => + semigroup.fromCombine((self, that) => pipe(self, or(that))) /** * @category instances @@ -295,7 +296,7 @@ export const getMonoidAny = (): monoid.Monoid> => { * @since 1.0.0 */ export const getSemigroupAll = (): semigroup.Semigroup> => - semigroup.fromCombine(and) + semigroup.fromCombine((self, that) => pipe(self, and(that))) /** * @category instances diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 2fbcdee0b..f3689b551 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1876,7 +1876,7 @@ export const foldMap: (M: Monoid) => (f: (a: A) => M) => (self: Readonl export const foldMapWithIndex = (Monoid: Monoid) => (f: (a: A, i: number) => M) => (self: ReadonlyArray): M => - self.reduce((m, a, i) => Monoid.combine(f(a, i))(m), Monoid.empty) + self.reduce((m, a, i) => Monoid.combine(m, f(a, i)), Monoid.empty) /** * @category folding @@ -1892,7 +1892,7 @@ export const foldMapNonEmpty = (S: Semigroup) => export const foldMapNonEmptyWithIndex = (S: Semigroup) => (f: (a: A, i: number) => S) => (self: NonEmptyReadonlyArray): S => - tailNonEmpty(self).reduce((s, a, i) => S.combine(f(a, i + 1))(s), f(headNonEmpty(self), 0)) + tailNonEmpty(self).reduce((s, a, i) => S.combine(s, f(a, i + 1)), f(headNonEmpty(self), 0)) /** * @category folding @@ -2107,7 +2107,7 @@ export const extend = ( */ export const min = (O: Order): ((self: NonEmptyReadonlyArray) => A) => { const S = semigroup.min(O) - return (self) => self.reduce((a, acc) => S.combine(acc)(a)) + return (self) => self.reduce(S.combine) } /** @@ -2115,7 +2115,7 @@ export const min = (O: Order): ((self: NonEmptyReadonlyArray) => A) => */ export const max = (O: Order): ((self: NonEmptyReadonlyArray) => A) => { const S = semigroup.max(O) - return (self) => self.reduce((a, acc) => S.combine(acc)(a)) + return (self) => self.reduce(S.combine) } /** @@ -2139,7 +2139,7 @@ export const unfold = (b: B, f: (b: B) => Option): Array< * @since 1.0.0 */ export const getUnionSemigroup = (equivalence: Equivalence): Semigroup> => - fromCombine(union(equivalence)) as any + fromCombine((self, that) => pipe(self, union(equivalence)(that))) /** * @category instances @@ -2161,7 +2161,8 @@ export const getUnionMonoid = (equivalence: Equivalence): Monoid( equivalence: Equivalence -): Semigroup> => fromCombine(intersection(equivalence)) as any +): Semigroup> => + fromCombine((self, that) => pipe(self, intersection(equivalence)(that))) /** * Returns a `Semigroup` for `ReadonlyArray`. diff --git a/src/String.ts b/src/String.ts index 747df0de3..54d974db4 100644 --- a/src/String.ts +++ b/src/String.ts @@ -55,7 +55,8 @@ export const empty: "" = "" as const /** * @since 1.0.0 */ -export const concat: (that: string) => (self: string) => string = semigroup.string.combine +export const concat = (that: string) => + (self: string): string => semigroup.string.combine(self, that) /** * @example diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index 6e6f5d38e..018949791 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -142,7 +142,7 @@ export const record = ( * @since 2.10.0 */ export const getSemigroup = (): Semigroup> => ({ - combine: (that) => (self) => (x, y) => self(x, y) && that(x, y), + combine: (self, that) => (x, y) => self(x, y) && that(x, y), combineMany: (collection) => self => (x, y) => { diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index dc8321dff..1f42f37d9 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -131,15 +131,14 @@ export const contramap = (f: (b: B) => A) => * @since 1.0.0 */ export const getSemigroup = (): Semigroup> => ({ - combine: (O2) => - (O1) => - fromCompare((self, that) => { - const out = O1.compare(self, that) - if (out !== 0) { - return out - } - return O2.compare(self, that) - }), + combine: (O1, O2) => + fromCompare((self, that) => { + const out = O1.compare(self, that) + if (out !== 0) { + return out + } + return O2.compare(self, that) + }), combineMany: (collection) => (self) => fromCompare((a1, a2) => { diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index 064dc041b..a83b700f2 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -20,7 +20,7 @@ export interface SemiApplicative extends SemiProduct, C */ export const liftSemigroup = (F: SemiApplicative) => (S: Semigroup): Semigroup> => ({ - combine: that => self => pipe(self, F.product(that), F.map(([a1, a2]) => S.combine(a2)(a1))), + combine: (self, that) => pipe(self, F.product(that), F.map(([a1, a2]) => S.combine(a1, a2))), combineMany: collection => self => pipe( diff --git a/src/typeclass/SemiCoproduct.ts b/src/typeclass/SemiCoproduct.ts index d13d6e044..c1523d660 100644 --- a/src/typeclass/SemiCoproduct.ts +++ b/src/typeclass/SemiCoproduct.ts @@ -1,6 +1,7 @@ /** * @since 1.0.0 */ +import { pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import type { Invariant } from "@fp-ts/core/typeclass/Invariant" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" @@ -28,6 +29,6 @@ export const getSemigroup = (F: SemiCoproduct) => (): Semigroup< Kind > => ({ - combine: F.coproduct, + combine: (self, that) => pipe(self, F.coproduct(that)), combineMany: F.coproductMany }) diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index ec36c5743..4d14c9f94 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -31,7 +31,7 @@ import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" * @since 1.0.0 */ export interface Semigroup { - readonly combine: (that: A) => (self: A) => A + readonly combine: (self: A, that: A) => A readonly combineMany: (collection: Iterable) => (self: A) => A } @@ -55,7 +55,7 @@ export const fromCombine = (combine: Semigroup["combine"]): Semigroup = (self) => { let out: A = self for (const a of collection) { - out = combine(a)(out) + out = combine(out, a) } return out } @@ -65,9 +65,7 @@ export const fromCombine = (combine: Semigroup["combine"]): Semigroup = * @category instances * @since 1.0.0 */ -export const string: Semigroup = fromCombine((that: string) => - (self: string): string => self + that -) +export const string: Semigroup = fromCombine((self, that) => self + that) /** * `number` semigroup under addition. @@ -75,9 +73,7 @@ export const string: Semigroup = fromCombine((that: string) => * @category instances * @since 1.0.0 */ -export const numberSum: Semigroup = fromCombine((that: number) => - (self: number): number => self + that -) +export const numberSum: Semigroup = fromCombine((self, that) => self + that) /** * `number` semigroup under multiplication. @@ -86,7 +82,7 @@ export const numberSum: Semigroup = fromCombine((that: number) => * @since 1.0.0 */ export const numberMultiply: Semigroup = { - combine: (that: number) => (self: number): number => self * that, + combine: (self, that) => self * that, combineMany: (collection) => (self) => { if (self === 0) { @@ -109,9 +105,7 @@ export const numberMultiply: Semigroup = { * @category instances * @since 1.0.0 */ -export const bigintSum: Semigroup = fromCombine((that: bigint) => - (self: bigint): bigint => self + that -) +export const bigintSum: Semigroup = fromCombine((self, that) => self + that) /** * `bigint` semigroup under multiplication. @@ -120,7 +114,7 @@ export const bigintSum: Semigroup = fromCombine((that: bigint) => * @since 1.0.0 */ export const bigintMultiply: Semigroup = { - combine: (that: bigint) => (self: bigint): bigint => self * that, + combine: (self, that) => self * that, combineMany: (collection) => (self) => { if (self === 0n) { @@ -144,7 +138,7 @@ export const bigintMultiply: Semigroup = { * @since 1.0.0 */ export const booleanAll: Semigroup = { - combine: (that: boolean) => (self: boolean): boolean => self && that, + combine: (self, that) => self && that, combineMany: (collection) => (self) => { if (self === false) { @@ -166,7 +160,7 @@ export const booleanAll: Semigroup = { * @since 1.0.0 */ export const booleanAny: Semigroup = { - combine: (that: boolean) => (self: boolean): boolean => self || that, + combine: (self, that) => self || that, combineMany: (collection) => (self) => { if (self === true) { @@ -192,7 +186,7 @@ export const booleanAny: Semigroup = { export const tuple = >( ...semigroups: { readonly [K in keyof A]: Semigroup } ): Semigroup => - fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) + fromCombine((self, that) => semigroups.map((S, i) => S.combine(self[i], that[i])) as any) /** * Given a type `A`, this function creates and returns a `Semigroup` for `Array`. @@ -201,7 +195,7 @@ export const tuple = >( * @category combinators * @since 1.0.0 */ -export const array = (): Semigroup> => fromCombine(that => self => self.concat(that)) +export const array = (): Semigroup> => fromCombine((self, that) => self.concat(that)) /** * Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. @@ -223,17 +217,15 @@ export const readonlyArray: () => Semigroup> = array as any export const struct = (semigroups: { readonly [K in keyof A]: Semigroup }): Semigroup< { readonly [K in keyof A]: A[K] } > => - fromCombine((that) => - (self) => { - const r = {} as any - for (const k in semigroups) { - if (Object.prototype.hasOwnProperty.call(semigroups, k)) { - r[k] = semigroups[k].combine(that[k])(self[k]) - } + fromCombine((self, that) => { + const r = {} as any + for (const k in semigroups) { + if (Object.prototype.hasOwnProperty.call(semigroups, k)) { + r[k] = semigroups[k].combine(self[k], that[k]) } - return r } - ) + return r + }) /** * `Semigroup` that returns last minimum of elements. @@ -242,7 +234,7 @@ export const struct = (semigroups: { readonly [K in keyof A]: Semigroup * @since 1.0.0 */ export const min = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(self, that) === -1 ? self : that) + fromCombine((self, that) => O.compare(self, that) === -1 ? self : that) /** * `Semigroup` that returns last maximum of elements. @@ -251,14 +243,14 @@ export const min = (O: Order): Semigroup => * @since 1.0.0 */ export const max = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(self, that) === 1 ? self : that) + fromCombine((self, that) => O.compare(self, that) === 1 ? self : that) /** * @category constructors * @since 1.0.0 */ export const constant = (a: A): Semigroup => ({ - combine: () => () => a, + combine: () => a, combineMany: () => () => a }) @@ -268,12 +260,12 @@ export const constant = (a: A): Semigroup => ({ * @since 1.0.0 */ export const reverse = (S: Semigroup): Semigroup => ({ - combine: (that) => (self) => S.combine(self)(that), + combine: (self, that) => S.combine(that, self), combineMany: (collection) => (self) => { const reversed = Array.from(collection).reverse() return reversed.length > 0 ? - S.combine(self)(S.combineMany(reversed.slice(1))(reversed[0])) : + S.combine(S.combineMany(reversed.slice(1))(reversed[0]), self) : self } }) @@ -283,9 +275,7 @@ export const reverse = (S: Semigroup): Semigroup => ({ */ export const intercalate = (separator: A) => (S: Semigroup): Semigroup => - fromCombine( - (that) => S.combineMany([separator, that]) - ) + fromCombine((self, that) => S.combineMany([separator, that])(self)) /** * Always return the first argument. @@ -294,7 +284,7 @@ export const intercalate = (separator: A) => * @since 1.0.0 */ export const first = (): Semigroup => ({ - combine: () => a => a, + combine: (a) => a, combineMany: () => a => a }) @@ -305,7 +295,7 @@ export const first = (): Semigroup => ({ * @since 1.0.0 */ export const last = (): Semigroup => ({ - combine: second => () => second, + combine: (_, second) => second, combineMany: collection => self => { let a: A = self @@ -323,7 +313,7 @@ export const imap = ( from: (b: B) => A ) => (S: Semigroup): Semigroup => ({ - combine: that => self => to(S.combine(from(that))(from(self))), + combine: (self, that) => to(S.combine(from(self), from(that))), combineMany: (collection) => self => to( diff --git a/test/Bigint.ts b/test/Bigint.ts index 164731dc9..02f76c44f 100644 --- a/test/Bigint.ts +++ b/test/Bigint.ts @@ -42,7 +42,7 @@ describe.concurrent("Bigint", () => { }) it("SemigroupSum", () => { - deepStrictEqual(pipe(2n, Bigint.SemigroupSum.combine(3n)), 5n) + deepStrictEqual(Bigint.SemigroupSum.combine(2n, 3n), 5n) }) it("MonoidSum", () => { @@ -50,7 +50,7 @@ describe.concurrent("Bigint", () => { }) it("SemigroupMultiply", () => { - deepStrictEqual(pipe(2n, Bigint.SemigroupMultiply.combine(3n)), 6n) + deepStrictEqual(Bigint.SemigroupMultiply.combine(2n, 3n), 6n) deepStrictEqual(pipe(0n, Bigint.SemigroupMultiply.combineMany([1n, 2n, 3n])), 0n) deepStrictEqual(pipe(2n, Bigint.SemigroupMultiply.combineMany([1n, 0n, 3n])), 0n) }) diff --git a/test/Function.ts b/test/Function.ts index 6f9c6e5e9..2bbcc7d75 100644 --- a/test/Function.ts +++ b/test/Function.ts @@ -11,7 +11,7 @@ describe.concurrent("Function", () => { it("getSemigroup", () => { const S = Function.getSemigroup(Number.SemigroupSum)() const f = (s: string) => s === "a" ? 0 : 1 - const g = Function.pipe(size, S.combine(f)) + const g = S.combine(size, f) deepStrictEqual(g(""), 1) deepStrictEqual(g("a"), 1) deepStrictEqual(g("b"), 2) @@ -21,12 +21,12 @@ describe.concurrent("Function", () => { it("getMonoid", () => { const M = Function.getMonoid(Number.MonoidSum)() const f = (s: string) => s === "a" ? 0 : 1 - const g = Function.pipe(size, M.combine(f)) + const g = M.combine(size, f) deepStrictEqual(g(""), 1) deepStrictEqual(g("a"), 1) deepStrictEqual(g("b"), 2) - deepStrictEqual(Function.pipe(size, M.combine(M.empty))("a"), 1) - deepStrictEqual(Function.pipe(M.empty, M.combine(size))("a"), 1) + deepStrictEqual(M.combine(size, M.empty)("a"), 1) + deepStrictEqual(M.combine(M.empty, size)("a"), 1) deepStrictEqual(M.combineAll([size, size])("a"), 2) }) diff --git a/test/Number.ts b/test/Number.ts index 85a945f04..7f7cf69ea 100644 --- a/test/Number.ts +++ b/test/Number.ts @@ -46,7 +46,7 @@ describe.concurrent("Number", () => { }) it("SemigroupSum", () => { - deepStrictEqual(pipe(2, Number.SemigroupSum.combine(3)), 5) + deepStrictEqual(Number.SemigroupSum.combine(2, 3), 5) }) it("MonoidSum", () => { @@ -54,7 +54,7 @@ describe.concurrent("Number", () => { }) it("SemigroupMultiply", () => { - deepStrictEqual(pipe(2, Number.SemigroupMultiply.combine(3)), 6) + deepStrictEqual(Number.SemigroupMultiply.combine(2, 3), 6) deepStrictEqual(pipe(0, Number.SemigroupMultiply.combineMany([1, 2, 3])), 0) deepStrictEqual(pipe(2, Number.SemigroupMultiply.combineMany([1, 0, 3])), 0) }) diff --git a/test/Option.ts b/test/Option.ts index 3cde1acdf..b897eeaf9 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -409,11 +409,11 @@ describe.concurrent("Option", () => { it("getMonoid", () => { const M = _.getMonoid(S.Semigroup) - deepStrictEqual(pipe(_.none(), M.combine(_.none())), _.none()) - deepStrictEqual(pipe(_.none(), M.combine(_.some("a"))), _.some("a")) - deepStrictEqual(pipe(_.some("a"), M.combine(_.none())), _.some("a")) - deepStrictEqual(pipe(_.some("b"), M.combine(_.some("a"))), _.some("ba")) - deepStrictEqual(pipe(_.some("a"), M.combine(_.some("b"))), _.some("ab")) + deepStrictEqual(M.combine(_.none(), _.none()), _.none()) + deepStrictEqual(M.combine(_.none(), _.some("a")), _.some("a")) + deepStrictEqual(M.combine(_.some("a"), _.none()), _.some("a")) + deepStrictEqual(M.combine(_.some("b"), _.some("a")), _.some("ba")) + deepStrictEqual(M.combine(_.some("a"), _.some("b")), _.some("ab")) deepStrictEqual(pipe(_.some("a"), M.combineMany([_.some("b")])), _.some("ab")) deepStrictEqual(pipe(_.none(), M.combineMany([_.some("b")])), _.some("b")) diff --git a/test/Ordering.ts b/test/Ordering.ts index 55580eaec..091af1c5a 100644 --- a/test/Ordering.ts +++ b/test/Ordering.ts @@ -21,10 +21,10 @@ describe("Ordering", () => { }) it("Semigroup", () => { - deepStrictEqual(pipe(0, _.Semigroup.combine(0)), 0) - deepStrictEqual(pipe(0, _.Semigroup.combine(1)), 1) - deepStrictEqual(pipe(1, _.Semigroup.combine(-1)), 1) - deepStrictEqual(pipe(-1, _.Semigroup.combine(1)), -1) + deepStrictEqual(_.Semigroup.combine(0, 0), 0) + deepStrictEqual(_.Semigroup.combine(0, 1), 1) + deepStrictEqual(_.Semigroup.combine(1, -1), 1) + deepStrictEqual(_.Semigroup.combine(-1, 1), -1) deepStrictEqual(pipe(0, _.Semigroup.combineMany([])), 0) deepStrictEqual(pipe(1, _.Semigroup.combineMany([])), 1) @@ -36,11 +36,11 @@ describe("Ordering", () => { }) it("Monoid", () => { - deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(0)), 0) - deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(1)), 1) - deepStrictEqual(pipe(_.Monoid.empty, _.Monoid.combine(-1)), -1) - deepStrictEqual(pipe(0, _.Monoid.combine(_.Monoid.empty)), 0) - deepStrictEqual(pipe(1, _.Monoid.combine(_.Monoid.empty)), 1) - deepStrictEqual(pipe(-1, _.Monoid.combine(_.Monoid.empty)), -1) + deepStrictEqual(_.Monoid.combine(_.Monoid.empty, 0), 0) + deepStrictEqual(_.Monoid.combine(_.Monoid.empty, 1), 1) + deepStrictEqual(_.Monoid.combine(_.Monoid.empty, -1), -1) + deepStrictEqual(_.Monoid.combine(0, _.Monoid.empty), 0) + deepStrictEqual(_.Monoid.combine(1, _.Monoid.empty), 1) + deepStrictEqual(_.Monoid.combine(-1, _.Monoid.empty), -1) }) }) diff --git a/test/Predicate.ts b/test/Predicate.ts index b675bf30f..269e68f27 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -115,7 +115,7 @@ describe.concurrent("Predicate", () => { it("getSemigroupAny", () => { const S = _.getSemigroupAny() - const predicate = pipe(isPositive, S.combine(isNegative)) + const predicate = S.combine(isPositive, isNegative) deepStrictEqual(predicate(0), false) deepStrictEqual(predicate(-1), true) deepStrictEqual(predicate(1), true) @@ -123,7 +123,7 @@ describe.concurrent("Predicate", () => { it("getMonoidAny", () => { const M = _.getMonoidAny() - const predicate = pipe(isPositive, M.combine(M.empty)) + const predicate = M.combine(isPositive, M.empty) deepStrictEqual(predicate(0), isPositive(0)) deepStrictEqual(predicate(-1), isPositive(-1)) deepStrictEqual(predicate(1), isPositive(1)) @@ -131,7 +131,7 @@ describe.concurrent("Predicate", () => { it("getSemigroupAll", () => { const S = _.getSemigroupAll() - const predicate = pipe(isPositive, S.combine(isLessThan2)) + const predicate = S.combine(isPositive, isLessThan2) deepStrictEqual(predicate(0), false) deepStrictEqual(predicate(-2), false) deepStrictEqual(predicate(1), true) @@ -139,7 +139,7 @@ describe.concurrent("Predicate", () => { it("getMonoidAll", () => { const M = _.getMonoidAll() - const predicate = pipe(isPositive, M.combine(M.empty)) + const predicate = M.combine(isPositive, M.empty) deepStrictEqual(predicate(0), isPositive(0)) deepStrictEqual(predicate(-1), isPositive(-1)) deepStrictEqual(predicate(1), isPositive(1)) diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index ba9cbc34c..89b7281e9 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -1045,10 +1045,10 @@ describe.concurrent("ReadonlyArray", () => { it("getMonoid", () => { const M = RA.getMonoid() - deepStrictEqual(M.combine([3, 4])([1, 2]), [1, 2, 3, 4]) + deepStrictEqual(M.combine([1, 2], [3, 4]), [1, 2, 3, 4]) const x = [1, 2] - deepStrictEqual(M.combine(M.empty)(x), x) - deepStrictEqual(M.combine(x)(M.empty), x) + deepStrictEqual(M.combine(x, M.empty), x) + deepStrictEqual(M.combine(M.empty, x), x) deepStrictEqual(M.combineAll([[1, 2], [3, 4, 5], [5, 6, 7, 1]]), [1, 2, 3, 4, 5, 5, 6, 7, 1]) }) @@ -1460,12 +1460,12 @@ describe.concurrent("ReadonlyArray", () => { it("getSemigroup", () => { const S = RA.getSemigroup() - expect(pipe([1, 2], S.combine([2, 3]))).toEqual([1, 2, 2, 3]) + expect(S.combine([1, 2], [2, 3])).toEqual([1, 2, 2, 3]) }) it("getUnionSemigroup", () => { const S = RA.getUnionSemigroup(Number.Equivalence) - expect(pipe([1, 2], S.combine([2, 3]))).toEqual([1, 2, 3]) + expect(S.combine([1, 2], [2, 3])).toEqual([1, 2, 3]) }) it("intersection", () => { @@ -1485,22 +1485,22 @@ describe.concurrent("ReadonlyArray", () => { it("getUnionMonoid", () => { const M = RA.getUnionMonoid(Number.Equivalence) const two: ReadonlyArray = [1, 2] - deepStrictEqual(M.combine([3, 4])(two), [1, 2, 3, 4]) - deepStrictEqual(M.combine([2, 3])(two), [1, 2, 3]) - deepStrictEqual(M.combine([1, 2])(two), [1, 2]) + deepStrictEqual(M.combine(two, [3, 4]), [1, 2, 3, 4]) + deepStrictEqual(M.combine(two, [2, 3]), [1, 2, 3]) + deepStrictEqual(M.combine(two, [1, 2]), [1, 2]) - deepStrictEqual(M.combine(two)(M.empty), two) - deepStrictEqual(M.combine(M.empty)(two), two) - deepStrictEqual(M.combine(M.empty)(M.empty), M.empty) + deepStrictEqual(M.combine(M.empty, two), two) + deepStrictEqual(M.combine(two, M.empty), two) + deepStrictEqual(M.combine(M.empty, M.empty), M.empty) deepStrictEqual(M.combineAll([[1, 2], [3, 4, 5], [5, 6, 7, 1]]), [1, 2, 3, 4, 5, 6, 7]) }) it("getIntersectionSemigroup", () => { const S = RA.getIntersectionSemigroup(Number.Equivalence) - deepStrictEqual(S.combine([1, 2])([3, 4]), []) - deepStrictEqual(S.combine([1, 2])([2, 3]), [2]) - deepStrictEqual(S.combine([1, 2])([1, 2]), [1, 2]) + deepStrictEqual(S.combine([3, 4], [1, 2]), []) + deepStrictEqual(S.combine([2, 3], [1, 2]), [2]) + deepStrictEqual(S.combine([1, 2], [1, 2]), [1, 2]) }) it("should be safe when calling map with a binary function", () => { diff --git a/test/String.ts b/test/String.ts index 31f099c75..fba32f3c7 100644 --- a/test/String.ts +++ b/test/String.ts @@ -15,7 +15,7 @@ describe.concurrent("String", () => { }) it("Semigroup", () => { - expect(String.Semigroup.combine("b")("a")).toEqual("ab") + expect(String.Semigroup.combine("a", "b")).toEqual("ab") expect(String.Semigroup.combineMany(["b", "c"])("a")).toEqual("abc") expect(String.Semigroup.combineMany([])("a")).toEqual("a") }) diff --git a/test/typeclass/Applicative.ts b/test/typeclass/Applicative.ts index c62252223..fb426b75d 100644 --- a/test/typeclass/Applicative.ts +++ b/test/typeclass/Applicative.ts @@ -1,4 +1,3 @@ -import { pipe } from "@fp-ts/core/Function" import * as N from "@fp-ts/core/Number" import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/Applicative" @@ -8,9 +7,9 @@ describe("Applicative", () => { it("liftMonoid", () => { const liftMonoid = _.liftMonoid(O.Applicative) const M = liftMonoid(N.MonoidSum) - U.deepStrictEqual(pipe(O.none(), M.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.none(), M.combine(O.some(2))), O.none()) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.some(2))), O.some(3)) + U.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) + U.deepStrictEqual(M.combine(O.some(1), O.none()), O.none()) + U.deepStrictEqual(M.combine(O.none(), O.some(2)), O.none()) + U.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(3)) }) }) diff --git a/test/typeclass/Coproduct.ts b/test/typeclass/Coproduct.ts index 0a138b74e..93c2fe019 100644 --- a/test/typeclass/Coproduct.ts +++ b/test/typeclass/Coproduct.ts @@ -1,4 +1,3 @@ -import { pipe } from "@fp-ts/core/Function" import * as O from "@fp-ts/core/Option" import * as _ from "@fp-ts/core/typeclass/Coproduct" import * as U from "../util" @@ -6,13 +5,13 @@ import * as U from "../util" describe("Coproduct", () => { it("getMonoid", () => { const M = _.getMonoid(O.Alternative)() - U.deepStrictEqual(pipe(O.none(), M.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.none())), O.some(1)) - U.deepStrictEqual(pipe(O.none(), M.combine(O.some(2))), O.some(2)) - U.deepStrictEqual(pipe(O.some(1), M.combine(O.some(2))), O.some(1)) + U.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) + U.deepStrictEqual(M.combine(O.some(1), O.none()), O.some(1)) + U.deepStrictEqual(M.combine(O.none(), O.some(2)), O.some(2)) + U.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(1)) - U.deepStrictEqual(pipe(M.empty, M.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(M.empty, M.combine(O.some(2))), O.some(2)) - U.deepStrictEqual(pipe(O.some(1), M.combine(M.empty)), O.some(1)) + U.deepStrictEqual(M.combine(M.empty, O.none()), O.none()) + U.deepStrictEqual(M.combine(M.empty, O.some(2)), O.some(2)) + U.deepStrictEqual(M.combine(O.some(1), M.empty), O.some(1)) }) }) diff --git a/test/typeclass/Equivalence.ts b/test/typeclass/Equivalence.ts index 6411c8247..1df2bc0f2 100644 --- a/test/typeclass/Equivalence.ts +++ b/test/typeclass/Equivalence.ts @@ -102,7 +102,7 @@ describe("Equivalence", () => { const S = _.getSemigroup() const E0: _.Equivalence = _.contramap((x: T) => x[0])(_.string) const E1: _.Equivalence = _.contramap((x: T) => x[1])(_.number) - const eqE0E1 = pipe(E0, S.combine(E1)) + const eqE0E1 = S.combine(E0, E1) expect(eqE0E1(["a", 1, true], ["a", 1, true])).toEqual(true) expect(eqE0E1(["a", 1, true], ["a", 1, false])).toEqual(true) expect(eqE0E1(["a", 1, true], ["b", 1, true])).toEqual(false) diff --git a/test/typeclass/Invariant.ts b/test/typeclass/Invariant.ts index e10460ea9..f1572662d 100644 --- a/test/typeclass/Invariant.ts +++ b/test/typeclass/Invariant.ts @@ -9,13 +9,13 @@ import * as U from "../util" describe("Invariant", () => { it("imapComposition", () => { const imap = _.imapComposition(semigroup.Invariant, O.Invariant) - const S = pipe(O.getMonoid(String.Semigroup), imap(s => [s] as const, ([s]) => s)) - U.deepStrictEqual(pipe(O.none(), S.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.none(), S.combine(O.some(["b"]))), O.some(["b"] as const)) - U.deepStrictEqual(pipe(O.some(["a"] as const), S.combine(O.none())), O.some(["a"] as const)) + const S = pipe(O.getMonoid(String.Semigroup), imap(s => [s], ([s]) => s)) + U.deepStrictEqual(S.combine(O.none(), O.none()), O.none()) + U.deepStrictEqual(S.combine(O.none(), O.some(["b"])), O.some(["b"])) + U.deepStrictEqual(S.combine(O.some(["a"]), O.none()), O.some(["a"])) U.deepStrictEqual( - pipe(O.some(["a"] as const), S.combine(O.some(["b"]))), - O.some(["ab"] as const) + S.combine(O.some(["a"]), O.some(["b"])), + O.some(["ab"]) ) }) diff --git a/test/typeclass/Monoid.ts b/test/typeclass/Monoid.ts index 1ce38fb06..d44612787 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -25,9 +25,9 @@ describe("Monoid", () => { it("reverse", () => { const M = monoid.reverse(String.Monoid) - U.deepStrictEqual(pipe("a", M.combine("b")), "ba") - U.deepStrictEqual(pipe("a", M.combine(M.empty)), "a") - U.deepStrictEqual(pipe(M.empty, M.combine("a")), "a") + U.deepStrictEqual(M.combine("a", "b"), "ba") + U.deepStrictEqual(M.combine("a", M.empty), "a") + U.deepStrictEqual(M.combine(M.empty, "a"), "a") U.deepStrictEqual(pipe("a", M.combineMany([])), "a") U.deepStrictEqual(pipe("a", M.combineMany(["b", "c", "d"])), "dcba") U.deepStrictEqual(pipe("a", M.combineMany([M.empty])), "a") @@ -41,7 +41,7 @@ describe("Monoid", () => { age: N.MonoidSum }) U.deepStrictEqual(M.empty, { name: "", age: 0 }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, M.combine({ name: "b", age: 20 })), { + U.deepStrictEqual(M.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { name: "ab", age: 30 }) @@ -60,6 +60,6 @@ describe("Monoid", () => { N.MonoidSum ) U.deepStrictEqual(M.empty, ["", 0]) - U.deepStrictEqual(pipe(["a", 10], M.combine(["b", 20])), ["ab", 30]) + U.deepStrictEqual(M.combine(["a", 10], ["b", 20]), ["ab", 30]) }) }) diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index aca17cdde..4e03f51ba 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -55,13 +55,13 @@ describe("Order", () => { string.Order, _.contramap((x: T) => x[1]) ) - U.deepStrictEqual(sort(pipe(sortByFst, S.combine(sortBySnd)))(tuples), [ + U.deepStrictEqual(sort(S.combine(sortByFst, sortBySnd))(tuples), [ [1, "b"], [1, "c"], [2, "a"], [2, "c"] ]) - U.deepStrictEqual(sort(pipe(sortBySnd, S.combine(sortByFst)))(tuples), [ + U.deepStrictEqual(sort(S.combine(sortBySnd, sortByFst))(tuples), [ [2, "a"], [1, "b"], [1, "c"], diff --git a/test/typeclass/Product.ts b/test/typeclass/Product.ts index 76cc0e81c..2f917c38d 100644 --- a/test/typeclass/Product.ts +++ b/test/typeclass/Product.ts @@ -1,5 +1,4 @@ import * as Boolean from "@fp-ts/core/Boolean" -import { pipe } from "@fp-ts/core/Function" import * as Number from "@fp-ts/core/Number" import * as O from "@fp-ts/core/Option" import * as P from "@fp-ts/core/Predicate" @@ -23,9 +22,9 @@ describe("Product", () => { it("Invariant (Semigroup)", () => { const tuple = _.tuple(semigroup.Product) - U.deepStrictEqual(pipe([], tuple().combine([])), []) + U.deepStrictEqual(tuple().combine([], []), []) const S = tuple(String.Semigroup, Number.SemigroupSum) - U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) + U.deepStrictEqual(S.combine(["a", 2], ["b", 3]), ["ab", 5]) }) it("Contravariant (Predicate)", () => { @@ -54,9 +53,9 @@ describe("Product", () => { it("Invariant (Semigroup)", () => { const struct = _.struct(semigroup.Product) - U.deepStrictEqual(pipe({}, struct({}).combine({})), {}) + U.deepStrictEqual(struct({}).combine({}, {}), {}) const S = struct({ x: String.Semigroup, y: Number.SemigroupSum }) - U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) + U.deepStrictEqual(S.combine({ x: "a", y: 2 }, { x: "b", y: 3 }), { x: "ab", y: 5 }) }) it("Contravariant (Predicate)", () => { diff --git a/test/typeclass/SemiApplicative.ts b/test/typeclass/SemiApplicative.ts index fad3732da..2bec885af 100644 --- a/test/typeclass/SemiApplicative.ts +++ b/test/typeclass/SemiApplicative.ts @@ -33,10 +33,10 @@ describe("SemiApplicative", () => { it("liftSemigroup", () => { const liftSemigroup = _.liftSemigroup(O.SemiApplicative) const S = liftSemigroup(String.Semigroup) - U.deepStrictEqual(pipe(O.none(), S.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.none(), S.combine(O.some("b"))), O.none()) - U.deepStrictEqual(pipe(O.some("a"), S.combine(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some("a"), S.combine(O.some("b"))), O.some("ab")) + U.deepStrictEqual(S.combine(O.none(), O.none()), O.none()) + U.deepStrictEqual(S.combine(O.none(), O.some("b")), O.none()) + U.deepStrictEqual(S.combine(O.some("a"), O.none()), O.none()) + U.deepStrictEqual(S.combine(O.some("a"), O.some("b")), O.some("ab")) U.deepStrictEqual(pipe(O.some("a"), S.combineMany([O.some("b"), O.some("c")])), O.some("abc")) }) diff --git a/test/typeclass/SemiCoproduct.ts b/test/typeclass/SemiCoproduct.ts index 1049fd717..29ca03bf3 100644 --- a/test/typeclass/SemiCoproduct.ts +++ b/test/typeclass/SemiCoproduct.ts @@ -1,14 +1,13 @@ import * as E from "@fp-ts/core/Either" -import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/typeclass/SemiCoproduct" import * as U from "../util" describe("SemiCoproduct", () => { it("getSemigroup", () => { const S = _.getSemigroup(E.SemiCoproduct)() - U.deepStrictEqual(pipe(E.right(1), S.combine(E.right(2))), E.right(1)) - U.deepStrictEqual(pipe(E.left("a"), S.combine(E.right(2))), E.right(2)) - U.deepStrictEqual(pipe(E.right(1), S.combine(E.left("b"))), E.right(1)) - U.deepStrictEqual(pipe(E.left("a"), S.combine(E.left("b"))), E.left("b")) + U.deepStrictEqual(S.combine(E.right(1), E.right(2)), E.right(1)) + U.deepStrictEqual(S.combine(E.left("a"), E.right(2)), E.right(2)) + U.deepStrictEqual(S.combine(E.right(1), E.left("b")), E.right(1)) + U.deepStrictEqual(S.combine(E.left("a"), E.left("b")), E.left("b")) }) }) diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 0befff78a..29c875655 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -195,7 +195,7 @@ describe("SemiProduct", () => { it("Invariant (Semigroup)", () => { const nonEmptyTuple = _.nonEmptyTuple(semigroup.SemiProduct) const S = nonEmptyTuple(String.Semigroup, Number.SemigroupSum) - U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) + U.deepStrictEqual(S.combine(["a", 2], ["b", 3]), ["ab", 5]) }) it("Contravariant (Predicate)", () => { @@ -223,7 +223,7 @@ describe("SemiProduct", () => { it("Invariant (Semigroup)", () => { const nonEmptyStruct = _.nonEmptyStruct(semigroup.Product) const S = nonEmptyStruct({ x: String.Semigroup, y: Number.SemigroupSum }) - U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) + U.deepStrictEqual(S.combine({ x: "a", y: 2 }, { x: "b", y: 3 }), { x: "ab", y: 5 }) }) it("Contravariant (Predicate)", () => { diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index 537d82ce9..127d9d94f 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -12,7 +12,7 @@ describe("Semigroup", () => { it("reverse", () => { const A = _.reverse(String.Semigroup) - U.deepStrictEqual(pipe("a", A.combine("b")), "ba") + U.deepStrictEqual(A.combine("a", "b"), "ba") U.deepStrictEqual(pipe("a", A.combineMany([])), "a") U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "ba") U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "dcba") @@ -20,14 +20,14 @@ describe("Semigroup", () => { it("constant", () => { const A = _.constant("-") - U.deepStrictEqual(pipe("a", A.combine("b")), "-") + U.deepStrictEqual(A.combine("a", "b"), "-") U.deepStrictEqual(pipe("a", A.combineMany([])), "-") U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "-") }) it("intercalate", () => { const A = pipe(String.Semigroup, _.intercalate("|")) - U.deepStrictEqual(pipe("a", A.combine("b")), "a|b") + U.deepStrictEqual(A.combine("a", "b"), "a|b") U.deepStrictEqual(pipe("a", A.combineMany([])), "a") U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "a|b") U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "a|b|c|d") @@ -70,7 +70,7 @@ describe("Semigroup", () => { name: String.Semigroup, age: Number.SemigroupSum }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combine({ name: "b", age: 20 })), { + U.deepStrictEqual(A.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { name: "ab", age: 30 }) @@ -96,7 +96,7 @@ describe("Semigroup", () => { String.Semigroup, Number.SemigroupSum ) - U.deepStrictEqual(pipe(["a", 10], A.combine(["b", 20])), ["ab", 30]) + U.deepStrictEqual(A.combine(["a", 10], ["b", 20]), ["ab", 30]) U.deepStrictEqual(pipe(["a", 10], A.combineMany([])), ["a", 10]) U.deepStrictEqual(pipe(["a", 10], A.combineMany([["b", 20]])), ["ab", 30]) U.deepStrictEqual(pipe(["a", 10], A.combineMany([["b", 20], ["c", 30]])), ["abc", 60]) @@ -104,14 +104,14 @@ describe("Semigroup", () => { it("first", () => { const A = _.first() - U.deepStrictEqual(pipe(1, A.combine(2)), 1) + U.deepStrictEqual(A.combine(1, 2), 1) U.deepStrictEqual(pipe(1, A.combineMany([])), 1) U.deepStrictEqual(pipe(1, A.combineMany([2, 3, 4, 5, 6])), 1) }) it("last", () => { const A = _.last() - U.deepStrictEqual(pipe(1, A.combine(2)), 2) + U.deepStrictEqual(A.combine(1, 2), 2) U.deepStrictEqual(pipe(1, A.combineMany([])), 1) U.deepStrictEqual(pipe(1, A.combineMany([2, 3, 4, 5, 6])), 6) }) @@ -119,7 +119,7 @@ describe("Semigroup", () => { it("imap", () => { const imap = _.imap const To = imap((s: string) => [s], ([s]) => s)(String.Semigroup) - U.deepStrictEqual(pipe(["a"], To.combine(["b"])), ["ab"]) + U.deepStrictEqual(To.combine(["a"], ["b"]), ["ab"]) U.deepStrictEqual(pipe(["a"], To.combineMany([])), ["a"]) U.deepStrictEqual(pipe(["a"], To.combineMany([["b"]])), ["ab"]) U.deepStrictEqual(pipe(["a"], To.combineMany([["b"], ["c"]])), ["abc"]) @@ -147,7 +147,7 @@ describe("Semigroup", () => { ([a, b, c]): [[string, number], number] => [[a, b], c] ) ) - U.deepStrictEqual(pipe(["a", 2, 3], A.combine(["b", 3, 4])), ["ab", 5, 12]) + U.deepStrictEqual(A.combine(["a", 2, 3], ["b", 3, 4]), ["ab", 5, 12]) }) it("productMany", () => { @@ -155,6 +155,6 @@ describe("Semigroup", () => { String.Semigroup, _.SemiProduct.productMany([String.Semigroup, String.Semigroup]) ) - U.deepStrictEqual(pipe(["a", "b", "c"], A.combine(["d", "e", "f"])), ["ad", "be", "cf"]) + U.deepStrictEqual(A.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) }) }) From 016764fac967bd38a48fdc6ddf50ccf2c8ba3b43 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 15:00:11 +0100 Subject: [PATCH 61/80] Semigroup: make combineMany binary --- CHANGELOG.md | 1 + src/Function.ts | 6 +- src/Identity.ts | 2 +- src/Option.ts | 15 ++-- src/Ordering.ts | 17 ++-- src/Predicate.ts | 4 +- src/ReadonlyArray.ts | 6 +- src/typeclass/Equivalence.ts | 19 ++--- src/typeclass/Monoid.ts | 18 ++-- src/typeclass/Order.ts | 23 +++--- src/typeclass/SemiApplicative.ts | 13 ++- src/typeclass/SemiCoproduct.ts | 2 +- src/typeclass/Semigroup.ts | 133 ++++++++++++++---------------- test/Bigint.ts | 5 +- test/Boolean.ts | 12 +-- test/Function.ts | 2 +- test/Number.ts | 5 +- test/Option.ts | 6 +- test/Ordering.ts | 15 ++-- test/String.ts | 4 +- test/typeclass/Equivalence.ts | 2 +- test/typeclass/Monoid.ts | 9 +- test/typeclass/Order.ts | 8 +- test/typeclass/SemiApplicative.ts | 2 +- test/typeclass/SemiCoproduct.ts | 1 + test/typeclass/Semigroup.ts | 119 +++++++++++++------------- 26 files changed, 210 insertions(+), 239 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cd53a15..28dde2d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - `Semigroup` - make `combine` binary + - make `combineMany` binary - `Semiproduct` - rename `productFlatten` to `element` - `Order` diff --git a/src/Function.ts b/src/Function.ts index 109a6c259..ebe780f4e 100644 --- a/src/Function.ts +++ b/src/Function.ts @@ -79,11 +79,7 @@ export const getMonoid = (Monoid: monoid.Monoid) => (): monoid.Monoid<(a: A) => M> => { const S = getSemigroup(Monoid)() const empty = () => Monoid.empty - return ({ - ...S, - combineAll: (collection) => S.combineMany(collection)(empty), - empty - }) + return ({ ...S, combineAll: (collection) => S.combineMany(empty, collection), empty }) } /** diff --git a/src/Identity.ts b/src/Identity.ts index 59cd59f0b..3a87f7966 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -405,7 +405,7 @@ export const getSemiCoproduct = ( ): semiCoproduct.SemiCoproduct> => ({ imap: Invariant.imap, coproduct: (that) => (self) => S.combine(self, that), - coproductMany: S.combineMany + coproductMany: (collection) => (self) => S.combineMany(self, collection) }) /** diff --git a/src/Option.ts b/src/Option.ts index 9c4477cfa..368dcad20 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -547,14 +547,13 @@ export const getMonoid = ( isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(self.value, that.value)) return ({ combine, - combineMany: (others) => - (start) => { - let c = start - for (const o of others) { - c = combine(c, o) - } - return c - }, + combineMany: (self, collection) => { + let c = self + for (const o of collection) { + c = combine(c, o) + } + return c + }, combineAll: (collection: Iterable>): Option => { let c: Option = option.none for (const o of collection) { diff --git a/src/Ordering.ts b/src/Ordering.ts index 67ccf3917..65782eefe 100644 --- a/src/Ordering.ts +++ b/src/Ordering.ts @@ -32,19 +32,18 @@ export const match = ( */ export const Semigroup: semigroup.Semigroup = { combine: (self, that) => self !== 0 ? self : that, - combineMany: (collection) => - (self) => { - let ordering = self + combineMany: (self, collection) => { + let ordering = self + if (ordering !== 0) { + return ordering + } + for (ordering of collection) { if (ordering !== 0) { return ordering } - for (ordering of collection) { - if (ordering !== 0) { - return ordering - } - } - return ordering } + return ordering + } } /** diff --git a/src/Predicate.ts b/src/Predicate.ts index fb3fe7e63..3bae8a9c8 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -286,7 +286,7 @@ export const getMonoidAny = (): monoid.Monoid> => { return ({ combine: S.combine, combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(collection)(constFalse), + combineAll: (collection) => S.combineMany(constFalse, collection), empty: constFalse }) } @@ -307,7 +307,7 @@ export const getMonoidAll = (): monoid.Monoid> => { return ({ combine: S.combine, combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(collection)(constTrue), + combineAll: (collection) => S.combineMany(constTrue, collection), empty: constTrue }) } diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index f3689b551..2a944f518 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2077,7 +2077,7 @@ export const intercalateNonEmpty = ( ) => (middle: A) => (self: NonEmptyReadonlyArray): A => - semigroup.intercalate(middle)(S).combineMany(tailNonEmpty(self))(headNonEmpty(self)) + semigroup.intercalate(middle)(S).combineMany(headNonEmpty(self), tailNonEmpty(self)) /** * @since 1.0.0 @@ -2150,8 +2150,8 @@ export const getUnionMonoid = (equivalence: Equivalence): Monoid S.combineMany(collection)(empty()), - empty: empty() + combineAll: (collection) => S.combineMany([], collection), + empty: [] }) } diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index 018949791..3929cb0e7 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -143,19 +143,18 @@ export const record = ( */ export const getSemigroup = (): Semigroup> => ({ combine: (self, that) => (x, y) => self(x, y) && that(x, y), - combineMany: (collection) => - self => - (x, y) => { - if (!self(x, y)) { + combineMany: (self, collection) => + (x, y) => { + if (!self(x, y)) { + return false + } + for (const equivalence of collection) { + if (!equivalence(x, y)) { return false } - for (const equivalence of collection) { - if (!equivalence(x, y)) { - return false - } - } - return true } + return true + } }) const empty: Equivalence = () => true diff --git a/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index 55081696b..fd2c7cfa0 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -21,7 +21,7 @@ export interface Monoid extends Semigroup { export const fromSemigroup = (S: Semigroup, empty: Monoid["empty"]): Monoid => ({ ...S, empty, - combineAll: collection => S.combineMany(collection)(empty) + combineAll: collection => S.combineMany(empty, collection) }) /** @@ -58,7 +58,7 @@ export const reverse = (M: Monoid): Monoid => fromSemigroup(semigroup.r */ export const string: Monoid = { ...semigroup.string, - combineAll: (collection) => semigroup.string.combineMany(collection)(""), + combineAll: (collection) => semigroup.string.combineMany("", collection), empty: "" } @@ -72,7 +72,7 @@ export const string: Monoid = { */ export const numberSum: Monoid = { ...semigroup.numberSum, - combineAll: (collection) => semigroup.numberSum.combineMany(collection)(0), + combineAll: (collection) => semigroup.numberSum.combineMany(0, collection), empty: 0 } @@ -86,7 +86,7 @@ export const numberSum: Monoid = { */ export const numberMultiply: Monoid = { ...semigroup.numberMultiply, - combineAll: (collection) => semigroup.numberMultiply.combineMany(collection)(1), + combineAll: (collection) => semigroup.numberMultiply.combineMany(1, collection), empty: 1 } @@ -100,7 +100,7 @@ export const numberMultiply: Monoid = { */ export const bigintSum: Monoid = { ...semigroup.bigintSum, - combineAll: (collection) => semigroup.bigintSum.combineMany(collection)(0n), + combineAll: (collection) => semigroup.bigintSum.combineMany(0n, collection), empty: 0n } @@ -114,7 +114,7 @@ export const bigintSum: Monoid = { */ export const bigintMultiply: Monoid = { ...semigroup.bigintMultiply, - combineAll: (collection) => semigroup.bigintMultiply.combineMany(collection)(1n), + combineAll: (collection) => semigroup.bigintMultiply.combineMany(1n, collection), empty: 1n } @@ -128,7 +128,7 @@ export const bigintMultiply: Monoid = { */ export const booleanAll: Monoid = { ...semigroup.booleanAll, - combineAll: (all) => semigroup.booleanAll.combineMany(all)(true), + combineAll: (collection) => semigroup.booleanAll.combineMany(true, collection), empty: true } @@ -142,7 +142,7 @@ export const booleanAll: Monoid = { */ export const booleanAny: Monoid = { ...semigroup.booleanAny, - combineAll: (all) => semigroup.booleanAny.combineMany(all)(false), + combineAll: (collection) => semigroup.booleanAny.combineMany(false, collection), empty: false } @@ -171,7 +171,7 @@ export const array = (): Monoid> => { return ({ combine: S.combine, combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(collection)([]), + combineAll: (collection) => S.combineMany([], collection), empty: [] }) } diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 1f42f37d9..3158a1821 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -139,21 +139,20 @@ export const getSemigroup = (): Semigroup> => ({ } return O2.compare(self, that) }), - combineMany: (collection) => - (self) => - fromCompare((a1, a2) => { - let out = self.compare(a1, a2) + combineMany: (self, collection) => + fromCompare((a1, a2) => { + let out = self.compare(a1, a2) + if (out !== 0) { + return out + } + for (const O of collection) { + out = O.compare(a1, a2) if (out !== 0) { return out } - for (const O of collection) { - out = O.compare(a1, a2) - if (out !== 0) { - return out - } - } - return out - }) + } + return out + }) }) const empty: Order = fromCompare(() => 0) diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index a83b700f2..063707118 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -21,13 +21,12 @@ export interface SemiApplicative extends SemiProduct, C export const liftSemigroup = (F: SemiApplicative) => (S: Semigroup): Semigroup> => ({ combine: (self, that) => pipe(self, F.product(that), F.map(([a1, a2]) => S.combine(a1, a2))), - combineMany: collection => - self => - pipe( - self, - F.productMany(collection), - F.map(([head, ...tail]) => pipe(head, S.combineMany(tail))) - ) + combineMany: (self, collection) => + pipe( + self, + F.productMany(collection), + F.map(([head, ...tail]) => S.combineMany(head, tail)) + ) }) /** diff --git a/src/typeclass/SemiCoproduct.ts b/src/typeclass/SemiCoproduct.ts index c1523d660..c659b3408 100644 --- a/src/typeclass/SemiCoproduct.ts +++ b/src/typeclass/SemiCoproduct.ts @@ -30,5 +30,5 @@ export const getSemigroup = (F: SemiCoproduct) => Kind > => ({ combine: (self, that) => pipe(self, F.coproduct(that)), - combineMany: F.coproductMany + combineMany: (self, collection) => pipe(self, F.coproductMany(collection)) }) diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 4d14c9f94..464ed4d4a 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -21,6 +21,7 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" +import { fromIterable } from "@fp-ts/core/internal/ReadonlyArray" import type * as invariant from "@fp-ts/core/typeclass/Invariant" import type { Order } from "@fp-ts/core/typeclass/Order" import type * as product from "@fp-ts/core/typeclass/Product" @@ -32,7 +33,7 @@ import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" */ export interface Semigroup { readonly combine: (self: A, that: A) => A - readonly combineMany: (collection: Iterable) => (self: A) => A + readonly combineMany: (self: A, collection: Iterable) => A } /** @@ -51,14 +52,13 @@ export interface SemigroupTypeLambda extends TypeLambda { */ export const fromCombine = (combine: Semigroup["combine"]): Semigroup => ({ combine, - combineMany: (collection) => - (self) => { - let out: A = self - for (const a of collection) { - out = combine(out, a) - } - return out + combineMany: (self, collection) => { + let out: A = self + for (const a of collection) { + out = combine(out, a) } + return out + } }) /** @@ -83,20 +83,19 @@ export const numberSum: Semigroup = fromCombine((self, that) => self + t */ export const numberMultiply: Semigroup = { combine: (self, that) => self * that, - combineMany: (collection) => - (self) => { - if (self === 0) { + combineMany: (self, collection) => { + if (self === 0) { + return 0 + } + let out = self + for (const n of collection) { + if (n === 0) { return 0 } - let out = self - for (const n of collection) { - if (n === 0) { - return 0 - } - out = out * n - } - return out + out = out * n } + return out + } } /** @@ -115,20 +114,19 @@ export const bigintSum: Semigroup = fromCombine((self, that) => self + t */ export const bigintMultiply: Semigroup = { combine: (self, that) => self * that, - combineMany: (collection) => - (self) => { - if (self === 0n) { + combineMany: (self, collection) => { + if (self === 0n) { + return 0n + } + let out = self + for (const n of collection) { + if (n === 0n) { return 0n } - let out = self - for (const n of collection) { - if (n === 0n) { - return 0n - } - out = out * n - } - return out + out = out * n } + return out + } } /** @@ -139,18 +137,17 @@ export const bigintMultiply: Semigroup = { */ export const booleanAll: Semigroup = { combine: (self, that) => self && that, - combineMany: (collection) => - (self) => { - if (self === false) { + combineMany: (self, collection) => { + if (self === false) { + return false + } + for (const b of collection) { + if (b === false) { return false } - for (const b of collection) { - if (b === false) { - return false - } - } - return true } + return true + } } /** @@ -161,18 +158,17 @@ export const booleanAll: Semigroup = { */ export const booleanAny: Semigroup = { combine: (self, that) => self || that, - combineMany: (collection) => - (self) => { - if (self === true) { + combineMany: (self, collection) => { + if (self === true) { + return true + } + for (const b of collection) { + if (b === true) { return true } - for (const b of collection) { - if (b === true) { - return true - } - } - return false } + return false + } } /** @@ -251,7 +247,7 @@ export const max = (O: Order): Semigroup => */ export const constant = (a: A): Semigroup => ({ combine: () => a, - combineMany: () => () => a + combineMany: () => a }) /** @@ -261,13 +257,12 @@ export const constant = (a: A): Semigroup => ({ */ export const reverse = (S: Semigroup): Semigroup => ({ combine: (self, that) => S.combine(that, self), - combineMany: (collection) => - (self) => { - const reversed = Array.from(collection).reverse() - return reversed.length > 0 ? - S.combine(S.combineMany(reversed.slice(1))(reversed[0]), self) : - self - } + combineMany: (self, collection) => { + const reversed = Array.from(collection).reverse() + return reversed.length > 0 ? + S.combine(S.combineMany(reversed[0], reversed.slice(1)), self) : + self + } }) /** @@ -275,7 +270,7 @@ export const reverse = (S: Semigroup): Semigroup => ({ */ export const intercalate = (separator: A) => (S: Semigroup): Semigroup => - fromCombine((self, that) => S.combineMany([separator, that])(self)) + fromCombine((self, that) => S.combineMany(self, [separator, that])) /** * Always return the first argument. @@ -285,7 +280,7 @@ export const intercalate = (separator: A) => */ export const first = (): Semigroup => ({ combine: (a) => a, - combineMany: () => a => a + combineMany: (a) => a }) /** @@ -296,13 +291,12 @@ export const first = (): Semigroup => ({ */ export const last = (): Semigroup => ({ combine: (_, second) => second, - combineMany: collection => - self => { - let a: A = self - // eslint-disable-next-line no-empty - for (a of collection) {} - return a - } + combineMany: (self, collection) => { + let a: A = self + // eslint-disable-next-line no-empty + for (a of collection) {} + return a + } }) /** @@ -314,13 +308,8 @@ export const imap = ( ) => (S: Semigroup): Semigroup => ({ combine: (self, that) => to(S.combine(from(self), from(that))), - combineMany: (collection) => - self => - to( - S.combineMany( - (Array.isArray(collection) ? collection : Array.from(collection)).map(from) - )(from(self)) - ) + combineMany: (self, collection) => + to(S.combineMany(from(self), (fromIterable(collection)).map(from))) }) /** diff --git a/test/Bigint.ts b/test/Bigint.ts index 02f76c44f..94468621e 100644 --- a/test/Bigint.ts +++ b/test/Bigint.ts @@ -1,5 +1,4 @@ import * as Bigint from "@fp-ts/core/Bigint" -import { pipe } from "@fp-ts/core/Function" import { deepStrictEqual } from "@fp-ts/core/test/util" describe.concurrent("Bigint", () => { @@ -51,8 +50,8 @@ describe.concurrent("Bigint", () => { it("SemigroupMultiply", () => { deepStrictEqual(Bigint.SemigroupMultiply.combine(2n, 3n), 6n) - deepStrictEqual(pipe(0n, Bigint.SemigroupMultiply.combineMany([1n, 2n, 3n])), 0n) - deepStrictEqual(pipe(2n, Bigint.SemigroupMultiply.combineMany([1n, 0n, 3n])), 0n) + deepStrictEqual(Bigint.SemigroupMultiply.combineMany(0n, [1n, 2n, 3n]), 0n) + deepStrictEqual(Bigint.SemigroupMultiply.combineMany(2n, [1n, 0n, 3n]), 0n) }) it("MonoidMultiply", () => { diff --git a/test/Boolean.ts b/test/Boolean.ts index 8367ba376..39bd328cb 100644 --- a/test/Boolean.ts +++ b/test/Boolean.ts @@ -38,9 +38,9 @@ describe.concurrent("Boolean", () => { describe.concurrent("MonoidAll", () => { it("baseline", () => { - deepStrictEqual(Boolean.MonoidAll.combineMany([true, true])(true), true) - deepStrictEqual(Boolean.MonoidAll.combineMany([true, false])(true), false) - deepStrictEqual(Boolean.MonoidAll.combineMany([true, false])(false), false) + deepStrictEqual(Boolean.MonoidAll.combineMany(true, [true, true]), true) + deepStrictEqual(Boolean.MonoidAll.combineMany(true, [true, false]), false) + deepStrictEqual(Boolean.MonoidAll.combineMany(false, [true, false]), false) deepStrictEqual(Boolean.MonoidAll.combineAll([true, true, true]), true) deepStrictEqual(Boolean.MonoidAll.combineAll([true, true, false]), false) }) @@ -54,9 +54,9 @@ describe.concurrent("Boolean", () => { describe.concurrent("MonoidAny", () => { it("baseline", () => { - deepStrictEqual(Boolean.MonoidAny.combineMany([true, true])(true), true) - deepStrictEqual(Boolean.MonoidAny.combineMany([true, false])(true), true) - deepStrictEqual(Boolean.MonoidAny.combineMany([false, false])(false), false) + deepStrictEqual(Boolean.MonoidAny.combineMany(true, [true, true]), true) + deepStrictEqual(Boolean.MonoidAny.combineMany(true, [true, false]), true) + deepStrictEqual(Boolean.MonoidAny.combineMany(false, [false, false]), false) deepStrictEqual(Boolean.MonoidAny.combineAll([true, true, true]), true) deepStrictEqual(Boolean.MonoidAny.combineAll([true, true, false]), true) deepStrictEqual(Boolean.MonoidAny.combineAll([false, false, false]), false) diff --git a/test/Function.ts b/test/Function.ts index 2bbcc7d75..969d19b27 100644 --- a/test/Function.ts +++ b/test/Function.ts @@ -15,7 +15,7 @@ describe.concurrent("Function", () => { deepStrictEqual(g(""), 1) deepStrictEqual(g("a"), 1) deepStrictEqual(g("b"), 2) - deepStrictEqual(S.combineMany([size, size])(size)("a"), 3) + deepStrictEqual(S.combineMany(size, [size, size])("a"), 3) }) it("getMonoid", () => { diff --git a/test/Number.ts b/test/Number.ts index 7f7cf69ea..a3f6c334e 100644 --- a/test/Number.ts +++ b/test/Number.ts @@ -1,4 +1,3 @@ -import { pipe } from "@fp-ts/core/Function" import * as Number from "@fp-ts/core/Number" import { deepStrictEqual } from "@fp-ts/core/test/util" @@ -55,8 +54,8 @@ describe.concurrent("Number", () => { it("SemigroupMultiply", () => { deepStrictEqual(Number.SemigroupMultiply.combine(2, 3), 6) - deepStrictEqual(pipe(0, Number.SemigroupMultiply.combineMany([1, 2, 3])), 0) - deepStrictEqual(pipe(2, Number.SemigroupMultiply.combineMany([1, 0, 3])), 0) + deepStrictEqual(Number.SemigroupMultiply.combineMany(0, [1, 2, 3]), 0) + deepStrictEqual(Number.SemigroupMultiply.combineMany(2, [1, 0, 3]), 0) }) it("MonoidMultiply", () => { diff --git a/test/Option.ts b/test/Option.ts index b897eeaf9..93f1da94b 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -415,9 +415,9 @@ describe.concurrent("Option", () => { deepStrictEqual(M.combine(_.some("b"), _.some("a")), _.some("ba")) deepStrictEqual(M.combine(_.some("a"), _.some("b")), _.some("ab")) - deepStrictEqual(pipe(_.some("a"), M.combineMany([_.some("b")])), _.some("ab")) - deepStrictEqual(pipe(_.none(), M.combineMany([_.some("b")])), _.some("b")) - deepStrictEqual(pipe(_.some("a"), M.combineMany([_.none()])), _.some("a")) + deepStrictEqual(M.combineMany(_.some("a"), [_.some("b")]), _.some("ab")) + deepStrictEqual(M.combineMany(_.none(), [_.some("b")]), _.some("b")) + deepStrictEqual(M.combineMany(_.some("a"), [_.none()]), _.some("a")) deepStrictEqual(pipe(M.combineAll([])), _.none()) deepStrictEqual(pipe(M.combineAll([_.some("a")])), _.some("a")) diff --git a/test/Ordering.ts b/test/Ordering.ts index 091af1c5a..5f2682083 100644 --- a/test/Ordering.ts +++ b/test/Ordering.ts @@ -1,4 +1,3 @@ -import { pipe } from "@fp-ts/core/Function" import * as _ from "@fp-ts/core/Ordering" import { deepStrictEqual } from "./util" @@ -26,13 +25,13 @@ describe("Ordering", () => { deepStrictEqual(_.Semigroup.combine(1, -1), 1) deepStrictEqual(_.Semigroup.combine(-1, 1), -1) - deepStrictEqual(pipe(0, _.Semigroup.combineMany([])), 0) - deepStrictEqual(pipe(1, _.Semigroup.combineMany([])), 1) - deepStrictEqual(pipe(-1, _.Semigroup.combineMany([])), -1) - deepStrictEqual(pipe(0, _.Semigroup.combineMany([0, 0, 0])), 0) - deepStrictEqual(pipe(0, _.Semigroup.combineMany([0, 0, 1])), 1) - deepStrictEqual(pipe(1, _.Semigroup.combineMany([0, 0, -1])), 1) - deepStrictEqual(pipe(-1, _.Semigroup.combineMany([0, 0, 1])), -1) + deepStrictEqual(_.Semigroup.combineMany(0, []), 0) + deepStrictEqual(_.Semigroup.combineMany(1, []), 1) + deepStrictEqual(_.Semigroup.combineMany(-1, []), -1) + deepStrictEqual(_.Semigroup.combineMany(0, [0, 0, 0]), 0) + deepStrictEqual(_.Semigroup.combineMany(0, [0, 0, 1]), 1) + deepStrictEqual(_.Semigroup.combineMany(1, [0, 0, -1]), 1) + deepStrictEqual(_.Semigroup.combineMany(-1, [0, 0, 1]), -1) }) it("Monoid", () => { diff --git a/test/String.ts b/test/String.ts index fba32f3c7..db96267c7 100644 --- a/test/String.ts +++ b/test/String.ts @@ -16,8 +16,8 @@ describe.concurrent("String", () => { it("Semigroup", () => { expect(String.Semigroup.combine("a", "b")).toEqual("ab") - expect(String.Semigroup.combineMany(["b", "c"])("a")).toEqual("abc") - expect(String.Semigroup.combineMany([])("a")).toEqual("a") + expect(String.Semigroup.combineMany("a", ["b", "c"])).toEqual("abc") + expect(String.Semigroup.combineMany("a", [])).toEqual("a") }) it("Monoid", () => { diff --git a/test/typeclass/Equivalence.ts b/test/typeclass/Equivalence.ts index 1df2bc0f2..2eaaf393d 100644 --- a/test/typeclass/Equivalence.ts +++ b/test/typeclass/Equivalence.ts @@ -108,7 +108,7 @@ describe("Equivalence", () => { expect(eqE0E1(["a", 1, true], ["b", 1, true])).toEqual(false) expect(eqE0E1(["a", 1, true], ["a", 2, false])).toEqual(false) const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) - const eqE0E1E2 = S.combineMany([E1, E2])(E0) + const eqE0E1E2 = S.combineMany(E0, [E1, E2]) expect(eqE0E1E2(["a", 1, true], ["a", 1, true])).toEqual(true) expect(eqE0E1E2(["a", 1, true], ["b", 1, true])).toEqual(false) expect(eqE0E1E2(["a", 1, true], ["a", 2, true])).toEqual(false) diff --git a/test/typeclass/Monoid.ts b/test/typeclass/Monoid.ts index d44612787..c8bec081a 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -1,4 +1,3 @@ -import { pipe } from "@fp-ts/core/Function" import * as N from "@fp-ts/core/Number" import * as String from "@fp-ts/core/String" import * as monoid from "@fp-ts/core/typeclass/Monoid" @@ -28,10 +27,10 @@ describe("Monoid", () => { U.deepStrictEqual(M.combine("a", "b"), "ba") U.deepStrictEqual(M.combine("a", M.empty), "a") U.deepStrictEqual(M.combine(M.empty, "a"), "a") - U.deepStrictEqual(pipe("a", M.combineMany([])), "a") - U.deepStrictEqual(pipe("a", M.combineMany(["b", "c", "d"])), "dcba") - U.deepStrictEqual(pipe("a", M.combineMany([M.empty])), "a") - U.deepStrictEqual(pipe(M.empty, M.combineMany(["a"])), "a") + U.deepStrictEqual(M.combineMany("a", []), "a") + U.deepStrictEqual(M.combineMany("a", ["b", "c", "d"]), "dcba") + U.deepStrictEqual(M.combineMany("a", [M.empty]), "a") + U.deepStrictEqual(M.combineMany(M.empty, ["a"]), "a") }) describe("struct", () => { diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 4e03f51ba..1f4c8edca 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -67,13 +67,13 @@ describe("Order", () => { [1, "c"], [2, "c"] ]) - U.deepStrictEqual(sort(pipe(sortBySnd, S.combineMany([])))(tuples), [ + U.deepStrictEqual(sort(S.combineMany(sortBySnd, []))(tuples), [ [2, "a"], [1, "b"], [2, "c"], [1, "c"] ]) - U.deepStrictEqual(sort(pipe(sortBySnd, S.combineMany([sortByFst])))(tuples), [ + U.deepStrictEqual(sort(S.combineMany(sortBySnd, [sortByFst]))(tuples), [ [2, "a"], [1, "b"], [1, "c"], @@ -98,13 +98,13 @@ describe("Order", () => { string.Order, _.contramap((x: T) => x[1]) ) - U.deepStrictEqual(sort(pipe(M.empty, M.combineMany([sortByFst, sortBySnd])))(tuples), [ + U.deepStrictEqual(sort(M.combineMany(M.empty, [sortByFst, sortBySnd]))(tuples), [ [1, "b"], [1, "c"], [2, "a"], [2, "c"] ]) - U.deepStrictEqual(sort(pipe(sortBySnd, M.combineMany([sortByFst, M.empty])))(tuples), [ + U.deepStrictEqual(sort(M.combineMany(sortBySnd, [sortByFst, M.empty]))(tuples), [ [2, "a"], [1, "b"], [1, "c"], diff --git a/test/typeclass/SemiApplicative.ts b/test/typeclass/SemiApplicative.ts index 2bec885af..a7ca047c9 100644 --- a/test/typeclass/SemiApplicative.ts +++ b/test/typeclass/SemiApplicative.ts @@ -38,7 +38,7 @@ describe("SemiApplicative", () => { U.deepStrictEqual(S.combine(O.some("a"), O.none()), O.none()) U.deepStrictEqual(S.combine(O.some("a"), O.some("b")), O.some("ab")) - U.deepStrictEqual(pipe(O.some("a"), S.combineMany([O.some("b"), O.some("c")])), O.some("abc")) + U.deepStrictEqual(S.combineMany(O.some("a"), [O.some("b"), O.some("c")]), O.some("abc")) }) it("lift2", () => { diff --git a/test/typeclass/SemiCoproduct.ts b/test/typeclass/SemiCoproduct.ts index 29ca03bf3..7ac9c273b 100644 --- a/test/typeclass/SemiCoproduct.ts +++ b/test/typeclass/SemiCoproduct.ts @@ -9,5 +9,6 @@ describe("SemiCoproduct", () => { U.deepStrictEqual(S.combine(E.left("a"), E.right(2)), E.right(2)) U.deepStrictEqual(S.combine(E.right(1), E.left("b")), E.right(1)) U.deepStrictEqual(S.combine(E.left("a"), E.left("b")), E.left("b")) + U.deepStrictEqual(S.combineMany(E.left("a"), [E.left("b")]), E.left("b")) }) }) diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index 127d9d94f..65b13bb47 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -11,79 +11,79 @@ describe("Semigroup", () => { }) it("reverse", () => { - const A = _.reverse(String.Semigroup) - U.deepStrictEqual(A.combine("a", "b"), "ba") - U.deepStrictEqual(pipe("a", A.combineMany([])), "a") - U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "ba") - U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "dcba") + const S = _.reverse(String.Semigroup) + U.deepStrictEqual(S.combine("a", "b"), "ba") + U.deepStrictEqual(S.combineMany("a", []), "a") + U.deepStrictEqual(S.combineMany("a", ["b"]), "ba") + U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "dcba") }) it("constant", () => { - const A = _.constant("-") - U.deepStrictEqual(A.combine("a", "b"), "-") - U.deepStrictEqual(pipe("a", A.combineMany([])), "-") - U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "-") + const S = _.constant("-") + U.deepStrictEqual(S.combine("a", "b"), "-") + U.deepStrictEqual(S.combineMany("a", []), "-") + U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "-") }) it("intercalate", () => { - const A = pipe(String.Semigroup, _.intercalate("|")) - U.deepStrictEqual(A.combine("a", "b"), "a|b") - U.deepStrictEqual(pipe("a", A.combineMany([])), "a") - U.deepStrictEqual(pipe("a", A.combineMany(["b"])), "a|b") - U.deepStrictEqual(pipe("a", A.combineMany(["b", "c", "d"])), "a|b|c|d") + const S = pipe(String.Semigroup, _.intercalate("|")) + U.deepStrictEqual(S.combine("a", "b"), "a|b") + U.deepStrictEqual(S.combineMany("a", []), "a") + U.deepStrictEqual(S.combineMany("a", ["b"]), "a|b") + U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "a|b|c|d") }) describe("min", () => { it("should return the minimum", () => { - const A = _.min(Number.Order) - U.deepStrictEqual(pipe(1, A.combineMany([])), 1) - U.deepStrictEqual(pipe(1, A.combineMany([3, 2])), 1) + const S = _.min(Number.Order) + U.deepStrictEqual(S.combineMany(1, []), 1) + U.deepStrictEqual(S.combineMany(1, [3, 2]), 1) }) it("should return the last minimum", () => { type Item = { a: number } const A = _.min(pipe(Number.Order, order.contramap((_: Item) => _.a))) const item: Item = { a: 1 } - U.strictEqual(pipe({ a: 2 }, A.combineMany([{ a: 1 }, item])), item) - U.strictEqual(pipe(item, A.combineMany([])), item) + U.strictEqual(A.combineMany({ a: 2 }, [{ a: 1 }, item]), item) + U.strictEqual(A.combineMany(item, []), item) }) }) describe("max", () => { it("should return the maximum", () => { - const A = _.max(Number.Order) - U.deepStrictEqual(pipe(1, A.combineMany([])), 1) - U.deepStrictEqual(pipe(1, A.combineMany([3, 2])), 3) + const S = _.max(Number.Order) + U.deepStrictEqual(S.combineMany(1, []), 1) + U.deepStrictEqual(S.combineMany(1, [3, 2]), 3) }) it("should return the last minimum", () => { type Item = { a: number } - const A = _.max(pipe(Number.Order, order.contramap((_: Item) => _.a))) + const S = _.max(pipe(Number.Order, order.contramap((_: Item) => _.a))) const item: Item = { a: 2 } - U.strictEqual(pipe({ a: 1 }, A.combineMany([{ a: 2 }, item])), item) - U.strictEqual(pipe(item, A.combineMany([])), item) + U.strictEqual(S.combineMany({ a: 1 }, [{ a: 2 }, item]), item) + U.strictEqual(S.combineMany(item, []), item) }) }) it("struct", () => { - const A = _.struct({ + const S = _.struct({ name: String.Semigroup, age: Number.SemigroupSum }) - U.deepStrictEqual(A.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { + U.deepStrictEqual(S.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { name: "ab", age: 30 }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combineMany([])), { + U.deepStrictEqual(S.combineMany({ name: "a", age: 10 }, []), { name: "a", age: 10 }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combineMany([{ name: "b", age: 20 }])), { + U.deepStrictEqual(S.combineMany({ name: "a", age: 10 }, [{ name: "b", age: 20 }]), { name: "ab", age: 30 }) U.deepStrictEqual( - pipe({ name: "a", age: 10 }, A.combineMany([{ name: "b", age: 20 }, { name: "c", age: 30 }])), + S.combineMany({ name: "a", age: 10 }, [{ name: "b", age: 20 }, { name: "c", age: 30 }]), { name: "abc", age: 60 @@ -92,53 +92,46 @@ describe("Semigroup", () => { }) it("tuple", () => { - const A = _.tuple( + const S = _.tuple( String.Semigroup, Number.SemigroupSum ) - U.deepStrictEqual(A.combine(["a", 10], ["b", 20]), ["ab", 30]) - U.deepStrictEqual(pipe(["a", 10], A.combineMany([])), ["a", 10]) - U.deepStrictEqual(pipe(["a", 10], A.combineMany([["b", 20]])), ["ab", 30]) - U.deepStrictEqual(pipe(["a", 10], A.combineMany([["b", 20], ["c", 30]])), ["abc", 60]) + U.deepStrictEqual(S.combine(["a", 10], ["b", 20]), ["ab", 30]) + U.deepStrictEqual(S.combineMany(["a", 10], []), ["a", 10]) + U.deepStrictEqual(S.combineMany(["a", 10], [["b", 20]]), ["ab", 30]) + U.deepStrictEqual(S.combineMany(["a", 10], [["b", 20], ["c", 30]]), ["abc", 60]) }) it("first", () => { - const A = _.first() - U.deepStrictEqual(A.combine(1, 2), 1) - U.deepStrictEqual(pipe(1, A.combineMany([])), 1) - U.deepStrictEqual(pipe(1, A.combineMany([2, 3, 4, 5, 6])), 1) + const S = _.first() + U.deepStrictEqual(S.combine(1, 2), 1) + U.deepStrictEqual(S.combineMany(1, []), 1) + U.deepStrictEqual(S.combineMany(1, [2, 3, 4, 5, 6]), 1) }) it("last", () => { - const A = _.last() - U.deepStrictEqual(A.combine(1, 2), 2) - U.deepStrictEqual(pipe(1, A.combineMany([])), 1) - U.deepStrictEqual(pipe(1, A.combineMany([2, 3, 4, 5, 6])), 6) + const S = _.last() + U.deepStrictEqual(S.combine(1, 2), 2) + U.deepStrictEqual(S.combineMany(1, []), 1) + U.deepStrictEqual(S.combineMany(1, [2, 3, 4, 5, 6]), 6) }) it("imap", () => { const imap = _.imap - const To = imap((s: string) => [s], ([s]) => s)(String.Semigroup) - U.deepStrictEqual(To.combine(["a"], ["b"]), ["ab"]) - U.deepStrictEqual(pipe(["a"], To.combineMany([])), ["a"]) - U.deepStrictEqual(pipe(["a"], To.combineMany([["b"]])), ["ab"]) - U.deepStrictEqual(pipe(["a"], To.combineMany([["b"], ["c"]])), ["abc"]) - - U.deepStrictEqual( - pipe( - ["a"], - _.Invariant.imap((s: string) => [s], ([s]) => s)(String.Semigroup).combineMany([["b"], [ - "c" - ]]) - ), - ["abc"] - ) + const S1 = imap((s: string) => [s], ([s]) => s)(String.Semigroup) + U.deepStrictEqual(S1.combine(["a"], ["b"]), ["ab"]) + U.deepStrictEqual(S1.combineMany(["a"], []), ["a"]) + U.deepStrictEqual(S1.combineMany(["a"], [["b"]]), ["ab"]) + U.deepStrictEqual(S1.combineMany(["a"], [["b"], ["c"]]), ["abc"]) // should handle an Iterable - U.deepStrictEqual(pipe(["a"], To.combineMany(new Set([["b"], ["c"]]))), ["abc"]) + U.deepStrictEqual(S1.combineMany(["a"], new Set([["b"], ["c"]])), ["abc"]) + + const S2 = pipe(String.Semigroup, _.Invariant.imap((s: string) => [s], ([s]) => s)) + U.deepStrictEqual(S2.combineMany(["a"], [["b"], ["c"]]), ["abc"]) }) it("product", () => { - const A = pipe( + const S = pipe( String.Semigroup, _.SemiProduct.product(Number.SemigroupSum), _.SemiProduct.product(Number.SemigroupMultiply), @@ -147,14 +140,14 @@ describe("Semigroup", () => { ([a, b, c]): [[string, number], number] => [[a, b], c] ) ) - U.deepStrictEqual(A.combine(["a", 2, 3], ["b", 3, 4]), ["ab", 5, 12]) + U.deepStrictEqual(S.combine(["a", 2, 3], ["b", 3, 4]), ["ab", 5, 12]) }) it("productMany", () => { - const A = pipe( + const S = pipe( String.Semigroup, _.SemiProduct.productMany([String.Semigroup, String.Semigroup]) ) - U.deepStrictEqual(A.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) + U.deepStrictEqual(S.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) }) }) From 64ff8de2699dd7fc7bb3d13a6d3913bc99e45725 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 15:52:08 +0100 Subject: [PATCH 62/80] SemiProduct: make product binary --- CHANGELOG.md | 3 +- src/Either.ts | 23 ++---------- src/Identity.ts | 19 ++-------- src/Option.ts | 22 ++--------- src/Predicate.ts | 22 ++--------- src/ReadonlyArray.ts | 43 +++++++++------------- src/These.ts | 57 ++++++++++++----------------- src/typeclass/Equivalence.ts | 2 +- src/typeclass/Order.ts | 2 +- src/typeclass/SemiApplicative.ts | 14 +++---- src/typeclass/SemiProduct.ts | 34 +++++++---------- src/typeclass/Semigroup.ts | 2 +- test/Either.ts | 35 ++++++++++-------- test/Identity.ts | 6 +-- test/Option.ts | 12 +++--- test/Predicate.ts | 12 +++--- test/ReadonlyArray.ts | 28 ++++++-------- test/These.ts | 63 +++++++++++++++++--------------- test/typeclass/Equivalence.ts | 4 +- test/typeclass/Order.ts | 4 +- test/typeclass/SemiProduct.ts | 38 ++++++++++--------- test/typeclass/Semigroup.ts | 7 ++-- 22 files changed, 186 insertions(+), 266 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28dde2d23..56617abaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ - `Semigroup` - make `combine` binary - make `combineMany` binary -- `Semiproduct` +- `SemiProduct` + - make `product` binary - rename `productFlatten` to `element` - `Order` - make `compare` binary diff --git a/src/Either.ts b/src/Either.ts index a11d9e99a..059954922 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -359,20 +359,7 @@ export const Monad: monad.Monad = { ...FlatMap } -/** - * @since 1.0.0 - */ -export const product = ( - that: Either -) => - (self: Either): Either => - isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self - -/** - * @category error handling - * @since 1.0.0 - */ -export const productMany = ( +const productMany = ( collection: Iterable> ) => (self: Either): Either]> => { @@ -395,7 +382,8 @@ export const productMany = ( */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, - product, + product: (self, that) => + isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self, productMany } @@ -425,10 +413,7 @@ export const element: ( ) => Either = semiProduct .element(SemiProduct) -/** - * @since 1.0.0 - */ -export const productAll = ( +const productAll = ( collection: Iterable> ): Either> => { const out: Array = [] diff --git a/src/Identity.ts b/src/Identity.ts index 3a87f7966..2e453e453 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -251,17 +251,7 @@ export const Monad: monad.Monad = { ...FlatMap } -/** - * @since 1.0.0 - */ -export const product = ( - that: Identity -) => (self: Identity): Identity<[A, B]> => [self, that] - -/** - * @since 1.0.0 - */ -export const productMany = (collection: Iterable>) => +const productMany = (collection: Iterable>) => (self: Identity): Identity<[A, ...Array]> => [self, ...collection] /** @@ -270,7 +260,7 @@ export const productMany = (collection: Iterable>) => */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, - product, + product: (self, that) => [self, that], productMany } @@ -298,10 +288,7 @@ export const element: ( ) => >(self: Identity) => Identity<[...A, B]> = semiProduct .element(SemiProduct) -/** - * @since 1.0.0 - */ -export const productAll = (collection: Iterable>): Identity> => +const productAll = (collection: Iterable>): Identity> => readonlyArray.fromIterable(collection) /** diff --git a/src/Option.ts b/src/Option.ts index 368dcad20..ed68076fd 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -406,19 +406,7 @@ export const Monad: monad.Monad = { ...FlatMap } -/** - * @since 1.0.0 - */ -export const product = ( - that: Option -) => - (self: Option): Option<[A, B]> => - isSome(self) && isSome(that) ? some([self.value, that.value]) : option.none - -/** - * @since 1.0.0 - */ -export const productMany = (collection: Iterable>) => +const productMany = (collection: Iterable>) => (self: Option): Option<[A, ...Array]> => { if (isNone(self)) { return option.none @@ -439,7 +427,8 @@ export const productMany = (collection: Iterable>) => */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, - product, + product: (self, that) => + isSome(self) && isSome(that) ? some([self.value, that.value]) : option.none, productMany } @@ -465,10 +454,7 @@ export const element: ( ) => >(self: Option) => Option<[...A, B]> = semiProduct .element(SemiProduct) -/** - * @since 1.0.0 - */ -export const productAll = (collection: Iterable>): Option> => { +const productAll = (collection: Iterable>): Option> => { const out: Array = [] for (const o of collection) { if (isNone(o)) { diff --git a/src/Predicate.ts b/src/Predicate.ts index 3bae8a9c8..4aac24a4b 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -151,16 +151,7 @@ export const Do: Predicate<{}> = of_.Do(Of) */ export const unit: Predicate = of_.unit(Of) -/** - * @since 1.0.0 - */ -export const product = (that: Predicate) => - (self: Predicate): Predicate => ([a, b]) => self(a) && that(b) - -/** - * @since 1.0.0 - */ -export const productMany = (collection: Iterable>) => +const productMany = (collection: Iterable>) => (self: Predicate): Predicate]> => { return ([head, ...tail]) => { if (self(head) === false) { @@ -182,14 +173,11 @@ export const productMany = (collection: Iterable>) => */ export const SemiProduct: semiProduct.SemiProduct = { imap, - product, + product: (self, that) => ([a, b]) => self(a) && that(b), productMany } -/** - * @since 1.0.0 - */ -export const productAll = ( +const productAll = ( collection: Iterable> ): Predicate> => (as) => { @@ -207,9 +195,7 @@ export const productAll = ( * @since 1.0.0 */ export const Product: product_.Product = { - imap, - product, - productMany, + ...SemiProduct, of, productAll } diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 2a944f518..9af39fe13 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1675,37 +1675,30 @@ export const sequenceNonEmpty = ( self: NonEmptyReadonlyArray> ) => Kind>) => traverseNonEmpty(F)(identity) -/** - * @since 1.0.0 - */ -export const product = ( +const product = ( + self: ReadonlyArray, that: ReadonlyArray -) => - (self: ReadonlyArray): Array<[A, B]> => { - if (isEmpty(self) || isEmpty(that)) { - return empty() - } - const out: Array<[A, B]> = [] - for (let i = 0; i < self.length; i++) { - for (let j = 0; j < that.length; j++) { - out.push([self[i], that[j]]) - } +): Array<[A, B]> => { + if (isEmpty(self) || isEmpty(that)) { + return empty() + } + const out: Array<[A, B]> = [] + for (let i = 0; i < self.length; i++) { + for (let j = 0; j < that.length; j++) { + out.push([self[i], that[j]]) } - return out } + return out +} -/** - * @since 1.0.0 - */ -export const productMany: ( +const productMany: ( collection: Iterable> -) => (self: ReadonlyArray) => Array<[A, ...Array]> = semiProduct - .productMany(Covariant, product) as any +) => (self: ReadonlyArray) => Array<[A, ...Array]> = semiProduct.productMany( + Covariant, + product +) as any -/** - * @since 1.0.0 - */ -export const productAll = ( +const productAll = ( collection: Iterable> ): Array> => { const arrays = Array.from(collection) diff --git a/src/These.ts b/src/These.ts index b4b076a36..01eb8ed30 100644 --- a/src/These.ts +++ b/src/These.ts @@ -952,44 +952,36 @@ export const filterMap = ( return option.isNone(ob) ? left(onNone()) : both(self.left, ob.value) } -/** - * @since 1.0.0 - */ -export const product = (that: Validated) => - ( - self: Validated - ): Validated => { - if (isLeft(self)) { - return self - } - if (isRight(self)) { - if (isLeft(that)) { - return that - } - if (isRight(that)) { - return right([self.right, that.right]) - } - return both(that.left, [self.right, that.right]) - } +const product = ( + self: Validated, + that: Validated +): Validated => { + if (isLeft(self)) { + return self + } + if (isRight(self)) { if (isLeft(that)) { - return left(RA.appendAllNonEmpty(that.left)(self.left)) + return that } if (isRight(that)) { - return both(self.left, [self.right, that.right]) + return right([self.right, that.right]) } - return both(RA.appendAllNonEmpty(that.left)(self.left), [self.right, that.right]) + return both(that.left, [self.right, that.right]) + } + if (isLeft(that)) { + return left(RA.appendAllNonEmpty(that.left)(self.left)) } + if (isRight(that)) { + return both(self.left, [self.right, that.right]) + } + return both(RA.appendAllNonEmpty(that.left)(self.left), [self.right, that.right]) +} -/** - * @since 1.0.0 - */ -export const productMany = ( +const productMany = ( collection: Iterable> ) => - ( - self: Validated - ): Validated]> => - pipe(self, product(productAll(collection)), map(([a, as]) => [a, ...as])) + (self: Validated): Validated]> => + pipe(product(self, productAll(collection)), map(([a, as]) => [a, ...as])) /** * @category instances @@ -1054,10 +1046,7 @@ export const getFirstLeftSemigroup: ( ) => Semigroup> = semiApplicative .liftSemigroup(SemiApplicative) -/** - * @since 1.0.0 - */ -export const productAll = ( +const productAll = ( collection: Iterable> ): Validated> => { const rights: Array = [] diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index 3929cb0e7..d13ba4380 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -195,7 +195,7 @@ export const Invariant: invariant.Invariant = { */ export const SemiProduct: semiProduct.SemiProduct = { imap: Contravariant.imap, - product: that => self => tuple(self, that), + product: tuple, productMany: collection => self => tuple(self, ...collection) } diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 3158a1821..a52386b74 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -185,7 +185,7 @@ export const Invariant: invariant.Invariant = { */ export const SemiProduct: semiProduct.SemiProduct = { imap: Contravariant.imap, - product: that => self => tuple(self, that), + product: tuple, productMany: collection => self => tuple(self, ...collection) } diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index 063707118..2a31677d7 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -20,7 +20,7 @@ export interface SemiApplicative extends SemiProduct, C */ export const liftSemigroup = (F: SemiApplicative) => (S: Semigroup): Semigroup> => ({ - combine: (self, that) => pipe(self, F.product(that), F.map(([a1, a2]) => S.combine(a1, a2))), + combine: (self, that) => pipe(F.product(self, that), F.map(([a1, a2]) => S.combine(a1, a2))), combineMany: (self, collection) => pipe( self, @@ -38,7 +38,7 @@ export const ap = (F: SemiApplicative) => ) => ( self: Kind B> - ): Kind => pipe(self, F.product(fa), F.map(([f, a]) => f(a))) + ): Kind => pipe(F.product(self, fa), F.map(([f, a]) => f(a))) /** * @since 1.0.0 @@ -49,7 +49,7 @@ export const andThenDiscard = (F: SemiApplicative) => ) => ( self: Kind - ): Kind => pipe(self, F.product(that), F.map(([a]) => a)) + ): Kind => pipe(F.product(self, that), F.map(([a]) => a)) /** * @since 1.0.0 @@ -60,7 +60,7 @@ export const andThen = (F: SemiApplicative) => ) => ( self: Kind - ): Kind => pipe(self, F.product(that), F.map(([_, a]) => a)) + ): Kind => pipe(F.product(self, that), F.map(([_, a]) => a)) /** * Lifts a binary function into `F`. @@ -72,7 +72,7 @@ export const lift2 = (F: SemiApplicative) => ( fa: Kind, fb: Kind - ): Kind => pipe(fa, F.product(fb), F.map(([a, b]) => f(a, b))) + ): Kind => pipe(F.product(fa, fb), F.map(([a, b]) => f(a, b))) /** * Lifts a ternary function into 'F'. @@ -87,8 +87,6 @@ export const lift3 = (F: SemiApplicative) => fc: Kind ): Kind => pipe( - fa, - F.product(fb), - F.product(fc), + F.product(F.product(fa, fb), fc), F.map(([[a, b], c]) => f(a, b, c)) ) diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 2bcf5ddac..6fec78bdb 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -12,10 +12,9 @@ import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" * @since 1.0.0 */ export interface SemiProduct extends Invariant { - readonly product: ( + readonly product: ( + self: Kind, that: Kind - ) => ( - self: Kind ) => Kind readonly productMany: ( @@ -32,18 +31,16 @@ export const productComposition = ( F: SemiApplicative, G: SemiProduct ) => - ( + ( + self: Kind>, that: Kind> - ) => - ( - self: Kind> - ): Kind< - F, - FR1 & FR2, - FO1 | FO2, - FE1 | FE2, - Kind - > => pipe(self, F.product(that), F.map(([ga, gb]) => pipe(ga, G.product(gb)))) + ): Kind< + F, + FR1 & FR2, + FO1 | FO2, + FE1 | FE2, + Kind + > => pipe(F.product(self, that), F.map(([ga, gb]) => G.product(ga, gb))) /** * Returns a default `productMany` composition. @@ -86,8 +83,7 @@ export const productMany = ( ) for (const fa of collection) { out = pipe( - out, - product(fa), + product(out, fa), Covariant.map(([[head, ...tail], a]): [A, ...Array] => [head, ...tail, a]) ) } @@ -112,8 +108,7 @@ export const andThenBind = (F: SemiProduct) => { [K in keyof A | N]: K extends keyof A ? A[K] : B } > => pipe( - self, - F.product(that), + F.product(self, that), F.imap( ([a, b]) => Object.assign({}, a, { [name]: b }) as any, ({ [name]: b, ...rest }) => [rest, b] as any @@ -133,8 +128,7 @@ export const element = (F: SemiProduct) => self: Kind ): Kind => pipe( - self, - F.product(that), + F.product(self, that), F.imap(([a, b]) => [...a, b], ab => [ab.slice(0, -1), ab[ab.length - 1]] as any) ) diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 464ed4d4a..a72572d5e 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -326,7 +326,7 @@ export const Invariant: invariant.Invariant = { */ export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, - product: that => self => tuple(self, that), + product: tuple, productMany: collection => self => tuple(self, ...collection) } diff --git a/test/Either.ts b/test/Either.ts index fe9844b2a..37b709857 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -44,10 +44,8 @@ describe.concurrent("Either", () => { expect(_.Monad).exist expect(_.SemiProduct).exist - expect(_.product).exist expect(_.Product).exist - expect(_.productAll).exist expect(_.tuple).exist expect(_.struct).exist @@ -381,36 +379,41 @@ describe.concurrent("Either", () => { }) it("product", () => { - deepStrictEqual(pipe(_.right(1), _.product(_.right("a"))), _.right([1, "a"])) - deepStrictEqual(pipe(_.right(1), _.product(_.left("e2"))), _.left("e2")) - deepStrictEqual(pipe(_.left("e1"), _.product(_.right("a"))), _.left("e1")) - deepStrictEqual(pipe(_.left("e1"), _.product(_.left("2"))), _.left("e1")) + const product = _.SemiProduct.product + deepStrictEqual(product(_.right(1), _.right("a")), _.right([1, "a"])) + deepStrictEqual(product(_.right(1), _.left("e2")), _.left("e2")) + deepStrictEqual(product(_.left("e1"), _.right("a")), _.left("e1")) + deepStrictEqual(product(_.left("e1"), _.left("2")), _.left("e1")) }) it("productMany", () => { - deepStrictEqual(pipe(_.right(1), _.productMany([])), _.right([1])) + const productMany: ( + collection: Iterable<_.Either> + ) => (self: _.Either) => _.Either]> = _.SemiProduct.productMany + + deepStrictEqual(pipe(_.right(1), productMany([])), _.right([1])) deepStrictEqual( - pipe(_.right(1), _.productMany([_.right(2), _.right(3)])), + pipe(_.right(1), productMany([_.right(2), _.right(3)])), _.right([1, 2, 3]) ) deepStrictEqual( - pipe(_.right(1), _.productMany([_.left("e"), _.right(3)])), - _.left("e") - ) - deepStrictEqual( - pipe(_.left("e"), _.productMany([_.right(2), _.right(3)])), + pipe(_.right(1), productMany([_.left("e"), _.right(3)])), _.left("e") ) + expect( + pipe(_.left("e"), productMany([_.right(2), _.right(3)])) + ).toEqual(_.left("e")) }) it("productAll", () => { - deepStrictEqual(_.productAll([]), _.right([])) + const productAll = _.Product.productAll + deepStrictEqual(productAll([]), _.right([])) deepStrictEqual( - _.productAll([_.right(1), _.right(2), _.right(3)]), + productAll([_.right(1), _.right(2), _.right(3)]), _.right([1, 2, 3]) ) deepStrictEqual( - _.productAll([_.left("e"), _.right(2), _.right(3)]), + productAll([_.left("e"), _.right(2), _.right(3)]), _.left("e") ) }) diff --git a/test/Identity.ts b/test/Identity.ts index fbc74fe38..8f130aa9b 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -38,13 +38,10 @@ describe.concurrent("Identity", () => { expect(_.Monad).exist expect(_.SemiProduct).exist - expect(_.product).exist - expect(_.productMany).exist expect(_.andThenBind).exist expect(_.element).exist expect(_.Product).exist - expect(_.productAll).exist expect(_.tuple).exist expect(_.struct).exist @@ -100,7 +97,8 @@ describe.concurrent("Identity", () => { }) it("product", () => { - U.deepStrictEqual(pipe("a", _.product("b")), ["a", "b"]) + const product = _.SemiProduct.product + U.deepStrictEqual(product("a", "b"), ["a", "b"]) }) it("getSemiCoproduct", () => { diff --git a/test/Option.ts b/test/Option.ts index 93f1da94b..cd3e30016 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -42,11 +42,8 @@ describe.concurrent("Option", () => { expect(_.Monad).exist expect(_.SemiProduct).exist - expect(_.product).exist - expect(_.productMany).exist expect(_.Product).exist - expect(_.productAll).exist expect(_.tuple).exist expect(_.struct).exist @@ -211,11 +208,12 @@ describe.concurrent("Option", () => { }) it("product", () => { - deepStrictEqual(pipe(_.none(), _.product(_.none())), _.none()) - deepStrictEqual(pipe(_.some(1), _.product(_.none())), _.none()) - deepStrictEqual(pipe(_.none(), _.product(_.some("a"))), _.none()) + const product = _.SemiProduct.product + deepStrictEqual(product(_.none(), _.none()), _.none()) + deepStrictEqual(product(_.some(1), _.none()), _.none()) + deepStrictEqual(product(_.none(), _.some("a")), _.none()) deepStrictEqual( - pipe(_.some(1), _.product(_.some("a"))), + product(_.some(1), _.some("a")), _.some([1, "a"]) ) }) diff --git a/test/Predicate.ts b/test/Predicate.ts index 269e68f27..9298ca40c 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -32,13 +32,10 @@ describe.concurrent("Predicate", () => { expect(_.Do).exist expect(_.SemiProduct).exist - expect(_.product).exist - expect(_.productMany).exist expect(_.andThenBind).exist expect(_.element).exist expect(_.Product).exist - expect(_.productAll).exist expect(_.tuple).exist expect(_.struct).exist }) @@ -69,7 +66,8 @@ describe.concurrent("Predicate", () => { }) it("product", () => { - const p = pipe(isPositive, _.product(isNegative)) + const product = _.SemiProduct.product + const p = product(isPositive, isNegative) deepStrictEqual(p([1, -1]), true) deepStrictEqual(p([1, 1]), false) deepStrictEqual(p([-1, -1]), false) @@ -77,7 +75,8 @@ describe.concurrent("Predicate", () => { }) it("productMany", () => { - const p = pipe(isPositive, _.productMany([isNegative])) + const productMany = _.SemiProduct.productMany + const p = pipe(isPositive, productMany([isNegative])) deepStrictEqual(p([1, -1]), true) deepStrictEqual(p([1, 1]), false) deepStrictEqual(p([-1, -1]), false) @@ -85,7 +84,8 @@ describe.concurrent("Predicate", () => { }) it("productAll", () => { - const p = _.productAll([isPositive, isNegative]) + const productAll = _.Product.productAll + const p = productAll([isPositive, isNegative]) deepStrictEqual(p([1, -1]), true) deepStrictEqual(p([1, 1]), false) deepStrictEqual(p([-1, -1]), false) diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 89b7281e9..0784ea741 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -7,7 +7,6 @@ import * as RA from "@fp-ts/core/ReadonlyArray" import * as String from "@fp-ts/core/String" import { deepStrictEqual, double, strictEqual } from "@fp-ts/core/test/util" import * as Order from "@fp-ts/core/typeclass/Order" -import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as assert from "assert" import * as fc from "fast-check" @@ -41,15 +40,10 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.Monad).exist expect(RA.SemiProduct).exist - expect(RA.product).exist - expect(RA.productMany).exist expect(RA.andThenBind).exist expect(RA.element).exist expect(RA.Product).exist - expect(RA.productAll).exist - // expect(ReadonlyArray.tuple).exist - // expect(ReadonlyArray.struct).exist expect(RA.SemiApplicative).exist expect(RA.liftSemigroup).exist @@ -1608,9 +1602,10 @@ describe.concurrent("ReadonlyArray", () => { }) test("product", () => { - expect(pipe([], RA.product(["a", "b"]))).toEqual([]) - expect(pipe([1, 2], RA.product([]))).toEqual([]) - expect(pipe([1, 2], RA.product(["a", "b"]))).toEqual([ + const product = RA.SemiProduct.product + expect(product([], ["a", "b"])).toEqual([]) + expect(product([1, 2], [])).toEqual([]) + expect(product([1, 2], ["a", "b"])).toEqual([ [1, "a"], [1, "b"], [2, "a"], @@ -1619,10 +1614,10 @@ describe.concurrent("ReadonlyArray", () => { }) test("productMany", () => { - const productMany = semiProduct.productMany(RA.Covariant, RA.product) + const productMany = RA.SemiProduct.productMany expect(pipe( [], - RA.productMany([ + productMany([ [2], [4, 5], [8, 9, 10] @@ -1637,14 +1632,14 @@ describe.concurrent("ReadonlyArray", () => { )) expect(pipe( [1, 2, 3], - RA.productMany([]) + productMany([]) )).toEqual(pipe( [1, 2, 3], productMany([]) )) expect(pipe( [1, 2, 3], - RA.productMany([ + productMany([ [2], [4, 5], [8, 9, 10] @@ -1660,9 +1655,10 @@ describe.concurrent("ReadonlyArray", () => { }) test("productAll", () => { - expect(RA.productAll([])).toEqual([]) - expect(RA.productAll([[1, 2, 3]])).toEqual([[1], [2], [3]]) - expect(RA.productAll([[1, 2, 3], [4, 5]])).toEqual([[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [ + const productAll = RA.Product.productAll + expect(productAll([])).toEqual([]) + expect(productAll([[1, 2, 3]])).toEqual([[1], [2], [3]]) + expect(productAll([[1, 2, 3], [4, 5]])).toEqual([[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [ 3, 5 ]]) diff --git a/test/These.ts b/test/These.ts index 680bc8efd..2b9792baa 100644 --- a/test/These.ts +++ b/test/These.ts @@ -44,13 +44,10 @@ describe("These", () => { expect(_.Monad).exist expect(_.SemiProduct).exist - expect(_.product).exist - expect(_.productMany).exist expect(_.andThenBind).exist expect(_.element).exist expect(_.Product).exist - expect(_.productAll).exist expect(_.tuple).exist expect(_.struct).exist @@ -274,17 +271,18 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - expect(pipe(_.right(1), _.product(_.right(2)))).toEqual(_.right([1, 2])) - U.deepStrictEqual(pipe(_.right(1), _.product(_.left(b))), _.left(b)) - expect(pipe(_.right(1), _.product(_.both(b, 2)))).toEqual(_.both(b, [1, 2])) + const product = _.SemiProduct.product + expect(product(_.right(1), _.right(2))).toEqual(_.right([1, 2])) + U.deepStrictEqual(product(_.right(1), _.left(b)), _.left(b)) + expect(product(_.right(1), _.both(b, 2))).toEqual(_.both(b, [1, 2])) - U.deepStrictEqual(pipe(_.left(a), _.product(_.right(2))), _.left(a)) - U.deepStrictEqual(pipe(_.left(a), _.product(_.left(b))), _.left(a)) - U.deepStrictEqual(pipe(_.left(a), _.product(_.both(b, 2))), _.left(a)) + U.deepStrictEqual(product(_.left(a), _.right(2)), _.left(a)) + U.deepStrictEqual(product(_.left(a), _.left(b)), _.left(a)) + U.deepStrictEqual(product(_.left(a), _.both(b, 2)), _.left(a)) - expect(pipe(_.both(a, 1), _.product(_.right(2)))).toEqual(_.both(a, [1, 2])) - expect(pipe(_.both(a, 1), _.product(_.left(b)))).toEqual(_.left(ab)) - expect(pipe(_.both(a, 1), _.product(_.both(b, 2)))).toEqual(_.both(ab, [1, 2])) + expect(product(_.both(a, 1), _.right(2))).toEqual(_.both(a, [1, 2])) + expect(product(_.both(a, 1), _.left(b))).toEqual(_.left(ab)) + expect(product(_.both(a, 1), _.both(b, 2))).toEqual(_.both(ab, [1, 2])) }) it("productMany", () => { @@ -292,27 +290,31 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - expect(pipe(_.right(1), _.productMany([_.right(2)]))).toEqual(_.right([1, 2])) + const productMany: ( + collection: Iterable<_.Validated> + ) => (self: _.Validated) => _.Validated]> = _.SemiProduct.productMany + + expect(pipe(_.right(1), productMany([_.right(2)]))).toEqual(_.right([1, 2])) U.deepStrictEqual( - pipe(_.right(1), _.productMany([_.left(b)])), + pipe(_.right(1), productMany([_.left(b)])), _.left(b) ) expect( - pipe(_.right(1), _.productMany([_.both(b, 2)])) + pipe(_.right(1), productMany([_.both(b, 2)])) ).toEqual( _.both(b, [1, 2]) ) - U.deepStrictEqual(pipe(_.left(a), _.productMany([_.right(2)])), _.left(a)) - U.deepStrictEqual(pipe(_.left(a), _.productMany([_.left(b)])), _.left(a)) + U.deepStrictEqual(pipe(_.left(a), productMany([_.right(2)])), _.left(a)) + U.deepStrictEqual(pipe(_.left(a), productMany([_.left(b)])), _.left(a)) U.deepStrictEqual( - pipe(_.left(a), _.productMany([_.both(b, 2)])), + pipe(_.left(a), productMany([_.both(b, 2)])), _.left(a) ) - expect(pipe(_.both(a, 1), _.productMany([_.right(2)]))).toEqual(_.both(a, [1, 2])) - expect(pipe(_.both(a, 1), _.productMany([_.left(b)]))).toEqual(_.left(ab)) - expect(pipe(_.both(a, 1), _.productMany([_.both(b, 2)]))).toEqual( + expect(pipe(_.both(a, 1), productMany([_.right(2)]))).toEqual(_.both(a, [1, 2])) + expect(pipe(_.both(a, 1), productMany([_.left(b)]))).toEqual(_.left(ab)) + expect(pipe(_.both(a, 1), productMany([_.both(b, 2)]))).toEqual( _.both(ab, [1, 2]) ) }) @@ -322,17 +324,18 @@ describe("These", () => { const b = ["b"] as const const ab = ["a", "b"] as const - U.deepStrictEqual(_.productAll([_.right(1), _.right(2)]), _.right([1, 2])) - U.deepStrictEqual(_.productAll([_.right(1), _.left(b)]), _.left(b)) - U.deepStrictEqual(_.productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2])) + const productAll = _.Product.productAll + U.deepStrictEqual(productAll([_.right(1), _.right(2)]), _.right([1, 2])) + U.deepStrictEqual(productAll([_.right(1), _.left(b)]), _.left(b)) + U.deepStrictEqual(productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2])) - U.deepStrictEqual(_.productAll([_.left(a), _.right(2)]), _.left(a)) - U.deepStrictEqual(_.productAll([_.left(a), _.left(b)]), _.left(a)) - U.deepStrictEqual(_.productAll([_.left(a), _.both(b, 2)]), _.left(a)) + U.deepStrictEqual(productAll([_.left(a), _.right(2)]), _.left(a)) + U.deepStrictEqual(productAll([_.left(a), _.left(b)]), _.left(a)) + U.deepStrictEqual(productAll([_.left(a), _.both(b, 2)]), _.left(a)) - U.deepStrictEqual(_.productAll([_.both(a, 1), _.right(2)]), _.both(a, [1, 2])) - expect(_.productAll([_.both(a, 1), _.left(b)])).toEqual(_.left(ab)) - expect(_.productAll([_.both(a, 1), _.both(b, 2)])).toEqual(_.both(ab, [1, 2])) + U.deepStrictEqual(productAll([_.both(a, 1), _.right(2)]), _.both(a, [1, 2])) + expect(productAll([_.both(a, 1), _.left(b)])).toEqual(_.left(ab)) + expect(productAll([_.both(a, 1), _.both(b, 2)])).toEqual(_.both(ab, [1, 2])) }) it("flatMap", () => { diff --git a/test/typeclass/Equivalence.ts b/test/typeclass/Equivalence.ts index 2eaaf393d..2668fd960 100644 --- a/test/typeclass/Equivalence.ts +++ b/test/typeclass/Equivalence.ts @@ -137,9 +137,9 @@ describe("Equivalence", () => { }) it("SemiProduct/product", () => { - const eq = pipe( + const eq = _.SemiProduct.product( _.string, - _.SemiProduct.product(_.string) + _.string ) expect(eq(["a", "b"], ["a", "b"])).toEqual(true) expect(eq(["a", "b"], ["a", "c"])).toEqual(false) diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 1f4c8edca..bf41c8e08 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -197,9 +197,9 @@ describe("Order", () => { describe("SemiProduct", () => { it("product", () => { - const O = pipe( + const O = _.SemiProduct.product( string.Order, - _.SemiProduct.product(number.Order) + number.Order ) U.deepStrictEqual(O.compare(["a", 1], ["a", 2]), -1) U.deepStrictEqual(O.compare(["a", 1], ["a", 1]), 0) diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 29c875655..3595e6223 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -51,16 +51,18 @@ describe("SemiProduct", () => { U.deepStrictEqual(actual, expected) } - const product = (that: ReadonlyArray) => - (self: ReadonlyArray): ReadonlyArray<[A, B]> => { - const out: Array<[A, B]> = [] - for (const a of self) { - for (const b of that) { - out.push([a, b]) - } + const product = ( + self: ReadonlyArray, + that: ReadonlyArray + ): ReadonlyArray<[A, B]> => { + const out: Array<[A, B]> = [] + for (const a of self) { + for (const b of that) { + out.push([a, b]) } - return out } + return out + } const productMany = _.productMany( RA.Covariant, @@ -82,21 +84,21 @@ describe("SemiProduct", () => { describe("productComposition", () => { it("ReadonlyArray", () => { const product = _.productComposition(RA.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe([], product([O.none()])), []) - U.deepStrictEqual(pipe([O.none()], product([])), []) - U.deepStrictEqual(pipe([O.none()], product([O.none()])), [O.none()]) - expect(pipe([O.some(1)], product([O.some(2)]))).toEqual([O.some([1, 2])]) + U.deepStrictEqual(product([], [O.none()]), []) + U.deepStrictEqual(product([O.none()], []), []) + U.deepStrictEqual(product([O.none()], [O.none()]), [O.none()]) + expect(product([O.some(1)], [O.some(2)])).toEqual([O.some([1, 2])]) }) it("Option", () => { const product = _.productComposition(O.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe(O.none(), product(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some(O.none()), product(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.none())), O.none()) - U.deepStrictEqual(pipe(O.some(O.some(1)), product(O.some(O.none()))), O.some(O.none())) - U.deepStrictEqual(pipe(O.some(O.none()), product(O.some(O.some(2)))), O.some(O.none())) + U.deepStrictEqual(product(O.none(), O.none()), O.none()) + U.deepStrictEqual(product(O.some(O.none()), O.none()), O.none()) + U.deepStrictEqual(product(O.some(O.some(1)), O.none()), O.none()) + U.deepStrictEqual(product(O.some(O.some(1)), O.some(O.none())), O.some(O.none())) + U.deepStrictEqual(product(O.some(O.none()), O.some(O.some(2))), O.some(O.none())) U.deepStrictEqual( - pipe(O.some(O.some(1)), product(O.some(O.some(2)))), + product(O.some(O.some(1)), O.some(O.some(2))), O.some(O.some([1, 2])) ) }) diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index 65b13bb47..46ebaa0ee 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -132,9 +132,10 @@ describe("Semigroup", () => { it("product", () => { const S = pipe( - String.Semigroup, - _.SemiProduct.product(Number.SemigroupSum), - _.SemiProduct.product(Number.SemigroupMultiply), + _.SemiProduct.product( + _.SemiProduct.product(String.Semigroup, Number.SemigroupSum), + Number.SemigroupMultiply + ), _.imap( ([[a, b], c]): [string, number, number] => [a, b, c], ([a, b, c]): [[string, number], number] => [[a, b], c] From 75ee446536e5ee18b809ef7151a7b1bdcbd9d0f5 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 16:12:31 +0100 Subject: [PATCH 63/80] SemiProduct: make productMany binary --- CHANGELOG.md | 1 + src/Either.ts | 24 ++++++++--------- src/Identity.ts | 6 +++-- src/Option.ts | 24 +++++++++-------- src/Predicate.ts | 24 +++++++++-------- src/ReadonlyArray.ts | 14 +++++----- src/These.ts | 6 ++--- src/typeclass/Equivalence.ts | 2 +- src/typeclass/Order.ts | 2 +- src/typeclass/SemiApplicative.ts | 3 +-- src/typeclass/SemiProduct.ts | 46 +++++++++++++++----------------- src/typeclass/Semigroup.ts | 2 +- test/Either.ts | 11 ++++---- test/Identity.ts | 2 +- test/Option.ts | 15 ++++------- test/Predicate.ts | 2 +- test/ReadonlyArray.ts | 41 ---------------------------- test/These.ts | 21 ++++++++------- test/typeclass/Equivalence.ts | 5 +--- test/typeclass/Order.ts | 5 +--- test/typeclass/SemiProduct.ts | 24 ++++++++--------- test/typeclass/Semigroup.ts | 5 +--- 22 files changed, 117 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56617abaf..0ff608fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - make `combineMany` binary - `SemiProduct` - make `product` binary + - make `productMany` binary - rename `productFlatten` to `element` - `Order` - make `compare` binary diff --git a/src/Either.ts b/src/Either.ts index 059954922..c9b7cf5db 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -360,21 +360,21 @@ export const Monad: monad.Monad = { } const productMany = ( + self: Either, collection: Iterable> -) => - (self: Either): Either]> => { - if (isLeft(self)) { - return self - } - const out: [A, ...Array] = [self.right] - for (const e of collection) { - if (isLeft(e)) { - return e - } - out.push(e.right) +): Either]> => { + if (isLeft(self)) { + return self + } + const out: [A, ...Array] = [self.right] + for (const e of collection) { + if (isLeft(e)) { + return e } - return right(out) + out.push(e.right) } + return right(out) +} /** * @category instances diff --git a/src/Identity.ts b/src/Identity.ts index 2e453e453..f65883432 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -251,8 +251,10 @@ export const Monad: monad.Monad = { ...FlatMap } -const productMany = (collection: Iterable>) => - (self: Identity): Identity<[A, ...Array]> => [self, ...collection] +const productMany = ( + self: Identity, + collection: Iterable> +): Identity<[A, ...Array]> => [self, ...collection] /** * @category instances diff --git a/src/Option.ts b/src/Option.ts index ed68076fd..a2d2e70a9 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -406,20 +406,22 @@ export const Monad: monad.Monad = { ...FlatMap } -const productMany = (collection: Iterable>) => - (self: Option): Option<[A, ...Array]> => { - if (isNone(self)) { +const productMany = ( + self: Option, + collection: Iterable> +): Option<[A, ...Array]> => { + if (isNone(self)) { + return option.none + } + const out: [A, ...Array] = [self.value] + for (const o of collection) { + if (isNone(o)) { return option.none } - const out: [A, ...Array] = [self.value] - for (const o of collection) { - if (isNone(o)) { - return option.none - } - out.push(o.value) - } - return some(out) + out.push(o.value) } + return some(out) +} /** * @category instances diff --git a/src/Predicate.ts b/src/Predicate.ts index 4aac24a4b..55c0042e4 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -151,21 +151,23 @@ export const Do: Predicate<{}> = of_.Do(Of) */ export const unit: Predicate = of_.unit(Of) -const productMany = (collection: Iterable>) => - (self: Predicate): Predicate]> => { - return ([head, ...tail]) => { - if (self(head) === false) { +const productMany = ( + self: Predicate, + collection: Iterable> +): Predicate]> => { + return ([head, ...tail]) => { + if (self(head) === false) { + return false + } + const predicates = readonlyArray.fromIterable(collection) + for (let i = 0; i < predicates.length; i++) { + if (predicates[i](tail[i]) === false) { return false } - const predicates = readonlyArray.fromIterable(collection) - for (let i = 0; i < predicates.length; i++) { - if (predicates[i](tail[i]) === false) { - return false - } - } - return true } + return true } +} /** * @category instances diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 9af39fe13..fbec99624 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1627,8 +1627,8 @@ export const traverseNonEmptyWithIndex = ( ) => (f: (a: A, i: number) => Kind) => (self: NonEmptyReadonlyArray): Kind> => { - const fbs = pipe(self, mapNonEmptyWithIndex(f)) - return pipe(headNonEmpty(fbs), F.productMany(tailNonEmpty(fbs))) + const [head, ...tail] = pipe(self, mapNonEmptyWithIndex(f)) + return F.productMany(head, tail) } /** @@ -1692,8 +1692,9 @@ const product = ( } const productMany: ( + self: ReadonlyArray, collection: Iterable> -) => (self: ReadonlyArray) => Array<[A, ...Array]> = semiProduct.productMany( +) => Array<[A, ...Array]> = semiProduct.productMany( Covariant, product ) as any @@ -1701,11 +1702,8 @@ const productMany: ( const productAll = ( collection: Iterable> ): Array> => { - const arrays = Array.from(collection) - if (isEmpty(arrays)) { - return empty() - } - return productMany(arrays.slice(1))(arrays[0]) + const arrays = fromIterable(collection) + return isEmpty(arrays) ? empty() : productMany(arrays[0], arrays.slice(1)) } /** diff --git a/src/These.ts b/src/These.ts index 01eb8ed30..25e1ea22d 100644 --- a/src/These.ts +++ b/src/These.ts @@ -978,10 +978,10 @@ const product = ( } const productMany = ( + self: Validated, collection: Iterable> -) => - (self: Validated): Validated]> => - pipe(product(self, productAll(collection)), map(([a, as]) => [a, ...as])) +): Validated]> => + pipe(product(self, productAll(collection)), map(([a, as]) => [a, ...as])) /** * @category instances diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts index d13ba4380..78146c738 100644 --- a/src/typeclass/Equivalence.ts +++ b/src/typeclass/Equivalence.ts @@ -196,7 +196,7 @@ export const Invariant: invariant.Invariant = { export const SemiProduct: semiProduct.SemiProduct = { imap: Contravariant.imap, product: tuple, - productMany: collection => self => tuple(self, ...collection) + productMany: (self, collection) => tuple(self, ...collection) } /** diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index a52386b74..85b762d09 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -186,7 +186,7 @@ export const Invariant: invariant.Invariant = { export const SemiProduct: semiProduct.SemiProduct = { imap: Contravariant.imap, product: tuple, - productMany: collection => self => tuple(self, ...collection) + productMany: (self, collection) => tuple(self, ...collection) } /** diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index 2a31677d7..48098ba35 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -23,8 +23,7 @@ export const liftSemigroup = (F: SemiApplicative) => combine: (self, that) => pipe(F.product(self, that), F.map(([a1, a2]) => S.combine(a1, a2))), combineMany: (self, collection) => pipe( - self, - F.productMany(collection), + F.productMany(self, collection), F.map(([head, ...tail]) => S.combineMany(head, tail)) ) }) diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index 6fec78bdb..f16678bbd 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -18,8 +18,9 @@ export interface SemiProduct extends Invariant { ) => Kind readonly productMany: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind]> + ) => Kind]> } /** @@ -52,16 +53,13 @@ export const productManyComposition = ) => ( + self: Kind>, collection: Iterable>> - ) => - ( - self: Kind> - ): Kind]>> => - pipe( - self, - F.productMany(collection), - F.map(([ga, ...gas]) => pipe(ga, G.productMany(gas))) - ) + ): Kind]>> => + pipe( + F.productMany(self, collection), + F.map(([ga, ...gas]) => G.productMany(ga, gas)) + ) /** * Returns a default `productMany` implementation (useful for tests). @@ -74,21 +72,21 @@ export const productMany = ( product: SemiProduct["product"] ): SemiProduct["productMany"] => ( + self: Kind, collection: Iterable> - ) => - (self: Kind) => { - let out = pipe( - self, - Covariant.map((a): [A, ...Array] => [a]) + ) => { + let out = pipe( + self, + Covariant.map((a): [A, ...Array] => [a]) + ) + for (const fa of collection) { + out = pipe( + product(out, fa), + Covariant.map(([[head, ...tail], a]): [A, ...Array] => [head, ...tail, a]) ) - for (const fa of collection) { - out = pipe( - product(out, fa), - Covariant.map(([[head, ...tail], a]): [A, ...Array] => [head, ...tail, a]) - ) - } - return out } + return out + } /** * @since 1.0.0 @@ -144,7 +142,7 @@ export const nonEmptyTuple = (F: SemiProduct) => ([T[number]] extends [Kind] ? O : never), ([T[number]] extends [Kind] ? E : never), { [I in keyof T]: [T[I]] extends [Kind] ? A : never } - > => F.productMany(components.slice(1))(components[0]) as any + > => F.productMany(components[0], components.slice(1)) as any type EnforceNonEmptyRecord = keyof R extends never ? never : R @@ -163,7 +161,7 @@ export const nonEmptyStruct = (F: SemiProduct) => > => { const keys = Object.keys(fields) return pipe( - F.productMany(keys.slice(1).map(k => fields[k]))(fields[keys[0]]), + F.productMany(fields[keys[0]], keys.slice(1).map(k => fields[k])), F.imap(([value, ...values]) => { const out: any = { [keys[0]]: value } for (let i = 0; i < values.length; i++) { diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index a72572d5e..abb2b3cea 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -327,7 +327,7 @@ export const Invariant: invariant.Invariant = { export const SemiProduct: semiProduct.SemiProduct = { ...Invariant, product: tuple, - productMany: collection => self => tuple(self, ...collection) + productMany: (self, collection) => tuple(self, ...collection) } /** diff --git a/test/Either.ts b/test/Either.ts index 37b709857..88f135658 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -388,20 +388,21 @@ describe.concurrent("Either", () => { it("productMany", () => { const productMany: ( + self: _.Either, collection: Iterable<_.Either> - ) => (self: _.Either) => _.Either]> = _.SemiProduct.productMany + ) => _.Either]> = _.SemiProduct.productMany - deepStrictEqual(pipe(_.right(1), productMany([])), _.right([1])) + deepStrictEqual(productMany(_.right(1), []), _.right([1])) deepStrictEqual( - pipe(_.right(1), productMany([_.right(2), _.right(3)])), + productMany(_.right(1), [_.right(2), _.right(3)]), _.right([1, 2, 3]) ) deepStrictEqual( - pipe(_.right(1), productMany([_.left("e"), _.right(3)])), + productMany(_.right(1), [_.left("e"), _.right(3)]), _.left("e") ) expect( - pipe(_.left("e"), productMany([_.right(2), _.right(3)])) + productMany(_.left("e"), [_.right(2), _.right(3)]) ).toEqual(_.left("e")) }) diff --git a/test/Identity.ts b/test/Identity.ts index 8f130aa9b..77006ee89 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -81,7 +81,7 @@ describe.concurrent("Identity", () => { }) it("SemiProduct", () => { - U.deepStrictEqual(pipe("a", _.SemiProduct.productMany(["b", "c"])), ["a", "b", "c"]) + U.deepStrictEqual(_.SemiProduct.productMany("a", ["b", "c"]), ["a", "b", "c"]) }) it("Product", () => { diff --git a/test/Option.ts b/test/Option.ts index cd3e30016..ef147d596 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -219,16 +219,11 @@ describe.concurrent("Option", () => { }) it("productMany", () => { - deepStrictEqual(pipe(_.none(), _.SemiProduct.productMany([])), _.none()) - deepStrictEqual(pipe(_.some(1), _.SemiProduct.productMany([])), _.some([1])) - deepStrictEqual( - pipe(_.some(1), _.SemiProduct.productMany([_.none() as _.Option])), - _.none() - ) - deepStrictEqual( - pipe(_.some(1), _.SemiProduct.productMany([_.some(2)])), - _.some([1, 2]) - ) + const productMany = _.SemiProduct.productMany + deepStrictEqual(productMany(_.none(), []), _.none()) + deepStrictEqual(productMany(_.some(1), []), _.some([1])) + deepStrictEqual(productMany(_.some(1), [_.none()]), _.none()) + deepStrictEqual(productMany(_.some(1), [_.some(2)]), _.some([1, 2])) }) it("productAll", () => { diff --git a/test/Predicate.ts b/test/Predicate.ts index 9298ca40c..5825f3879 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -76,7 +76,7 @@ describe.concurrent("Predicate", () => { it("productMany", () => { const productMany = _.SemiProduct.productMany - const p = pipe(isPositive, productMany([isNegative])) + const p = productMany(isPositive, [isNegative]) deepStrictEqual(p([1, -1]), true) deepStrictEqual(p([1, 1]), false) deepStrictEqual(p([-1, -1]), false) diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 0784ea741..b6a4e9dfe 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -1613,47 +1613,6 @@ describe.concurrent("ReadonlyArray", () => { ]) }) - test("productMany", () => { - const productMany = RA.SemiProduct.productMany - expect(pipe( - [], - productMany([ - [2], - [4, 5], - [8, 9, 10] - ]) - )).toEqual(pipe( - [], - productMany([ - [2], - [4, 5], - [8, 9, 10] - ]) - )) - expect(pipe( - [1, 2, 3], - productMany([]) - )).toEqual(pipe( - [1, 2, 3], - productMany([]) - )) - expect(pipe( - [1, 2, 3], - productMany([ - [2], - [4, 5], - [8, 9, 10] - ]) - )).toEqual(pipe( - [1, 2, 3], - productMany([ - [2], - [4, 5], - [8, 9, 10] - ]) - )) - }) - test("productAll", () => { const productAll = RA.Product.productAll expect(productAll([])).toEqual([]) diff --git a/test/These.ts b/test/These.ts index 2b9792baa..eaed39a55 100644 --- a/test/These.ts +++ b/test/These.ts @@ -291,30 +291,31 @@ describe("These", () => { const ab = ["a", "b"] as const const productMany: ( + self: _.Validated, collection: Iterable<_.Validated> - ) => (self: _.Validated) => _.Validated]> = _.SemiProduct.productMany + ) => _.Validated]> = _.SemiProduct.productMany - expect(pipe(_.right(1), productMany([_.right(2)]))).toEqual(_.right([1, 2])) + expect(productMany(_.right(1), [_.right(2)])).toEqual(_.right([1, 2])) U.deepStrictEqual( - pipe(_.right(1), productMany([_.left(b)])), + productMany(_.right(1), [_.left(b)]), _.left(b) ) expect( - pipe(_.right(1), productMany([_.both(b, 2)])) + productMany(_.right(1), [_.both(b, 2)]) ).toEqual( _.both(b, [1, 2]) ) - U.deepStrictEqual(pipe(_.left(a), productMany([_.right(2)])), _.left(a)) - U.deepStrictEqual(pipe(_.left(a), productMany([_.left(b)])), _.left(a)) + U.deepStrictEqual(productMany(_.left(a), [_.right(2)]), _.left(a)) + U.deepStrictEqual(productMany(_.left(a), [_.left(b)]), _.left(a)) U.deepStrictEqual( - pipe(_.left(a), productMany([_.both(b, 2)])), + productMany(_.left(a), [_.both(b, 2)]), _.left(a) ) - expect(pipe(_.both(a, 1), productMany([_.right(2)]))).toEqual(_.both(a, [1, 2])) - expect(pipe(_.both(a, 1), productMany([_.left(b)]))).toEqual(_.left(ab)) - expect(pipe(_.both(a, 1), productMany([_.both(b, 2)]))).toEqual( + expect(productMany(_.both(a, 1), [_.right(2)])).toEqual(_.both(a, [1, 2])) + expect(productMany(_.both(a, 1), [_.left(b)])).toEqual(_.left(ab)) + expect(productMany(_.both(a, 1), [_.both(b, 2)])).toEqual( _.both(ab, [1, 2]) ) }) diff --git a/test/typeclass/Equivalence.ts b/test/typeclass/Equivalence.ts index 2668fd960..8bef6a0b1 100644 --- a/test/typeclass/Equivalence.ts +++ b/test/typeclass/Equivalence.ts @@ -146,10 +146,7 @@ describe("Equivalence", () => { }) it("SemiProduct/productMany", () => { - const eq = pipe( - _.string, - _.SemiProduct.productMany([_.string]) - ) + const eq = _.SemiProduct.productMany(_.string, [_.string]) expect(eq(["a"], ["a"])).toEqual(true) expect(eq(["a"], ["b"])).toEqual(false) expect(eq(["a"], ["a", "b"])).toEqual(false) diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index bf41c8e08..1d82bb7bb 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -208,10 +208,7 @@ describe("Order", () => { }) it("productMany", () => { - const O = pipe( - string.Order, - _.SemiProduct.productMany([string.Order, string.Order]) - ) + const O = _.SemiProduct.productMany(string.Order, [string.Order, string.Order]) U.deepStrictEqual(O.compare(["a", "b"], ["a", "c"]), -1) U.deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) U.deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 3595e6223..57a00fbdc 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -45,7 +45,7 @@ describe("SemiProduct", () => { } return fas } - const actual = pipe(self, SemiApplicative.productMany(collection)) + const actual = SemiApplicative.productMany(self, collection) const expected = pipe(self, productManyFromAp(collection)) // console.log(expected) U.deepStrictEqual(actual, expected) @@ -107,18 +107,18 @@ describe("SemiProduct", () => { describe("productManyComposition", () => { it("ReadonlyArray", () => { const productMany = _.productManyComposition(RA.SemiApplicative, O.SemiProduct) - expect(pipe([O.some(1), O.none()], productMany([]))).toEqual([ + expect(productMany([O.some(1), O.none()], [])).toEqual([ O.some([1]), O.none() ]) - expect(pipe([O.some(1), O.none()], productMany([[O.some(2), O.none()]]))).toEqual([ + expect(productMany([O.some(1), O.none()], [[O.some(2), O.none()]])).toEqual([ O.some([1, 2]), O.none(), O.none(), O.none() ]) expect( - pipe([O.some(1), O.some(2)], productMany([[O.some(3), O.some(4)], [O.some(5)]])) + productMany([O.some(1), O.some(2)], [[O.some(3), O.some(4)], [O.some(5)]]) ).toEqual( [ O.some([1, 3, 5]), @@ -131,18 +131,18 @@ describe("SemiProduct", () => { it("Option", () => { const productMany = _.productManyComposition(O.SemiApplicative, O.SemiProduct) - U.deepStrictEqual(pipe(O.none(), productMany([])), O.none()) - U.deepStrictEqual(pipe(O.some(O.none()), productMany([])), O.some(O.none())) - U.deepStrictEqual(pipe(O.some(O.some(1)), productMany([])), O.some(O.some([1]))) - U.deepStrictEqual(pipe(O.none(), productMany([O.none()])), O.none()) - U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.none()])), O.none()) - U.deepStrictEqual(pipe(O.some(O.none()), productMany([O.some(O.none())])), O.some(O.none())) + U.deepStrictEqual(productMany(O.none(), []), O.none()) + U.deepStrictEqual(productMany(O.some(O.none()), []), O.some(O.none())) + U.deepStrictEqual(productMany(O.some(O.some(1)), []), O.some(O.some([1]))) + U.deepStrictEqual(productMany(O.none(), [O.none()]), O.none()) + U.deepStrictEqual(productMany(O.some(O.none()), [O.none()]), O.none()) + U.deepStrictEqual(productMany(O.some(O.none()), [O.some(O.none())]), O.some(O.none())) U.deepStrictEqual( - pipe(O.some(O.none()), productMany([O.some(O.some("a"))])), + productMany(O.some(O.none()), [O.some(O.some("a"))]), O.some(O.none()) ) U.deepStrictEqual( - pipe(O.some(O.some(1)), productMany([O.some(O.some(2))])), + productMany(O.some(O.some(1)), [O.some(O.some(2))]), O.some(O.some([1, 2])) ) }) diff --git a/test/typeclass/Semigroup.ts b/test/typeclass/Semigroup.ts index 46ebaa0ee..2612c7890 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -145,10 +145,7 @@ describe("Semigroup", () => { }) it("productMany", () => { - const S = pipe( - String.Semigroup, - _.SemiProduct.productMany([String.Semigroup, String.Semigroup]) - ) + const S = _.SemiProduct.productMany(String.Semigroup, [String.Semigroup, String.Semigroup]) U.deepStrictEqual(S.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) }) }) From 292848f704a82a62afa9ac4caac9c08a0cc581c1 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 16:21:30 +0100 Subject: [PATCH 64/80] SemiCoproduct: make coproduct binary --- CHANGELOG.md | 2 ++ src/Either.ts | 2 +- src/Identity.ts | 2 +- src/Option.ts | 13 ++----------- src/These.ts | 2 +- src/typeclass/Foldable.ts | 5 +---- src/typeclass/SemiCoproduct.ts | 7 +++---- src/typeclass/Semigroup.ts | 4 ++-- test/Either.ts | 9 +++++---- test/Identity.ts | 4 ++-- test/Option.ts | 19 +++++++++---------- test/These.ts | 13 +++++++------ 12 files changed, 36 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ff608fec..71c616c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - `Semigroup` - make `combine` binary - make `combineMany` binary +- `SemiCoproduct` + - make `coproduct` binary - `SemiProduct` - make `product` binary - make `productMany` binary diff --git a/src/Either.ts b/src/Either.ts index c9b7cf5db..d4b2d37c1 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -564,7 +564,7 @@ export const firstSuccessOf = (collection: Iterable>) => */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, - coproduct: (that) => (self) => isRight(self) ? self : that, + coproduct: (self, that) => isRight(self) ? self : that, coproductMany: firstSuccessOf } diff --git a/src/Identity.ts b/src/Identity.ts index f65883432..a0b3f8062 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -393,7 +393,7 @@ export const getSemiCoproduct = ( S: Semigroup ): semiCoproduct.SemiCoproduct> => ({ imap: Invariant.imap, - coproduct: (that) => (self) => S.combine(self, that), + coproduct: (self, that) => S.combine(self, that), coproductMany: (collection) => (self) => S.combineMany(self, collection) }) diff --git a/src/Option.ts b/src/Option.ts index a2d2e70a9..af2524d3f 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -615,12 +615,6 @@ export const getFirstNoneMonoid: (M: Monoid) => Monoid> = applic Applicative ) -/** - * @since 1.0.0 - */ -export const coproduct = (that: Option) => - (self: Option): Option => isSome(self) ? self : that - /** * @category error handling * @since 1.0.0 @@ -645,7 +639,7 @@ export const firstSomeOf = (collection: Iterable>) => */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, - coproduct, + coproduct: (self, that) => isSome(self) ? self : that, coproductMany: firstSomeOf } @@ -667,10 +661,7 @@ export const coproductEither = (that: Option) => (self: Option): Option> => isNone(self) ? pipe(that, map(either.right)) : pipe(self, map(either.left)) -/** - * @since 1.0.0 - */ -export const coproductAll = (collection: Iterable>): Option => { +const coproductAll = (collection: Iterable>): Option => { const options = readonlyArray.fromIterable(collection) return options.length > 0 ? SemiCoproduct.coproductMany(options.slice(1))(options[0]) : diff --git a/src/These.ts b/src/These.ts index 25e1ea22d..51d2fec40 100644 --- a/src/These.ts +++ b/src/These.ts @@ -879,7 +879,7 @@ export const firstRightOrBothOf = (collection: Iterable>) => */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, - coproduct: (that) => (self) => isRightOrBoth(self) ? self : that, + coproduct: (self, that) => isRightOrBoth(self) ? self : that, coproductMany: firstRightOrBothOf } diff --git a/src/typeclass/Foldable.ts b/src/typeclass/Foldable.ts index 408c7406b..73bb55e9a 100644 --- a/src/typeclass/Foldable.ts +++ b/src/typeclass/Foldable.ts @@ -108,7 +108,4 @@ export const foldMapKind = (F: Foldable) => ( f: (a: A) => Kind ): (self: Kind) => Kind => - F.reduce>( - G.zero(), - (gb, a) => pipe(gb, G.coproduct(f(a))) - ) + F.reduce>(G.zero(), (gb, a) => G.coproduct(gb, f(a))) diff --git a/src/typeclass/SemiCoproduct.ts b/src/typeclass/SemiCoproduct.ts index c659b3408..e49e160b3 100644 --- a/src/typeclass/SemiCoproduct.ts +++ b/src/typeclass/SemiCoproduct.ts @@ -11,10 +11,9 @@ import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" * @since 1.0.0 */ export interface SemiCoproduct extends Invariant { - readonly coproduct: ( + readonly coproduct: ( + self: Kind, that: Kind - ) => ( - self: Kind ) => Kind readonly coproductMany: ( @@ -29,6 +28,6 @@ export const getSemigroup = (F: SemiCoproduct) => (): Semigroup< Kind > => ({ - combine: (self, that) => pipe(self, F.coproduct(that)), + combine: F.coproduct, combineMany: (self, collection) => pipe(self, F.coproductMany(collection)) }) diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index abb2b3cea..ea3916963 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -3,8 +3,8 @@ * * ```ts * export interface Semigroup { - * combine: (that: A) => (self: A) => A - * combineMany: (collection: Iterable) => (self: A) => A + * combine: (self: A, that: A) => A + * combineMany: (self: A, collection: Iterable) => A * } * ``` * diff --git a/test/Either.ts b/test/Either.ts index 88f135658..19b9c6788 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -420,10 +420,11 @@ describe.concurrent("Either", () => { }) it("coproduct", () => { - deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.right(2))), _.right(1)) - deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.left("e2"))), _.right(1)) - deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.right(2))), _.right(2)) - deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.left("e2"))), _.left("e2")) + const coproduct = _.SemiCoproduct.coproduct + deepStrictEqual(coproduct(_.right(1), _.right(2)), _.right(1)) + deepStrictEqual(coproduct(_.right(1), _.left("e2")), _.right(1)) + deepStrictEqual(coproduct(_.left("e1"), _.right(2)), _.right(2)) + deepStrictEqual(coproduct(_.left("e1"), _.left("e2")), _.left("e2")) }) it("coproductMany", () => { diff --git a/test/Identity.ts b/test/Identity.ts index 77006ee89..0a39f52f0 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -103,13 +103,13 @@ describe.concurrent("Identity", () => { it("getSemiCoproduct", () => { const F = _.getSemiCoproduct(String.Semigroup) - U.deepStrictEqual(pipe("a", F.coproduct("b")), "ab") + U.deepStrictEqual(F.coproduct("a", "b"), "ab") U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") }) it("getSemiAlternative", () => { const F = _.getSemiAlternative(String.Semigroup) - U.deepStrictEqual(pipe("a", F.coproduct("b")), "ab") + U.deepStrictEqual(F.coproduct("a", "b"), "ab") U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") }) diff --git a/test/Option.ts b/test/Option.ts index ef147d596..98401c6b1 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -60,10 +60,8 @@ describe.concurrent("Option", () => { expect(_.SemiCoproduct).exist expect(_.getFirstSomeSemigroup).exist // getSemigroup - expect(_.coproduct).exist expect(_.Coproduct).exist - expect(_.coproductAll).exist expect(_.SemiAlternative).exist @@ -197,10 +195,11 @@ describe.concurrent("Option", () => { }) it("coproductAll", () => { - deepStrictEqual(_.coproductAll([]), _.none()) - deepStrictEqual(_.coproductAll([_.some(1)]), _.some(1)) - deepStrictEqual(_.coproductAll([_.none(), _.some(1)]), _.some(1)) - deepStrictEqual(_.coproductAll([_.some(1), _.some(2)]), _.some(1)) + const coproductAll = _.Coproduct.coproductAll + deepStrictEqual(coproductAll([]), _.none()) + deepStrictEqual(coproductAll([_.some(1)]), _.some(1)) + deepStrictEqual(coproductAll([_.none(), _.some(1)]), _.some(1)) + deepStrictEqual(coproductAll([_.some(1), _.some(2)]), _.some(1)) }) it("unit", () => { @@ -236,10 +235,10 @@ describe.concurrent("Option", () => { it("SemiCoproduct", () => { const coproduct = _.SemiCoproduct.coproduct - deepStrictEqual(pipe(_.none(), coproduct(_.none())), _.none()) - deepStrictEqual(pipe(_.none(), coproduct(_.some(2))), _.some(2)) - deepStrictEqual(pipe(_.some(1), coproduct(_.none())), _.some(1)) - deepStrictEqual(pipe(_.some(1), coproduct(_.some(2))), _.some(1)) + deepStrictEqual(coproduct(_.none(), _.none()), _.none()) + deepStrictEqual(coproduct(_.none(), _.some(2)), _.some(2)) + deepStrictEqual(coproduct(_.some(1), _.none()), _.some(1)) + deepStrictEqual(coproduct(_.some(1), _.some(2)), _.some(1)) const coproductMany = _.SemiCoproduct.coproductMany deepStrictEqual(pipe(_.none(), coproductMany([])), _.none()) diff --git a/test/These.ts b/test/These.ts index eaed39a55..ffbdd0dd1 100644 --- a/test/These.ts +++ b/test/These.ts @@ -767,16 +767,17 @@ describe("These", () => { }) it("coproduct", () => { - U.deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.right(2))), _.right(1)) - U.deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproduct(_.left("e2"))), _.right(1)) - U.deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.right(2))), _.right(2)) - U.deepStrictEqual(pipe(_.left("e1"), _.SemiCoproduct.coproduct(_.left("e2"))), _.left("e2")) + const coproduct = _.SemiCoproduct.coproduct + U.deepStrictEqual(coproduct(_.right(1), _.right(2)), _.right(1)) + U.deepStrictEqual(coproduct(_.right(1), _.left("e2")), _.right(1)) + U.deepStrictEqual(coproduct(_.left("e1"), _.right(2)), _.right(2)) + U.deepStrictEqual(coproduct(_.left("e1"), _.left("e2")), _.left("e2")) U.deepStrictEqual( - pipe(_.both("e1", 1), _.SemiCoproduct.coproduct(_.right(2))), + coproduct(_.both("e1", 1), _.right(2)), _.both("e1", 1) ) U.deepStrictEqual( - pipe(_.both("e1", 1), _.SemiCoproduct.coproduct(_.left("e2"))), + coproduct(_.both("e1", 1), _.left("e2")), _.both("e1", 1) ) }) From 1a4b5eb21207c41be9251b469e0be1c495473ea3 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 16:29:29 +0100 Subject: [PATCH 65/80] SemiCoproduct: make coproductMany binary --- CHANGELOG.md | 1 + src/Either.ts | 2 +- src/Identity.ts | 4 ++-- src/Option.ts | 16 +++++++--------- src/These.ts | 2 +- src/typeclass/SemiCoproduct.ts | 11 +++++------ test/Either.ts | 21 +++++---------------- test/Identity.ts | 4 ++-- test/Option.ts | 12 ++++++------ test/These.ts | 20 ++++++++++++-------- 10 files changed, 42 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c616c58..1222b62be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - make `combineMany` binary - `SemiCoproduct` - make `coproduct` binary + - make `coproductMany` binary - `SemiProduct` - make `product` binary - make `productMany` binary diff --git a/src/Either.ts b/src/Either.ts index d4b2d37c1..c0f6e5127 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -565,7 +565,7 @@ export const firstSuccessOf = (collection: Iterable>) => export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, coproduct: (self, that) => isRight(self) ? self : that, - coproductMany: firstSuccessOf + coproductMany: (self, collection) => pipe(self, firstSuccessOf(collection)) } /** diff --git a/src/Identity.ts b/src/Identity.ts index a0b3f8062..e694c6248 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -393,8 +393,8 @@ export const getSemiCoproduct = ( S: Semigroup ): semiCoproduct.SemiCoproduct> => ({ imap: Invariant.imap, - coproduct: (self, that) => S.combine(self, that), - coproductMany: (collection) => (self) => S.combineMany(self, collection) + coproduct: S.combine, + coproductMany: S.combineMany }) /** diff --git a/src/Option.ts b/src/Option.ts index af2524d3f..47a09a263 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -640,7 +640,7 @@ export const firstSomeOf = (collection: Iterable>) => export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, coproduct: (self, that) => isSome(self) ? self : that, - coproductMany: firstSomeOf + coproductMany: (self, collection) => pipe(self, firstSomeOf(collection)) } /** @@ -661,13 +661,6 @@ export const coproductEither = (that: Option) => (self: Option): Option> => isNone(self) ? pipe(that, map(either.right)) : pipe(self, map(either.left)) -const coproductAll = (collection: Iterable>): Option => { - const options = readonlyArray.fromIterable(collection) - return options.length > 0 ? - SemiCoproduct.coproductMany(options.slice(1))(options[0]) : - option.none -} - /** * @category instances * @since 1.0.0 @@ -675,7 +668,12 @@ const coproductAll = (collection: Iterable>): Option => { export const Coproduct: coproduct_.Coproduct = { ...SemiCoproduct, zero: none, - coproductAll + coproductAll: collection => { + const options = readonlyArray.fromIterable(collection) + return options.length > 0 ? + SemiCoproduct.coproductMany(options[0], options.slice(1)) : + option.none + } } /** diff --git a/src/These.ts b/src/These.ts index 51d2fec40..f7569d8b0 100644 --- a/src/These.ts +++ b/src/These.ts @@ -880,7 +880,7 @@ export const firstRightOrBothOf = (collection: Iterable>) => export const SemiCoproduct: semiCoproduct.SemiCoproduct = { ...Invariant, coproduct: (self, that) => isRightOrBoth(self) ? self : that, - coproductMany: firstRightOrBothOf + coproductMany: (self, collection) => pipe(self, firstRightOrBothOf(collection)) } /** diff --git a/src/typeclass/SemiCoproduct.ts b/src/typeclass/SemiCoproduct.ts index e49e160b3..57d85526d 100644 --- a/src/typeclass/SemiCoproduct.ts +++ b/src/typeclass/SemiCoproduct.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import { pipe } from "@fp-ts/core/Function" + import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import type { Invariant } from "@fp-ts/core/typeclass/Invariant" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" @@ -17,17 +17,16 @@ export interface SemiCoproduct extends Invariant { ) => Kind readonly coproductMany: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind + ) => Kind } /** * @since 1.0.0 */ export const getSemigroup = (F: SemiCoproduct) => - (): Semigroup< - Kind - > => ({ + (): Semigroup> => ({ combine: F.coproduct, - combineMany: (self, collection) => pipe(self, F.coproductMany(collection)) + combineMany: F.coproductMany }) diff --git a/test/Either.ts b/test/Either.ts index 19b9c6788..8dd940be3 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -428,25 +428,14 @@ describe.concurrent("Either", () => { }) it("coproductMany", () => { - deepStrictEqual(pipe(_.right(1), _.SemiCoproduct.coproductMany([_.right(2)])), _.right(1)) + const coproductMany = _.SemiCoproduct.coproductMany + deepStrictEqual(coproductMany(_.right(1), [_.right(2)]), _.right(1)) deepStrictEqual( - pipe( - _.right(1) as _.Either, - _.SemiCoproduct.coproductMany([_.left("e2") as _.Either]) - ), + coproductMany(_.right(1), [_.left("e2")]), _.right(1) ) - deepStrictEqual( - pipe( - _.left("e1") as _.Either, - _.SemiCoproduct.coproductMany([_.right(2) as _.Either]) - ), - _.right(2) - ) - deepStrictEqual( - pipe(_.left("e1"), _.SemiCoproduct.coproductMany([_.left("e2")])), - _.left("e2") - ) + deepStrictEqual(coproductMany(_.left("e1"), [_.right(2)]), _.right(2)) + deepStrictEqual(coproductMany(_.left("e1"), [_.left("e2")]), _.left("e2")) }) it("element", () => { diff --git a/test/Identity.ts b/test/Identity.ts index 0a39f52f0..4bde2fe48 100644 --- a/test/Identity.ts +++ b/test/Identity.ts @@ -104,13 +104,13 @@ describe.concurrent("Identity", () => { it("getSemiCoproduct", () => { const F = _.getSemiCoproduct(String.Semigroup) U.deepStrictEqual(F.coproduct("a", "b"), "ab") - U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") + U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") }) it("getSemiAlternative", () => { const F = _.getSemiAlternative(String.Semigroup) U.deepStrictEqual(F.coproduct("a", "b"), "ab") - U.deepStrictEqual(pipe("a", F.coproductMany(["b", "c"])), "abc") + U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") }) it("reduce", () => { diff --git a/test/Option.ts b/test/Option.ts index 98401c6b1..ef5480f09 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -241,12 +241,12 @@ describe.concurrent("Option", () => { deepStrictEqual(coproduct(_.some(1), _.some(2)), _.some(1)) const coproductMany = _.SemiCoproduct.coproductMany - deepStrictEqual(pipe(_.none(), coproductMany([])), _.none()) - deepStrictEqual(pipe(_.none(), coproductMany([_.none()])), _.none()) - deepStrictEqual(pipe(_.none(), coproductMany([_.some(2)])), _.some(2)) - deepStrictEqual(pipe(_.some(1), coproductMany([])), _.some(1)) - deepStrictEqual(pipe(_.some(1), coproductMany([_.none() as _.Option])), _.some(1)) - deepStrictEqual(pipe(_.some(1), coproductMany([_.some(2)])), _.some(1)) + deepStrictEqual(coproductMany(_.none(), []), _.none()) + deepStrictEqual(coproductMany(_.none(), [_.none()]), _.none()) + deepStrictEqual(coproductMany(_.none(), [_.some(2)]), _.some(2)) + deepStrictEqual(coproductMany(_.some(1), []), _.some(1)) + deepStrictEqual(coproductMany(_.some(1), [_.none() as _.Option]), _.some(1)) + deepStrictEqual(coproductMany(_.some(1), [_.some(2)]), _.some(1)) }) it("fromIterable", () => { diff --git a/test/These.ts b/test/These.ts index ffbdd0dd1..66bb5b609 100644 --- a/test/These.ts +++ b/test/These.ts @@ -772,14 +772,18 @@ describe("These", () => { U.deepStrictEqual(coproduct(_.right(1), _.left("e2")), _.right(1)) U.deepStrictEqual(coproduct(_.left("e1"), _.right(2)), _.right(2)) U.deepStrictEqual(coproduct(_.left("e1"), _.left("e2")), _.left("e2")) - U.deepStrictEqual( - coproduct(_.both("e1", 1), _.right(2)), - _.both("e1", 1) - ) - U.deepStrictEqual( - coproduct(_.both("e1", 1), _.left("e2")), - _.both("e1", 1) - ) + U.deepStrictEqual(coproduct(_.both("e1", 1), _.right(2)), _.both("e1", 1)) + U.deepStrictEqual(coproduct(_.both("e1", 1), _.left("e2")), _.both("e1", 1)) + }) + + it("coproduct", () => { + const coproductMany = _.SemiCoproduct.coproductMany + U.deepStrictEqual(coproductMany(_.right(1), [_.right(2)]), _.right(1)) + U.deepStrictEqual(coproductMany(_.right(1), [_.left("e2")]), _.right(1)) + U.deepStrictEqual(coproductMany(_.left("e1"), [_.right(2)]), _.right(2)) + U.deepStrictEqual(coproductMany(_.left("e1"), [_.left("e2")]), _.left("e2")) + U.deepStrictEqual(coproductMany(_.both("e1", 1), [_.right(2)]), _.both("e1", 1)) + U.deepStrictEqual(coproductMany(_.both("e1", 1), [_.left("e2")]), _.both("e1", 1)) }) it("compact", () => { From 94edf17bc4a9ab77c08b22624bff3ab4c6dade84 Mon Sep 17 00:00:00 2001 From: gcanti Date: Fri, 20 Jan 2023 17:16:44 +0100 Subject: [PATCH 66/80] add Tuple module --- CHANGELOG.md | 3 ++- data.md | 30 ++++++++++++++--------- dtslint/ts4.7/Tuple.ts | 4 +++ src/ReadonlyArray.ts | 9 ------- src/Tuple.ts | 55 ++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 7 +++++- test/ReadonlyArray.ts | 1 - test/Tuple.ts | 17 +++++++++++++ 8 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 dtslint/ts4.7/Tuple.ts create mode 100644 src/Tuple.ts create mode 100644 test/Tuple.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1222b62be..e61d75168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ - add `array`, `readonlyArray` combinators - `Semigroup` - add `array`, `readonlyArray` combinators -- modules +- new modules - `typeclass/Equivalence` - `typeclass/Filterable` - `typeclass/TraversableFilterable` @@ -47,6 +47,7 @@ - `Struct` - `Symbol` - `These` + - `Tuple` ## 0.0.11 diff --git a/data.md b/data.md index 64e22438d..c55fe8867 100644 --- a/data.md +++ b/data.md @@ -4,18 +4,24 @@ This section covers the various modules and combinators that work with tuples. -| Module | Name | Given | To | -| ----------- | ------------- | --------------------------------------- | -------------------------------------- | -| Equivalence | tuple | `[Equivalence, Equivalence, ...]` | `Equivalence` | -| Order | tuple | `[Order, Order, ...]` | `Order` | -| Semigroup | tuple | `[Semigroup, Semigroup, ...]` | `Semigroup<[A, B, ...]>` | -| Monoid | tuple | `[Monoid, Monoid, ...]` | `Monoid<[A, B, ...]>` | -| SemiProduct | nonEmptyTuple | `[F, F, ...]` (cannot be empty) | `F<[A, B, ...]>` | -| Product | tuple | `[F, F, ...]` | `F<[A, B, ...]>` | -| Either | tuple | `[Either, Either, ...]` | `Either` | -| Option | tuple | `[Option, Option, ...]` | `Option<[A, B, ...]>` | -| Predicate | tuple | `[Predicate, Predicate, ...]` | `Predicate` | -| These | tuple | `[These, These, ...]` | `These` | +| Module | Name | Given | To | +| ----------- | --------------- | --------------------------------------- | -------------------------------------- | +| Equivalence | tuple | `[Equivalence, Equivalence, ...]` | `Equivalence` | +| Order | tuple | `[Order, Order, ...]` | `Order` | +| Semigroup | tuple | `[Semigroup, Semigroup, ...]` | `Semigroup<[A, B, ...]>` | +| Monoid | tuple | `[Monoid, Monoid, ...]` | `Monoid<[A, B, ...]>` | +| SemiProduct | nonEmptyTuple | `[F, F, ...]` (cannot be empty) | `F<[A, B, ...]>` | +| Product | tuple | `[F, F, ...]` | `F<[A, B, ...]>` | +| Either | tuple | `[Either, Either, ...]` | `Either` | +| Option | tuple | `[Option, Option, ...]` | `Option<[A, B, ...]>` | +| Predicate | tuple | `[Predicate, Predicate, ...]` | `Predicate` | +| These | tuple | `[These, These, ...]` | `These` | +| Tuple | getEquivalence | `[Equivalence, Equivalence, ...]` | `Equivalence` | +| Tuple | getOrder | `[Order, Order, ...]` | `Order` | +| Tuple | getSemigroup | `[Semigroup, Semigroup, ...]` | `Semigroup<[A, B, ...]>` | +| Tuple | getMonoid | `[Monoid, Monoid, ...]` | `Monoid<[A, B, ...]>` | +| Tuple | nonEmptyProduct | `[F, F, ...]` (cannot be empty) | `F<[A, B, ...]>` | +| Tuple | product | `[F, F, ...]` | `F<[A, B, ...]>` | ## arrays diff --git a/dtslint/ts4.7/Tuple.ts b/dtslint/ts4.7/Tuple.ts new file mode 100644 index 000000000..ffe7ae68b --- /dev/null +++ b/dtslint/ts4.7/Tuple.ts @@ -0,0 +1,4 @@ +import * as T from '@fp-ts/core/Tuple' + +// $ExpectType [string, number, boolean] +T.tuple('a', 1, true) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index fbec99624..16d096338 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -2077,15 +2077,6 @@ export const join: (sep: string) => (self: ReadonlyArray) => string = in string.Monoid ) -/** - * Adds an element to the end of a tuple. - * - * @since 1.0.0 - */ -export const element: (that: ReadonlyArray) => >( - self: ReadonlyArray -) => Array<[...A, B]> = semiProduct.element(SemiProduct) as any - /** * @since 1.0.0 */ diff --git a/src/Tuple.ts b/src/Tuple.ts new file mode 100644 index 000000000..50872e69b --- /dev/null +++ b/src/Tuple.ts @@ -0,0 +1,55 @@ +/** + * @since 1.0.0 + */ +import * as RA from "@fp-ts/core/ReadonlyArray" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" +import * as _product from "@fp-ts/core/typeclass/Product" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" + +/** + * @category constructors + * @since 1.0.0 + */ +export const tuple = >(...elements: A): A => elements + +/** + * Adds an element to the end of a tuple. + * + * @since 1.0.0 + */ +export const element: (that: ReadonlyArray) => >( + self: ReadonlyArray +) => Array<[...A, B]> = semiProduct.element(RA.SemiProduct) as any + +/** + * @since 1.0.0 + */ +export const getEquivalence = equivalence.tuple + +/** + * @since 1.0.0 + */ +export const getOrder = order.tuple + +/** + * @since 1.0.0 + */ +export const getSemigroup = semigroup.tuple + +/** + * @since 1.0.0 + */ +export const getMonoid = monoid.tuple + +/** + * @since 1.0.0 + */ +export const nonEmptyProduct = semiProduct.nonEmptyTuple + +/** + * @since 1.0.0 + */ +export const product = _product.tuple diff --git a/src/index.ts b/src/index.ts index 4deb9f9c0..03150d21a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ import * as string from "@fp-ts/core/String" import * as struct from "@fp-ts/core/Struct" import * as symbol from "@fp-ts/core/Symbol" import * as these from "@fp-ts/core/These" +import * as tuple from "@fp-ts/core/Tuple" // ------------------------------------------------------------------------------------- // typeclasses @@ -259,5 +260,9 @@ export { * @category typeclass * @since 1.0.0 */ - traversableFilterable + traversableFilterable, + /** + * @since 1.0.0 + */ + tuple } diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index b6a4e9dfe..366631809 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -41,7 +41,6 @@ describe.concurrent("ReadonlyArray", () => { expect(RA.SemiProduct).exist expect(RA.andThenBind).exist - expect(RA.element).exist expect(RA.Product).exist diff --git a/test/Tuple.ts b/test/Tuple.ts new file mode 100644 index 000000000..9a1212646 --- /dev/null +++ b/test/Tuple.ts @@ -0,0 +1,17 @@ +import * as T from "@fp-ts/core/Tuple" + +describe.concurrent("Tuple", () => { + it("exports", () => { + expect(T.element).exists + expect(T.getEquivalence).exists + expect(T.getOrder).exists + expect(T.getSemigroup).exists + expect(T.getMonoid).exists + expect(T.nonEmptyProduct).exists + expect(T.product).exists + }) + + it("tuple", () => { + expect(T.tuple("a", 1, true)).toEqual(["a", 1, true]) + }) +}) From 57983344c3654c503246bf7d6f304c79d4f76cfe Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 09:56:28 +0100 Subject: [PATCH 67/80] more docs --- CHANGELOG.md | 1 + Either.md | 141 ++++++++++++++++++++++++++++++++++++++++++ Option.md | 120 +++++++++++++++++++++++++++++++++++ src/Bigint.ts | 4 ++ src/Boolean.ts | 4 ++ src/Either.ts | 18 +++--- src/Number.ts | 4 ++ src/Option.ts | 4 -- src/ReadonlyArray.ts | 2 + src/ReadonlyRecord.ts | 2 + src/String.ts | 4 ++ src/Struct.ts | 2 + src/Tuple.ts | 2 + 13 files changed, 294 insertions(+), 14 deletions(-) create mode 100644 Either.md create mode 100644 Option.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e61d75168..d91339d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ **Breaking changes** +- remove `NonEmptyTraversable` module - `Semigroup` - make `combine` binary - make `combineMany` binary diff --git a/Either.md b/Either.md new file mode 100644 index 000000000..1b1bf3953 --- /dev/null +++ b/Either.md @@ -0,0 +1,141 @@ +# The `Either` data type + +The `Either` data type is a powerful and flexible tool for handling potentially failed computations in functional programming. It can be found in the `@fp-ts/core/Either` module, and it has two variants, `Left` and `Right`, which can be used to represent different outcomes. + +The `Left` variant is used to represent a failure, and it can contain useful information such as an error message or a failure code. The `Right` variant, on the other hand, is used to represent a successful outcome, and it can contain the result of the computation. + +Unlike the `Option` type, `Either` allows you to attach additional information to the failure case, making it more informative. +In this usage, `None` is replaced with a `Left` which can contain useful information. `Right` takes the place of `Some`. + +## Definition + +The `Either` data type is the union of two members: `Left` and `Right`. The way chosen by the `@fp-ts/core` library to model this union in TypeScript is to use a feature of the language called [Discriminating Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions): + +> A common technique for working with unions is to have a single field which uses literal types which you can use to let TypeScript narrow down the possible current type + +Here's the complete definition of the `Either` type: + +```ts +export type Left = { + readonly _tag: "Left"; + readonly left: E; +}; + +export type Right = { + readonly _tag: "Right"; + readonly right: A; +}; + +export type Either = Left | Right; +``` + +## Using `Either` + +To create an instance of `Either`, you can use the `right` and `left` constructors, which construct a new `Either` holding a `Right` or `Left` value respectively. + +```ts +import { left, right } from "@fp-ts/core/Either"; + +const success: Either = right(1); +const failure: Either = left("error message"); +``` + +You can also use the `fromOption` function to convert an `Option` to an `Either`. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { fromOption } from "@fp-ts/core/Either"; +import { none, some } from "@fp-ts/core/Option"; + +const success: Either = pipe( + some(1), + fromOption("error message") +); +const failure: Either = pipe( + none(), + fromOption("error message") +); +``` + +The `fromOption` function requires an argument because it needs to know what value to use for the `Left` variant of the `Either` type when given a `None`. In the example, the argument "error message" is used as the value for the `Left` variant when `None` is encountered. This allows `Either` to provide more information about why a failure occurred. + +## Working with `Either` + +Once you have an instance of `Either`, you can use the various functions provided in the `@fp-ts/core/Either` module to work with it. + +The `map` and `mapLeft` functions can be used to transform the `Right` and `Left` values respectively. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { left, right, map, mapLeft } from "@fp-ts/core/Either"; + +const success: Either = pipe( + right(1), + map((x) => x + 1) +); // right(2) + +const failure: Either = pipe( + left("error message"), + mapLeft((x) => x + "!") +); // left("error message!") +``` + +## Handling failing computations + +Let's see how to use the `Either` data type to model a computation that can fail, such as a function that can throw an exception based on certain conditions. Let's take the case of the following function: + +```ts +function parseNumber(s: string): number { + const n = parseFloat(s); + if (isNaN(n)) { + throw new Error(`Cannot parse '${s}' as a number`); + } + return n; +} +``` + +An alternative to throwing an exception is to always return a value, but this value will be of type `Either` instead of `number`, with the following interpretation: + +- if `parseNumber` returns a `Left` value, it means that the computation failed, and the `Left` contains an error message or other information about the failure +- if the result is instead a `Right` value, it means that the computation succeeded and the computed value is wrapped inside the `Right` + +Let's see how we can rewrite the `parseNumber` function without throwing exceptions and using the `Either` data type instead: + +```ts +import { Either, left, right } from "@fp-ts/core/Either"; + +function parseNumber(s: string): Either { + const n = parseFloat(s); + return isNaN(n) ? left(`Cannot parse '${s}' as a number`) : right(n); +} + +console.log(parseNumber("2")); // right(2) +console.log(parseNumber("Not a number")); // left("Cannot parse 'Not a number' as a number") +``` + +## Pattern matching + +The `match` function allows us to match on the `Left` and `Right` cases of an `Either` value and provide different actions for each. + +We can use the `match` function to handle the `Either` value returned by `parseNumber` and decide what to do based on whether it's a `Left` or a `Right`. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { match } from "@fp-ts/core/Either"; + +// parseNumber returns an Either +const result = parseNumber("Not a number"); + +// Using pipe and match to pattern match on the result +const output = pipe( + result, + match( + // If the result is a Left, return an error string + (error) => `Error: ${error}`, + // If the result is a Right, return a string with the number + (n) => `The number is ${n}` + ) +); + +console.log(output); // Output: Error: Cannot parse 'Not a number' as a number +``` diff --git a/Option.md b/Option.md new file mode 100644 index 000000000..02086f9cf --- /dev/null +++ b/Option.md @@ -0,0 +1,120 @@ +# The `Option` data type + +The `Option` data type is a powerful and flexible tool for handling potentially failed computations in functional programming. It can be found in the `@fp-ts/core/Option` module, and it has two variants, `None` and `Some`, which can be used to represent different outcomes. + +There are two possible interpretations of the `Option` data type: + +1. as a representation of the result of a computation that can fail or return a value of type `A` +2. as a representation of an optional value of type `A` + +In the first of these two interpretations, the `None` union member represents the result of a computation that has failed and therefore was not able to return any value, while the `Some` union member represents the result of a computation that has succeeded and was able to return a value of type `A`. + +In the second of these two interpretations, the `None` union member represents the absence of the value, while the `Some` union member represents the presence of the value of type `A` + +## Definition + +The `Option` data type is the union of two members: `None` and `Some`. The way chosen by the `@fp-ts/core` library to model this union in TypeScript is to use a feature of the language called [Discriminating Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions): + +> A common technique for working with unions is to have a single field which uses literal types which you can use to let TypeScript narrow down the possible current type + +Here's the complete definition of the `Option` type: + +```ts +export type None = { + readonly _tag: "None"; +}; + +export type Some = { + readonly _tag: "Some"; + readonly value: A; +}; + +export type Option = None | Some; +``` + +## Using `Option` + +To create an instance of `Option`, you can use the `some` and `none` constructors, which construct a new `Option` holding a `Some` or `None` value respectively. + +```ts +import { none, some } from "@fp-ts/core/Either"; + +const success: Option = some(1); +const failure: Option = none(); +``` + +## Working with `Option` + +Once you have an instance of `Option`, you can use the various functions provided in the `@fp-ts/core/Option` module to work with it. + +The `map` function can be used to transform the `Some` values. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { some, map } from "@fp-ts/core/Option"; + +const success: Option = pipe( + some(1), + map((x) => x + 1) +); // some(2) +``` + +## Handling failing computations + +Let's see how to use the `Option` data type to model a computation that can fail, such as a function that can throw an exception based on certain conditions. Let's take the case of the following function: + +```ts +function parseNumber(s: string): number { + const n = parseFloat(s); + if (isNaN(n)) { + throw new Error(); + } + return n; +} +``` + +An alternative to throwing an exception is to always return a value, but this value will be of type `Option` instead of `number`, with the following interpretation: + +- if `parseNumber` returns a `None` value, it means that the computation failed +- if the result is instead a `Some` value, it means that the computation succeeded and the computed value is wrapped inside the `Some` + +Let's see how we can rewrite the `parseNumber` function without throwing exceptions and using the `Option` data type instead: + +```ts +import { Option, none, some } from "@fp-ts/core/Option"; + +function parseNumber(s: string): Option { + const n = parseFloat(s); + return isNaN(n) ? none() : some(n); +} + +console.log(parseNumber("2")); // some(2) +console.log(parseNumber("Not a number")); // none() +``` + +## Pattern matching + +The `match` function allows us to match on the `None` and `Some` cases of an `Option` value and provide different actions for each. + +We can use the `match` function to handle the `Option` value returned by `parseNumber` and decide what to do based on whether it's a `None` or a `Some`. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { match } from "@fp-ts/core/Option"; + +// parseNumber returns an Option +const result = parseNumber("Not a number"); + +// Using pipe and match to pattern match on the result +const output = pipe( + result, + match( + // If the result is a None, return an error string + () => `Error: ${error}`, + // If the result is a Some, return a string with the number + (n) => `The number is ${n}` + ) +); + +console.log(output); // Output: Error: Cannot parse 'Not a number' as a number +``` diff --git a/src/Bigint.ts b/src/Bigint.ts index 9860f19a3..6248af689 100644 --- a/src/Bigint.ts +++ b/src/Bigint.ts @@ -1,4 +1,8 @@ /** + * This module provides utility functions and type class instances for working with the `bigint` type in TypeScript. + * It includes functions for basic arithmetic operations, as well as type class instances for + * `Equivalence`, `Order`, `Semigroup`, and `Monoid`. + * * @since 1.0.0 */ diff --git a/src/Boolean.ts b/src/Boolean.ts index bd07985dd..f3e6e4743 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -1,4 +1,8 @@ /** + * This module provides utility functions and type class instances for working with the `boolean` type in TypeScript. + * It includes functions for basic boolean operations, as well as type class instances for + * `Equivalence`, `Order`, `Semigroup`, and `Monoid`. + * * @since 1.0.0 */ import type { LazyArg } from "@fp-ts/core/Function" diff --git a/src/Either.ts b/src/Either.ts index c0f6e5127..29b339d7d 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -1,15 +1,13 @@ /** - * ```ts - * type Either = Left | Right - * ``` + * The `Either` data type is a powerful and flexible tool for handling potentially failed computations. + * It has two variants, `Left` and `Right`, which can be used to represent different outcomes. * - * Represents a value of one of two possible types (a disjoint union). + * The `Left` variant is used to represent a failure, and it can contain useful information such as an error message + * or a failure code. The `Right` variant is used to represent a successful outcome, and it can contain the result + * of the computation. * - * An instance of `Either` is either an instance of `Left` or `Right`. - * - * A common use of `Either` is as an alternative to `Option` for dealing with possible missing values. In this usage, - * `None` is replaced with a `Left` which can contain useful information. `Right` takes the place of `Some`. Convention - * dictates that `Left` is used for failure and `Right` is used for success. + * Unlike `Option`, `Either` allows you to attach additional information to the failure case, making it more + * informative than a simple `null` or `undefined`. * * @since 1.0.0 */ @@ -1004,7 +1002,7 @@ export const fromIterable = (onEmpty: LazyArg) => * import * as O from '@fp-ts/core/Option' * * assert.deepStrictEqual(pipe(O.some(1), E.fromOption(() => 'error')), E.right(1)) - * assert.deepStrictEqual(pipe(O.none, E.fromOption(() => 'error')), E.left('error')) + * assert.deepStrictEqual(pipe(O.none(), E.fromOption(() => 'error')), E.left('error')) * * @category conversions * @since 1.0.0 diff --git a/src/Number.ts b/src/Number.ts index 49ddd27eb..c5a4b5f9f 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -1,4 +1,8 @@ /** + * This module provides utility functions and type class instances for working with the `number` type in TypeScript. + * It includes functions for basic arithmetic operations, as well as type class instances for + * `Equivalence`, `Order`, `Semigroup`, and `Monoid`. + * * @since 1.0.0 */ import type { Ordering } from "@fp-ts/core/Ordering" diff --git a/src/Option.ts b/src/Option.ts index 47a09a263..479c75e30 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -1,8 +1,4 @@ /** - * ```ts - * type Option = None | Some - * ``` - * * The `Option` type can be interpreted in a few ways: * * 1) `Option` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 16d096338..7a41e7939 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -1,4 +1,6 @@ /** + * This module provides utility functions for working with arrays in TypeScript. + * * @since 1.0.0 */ import type { Either } from "@fp-ts/core/Either" diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts index a3f49e039..9936e7372 100644 --- a/src/ReadonlyRecord.ts +++ b/src/ReadonlyRecord.ts @@ -1,4 +1,6 @@ /** + * This module provides utility functions for working with records in TypeScript. + * * @since 1.0.0 */ diff --git a/src/String.ts b/src/String.ts index 54d974db4..0a3a89b21 100644 --- a/src/String.ts +++ b/src/String.ts @@ -1,4 +1,8 @@ /** + * This module provides utility functions and type class instances for working with the `string` type in TypeScript. + * It includes functions for basic string manipulation, as well as type class instances for + * `Equivalence`, `Order`, `Semigroup`, and `Monoid`. + * * @since 1.0.0 */ diff --git a/src/Struct.ts b/src/Struct.ts index 1efea549e..deceeb29f 100644 --- a/src/Struct.ts +++ b/src/Struct.ts @@ -1,4 +1,6 @@ /** + * This module provides utility functions for working with structs in TypeScript. + * * @since 1.0.0 */ diff --git a/src/Tuple.ts b/src/Tuple.ts index 50872e69b..ccfea26e3 100644 --- a/src/Tuple.ts +++ b/src/Tuple.ts @@ -1,4 +1,6 @@ /** + * This module provides utility functions for working with tuples in TypeScript. + * * @since 1.0.0 */ import * as RA from "@fp-ts/core/ReadonlyArray" From c1f7bd9b0544b2bbef3f0c8e83e1ad31a3fcf08e Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 10:16:46 +0100 Subject: [PATCH 68/80] Order: add struct --- data.md | 30 ++++++++++++++---------- src/Struct.ts | 36 ++++++++++++++++++++++++++++ src/typeclass/Order.ts | 21 +++++++++++++++++ test/Struct.ts | 9 +++++++ test/typeclass/Order.ts | 52 +++++++++++++++++++++-------------------- 5 files changed, 111 insertions(+), 37 deletions(-) diff --git a/data.md b/data.md index c55fe8867..7d753d734 100644 --- a/data.md +++ b/data.md @@ -42,18 +42,24 @@ This section covers the various modules and combinators that work with arrays. This section covers the various modules and combinators that work with structs. -| Module | Name | Given | To | -| ----------- | -------------- | ----------------------------------------------- | ----------------------------------------- | -| Equivalence | struct | `{ a: Equivalence, b: Equivalence, ... }` | `Equivalence<{ a: A, b: B, ... }>` | -| Order | NA | NA | NA | -| Semigroup | struct | `{ a: Semigroup, b: Semigroup, ... }` | `Semigroup<{ a: A, b: B, ... }>` | -| Monoid | struct | `{ a: Monoid, b: Monoid, ... }` | `Monoid<{ a: A, b: B, ... }>` | -| SemiProduct | nonEmptyStruct | `{ a: F, b: F, ... }` (cannot be empty) | `F<{ a: A, b: B, ... }>` | -| Product | struct | `{ a: F, b: F, ... }` | `F<{ a: A, b: B, ... }>` | -| Either | struct | `{ a: Either, b: Either, ... }` | `Either` | -| Option | struct | `{ a: Option, b: Option, ... }` | `Option<{ a: A, b: B }>` | -| Predicate | struct | `{ a: Predicate, b: Predicate, ... }` | `Predicate>` | -| These | struct | `{ a: These, b: These, ... }` | `These` | +| Module | Name | Given | To | +| ----------- | --------------- | ----------------------------------------------- | ----------------------------------------- | +| Equivalence | struct | `{ a: Equivalence, b: Equivalence, ... }` | `Equivalence<{ a: A, b: B, ... }>` | +| Order | struct | `{ a: Order, b: Order, ... }` | `Order<{ a: A, b: B, ... }>` | +| Semigroup | struct | `{ a: Semigroup, b: Semigroup, ... }` | `Semigroup<{ a: A, b: B, ... }>` | +| Monoid | struct | `{ a: Monoid, b: Monoid, ... }` | `Monoid<{ a: A, b: B, ... }>` | +| SemiProduct | nonEmptyStruct | `{ a: F, b: F, ... }` (cannot be empty) | `F<{ a: A, b: B, ... }>` | +| Product | struct | `{ a: F, b: F, ... }` | `F<{ a: A, b: B, ... }>` | +| Either | struct | `{ a: Either, b: Either, ... }` | `Either` | +| Option | struct | `{ a: Option, b: Option, ... }` | `Option<{ a: A, b: B }>` | +| Predicate | struct | `{ a: Predicate, b: Predicate, ... }` | `Predicate>` | +| These | struct | `{ a: These, b: These, ... }` | `These` | +| Struct | getEquivalence | `{ a: Equivalence, b: Equivalence, ... }` | `Equivalence<{ a: A, b: B, ... }>` | +| Struct | getOrder | `{ a: Order, b: Order, ... }` | `Order<{ a: A, b: B, ... }>` | +| Struct | getSemigroup | `{ a: Semigroup, b: Semigroup, ... }` | `Semigroup<{ a: A, b: B, ... }>` | +| Struct | getMonoid | `{ a: Monoid, b: Monoid, ... }` | `Monoid<{ a: A, b: B, ... }>` | +| Struct | nonEmptyProduct | `{ a: F, b: F, ... }` (cannot be empty) | `F<{ a: A, b: B, ... }>` | +| Struct | product | `{ a: F, b: F, ... }` | `F<{ a: A, b: B, ... }>` | ## records diff --git a/src/Struct.ts b/src/Struct.ts index deceeb29f..053d7eec1 100644 --- a/src/Struct.ts +++ b/src/Struct.ts @@ -3,6 +3,12 @@ * * @since 1.0.0 */ +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as order from "@fp-ts/core/typeclass/Order" +import * as _product from "@fp-ts/core/typeclass/Product" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" /** * Create a new object by picking properties of an existing object. @@ -35,3 +41,33 @@ export const omit = ]>( } return out } + +/** + * @since 1.0.0 + */ +export const getEquivalence = equivalence.struct + +/** + * @since 1.0.0 + */ +export const getOrder = order.struct + +/** + * @since 1.0.0 + */ +export const getSemigroup = semigroup.struct + +/** + * @since 1.0.0 + */ +export const getMonoid = monoid.struct + +/** + * @since 1.0.0 + */ +export const nonEmptyProduct = semiProduct.nonEmptyStruct + +/** + * @since 1.0.0 + */ +export const product = _product.struct diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index 85b762d09..0adbb5945 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -114,6 +114,27 @@ export const array = (O: Order): Order> => return number.compare(aLen, bLen) }) +/** + * This function creates and returns a new `Order` for a struct of values based on the given `Order`s + * for each property in the struct. + * + * @category combinators + * @since 1.0.0 + */ +export const struct = (orders: { readonly [K in keyof A]: Order }): Order< + { readonly [K in keyof A]: A[K] } +> => ({ + compare: (self, that) => { + for (const key of Object.keys(orders)) { + const o = orders[key].compare(self[key], that[key]) + if (o !== 0) { + return o + } + } + return 0 + } +}) + /** * @since 1.0.0 */ diff --git a/test/Struct.ts b/test/Struct.ts index 66e2a3735..a9526236b 100644 --- a/test/Struct.ts +++ b/test/Struct.ts @@ -2,6 +2,15 @@ import { pipe } from "@fp-ts/core/Function" import * as S from "@fp-ts/core/Struct" describe.concurrent("Struct", () => { + it("exports", () => { + expect(S.getEquivalence).exists + expect(S.getOrder).exists + expect(S.getSemigroup).exists + expect(S.getMonoid).exists + expect(S.nonEmptyProduct).exists + expect(S.product).exists + }) + it("pick", () => { expect(pipe({ a: "a", b: 1, c: true }, S.pick("a", "b"))).toEqual({ a: "a", b: 1 }) }) diff --git a/test/typeclass/Order.ts b/test/typeclass/Order.ts index 1d82bb7bb..9e59bcfb1 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -1,8 +1,5 @@ -import * as boolean from "@fp-ts/core/Boolean" import { pipe } from "@fp-ts/core/Function" -import * as number from "@fp-ts/core/Number" import { sort } from "@fp-ts/core/ReadonlyArray" -import * as string from "@fp-ts/core/String" import * as _ from "@fp-ts/core/typeclass/Order" import * as U from "../util" @@ -16,14 +13,22 @@ describe("Order", () => { }) it("tuple", () => { - const O = _.tuple(string.Order, number.Order, boolean.Order) + const O = _.tuple(_.string, _.number, _.boolean) U.deepStrictEqual(O.compare(["a", 1, true], ["b", 2, true]), -1) U.deepStrictEqual(O.compare(["a", 1, true], ["a", 2, true]), -1) U.deepStrictEqual(O.compare(["a", 1, true], ["a", 1, false]), 1) }) + it("struct", () => { + const O = _.struct({ a: _.string, b: _.number, c: _.boolean }) + U.deepStrictEqual(O.compare({ a: "a", b: 1, c: true }, { a: "b", b: 2, c: true }), -1) + U.deepStrictEqual(O.compare({ a: "a", b: 1, c: true }, { a: "a", b: 2, c: true }), -1) + U.deepStrictEqual(O.compare({ a: "a", b: 1, c: true }, { a: "a", b: 1, c: false }), 1) + U.deepStrictEqual(O.compare({ a: "a", b: 1, c: true }, { a: "a", b: 1, c: true }), 0) + }) + it("Contravariant", () => { - const O = pipe(number.Order, _.Contravariant.contramap((s: string) => s.length)) + const O = pipe(_.number, _.Contravariant.contramap((s: string) => s.length)) U.deepStrictEqual(O.compare("a", "b"), 0) U.deepStrictEqual(O.compare("a", "bb"), -1) U.deepStrictEqual(O.compare("aa", "b"), 1) @@ -31,7 +36,7 @@ describe("Order", () => { it("Invariant", () => { const O = _.Invariant.imap((s: string) => [s], ([s]) => s)( - string.Order + _.string ) U.deepStrictEqual(O.compare(["a"], ["b"]), -1) U.deepStrictEqual(O.compare(["a"], ["a"]), 0) @@ -48,11 +53,11 @@ describe("Order", () => { ] const S = _.getSemigroup() const sortByFst = pipe( - number.Order, + _.number, _.contramap((x: T) => x[0]) ) const sortBySnd = pipe( - string.Order, + _.string, _.contramap((x: T) => x[1]) ) U.deepStrictEqual(sort(S.combine(sortByFst, sortBySnd))(tuples), [ @@ -91,11 +96,11 @@ describe("Order", () => { ] const M = _.getMonoid() const sortByFst = pipe( - number.Order, + _.number, _.contramap((x: T) => x[0]) ) const sortBySnd = pipe( - string.Order, + _.string, _.contramap((x: T) => x[1]) ) U.deepStrictEqual(sort(M.combineMany(M.empty, [sortByFst, sortBySnd]))(tuples), [ @@ -113,7 +118,7 @@ describe("Order", () => { }) it("clamp", () => { - const clamp = _.clamp(number.Order) + const clamp = _.clamp(_.number) U.deepStrictEqual(clamp(1, 10)(2), 2) U.deepStrictEqual(clamp(1, 10)(10), 10) U.deepStrictEqual(clamp(1, 10)(20), 10) @@ -122,7 +127,7 @@ describe("Order", () => { }) it("between", () => { - const between = _.between(number.Order) + const between = _.between(_.number) U.deepStrictEqual(between(1, 10)(2), true) U.deepStrictEqual(between(1, 10)(10), true) U.deepStrictEqual(between(1, 10)(20), false) @@ -131,35 +136,35 @@ describe("Order", () => { }) it("reverse", () => { - const O = _.reverse(number.Order) + const O = _.reverse(_.number) U.deepStrictEqual(O.compare(1, 2), 1) U.deepStrictEqual(O.compare(2, 1), -1) U.deepStrictEqual(O.compare(2, 2), 0) }) it("lessThan", () => { - const lessThan = _.lessThan(number.Order) + const lessThan = _.lessThan(_.number) U.deepStrictEqual(pipe(0, lessThan(1)), true) U.deepStrictEqual(pipe(1, lessThan(1)), false) U.deepStrictEqual(pipe(2, lessThan(1)), false) }) it("lessThanOrEqualTo", () => { - const lessThanOrEqualTo = _.lessThanOrEqualTo(number.Order) + const lessThanOrEqualTo = _.lessThanOrEqualTo(_.number) U.deepStrictEqual(pipe(0, lessThanOrEqualTo(1)), true) U.deepStrictEqual(pipe(1, lessThanOrEqualTo(1)), true) U.deepStrictEqual(pipe(2, lessThanOrEqualTo(1)), false) }) it("greaterThan", () => { - const greaterThan = _.greaterThan(number.Order) + const greaterThan = _.greaterThan(_.number) U.deepStrictEqual(pipe(0, greaterThan(1)), false) U.deepStrictEqual(pipe(1, greaterThan(1)), false) U.deepStrictEqual(pipe(2, greaterThan(1)), true) }) it("greaterThanOrEqualTo", () => { - const greaterThanOrEqualTo = _.greaterThanOrEqualTo(number.Order) + const greaterThanOrEqualTo = _.greaterThanOrEqualTo(_.number) U.deepStrictEqual(pipe(0, greaterThanOrEqualTo(1)), false) U.deepStrictEqual(pipe(1, greaterThanOrEqualTo(1)), true) U.deepStrictEqual(pipe(2, greaterThanOrEqualTo(1)), true) @@ -169,7 +174,7 @@ describe("Order", () => { type A = { a: number } const min = _.min( pipe( - number.Order, + _.number, _.contramap((a: A) => a.a) ) ) @@ -184,7 +189,7 @@ describe("Order", () => { type A = { a: number } const max = _.max( pipe( - number.Order, + _.number, _.contramap((a: A) => a.a) ) ) @@ -197,10 +202,7 @@ describe("Order", () => { describe("SemiProduct", () => { it("product", () => { - const O = _.SemiProduct.product( - string.Order, - number.Order - ) + const O = _.SemiProduct.product(_.string, _.number) U.deepStrictEqual(O.compare(["a", 1], ["a", 2]), -1) U.deepStrictEqual(O.compare(["a", 1], ["a", 1]), 0) U.deepStrictEqual(O.compare(["a", 1], ["a", 0]), 1) @@ -208,7 +210,7 @@ describe("Order", () => { }) it("productMany", () => { - const O = _.SemiProduct.productMany(string.Order, [string.Order, string.Order]) + const O = _.SemiProduct.productMany(_.string, [_.string, _.string]) U.deepStrictEqual(O.compare(["a", "b"], ["a", "c"]), -1) U.deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) U.deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) @@ -226,7 +228,7 @@ describe("Order", () => { it("productAll", () => { const O = pipe( - _.Product.productAll([string.Order, string.Order, string.Order]) + _.Product.productAll([_.string, _.string, _.string]) ) U.deepStrictEqual(O.compare(["a", "b"], ["a", "c"]), -1) U.deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) From 0ff528f855203643df3377fbf7bea0480668dfee Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 10:47:16 +0100 Subject: [PATCH 69/80] Number: add SemigroupMin, SemigroupMax, MonoidMin, MonoidMax --- data.md | 4 ++++ src/Number.ts | 48 +++++++++++++++++++++++++++++++--------- src/Predicate.ts | 3 +-- src/Struct.ts | 4 ++-- src/Tuple.ts | 4 ++-- src/typeclass/Bounded.ts | 21 ++++++++++++++++++ test/Number.ts | 22 +++++++++++++----- 7 files changed, 85 insertions(+), 21 deletions(-) diff --git a/data.md b/data.md index 7d753d734..ca819e536 100644 --- a/data.md +++ b/data.md @@ -103,8 +103,12 @@ This section covers the various modules and combinators that work with records. | Number | Order | | `Order` | | Number | SemigroupSum | | `Semigroup` | | Number | SemigroupMultiply | | `Semigroup` | +| Number | SemigroupMax | | `Semigroup` | +| Number | SemigroupMin | | `Semigroup` | | Number | MonoidSum | | `Monoid` | | Number | MonoidMultiply | | `Monoid` | +| Number | MonoidMax | | `Monoid` | +| Number | MonoidMin | | `Monoid` | | Number | isNumber | | `Refinement` | ## booleans diff --git a/src/Number.ts b/src/Number.ts index c5a4b5f9f..c67370659 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -6,7 +6,6 @@ * @since 1.0.0 */ import type { Ordering } from "@fp-ts/core/Ordering" -import type { Refinement } from "@fp-ts/core/Predicate" import * as predicate from "@fp-ts/core/Predicate" import * as bounded from "@fp-ts/core/typeclass/Bounded" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" @@ -18,7 +17,7 @@ import * as semigroup from "@fp-ts/core/typeclass/Semigroup" * @category guards * @since 1.0.0 */ -export const isNumber: Refinement = predicate.isNumber +export const isNumber = predicate.isNumber /** * @since 1.0.0 @@ -35,7 +34,12 @@ export const multiply = (that: number) => /** * @since 1.0.0 */ -export const sub = (that: number) => (self: number): number => self - that +export const subtract = (that: number) => (self: number): number => self - that + +/** + * @since 1.0.0 + */ +export const divide = (that: number) => (self: number): number => self / that /** * @since 1.0.0 @@ -51,19 +55,19 @@ export const decrement = (n: number): number => n - 1 * @category instances * @since 1.0.0 */ -export const Equivalence: equivalence.Equivalence = equivalence.number +export const Equivalence = equivalence.number /** * @category instances * @since 1.0.0 */ -export const Order: order.Order = order.number +export const Order = order.number /** * @category instances * @since 1.0.0 */ -export const Bounded: bounded.Bounded = bounded.number +export const Bounded = bounded.number /** * `number` semigroup under addition. @@ -77,7 +81,19 @@ export const Bounded: bounded.Bounded = bounded.number * @category instances * @since 1.0.0 */ -export const SemigroupSum: semigroup.Semigroup = semigroup.numberSum +export const SemigroupSum = semigroup.numberSum + +/** + * @category instances + * @since 1.0.0 + */ +export const SemigroupMax = semigroup.max(Order) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemigroupMin = semigroup.min(Order) /** * `number` semigroup under multiplication. @@ -91,7 +107,7 @@ export const SemigroupSum: semigroup.Semigroup = semigroup.numberSum * @category instances * @since 1.0.0 */ -export const SemigroupMultiply: semigroup.Semigroup = semigroup.numberMultiply +export const SemigroupMultiply = semigroup.numberMultiply /** * `number` monoid under addition. @@ -101,7 +117,7 @@ export const SemigroupMultiply: semigroup.Semigroup = semigroup.numberMu * @category instances * @since 1.0.0 */ -export const MonoidSum: monoid.Monoid = monoid.numberSum +export const MonoidSum = monoid.numberSum /** * `number` monoid under multiplication. @@ -111,7 +127,19 @@ export const MonoidSum: monoid.Monoid = monoid.numberSum * @category instances * @since 1.0.0 */ -export const MonoidMultiply: monoid.Monoid = monoid.numberMultiply +export const MonoidMultiply = monoid.numberMultiply + +/** + * @category instances + * @since 1.0.0 + */ +export const MonoidMax = bounded.max(Bounded) + +/** + * @category instances + * @since 1.0.0 + */ +export const MonoidMin = bounded.min(Bounded) /** * @since 1.0.0 diff --git a/src/Predicate.ts b/src/Predicate.ts index 55c0042e4..2f55da2a7 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -238,8 +238,7 @@ export const tuple: >>( export const struct: >>( predicates: R ) => Predicate<{ readonly [K in keyof R]: [R[K]] extends [Predicate] ? A : never }> = - product_ - .struct(Product) + product_.struct(Product) /** * @since 1.0.0 diff --git a/src/Struct.ts b/src/Struct.ts index 053d7eec1..c4294f1c9 100644 --- a/src/Struct.ts +++ b/src/Struct.ts @@ -6,7 +6,7 @@ import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as order from "@fp-ts/core/typeclass/Order" -import * as _product from "@fp-ts/core/typeclass/Product" +import * as product_ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" @@ -70,4 +70,4 @@ export const nonEmptyProduct = semiProduct.nonEmptyStruct /** * @since 1.0.0 */ -export const product = _product.struct +export const product = product_.struct diff --git a/src/Tuple.ts b/src/Tuple.ts index ccfea26e3..6b072345c 100644 --- a/src/Tuple.ts +++ b/src/Tuple.ts @@ -7,7 +7,7 @@ import * as RA from "@fp-ts/core/ReadonlyArray" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as order from "@fp-ts/core/typeclass/Order" -import * as _product from "@fp-ts/core/typeclass/Product" +import * as product_ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" @@ -54,4 +54,4 @@ export const nonEmptyProduct = semiProduct.nonEmptyTuple /** * @since 1.0.0 */ -export const product = _product.tuple +export const product = product_.tuple diff --git a/src/typeclass/Bounded.ts b/src/typeclass/Bounded.ts index 6d59f747f..c188e2e61 100644 --- a/src/typeclass/Bounded.ts +++ b/src/typeclass/Bounded.ts @@ -2,8 +2,11 @@ * @since 1.0.0 */ import type { TypeLambda } from "@fp-ts/core/HKT" +import type { Monoid } from "@fp-ts/core/typeclass/Monoid" +import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as order from "@fp-ts/core/typeclass/Order" import type { Order } from "@fp-ts/core/typeclass/Order" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" /** * @category type class @@ -22,6 +25,24 @@ export interface BoundedTypeLambda extends TypeLambda { readonly type: Bounded } +/** + * `Monoid` that returns last minimum of elements. + * + * @category constructors + * @since 1.0.0 + */ +export const min = (B: Bounded): Monoid => + monoid.fromSemigroup(semigroup.min(B), B.maxBound) + +/** + * `Monoid` that returns last maximum of elements. + * + * @category constructors + * @since 1.0.0 + */ +export const max = (B: Bounded): Monoid => + monoid.fromSemigroup(semigroup.max(B), B.minBound) + /** * @category instances * @since 1.0.0 diff --git a/test/Number.ts b/test/Number.ts index a3f6c334e..86890685f 100644 --- a/test/Number.ts +++ b/test/Number.ts @@ -1,7 +1,15 @@ +import { pipe } from "@fp-ts/core/Function" import * as Number from "@fp-ts/core/Number" import { deepStrictEqual } from "@fp-ts/core/test/util" describe.concurrent("Number", () => { + it("exports", () => { + expect(Number.SemigroupMax).exists + expect(Number.SemigroupMin).exists + expect(Number.MonoidMax).exists + expect(Number.MonoidMin).exists + }) + it("isNumber", () => { expect(Number.isNumber(1)).toEqual(true) expect(Number.isNumber("a")).toEqual(false) @@ -9,15 +17,19 @@ describe.concurrent("Number", () => { }) it("sum", () => { - deepStrictEqual(Number.sum(1)(2), 3) + deepStrictEqual(pipe(1, Number.sum(2)), 3) + }) + + it("multiply", () => { + deepStrictEqual(pipe(2, Number.multiply(3)), 6) }) - it("sub", () => { - deepStrictEqual(Number.sub(1)(2), 1) + it("subtract", () => { + deepStrictEqual(pipe(3, Number.subtract(1)), 2) }) - it("multiply", () => { - deepStrictEqual(Number.multiply(3)(2), 6) + it("divide", () => { + deepStrictEqual(pipe(6, Number.divide(2)), 3) }) it("increment", () => { From d1e84abbbf8c9a71abe8618e7caaff66a2faefea Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 11:00:41 +0100 Subject: [PATCH 70/80] Boolean: add all, any --- src/Boolean.ts | 10 ++++++++++ src/Predicate.ts | 24 +++++------------------- test/Boolean.ts | 4 +++- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Boolean.ts b/src/Boolean.ts index f3e6e4743..ec0241537 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -125,3 +125,13 @@ export const or = (that: boolean) => * @since 1.0.0 */ export const not = (self: boolean): boolean => !self + +/** + * @since 1.0.0 + */ +export const all = MonoidAll.combineAll + +/** + * @since 1.0.0 + */ +export const any = MonoidAny.combineAll diff --git a/src/Predicate.ts b/src/Predicate.ts index 2f55da2a7..42ae6c8ae 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -6,7 +6,7 @@ import type { TypeLambda } from "@fp-ts/core/HKT" import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as invariant from "@fp-ts/core/typeclass/Invariant" -import type * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import * as product_ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" @@ -268,15 +268,8 @@ export const getSemigroupAny = (): semigroup.Semigroup> => * @category instances * @since 1.0.0 */ -export const getMonoidAny = (): monoid.Monoid> => { - const S = getSemigroupAny() - return ({ - combine: S.combine, - combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(constFalse, collection), - empty: constFalse - }) -} +export const getMonoidAny = (): monoid.Monoid> => + monoid.fromSemigroup(getSemigroupAny(), constFalse) /** * @category instances @@ -289,15 +282,8 @@ export const getSemigroupAll = (): semigroup.Semigroup> => * @category instances * @since 1.0.0 */ -export const getMonoidAll = (): monoid.Monoid> => { - const S = getSemigroupAll() - return ({ - combine: S.combine, - combineMany: S.combineMany, - combineAll: (collection) => S.combineMany(constTrue, collection), - empty: constTrue - }) -} +export const getMonoidAll = (): monoid.Monoid> => + monoid.fromSemigroup(getSemigroupAll(), constTrue) /** * @since 1.0.0 diff --git a/test/Boolean.ts b/test/Boolean.ts index 39bd328cb..cf6cc8705 100644 --- a/test/Boolean.ts +++ b/test/Boolean.ts @@ -3,11 +3,13 @@ import { pipe } from "@fp-ts/core/Function" import { deepStrictEqual } from "@fp-ts/core/test/util" describe.concurrent("Boolean", () => { - it("instances and derived exports", () => { + it("exports", () => { expect(Boolean.SemigroupAll).exist expect(Boolean.MonoidAll).exist expect(Boolean.SemigroupAny).exist expect(Boolean.MonoidAny).exist + expect(Boolean.all).exist + expect(Boolean.any).exist }) it("isBoolean", () => { From 80daf05b05c136c55a2d87dc7c691284ab9d3d0a Mon Sep 17 00:00:00 2001 From: Michael Arnaldi Date: Sat, 21 Jan 2023 10:41:18 +0000 Subject: [PATCH 71/80] Add tracing code for Option --- .changeset/red-moons-hope.md | 5 +++++ src/internal/Option.ts | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 .changeset/red-moons-hope.md diff --git a/.changeset/red-moons-hope.md b/.changeset/red-moons-hope.md new file mode 100644 index 000000000..142e4bf1b --- /dev/null +++ b/.changeset/red-moons-hope.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Add tracking code for Option diff --git a/src/internal/Option.ts b/src/internal/Option.ts index c3a101ea6..d5d85202c 100644 --- a/src/internal/Option.ts +++ b/src/internal/Option.ts @@ -6,9 +6,11 @@ import type { None, Option, Some } from "@fp-ts/core/Option" /** @internal */ export const isOption = (u: unknown): u is Option => - typeof u === "object" && u != null && "_tag" in u && + typeof u === "object" && u != null && structural in u && "_tag" in u && (u["_tag"] === "None" || u["_tag"] === "Some") +const structural = Symbol.for("@effect/data/Equal/structural") + /** @internal */ export const isNone = (fa: Option): fa is None => fa._tag === "None" @@ -16,10 +18,19 @@ export const isNone = (fa: Option): fa is None => fa._tag === "None" export const isSome = (fa: Option): fa is Some => fa._tag === "Some" /** @internal */ -export const none: Option = { _tag: "None" } +export const none: Option = Object.defineProperty( + { _tag: "None" }, + structural, + { enumerable: false, value: true } +) /** @internal */ -export const some = (a: A): Option => ({ _tag: "Some", value: a }) +export const some = (a: A): Option => + Object.defineProperty( + { _tag: "Some", value: a }, + structural, + { enumerable: false, value: true } + ) /** @internal */ export const fromNullable = ( From cff00472327203eafdf07600bb1485ac827eed3f Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 12:28:14 +0100 Subject: [PATCH 72/80] fix dtslint --- dtslint/ts4.7/SemiAlternative.ts | 2 +- dtslint/ts4.7/tsconfig.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dtslint/ts4.7/SemiAlternative.ts b/dtslint/ts4.7/SemiAlternative.ts index e59f824d0..6d6024319 100644 --- a/dtslint/ts4.7/SemiAlternative.ts +++ b/dtslint/ts4.7/SemiAlternative.ts @@ -16,4 +16,4 @@ declare const fb: RAW<{ b: number }, number, "fb"> declare const SemiAlternative: _.SemiAlternative // $ExpectType RAW<{ a: string; } & { b: number; }, string | number, "fa" | "fb"> -pipe(fa, SemiAlternative.coproduct(fb)) +SemiAlternative.coproduct(fa, fb) diff --git a/dtslint/ts4.7/tsconfig.json b/dtslint/ts4.7/tsconfig.json index 0696bacfb..cc38df3bd 100644 --- a/dtslint/ts4.7/tsconfig.json +++ b/dtslint/ts4.7/tsconfig.json @@ -11,8 +11,8 @@ "noUnusedParameters": false, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", - "target": "es2015", - "lib": ["es2015"], + "target": "ES2021", + "lib": ["ES2021"], "paths": { "@fp-ts/core": ["../../src/index.ts"], "@fp-ts/core/test/*": ["../../test/*"], From 45c4507600a837530da139a74538c604855eda92 Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 12:39:06 +0100 Subject: [PATCH 73/80] Spread structural-tracking across all data types --- src/These.ts | 21 ++++++++++----------- src/internal/Either.ts | 9 ++++++--- src/internal/Option.ts | 18 ++++++------------ src/internal/effect.ts | 6 ++++++ 4 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 src/internal/effect.ts diff --git a/src/These.ts b/src/These.ts index f7569d8b0..678d747da 100644 --- a/src/These.ts +++ b/src/These.ts @@ -19,6 +19,7 @@ import type { Either, Left, Right } from "@fp-ts/core/Either" import type { LazyArg } from "@fp-ts/core/Function" import { constNull, constUndefined, pipe } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import { structural } from "@fp-ts/core/internal/effect" import * as either from "@fp-ts/core/internal/Either" import * as option from "@fp-ts/core/internal/Option" import type { Option } from "@fp-ts/core/Option" @@ -87,13 +88,13 @@ export interface ValidatedTypeLambda extends TypeLambda { * @category constructors * @since 1.0.0 */ -export const left = (left: E): These => ({ _tag: "Left", left }) +export const left: (left: E) => These = either.left /** * @category constructors * @since 1.0.0 */ -export const right = (right: A): These => ({ _tag: "Right", right }) +export const right: (right: A) => These = either.right /** * Alias of `right`. @@ -107,11 +108,11 @@ export const of = right * @category constructors * @since 1.0.0 */ -export const both = (left: E, right: A): These => ({ - _tag: "Both", - left, - right -}) +export const both = (left: E, right: A): These => + Object.defineProperty({ _tag: "Both", left, right }, structural, { + enumerable: false, + value: true + }) /** * @category constructors @@ -222,10 +223,8 @@ export const isBoth = (self: These): self is Both => self._tag * @since 1.0.0 */ export const isThese = (u: unknown): u is These => - typeof u === "object" && - u != null && "_tag" in u && - (u["_tag"] === "Left" || u["_tag"] === "Right" || - u["_tag"] === "Both") + typeof u === "object" && u != null && structural in u && "_tag" in u && + (u["_tag"] === "Left" || u["_tag"] === "Right" || u["_tag"] === "Both") /** * Constructs a new `These` from a function that might throw. diff --git a/src/internal/Either.ts b/src/internal/Either.ts index fbc5bce28..61d472503 100644 --- a/src/internal/Either.ts +++ b/src/internal/Either.ts @@ -4,12 +4,13 @@ import type { Either, Left, Right } from "@fp-ts/core/Either" import type { LazyArg } from "@fp-ts/core/Function" +import { structural } from "@fp-ts/core/internal/effect" import * as option from "@fp-ts/core/internal/Option" import type { Option } from "@fp-ts/core/Option" /** @internal */ export const isEither = (u: unknown): u is Either => - typeof u === "object" && u != null && "_tag" in u && + typeof u === "object" && u != null && structural in u && "_tag" in u && (u["_tag"] === "Left" || u["_tag"] === "Right") /** @internal */ @@ -19,10 +20,12 @@ export const isLeft = (ma: Either): ma is Left => ma._tag === "Le export const isRight = (ma: Either): ma is Right => ma._tag === "Right" /** @internal */ -export const left = (e: E): Either => ({ _tag: "Left", left: e }) +export const left = (e: E): Either => + Object.defineProperty({ _tag: "Left", left: e }, structural, { enumerable: false, value: true }) /** @internal */ -export const right = (a: A): Either => ({ _tag: "Right", right: a }) +export const right = (a: A): Either => + Object.defineProperty({ _tag: "Right", right: a }, structural, { enumerable: false, value: true }) /** @internal */ export const getLeft = ( diff --git a/src/internal/Option.ts b/src/internal/Option.ts index d5d85202c..433decce3 100644 --- a/src/internal/Option.ts +++ b/src/internal/Option.ts @@ -2,6 +2,7 @@ * @since 1.0.0 */ +import { structural } from "@fp-ts/core/internal/effect" import type { None, Option, Some } from "@fp-ts/core/Option" /** @internal */ @@ -9,8 +10,6 @@ export const isOption = (u: unknown): u is Option => typeof u === "object" && u != null && structural in u && "_tag" in u && (u["_tag"] === "None" || u["_tag"] === "Some") -const structural = Symbol.for("@effect/data/Equal/structural") - /** @internal */ export const isNone = (fa: Option): fa is None => fa._tag === "None" @@ -18,19 +17,14 @@ export const isNone = (fa: Option): fa is None => fa._tag === "None" export const isSome = (fa: Option): fa is Some => fa._tag === "Some" /** @internal */ -export const none: Option = Object.defineProperty( - { _tag: "None" }, - structural, - { enumerable: false, value: true } -) +export const none: Option = Object.defineProperty({ _tag: "None" }, structural, { + enumerable: false, + value: true +}) /** @internal */ export const some = (a: A): Option => - Object.defineProperty( - { _tag: "Some", value: a }, - structural, - { enumerable: false, value: true } - ) + Object.defineProperty({ _tag: "Some", value: a }, structural, { enumerable: false, value: true }) /** @internal */ export const fromNullable = ( diff --git a/src/internal/effect.ts b/src/internal/effect.ts new file mode 100644 index 000000000..0e6da6670 --- /dev/null +++ b/src/internal/effect.ts @@ -0,0 +1,6 @@ +/** + * @since 1.0.0 + */ + +/** @internal */ +export const structural = Symbol.for("@effect/data/Equal/structural") From b497f28487ffd4aad2cb2a3d8ed31c1cdd94b37d Mon Sep 17 00:00:00 2001 From: gcanti Date: Sat, 21 Jan 2023 16:50:39 +0100 Subject: [PATCH 74/80] Bigint: add divide --- src/Bigint.ts | 7 ++++++- test/Bigint.ts | 15 ++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Bigint.ts b/src/Bigint.ts index 6248af689..c6b878dd8 100644 --- a/src/Bigint.ts +++ b/src/Bigint.ts @@ -33,7 +33,12 @@ export const multiply = (that: bigint) => /** * @since 1.0.0 */ -export const sub = (that: bigint) => (self: bigint): bigint => self - that +export const subtract = (that: bigint) => (self: bigint): bigint => self - that + +/** + * @since 1.0.0 + */ +export const divide = (that: bigint) => (self: bigint): bigint => self / that /** * @since 1.0.0 diff --git a/test/Bigint.ts b/test/Bigint.ts index 94468621e..4f0661ffd 100644 --- a/test/Bigint.ts +++ b/test/Bigint.ts @@ -1,4 +1,5 @@ import * as Bigint from "@fp-ts/core/Bigint" +import { pipe } from "@fp-ts/core/Function" import { deepStrictEqual } from "@fp-ts/core/test/util" describe.concurrent("Bigint", () => { @@ -10,15 +11,19 @@ describe.concurrent("Bigint", () => { }) it("sum", () => { - deepStrictEqual(Bigint.sum(1n)(2n), 3n) + deepStrictEqual(pipe(1n, Bigint.sum(2n)), 3n) }) - it("sub", () => { - deepStrictEqual(Bigint.sub(1n)(2n), 1n) + it("multiply", () => { + deepStrictEqual(pipe(2n, Bigint.multiply(3n)), 6n) }) - it("multiply", () => { - deepStrictEqual(Bigint.multiply(3n)(2n), 6n) + it("subtract", () => { + deepStrictEqual(pipe(3n, Bigint.subtract(1n)), 2n) + }) + + it("divide", () => { + deepStrictEqual(pipe(6n, Bigint.divide(2n)), 3n) }) it("increment", () => { From 9c84d793354e73f9dd4ff9c397e96a5c0110329e Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 06:16:28 +0100 Subject: [PATCH 75/80] chore --- Either.md | 10 +++++----- Option.md | 10 +++++----- README.md | 4 +++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Either.md b/Either.md index 1b1bf3953..26c6ea543 100644 --- a/Either.md +++ b/Either.md @@ -7,7 +7,7 @@ The `Left` variant is used to represent a failure, and it can contain useful inf Unlike the `Option` type, `Either` allows you to attach additional information to the failure case, making it more informative. In this usage, `None` is replaced with a `Left` which can contain useful information. `Right` takes the place of `Some`. -## Definition +# Definition The `Either` data type is the union of two members: `Left` and `Right`. The way chosen by the `@fp-ts/core` library to model this union in TypeScript is to use a feature of the language called [Discriminating Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions): @@ -29,7 +29,7 @@ export type Right = { export type Either = Left | Right; ``` -## Using `Either` +# Using `Either` To create an instance of `Either`, you can use the `right` and `left` constructors, which construct a new `Either` holding a `Right` or `Left` value respectively. @@ -59,7 +59,7 @@ const failure: Either = pipe( The `fromOption` function requires an argument because it needs to know what value to use for the `Left` variant of the `Either` type when given a `None`. In the example, the argument "error message" is used as the value for the `Left` variant when `None` is encountered. This allows `Either` to provide more information about why a failure occurred. -## Working with `Either` +# Working with `Either` Once you have an instance of `Either`, you can use the various functions provided in the `@fp-ts/core/Either` module to work with it. @@ -80,7 +80,7 @@ const failure: Either = pipe( ); // left("error message!") ``` -## Handling failing computations +# Handling failing computations Let's see how to use the `Either` data type to model a computation that can fail, such as a function that can throw an exception based on certain conditions. Let's take the case of the following function: @@ -113,7 +113,7 @@ console.log(parseNumber("2")); // right(2) console.log(parseNumber("Not a number")); // left("Cannot parse 'Not a number' as a number") ``` -## Pattern matching +# Pattern matching The `match` function allows us to match on the `Left` and `Right` cases of an `Either` value and provide different actions for each. diff --git a/Option.md b/Option.md index 02086f9cf..e03e6303c 100644 --- a/Option.md +++ b/Option.md @@ -11,7 +11,7 @@ In the first of these two interpretations, the `None` union member represents th In the second of these two interpretations, the `None` union member represents the absence of the value, while the `Some` union member represents the presence of the value of type `A` -## Definition +# Definition The `Option` data type is the union of two members: `None` and `Some`. The way chosen by the `@fp-ts/core` library to model this union in TypeScript is to use a feature of the language called [Discriminating Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions): @@ -32,7 +32,7 @@ export type Some = { export type Option = None | Some; ``` -## Using `Option` +# Using `Option` To create an instance of `Option`, you can use the `some` and `none` constructors, which construct a new `Option` holding a `Some` or `None` value respectively. @@ -43,7 +43,7 @@ const success: Option = some(1); const failure: Option = none(); ``` -## Working with `Option` +# Working with `Option` Once you have an instance of `Option`, you can use the various functions provided in the `@fp-ts/core/Option` module to work with it. @@ -59,7 +59,7 @@ const success: Option = pipe( ); // some(2) ``` -## Handling failing computations +# Handling failing computations Let's see how to use the `Option` data type to model a computation that can fail, such as a function that can throw an exception based on certain conditions. Let's take the case of the following function: @@ -92,7 +92,7 @@ console.log(parseNumber("2")); // some(2) console.log(parseNumber("Not a number")); // none() ``` -## Pattern matching +# Pattern matching The `match` function allows us to match on the `None` and `Some` cases of an `Option` value and provide different actions for each. diff --git a/README.md b/README.md index 3de06454e..0a1ed1e53 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ For those using [`fp-ts`](https://github.com/gcanti/fp-ts) v2 and its ecosystem, - [`io-ts`](https://github.com/gcanti/io-ts) -> [`@fp-ts/schema`](https://github.com/fp-ts/schema) - [`monocle-ts`](https://github.com/gcanti/monocle-ts) -> [`@fp-ts/optic`](https://github.com/fp-ts/optic) -Note that `@fp-ts/core` will not contain any effect system (e.g. `Task`, `TaskEither`, `ReaderTaskEither`) since the handling of effects is entirely delegated to the packages contained in [`@effect/*`](https://github.com/Effect-TS)." +Note that `@fp-ts/core` will not contain any effect system (e.g. `Task`, `TaskEither`, `ReaderTaskEither`) since the handling of effects is entirely delegated to the packages contained in [`@effect/*`](https://github.com/Effect-TS). # Installation @@ -48,6 +48,8 @@ npm install @fp-ts/core - [Typeclass overview](./typeclass.md) - [Data overview](./data.md) +- [The `Option` data type](./Option.md) +- [The `Either` data type](./Either.md) - [API Reference](https://fp-ts.github.io/core/) # License From 02ea72af63e53e0e673584dcccbbbaf1818878e7 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 14:21:04 +0100 Subject: [PATCH 76/80] Predicate: optimise getSemigroupAny, getSemigroupAll --- src/Predicate.ts | 36 +++++++++++++++++++++++++++++++----- test/Predicate.ts | 24 ++++++++++++++++-------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/Predicate.ts b/src/Predicate.ts index 42ae6c8ae..ee5cb5287 100644 --- a/src/Predicate.ts +++ b/src/Predicate.ts @@ -9,7 +9,7 @@ import * as invariant from "@fp-ts/core/typeclass/Invariant" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import * as product_ from "@fp-ts/core/typeclass/Product" -import * as semigroup from "@fp-ts/core/typeclass/Semigroup" +import type * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" /** @@ -261,8 +261,21 @@ export const and = (that: Predicate) => * @category instances * @since 1.0.0 */ -export const getSemigroupAny = (): semigroup.Semigroup> => - semigroup.fromCombine((self, that) => pipe(self, or(that))) +export const getSemigroupAny = (): semigroup.Semigroup> => ({ + combine: (self, that) => pipe(self, or(that)), + combineMany: (self, collection) => + a => { + if (self(a)) { + return true + } + for (const p of collection) { + if (p(a)) { + return true + } + } + return false + } +}) /** * @category instances @@ -275,8 +288,21 @@ export const getMonoidAny = (): monoid.Monoid> => * @category instances * @since 1.0.0 */ -export const getSemigroupAll = (): semigroup.Semigroup> => - semigroup.fromCombine((self, that) => pipe(self, and(that))) +export const getSemigroupAll = (): semigroup.Semigroup> => ({ + combine: (self, that) => pipe(self, and(that)), + combineMany: (self, collection) => + a => { + if (!self(a)) { + return false + } + for (const p of collection) { + if (!p(a)) { + return false + } + } + return true + } +}) /** * @category instances diff --git a/test/Predicate.ts b/test/Predicate.ts index 5825f3879..39c2325d7 100644 --- a/test/Predicate.ts +++ b/test/Predicate.ts @@ -115,10 +115,14 @@ describe.concurrent("Predicate", () => { it("getSemigroupAny", () => { const S = _.getSemigroupAny() - const predicate = S.combine(isPositive, isNegative) - deepStrictEqual(predicate(0), false) - deepStrictEqual(predicate(-1), true) - deepStrictEqual(predicate(1), true) + const p1 = S.combine(isPositive, isNegative) + deepStrictEqual(p1(0), false) + deepStrictEqual(p1(-1), true) + deepStrictEqual(p1(1), true) + const p2 = S.combineMany(isPositive, [isNegative]) + deepStrictEqual(p2(0), false) + deepStrictEqual(p2(-1), true) + deepStrictEqual(p2(1), true) }) it("getMonoidAny", () => { @@ -131,10 +135,14 @@ describe.concurrent("Predicate", () => { it("getSemigroupAll", () => { const S = _.getSemigroupAll() - const predicate = S.combine(isPositive, isLessThan2) - deepStrictEqual(predicate(0), false) - deepStrictEqual(predicate(-2), false) - deepStrictEqual(predicate(1), true) + const p1 = S.combine(isPositive, isLessThan2) + deepStrictEqual(p1(0), false) + deepStrictEqual(p1(-2), false) + deepStrictEqual(p1(1), true) + const p2 = S.combineMany(isPositive, [isLessThan2]) + deepStrictEqual(p2(0), false) + deepStrictEqual(p2(-2), false) + deepStrictEqual(p2(1), true) }) it("getMonoidAll", () => { From eaf2856e735743a48fa9f9f26c98ddc97bab2227 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 18:25:46 +0100 Subject: [PATCH 77/80] rename size to length --- src/Number.ts | 2 +- src/ReadonlyArray.ts | 2 +- src/String.ts | 14 ++++++++++---- test/Either.ts | 6 +++--- test/Function.ts | 18 +++++++++--------- test/ReadonlyArray.ts | 8 ++++---- test/String.ts | 14 +++++++++----- test/These.ts | 8 ++++---- 8 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/Number.ts b/src/Number.ts index c67370659..df58f654e 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -144,4 +144,4 @@ export const MonoidMin = bounded.min(Bounded) /** * @since 1.0.0 */ -export const sign = (n: number): Ordering => (n < 0 ? -1 : n > 0 ? 1 : 0) +export const sign = (n: number): Ordering => n < 0 ? -1 : n > 0 ? 1 : 0 diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts index 7a41e7939..3798937c7 100644 --- a/src/ReadonlyArray.ts +++ b/src/ReadonlyArray.ts @@ -270,7 +270,7 @@ export const isNonEmpty: (self: ReadonlyArray) => self is NonEmptyReadonly * @category getters * @since 1.0.0 */ -export const size = (self: ReadonlyArray): number => self.length +export const length = (self: ReadonlyArray): number => self.length const isOutOfBound = (i: number, as: ReadonlyArray): boolean => i < 0 || i >= as.length diff --git a/src/String.ts b/src/String.ts index 0a3a89b21..2c78d4c63 100644 --- a/src/String.ts +++ b/src/String.ts @@ -59,8 +59,7 @@ export const empty: "" = "" as const /** * @since 1.0.0 */ -export const concat = (that: string) => - (self: string): string => semigroup.string.combine(self, that) +export const concat = (that: string) => (self: string): string => Semigroup.combine(self, that) /** * @example @@ -154,6 +153,13 @@ export const slice = (start: number, end: number) => (s: string): string => s.sl */ export const isEmpty = (s: string): s is "" => s.length === 0 +/** + * Test whether a `string` is non empty. + * + * @since 1.0.0 + */ +export const isNonEmpty = (s: string): boolean => s.length > 0 + /** * Calculate the number of characters in a `string`. * @@ -161,11 +167,11 @@ export const isEmpty = (s: string): s is "" => s.length === 0 * import * as S from '@fp-ts/core/String' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe('abc', S.size), 3) + * assert.deepStrictEqual(pipe('abc', S.length), 3) * * @since 1.0.0 */ -export const size = (s: string): number => s.length +export const length = (s: string): number => s.length /** * @example diff --git a/test/Either.ts b/test/Either.ts index 8dd940be3..42a2de8b5 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -193,19 +193,19 @@ describe.concurrent("Either", () => { }) it("map", () => { - const f = _.map(String.size) + const f = _.map(String.length) deepStrictEqual(pipe(_.right("abc"), f), _.right(3)) deepStrictEqual(pipe(_.left("s"), f), _.left("s")) }) it("flatMap", () => { - const f = _.flatMap(flow(String.size, _.right)) + const f = _.flatMap(flow(String.length, _.right)) deepStrictEqual(pipe(_.right("abc"), f), _.right(3)) deepStrictEqual(pipe(_.left("maError"), f), _.left("maError")) }) it("bimap", () => { - const f = _.bimap(String.size, (n: number) => n > 2) + const f = _.bimap(String.length, (n: number) => n > 2) deepStrictEqual(pipe(_.right(1), f), _.right(false)) }) diff --git a/test/Function.ts b/test/Function.ts index 969d19b27..24a9a251a 100644 --- a/test/Function.ts +++ b/test/Function.ts @@ -1,37 +1,37 @@ import * as Function from "@fp-ts/core/Function" import * as Number from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" import { deepStrictEqual, double } from "@fp-ts/core/test/util" import * as assert from "assert" const f = (n: number): number => n + 1 const g = double -const size = (s: string): number => s.length describe.concurrent("Function", () => { it("getSemigroup", () => { const S = Function.getSemigroup(Number.SemigroupSum)() const f = (s: string) => s === "a" ? 0 : 1 - const g = S.combine(size, f) + const g = S.combine(String.length, f) deepStrictEqual(g(""), 1) deepStrictEqual(g("a"), 1) deepStrictEqual(g("b"), 2) - deepStrictEqual(S.combineMany(size, [size, size])("a"), 3) + deepStrictEqual(S.combineMany(String.length, [String.length, String.length])("a"), 3) }) it("getMonoid", () => { const M = Function.getMonoid(Number.MonoidSum)() const f = (s: string) => s === "a" ? 0 : 1 - const g = M.combine(size, f) + const g = M.combine(String.length, f) deepStrictEqual(g(""), 1) deepStrictEqual(g("a"), 1) deepStrictEqual(g("b"), 2) - deepStrictEqual(M.combine(size, M.empty)("a"), 1) - deepStrictEqual(M.combine(M.empty, size)("a"), 1) - deepStrictEqual(M.combineAll([size, size])("a"), 2) + deepStrictEqual(M.combine(String.length, M.empty)("a"), 1) + deepStrictEqual(M.combine(M.empty, String.length)("a"), 1) + deepStrictEqual(M.combineAll([String.length, String.length])("a"), 2) }) it("apply", () => { - deepStrictEqual(Function.apply("a")(size), 1) + deepStrictEqual(Function.apply("a")(String.length), 1) }) it("flip", () => { @@ -40,7 +40,7 @@ describe.concurrent("Function", () => { }) it("compose", () => { - deepStrictEqual(Function.pipe(size, Function.compose(double))("aaa"), 6) + deepStrictEqual(Function.pipe(String.length, Function.compose(double))("aaa"), 6) }) it("unsafeCoerce", () => { diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts index 366631809..bf717f723 100644 --- a/test/ReadonlyArray.ts +++ b/test/ReadonlyArray.ts @@ -1584,10 +1584,10 @@ describe.concurrent("ReadonlyArray", () => { deepStrictEqual(pipe([-1, -2, -3], RA.some(isPositive)), false) }) - it("size", () => { - deepStrictEqual(RA.size(RA.empty()), 0) - deepStrictEqual(RA.size([]), 0) - deepStrictEqual(RA.size(["a"]), 1) + it("length", () => { + deepStrictEqual(RA.length(RA.empty()), 0) + deepStrictEqual(RA.length([]), 0) + deepStrictEqual(RA.length(["a"]), 1) }) it("fromOption", () => { diff --git a/test/String.ts b/test/String.ts index db96267c7..0eb2bed73 100644 --- a/test/String.ts +++ b/test/String.ts @@ -44,15 +44,19 @@ describe.concurrent("String", () => { }) it("isEmpty", () => { - deepStrictEqual(String.isEmpty(String.empty), true) deepStrictEqual(String.isEmpty(""), true) deepStrictEqual(String.isEmpty("a"), false) }) - it("size", () => { - deepStrictEqual(String.size(String.empty), 0) - deepStrictEqual(String.size(""), 0) - deepStrictEqual(String.size("a"), 1) + it("isNonEmpty", () => { + deepStrictEqual(String.isNonEmpty(""), false) + deepStrictEqual(String.isNonEmpty("a"), true) + }) + + it("length", () => { + deepStrictEqual(String.length(""), 0) + deepStrictEqual(String.length("a"), 1) + deepStrictEqual(String.length("aaa"), 3) }) it("toUpperCase", () => { diff --git a/test/These.ts b/test/These.ts index 66bb5b609..1dc85baa4 100644 --- a/test/These.ts +++ b/test/These.ts @@ -91,14 +91,14 @@ describe("These", () => { }) it("bimap", () => { - const f = _.bimap(S.size, U.double) + const f = _.bimap(S.length, U.double) U.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) U.deepStrictEqual(pipe(_.right(2), f), _.right(4)) U.deepStrictEqual(pipe(_.both("eee", 1), f), _.both(3, 2)) }) it("mapLeft", () => { - const f = _.mapLeft(S.size) + const f = _.mapLeft(S.length) U.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) U.deepStrictEqual(pipe(_.right(2), f), _.right(2)) U.deepStrictEqual(pipe(_.both("eee", 1), f), _.both(3, 1)) @@ -411,8 +411,8 @@ describe("These", () => { }) it("match", () => { - const f = (s: string, n: number) => S.size(s) + U.double(n) - const match = _.match(S.size, U.double, f) + const f = (s: string, n: number) => S.length(s) + U.double(n) + const match = _.match(S.length, U.double, f) U.deepStrictEqual(match(_.left("foo")), 3) U.deepStrictEqual(match(_.right(1)), 2) U.deepStrictEqual(match(_.both("foo", 1)), 5) From ef2cbc40ccc378c22b6308bac2a8bb01148a5453 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 18:33:26 +0100 Subject: [PATCH 78/80] String: rename trimLeft, trimRight --- src/Number.ts | 11 +++++++++++ src/String.ts | 32 ++++++++++++++++++++++++++++---- src/Tuple.ts | 2 +- test/String.ts | 8 ++++---- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/Number.ts b/src/Number.ts index df58f654e..b1568fea2 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -145,3 +145,14 @@ export const MonoidMin = bounded.min(Bounded) * @since 1.0.0 */ export const sign = (n: number): Ordering => n < 0 ? -1 : n > 0 ? 1 : 0 + +/* + + Missing: + + - toFixed + - toPrecision + - toExponential + - toLocaleString + +*/ diff --git a/src/String.ts b/src/String.ts index 2c78d4c63..ce49115e1 100644 --- a/src/String.ts +++ b/src/String.ts @@ -111,22 +111,22 @@ export const trim = (s: string): string => s.trim() * import * as S from '@fp-ts/core/String' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(' a ', S.trimLeft), 'a ') + * assert.deepStrictEqual(pipe(' a ', S.trimStart), 'a ') * * @since 1.0.0 */ -export const trimLeft = (s: string): string => s.trimLeft() +export const trimStart = (s: string): string => s.trimStart() /** * @example * import * as S from '@fp-ts/core/String' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(' a ', S.trimRight), ' a') + * assert.deepStrictEqual(pipe(' a ', S.trimEnd), ' a') * * @since 1.0.0 */ -export const trimRight = (s: string): string => s.trimRight() +export const trimEnd = (s: string): string => s.trimEnd() /** * @example @@ -257,6 +257,30 @@ export const takeLeft = (n: number) => (self: string): string => self.slice(0, M export const takeRight = (n: number) => (s: string): string => s.slice(Math.max(0, s.length - Math.floor(n)), Infinity) +/* + + Missing: + + - charCodeAt + - substring + - at + - charAt + - codePointAt + - indexOf + - lastIndexOf + - localeCompare + - match + - matchAll + - normalize + - padEnd + - padStart + - repeat + - replaceAll + - search + - toLocaleLowerCase + - toLocaleUpperCase +*/ + // TODO: 100% coverage tests (ask Max) // const CR = 0x0d // const LF = 0x0a diff --git a/src/Tuple.ts b/src/Tuple.ts index 6b072345c..d2d20992b 100644 --- a/src/Tuple.ts +++ b/src/Tuple.ts @@ -18,7 +18,7 @@ import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" export const tuple = >(...elements: A): A => elements /** - * Adds an element to the end of a tuple. + * Appends an element to the end of a tuple. * * @since 1.0.0 */ diff --git a/test/String.ts b/test/String.ts index 0eb2bed73..76c514f5a 100644 --- a/test/String.ts +++ b/test/String.ts @@ -80,12 +80,12 @@ describe.concurrent("String", () => { deepStrictEqual(pipe(" a ", String.trim), "a") }) - it("trimLeft", () => { - deepStrictEqual(pipe(" a ", String.trimLeft), "a ") + it("trimStart", () => { + deepStrictEqual(pipe(" a ", String.trimStart), "a ") }) - it("trimRight", () => { - deepStrictEqual(pipe(" a ", String.trimRight), " a") + it("trimEnd", () => { + deepStrictEqual(pipe(" a ", String.trimEnd), " a") }) it("includes", () => { From 3c7465626c10edbe5c21c69aa83a470c7655cd67 Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 19:05:41 +0100 Subject: [PATCH 79/80] downgrade docs-ts --- docs-ts.json | 12 +----------- package.json | 2 +- pnpm-lock.yaml | 9 +++++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/docs-ts.json b/docs-ts.json index 3530339f7..ba45ceaea 100644 --- a/docs-ts.json +++ b/docs-ts.json @@ -1,14 +1,4 @@ { "exclude": ["src/internal/**/*.ts"], - "theme": "mikearnaldi/just-the-docs", - "compilerOptions": { - "noEmit": true, - "strict": true, - "paths": { - "@fp-ts/core": ["./src/index.ts"], - "@fp-ts/core/test/*": ["./test/*"], - "@fp-ts/core/examples/*": ["./examples/*"], - "@fp-ts/core/*": ["./src/*"] - } - } + "theme": "mikearnaldi/just-the-docs" } diff --git a/package.json b/package.json index c8f97183b..f4bbebe05 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c8": "^7.11.3", "concurrently": "^7.6.0", "cpx": "^1.5.0", - "docs-ts": "^0.7.0", + "docs-ts": "0.6.10", "dtslint": "github:gcanti/dtslint", "eslint": "^8.28.0", "eslint-import-resolver-typescript": "^3.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b1ddf22e..25ec441ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,7 +33,7 @@ importers: c8: ^7.11.3 concurrently: ^7.6.0 cpx: ^1.5.0 - docs-ts: ^0.7.0 + docs-ts: 0.6.10 dtslint: github:gcanti/dtslint eslint: ^8.28.0 eslint-import-resolver-typescript: ^3.5.2 @@ -76,7 +76,7 @@ importers: c8: 7.12.0 concurrently: 7.6.0 cpx: 1.5.0 - docs-ts: 0.7.0_brpckf4sz23pco3jyty2eys3iq + docs-ts: 0.6.10_pulkjstupuiumlrt7ngjhrpf2e_brpckf4sz23pco3jyty2eys3iq dtslint: github.com/gcanti/dtslint/4552d162099399c4e14f8486ced673411e5b3659 eslint: 8.28.0 eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee @@ -2381,8 +2381,8 @@ packages: dev: true patched: true - /docs-ts/0.7.0_brpckf4sz23pco3jyty2eys3iq: - resolution: {integrity: sha512-S9UM7Ddh0l3qwftSJiko+3+aGJrnGTnigKq4wpsOvQBmMJz7kMmdAeiTwNCRgzQI70oT3T2uyuzR9hy5JT2xPQ==} + /docs-ts/0.6.10_pulkjstupuiumlrt7ngjhrpf2e_brpckf4sz23pco3jyty2eys3iq: + resolution: {integrity: sha512-DTX9c5AJ92ojMOKqqvwF8t77C8Gdgs9FPB8seymHs+F+Wl6aapc3ZkHUM+p8o+jwcBmPoihxssdK903dfwQ1JQ==} hasBin: true peerDependencies: prettier: ^2.0.0 @@ -2402,6 +2402,7 @@ packages: ts-node: 8.10.2_typescript@4.9.3 typescript: 4.9.3 dev: true + patched: true /doctrine/2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} From 3e87daf0c262c046d4bba77533126907cd149c5d Mon Sep 17 00:00:00 2001 From: gcanti Date: Mon, 23 Jan 2023 19:19:22 +0100 Subject: [PATCH 80/80] update docs --- docs/modules/Bigint.ts.md | 184 ++ docs/modules/Boolean.ts.md | 236 ++ docs/modules/Either.ts.md | 1420 ++++++++++ docs/modules/Function.ts.md | 701 +++++ docs/modules/HKT.ts.md | 2 +- docs/modules/Identity.ts.md | 702 +++++ docs/modules/Number.ts.md | 268 ++ docs/modules/Option.ts.md | 1520 ++++++++++ docs/modules/Ordering.ts.md | 87 + docs/modules/Predicate.ts.md | 443 +++ docs/modules/ReadonlyArray.ts.md | 2438 +++++++++++++++++ docs/modules/ReadonlyRecord.ts.md | 78 + docs/modules/String.ts.md | 434 +++ docs/modules/Struct.ts.md | 117 + docs/modules/Symbol.ts.md | 30 + docs/modules/These.ts.md | 1517 ++++++++++ docs/modules/Tuple.ts.md | 116 + docs/modules/index.ts.md | 275 +- docs/modules/typeclass/Alternative.ts.md | 2 +- docs/modules/typeclass/Applicative.ts.md | 6 +- docs/modules/typeclass/Bicovariant.ts.md | 21 +- docs/modules/typeclass/Bounded.ts.md | 45 +- docs/modules/typeclass/Chainable.ts.md | 22 +- docs/modules/typeclass/Compactable.ts.md | 64 + docs/modules/typeclass/Contravariant.ts.md | 18 +- docs/modules/typeclass/Coproduct.ts.md | 4 +- docs/modules/typeclass/Covariant.ts.md | 37 +- docs/modules/typeclass/Equivalence.ts.md | 268 ++ docs/modules/typeclass/Filterable.ts.md | 98 + docs/modules/typeclass/FlatMap.ts.md | 20 +- docs/modules/typeclass/Foldable.ts.md | 49 +- docs/modules/typeclass/Invariant.ts.md | 21 +- docs/modules/typeclass/Monad.ts.md | 2 +- docs/modules/typeclass/Monoid.ts.md | 188 +- .../typeclass/NonEmptyTraversable.ts.md | 112 - docs/modules/typeclass/Of.ts.md | 11 +- docs/modules/typeclass/Order.ts.md | 128 +- docs/modules/typeclass/Pointed.ts.md | 2 +- docs/modules/typeclass/Product.ts.md | 28 +- docs/modules/typeclass/SemiAlternative.ts.md | 2 +- docs/modules/typeclass/SemiApplicative.ts.md | 43 +- docs/modules/typeclass/SemiCoproduct.ts.md | 14 +- docs/modules/typeclass/SemiProduct.ts.md | 102 +- docs/modules/typeclass/Semigroup.ts.md | 206 +- docs/modules/typeclass/Traversable.ts.md | 44 +- .../typeclass/TraversableFilterable.ts.md | 107 + src/Boolean.ts | 10 +- src/Either.ts | 4 +- src/Function.ts | 16 +- src/Number.ts | 4 +- src/Option.ts | 58 +- 51 files changed, 11732 insertions(+), 592 deletions(-) create mode 100644 docs/modules/Bigint.ts.md create mode 100644 docs/modules/Boolean.ts.md create mode 100644 docs/modules/Either.ts.md create mode 100644 docs/modules/Function.ts.md create mode 100644 docs/modules/Identity.ts.md create mode 100644 docs/modules/Number.ts.md create mode 100644 docs/modules/Option.ts.md create mode 100644 docs/modules/Ordering.ts.md create mode 100644 docs/modules/Predicate.ts.md create mode 100644 docs/modules/ReadonlyArray.ts.md create mode 100644 docs/modules/ReadonlyRecord.ts.md create mode 100644 docs/modules/String.ts.md create mode 100644 docs/modules/Struct.ts.md create mode 100644 docs/modules/Symbol.ts.md create mode 100644 docs/modules/These.ts.md create mode 100644 docs/modules/Tuple.ts.md create mode 100644 docs/modules/typeclass/Compactable.ts.md create mode 100644 docs/modules/typeclass/Equivalence.ts.md create mode 100644 docs/modules/typeclass/Filterable.ts.md delete mode 100644 docs/modules/typeclass/NonEmptyTraversable.ts.md create mode 100644 docs/modules/typeclass/TraversableFilterable.ts.md diff --git a/docs/modules/Bigint.ts.md b/docs/modules/Bigint.ts.md new file mode 100644 index 000000000..ccb2493f7 --- /dev/null +++ b/docs/modules/Bigint.ts.md @@ -0,0 +1,184 @@ +--- +title: Bigint.ts +nav_order: 1 +parent: Modules +--- + +## Bigint overview + +This module provides utility functions and type class instances for working with the `bigint` type in TypeScript. +It includes functions for basic arithmetic operations, as well as type class instances for +`Equivalence`, `Order`, `Semigroup`, and `Monoid`. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isBigint](#isbigint) +- [instances](#instances) + - [Equivalence](#equivalence) + - [MonoidMultiply](#monoidmultiply) + - [MonoidSum](#monoidsum) + - [Order](#order) + - [SemigroupMultiply](#semigroupmultiply) + - [SemigroupSum](#semigroupsum) +- [utils](#utils) + - [decrement](#decrement) + - [divide](#divide) + - [increment](#increment) + - [multiply](#multiply) + - [subtract](#subtract) + - [sum](#sum) + +--- + +# guards + +## isBigint + +**Signature** + +```ts +export declare const isBigint: (u: unknown) => u is bigint +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: any +``` + +Added in v1.0.0 + +## MonoidMultiply + +`bigint` monoid under multiplication. + +The `empty` value is `1n`. + +**Signature** + +```ts +export declare const MonoidMultiply: any +``` + +Added in v1.0.0 + +## MonoidSum + +`bigint` monoid under addition. + +The `empty` value is `0n`. + +**Signature** + +```ts +export declare const MonoidSum: any +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: any +``` + +Added in v1.0.0 + +## SemigroupMultiply + +`bigint` semigroup under multiplication. + +**Signature** + +```ts +export declare const SemigroupMultiply: any +``` + +Added in v1.0.0 + +## SemigroupSum + +`bigint` semigroup under addition. + +**Signature** + +```ts +export declare const SemigroupSum: any +``` + +Added in v1.0.0 + +# utils + +## decrement + +**Signature** + +```ts +export declare const decrement: (n: bigint) => bigint +``` + +Added in v1.0.0 + +## divide + +**Signature** + +```ts +export declare const divide: (that: bigint) => (self: bigint) => bigint +``` + +Added in v1.0.0 + +## increment + +**Signature** + +```ts +export declare const increment: (n: bigint) => bigint +``` + +Added in v1.0.0 + +## multiply + +**Signature** + +```ts +export declare const multiply: (that: bigint) => (self: bigint) => bigint +``` + +Added in v1.0.0 + +## subtract + +**Signature** + +```ts +export declare const subtract: (that: bigint) => (self: bigint) => bigint +``` + +Added in v1.0.0 + +## sum + +**Signature** + +```ts +export declare const sum: (that: bigint) => (self: bigint) => bigint +``` + +Added in v1.0.0 diff --git a/docs/modules/Boolean.ts.md b/docs/modules/Boolean.ts.md new file mode 100644 index 000000000..e96a52d2c --- /dev/null +++ b/docs/modules/Boolean.ts.md @@ -0,0 +1,236 @@ +--- +title: Boolean.ts +nav_order: 2 +parent: Modules +--- + +## Boolean overview + +This module provides utility functions and type class instances for working with the `boolean` type in TypeScript. +It includes functions for basic boolean operations, as well as type class instances for +`Equivalence`, `Order`, `Semigroup`, and `Monoid`. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combinators](#combinators) + - [and](#and) + - [not](#not) + - [or](#or) +- [guards](#guards) + - [isBoolean](#isboolean) +- [instances](#instances) + - [Equivalence](#equivalence) + - [MonoidAll](#monoidall) + - [MonoidAny](#monoidany) + - [Order](#order) + - [SemigroupAll](#semigroupall) + - [SemigroupAny](#semigroupany) +- [pattern matching](#pattern-matching) + - [match](#match) +- [utils](#utils) + - [all](#all) + - [any](#any) + +--- + +# combinators + +## and + +**Signature** + +```ts +export declare const and: (that: boolean) => (self: boolean) => boolean +``` + +Added in v1.0.0 + +## not + +**Signature** + +```ts +export declare const not: (self: boolean) => boolean +``` + +Added in v1.0.0 + +## or + +**Signature** + +```ts +export declare const or: (that: boolean) => (self: boolean) => boolean +``` + +Added in v1.0.0 + +# guards + +## isBoolean + +**Signature** + +```ts +export declare const isBoolean: any +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: any +``` + +Added in v1.0.0 + +## MonoidAll + +`boolean` monoid under conjunction. + +The `empty` value is `true`. + +**Signature** + +```ts +export declare const MonoidAll: any +``` + +Added in v1.0.0 + +## MonoidAny + +`boolean` monoid under disjunction. + +The `empty` value is `false`. + +**Signature** + +```ts +export declare const MonoidAny: any +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: any +``` + +Added in v1.0.0 + +## SemigroupAll + +`boolean` semigroup under conjunction. + +**Signature** + +```ts +export declare const SemigroupAll: any +``` + +**Example** + +```ts +import { SemigroupAll } from '@fp-ts/core/Boolean' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(SemigroupAll.combine(true, true), true) +assert.deepStrictEqual(SemigroupAll.combine(true, false), false) +``` + +Added in v1.0.0 + +## SemigroupAny + +`boolean` semigroup under disjunction. + +**Signature** + +```ts +export declare const SemigroupAny: any +``` + +**Example** + +```ts +import { SemigroupAny } from '@fp-ts/core/Boolean' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(SemigroupAny.combine(true, true), true) +assert.deepStrictEqual(SemigroupAny.combine(true, false), true) +assert.deepStrictEqual(SemigroupAny.combine(false, false), false) +``` + +Added in v1.0.0 + +# pattern matching + +## match + +Defines the match over a boolean value. +Takes two thunks `onTrue`, `onFalse` and a `boolean` value. +If `value` is `false`, `onFalse()` is returned, otherwise `onTrue()`. + +**Signature** + +```ts +export declare const match: (onFalse: any, onTrue: any) => (value: boolean) => A | B +``` + +**Example** + +```ts +import { some, map } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' +import { match } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual( + pipe( + some(true), + map( + match( + () => 'false', + () => 'true' + ) + ) + ), + some('true') +) +``` + +Added in v1.0.0 + +# utils + +## all + +**Signature** + +```ts +export declare const all: any +``` + +Added in v1.0.0 + +## any + +**Signature** + +```ts +export declare const any: any +``` + +Added in v1.0.0 diff --git a/docs/modules/Either.ts.md b/docs/modules/Either.ts.md new file mode 100644 index 000000000..59a192b5a --- /dev/null +++ b/docs/modules/Either.ts.md @@ -0,0 +1,1420 @@ +--- +title: Either.ts +nav_order: 3 +parent: Modules +--- + +## Either overview + +The `Either` data type is a powerful and flexible tool for handling potentially failed computations. +It has two variants, `Left` and `Right`, which can be used to represent different outcomes. + +The `Left` variant is used to represent a failure, and it can contain useful information such as an error message +or a failure code. The `Right` variant is used to represent a successful outcome, and it can contain the result +of the computation. + +Unlike `Option`, `Either` allows you to attach additional information to the failure case, making it more +informative than a simple `null` or `undefined`. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combining](#combining) + - [getFirstLeftMonoid](#getfirstleftmonoid) + - [getFirstLeftSemigroup](#getfirstleftsemigroup) + - [getFirstRightSemigroup](#getfirstrightsemigroup) +- [constructors](#constructors) + - [left](#left) + - [of](#of) + - [right](#right) +- [conversions](#conversions) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [fromOption](#fromoption) + - [toRefinement](#torefinement) +- [debugging](#debugging) + - [inspectLeft](#inspectleft) + - [inspectRight](#inspectright) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) +- [error handling](#error-handling) + - [catchAll](#catchall) + - [firstSuccessOf](#firstsuccessof) + - [mapLeft](#mapleft) + - [orElse](#orelse) + - [orElseEither](#orelseeither) + - [orElseFail](#orelsefail) + - [orElseSucceed](#orelsesucceed) + - [tapError](#taperror) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [filterMap](#filtermap) +- [getters](#getters) + - [getLeft](#getleft) + - [getOrElse](#getorelse) + - [getOrNull](#getornull) + - [getOrUndefined](#getorundefined) + - [getRight](#getright) + - [merge](#merge) +- [guards](#guards) + - [isEither](#iseither) + - [isLeft](#isleft) + - [isRight](#isright) +- [instances](#instances) + - [Applicative](#applicative) + - [Bicovariant](#bicovariant) + - [Chainable](#chainable) + - [Covariant](#covariant) + - [FlatMap](#flatmap) + - [Foldable](#foldable) + - [Invariant](#invariant) + - [Monad](#monad) + - [Of](#of) + - [Pointed](#pointed) + - [Product](#product) + - [SemiAlternative](#semialternative) + - [SemiApplicative](#semiapplicative) + - [SemiCoproduct](#semicoproduct) + - [SemiProduct](#semiproduct) + - [Traversable](#traversable) +- [interop](#interop) + - [fromThrowable](#fromthrowable) + - [getOrThrow](#getorthrow) + - [liftThrowable](#liftthrowable) +- [lifting](#lifting) + - [lift2](#lift2) + - [lift3](#lift3) + - [liftNullable](#liftnullable) + - [liftOption](#liftoption) + - [liftPredicate](#liftpredicate) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [bimap](#bimap) + - [flap](#flap) + - [imap](#imap) + - [map](#map) + - [tupled](#tupled) +- [models](#models) + - [Either (type alias)](#either-type-alias) + - [Left (type alias)](#left-type-alias) + - [Right (type alias)](#right-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) +- [sequencing](#sequencing) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) + - [flatMapNullable](#flatmapnullable) + - [flatMapOption](#flatmapoption) +- [traversing](#traversing) + - [sequence](#sequence) + - [traverse](#traverse) + - [traverseTap](#traversetap) +- [type lambdas](#type-lambdas) + - [EitherTypeLambda (interface)](#eithertypelambda-interface) +- [utils](#utils) + - [andThen](#andthen) + - [ap](#ap) + - [composeKleisliArrow](#composekleisliarrow) + - [contains](#contains) + - [element](#element) + - [exists](#exists) + - [flatten](#flatten) + - [reverse](#reverse) + - [struct](#struct) + - [tap](#tap) + - [tuple](#tuple) + - [unit](#unit) + +--- + +# combining + +## getFirstLeftMonoid + +Monoid returning the left-most `Left` value. If both operands are `Right`s then the inner values +are concatenated using the provided `Monoid`. + +The `empty` value is `right(M.empty)`. + +**Signature** + +```ts +export declare const getFirstLeftMonoid: (M: any) => any +``` + +Added in v1.0.0 + +## getFirstLeftSemigroup + +Semigroup returning the left-most `Left` value. If both operands are `Right`s then the inner values +are concatenated using the provided `Semigroup`. + +| x | y | x | > combine(y) | +| -------- | -------- | ------- | ------------- | +| left(a) | left(b) | left(a) | +| left(a) | right(2) | left(a) | +| right(1) | left(b) | left(b) | +| right(1) | right(2) | right(1 | > combine(2)) | + +**Signature** + +```ts +export declare const getFirstLeftSemigroup: (S: any) => any +``` + +Added in v1.0.0 + +## getFirstRightSemigroup + +Semigroup returning the left-most `Right` value. + +| x | y | x | > combine(y) | +| -------- | -------- | -------- | ------------ | +| left(a) | left(b) | left(b) | +| left(a) | right(2) | right(2) | +| right(1) | left(b) | right(1) | +| right(1) | right(2) | right(1) | + +**Signature** + +```ts +export declare const getFirstRightSemigroup: () => any +``` + +Added in v1.0.0 + +# constructors + +## left + +Constructs a new `Either` holding a `Left` value. This usually represents a failure, due to the right-bias of this +structure. + +**Signature** + +```ts +export declare const left: (e: E) => Either +``` + +Added in v1.0.0 + +## of + +Alias of `right`. + +**Signature** + +```ts +export declare const of:
(a: A) => Either +``` + +Added in v1.0.0 + +## right + +Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias +of this structure. + +**Signature** + +```ts +export declare const right: (a: A) => Either +``` + +Added in v1.0.0 + +# conversions + +## fromIterable + +**Signature** + +```ts +export declare const fromIterable: (onEmpty: any) => (collection: Iterable) => Either +``` + +Added in v1.0.0 + +## fromNullable + +Takes a lazy default and a nullable value, if the value is not nully, turn it into a `Right`, if the value is nully use +the provided default as a `Left`. + +**Signature** + +```ts +export declare const fromNullable: (onNullable: any) => (a: A) => Either> +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +const parse = E.fromNullable(() => 'nullable') + +assert.deepStrictEqual(parse(1), E.right(1)) +assert.deepStrictEqual(parse(null), E.left('nullable')) +``` + +Added in v1.0.0 + +## fromOption + +**Signature** + +```ts +export declare const fromOption: (onNone: any) => (self: any) => Either +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual( + pipe( + O.some(1), + E.fromOption(() => 'error') + ), + E.right(1) +) +assert.deepStrictEqual( + pipe( + O.none(), + E.fromOption(() => 'error') + ), + E.left('error') +) +``` + +Added in v1.0.0 + +## toRefinement + +Returns a `Refinement` from a `Either` returning function. +This function ensures that a `Refinement` definition is type-safe. + +**Signature** + +```ts +export declare const toRefinement: (f: (a: A) => Either) => any +``` + +Added in v1.0.0 + +# debugging + +## inspectLeft + +**Signature** + +```ts +export declare const inspectLeft: (onLeft: (e: E) => void) => (self: Either) => Either +``` + +Added in v1.0.0 + +## inspectRight + +**Signature** + +```ts +export declare const inspectRight: (onRight: (a: A) => void) => (self: Either) => Either +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: Either +``` + +Added in v1.0.0 + +## andThenBind + +A variant of `bind` that sequentially ignores the scope. + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: Either +) => (self: Either) => Either +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => Either +) => (self: Either) => Either +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (self: Either) => Either +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: ( + name: Exclude, + f: (a: A) => B +) => (self: Either) => Either +``` + +Added in v1.0.0 + +# error handling + +## catchAll + +Recovers from all errors. + +**Signature** + +```ts +export declare const catchAll: ( + onLeft: (e: E1) => Either +) => (self: Either) => Either +``` + +Added in v1.0.0 + +## firstSuccessOf + +**Signature** + +```ts +export declare const firstSuccessOf: (collection: Iterable>) => (self: Either) => Either +``` + +Added in v1.0.0 + +## mapLeft + +Returns an effect with its error channel mapped using the specified +function. This can be used to lift a "smaller" error into a "larger" error. + +**Signature** + +```ts +export declare const mapLeft: (f: (e: E) => G) => (self: Either) => Either +``` + +Added in v1.0.0 + +## orElse + +Executes this effect and returns its value, if it succeeds, but otherwise +executes the specified effect. + +| x | y | x | > orElse(y) | +| -------- | -------- | -------- | ----------- | +| left(a) | left(b) | left(b) | +| left(a) | right(2) | right(2) | +| right(1) | left(b) | right(1) | +| right(1) | right(2) | right(1) | + +**Signature** + +```ts +export declare const orElse: (that: Either) => (self: Either) => Either +``` + +Added in v1.0.0 + +## orElseEither + +Returns an effect that will produce the value of this effect, unless it +fails, in which case, it will produce the value of the specified effect. + +**Signature** + +```ts +export declare const orElseEither: ( + that: Either +) => (self: Either) => Either> +``` + +Added in v1.0.0 + +## orElseFail + +Executes this effect and returns its value, if it succeeds, but otherwise +fails with the specified error. + +**Signature** + +```ts +export declare const orElseFail: (onLeft: any) => (self: Either) => Either +``` + +Added in v1.0.0 + +## orElseSucceed + +Executes this effect and returns its value, if it succeeds, but otherwise +succeeds with the specified value. + +**Signature** + +```ts +export declare const orElseSucceed: (onLeft: any) => (self: Either) => Either +``` + +Added in v1.0.0 + +## tapError + +Returns an effect that effectfully "peeks" at the failure of this effect. + +**Signature** + +```ts +export declare const tapError: ( + onLeft: (e: E1) => Either +) => (self: Either) => Either +``` + +Added in v1.0.0 + +# filtering + +## compact + +**Signature** + +```ts +export declare const compact: (onNone: any) => (self: Either) => Either +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: any, onFalse: any): (self: Either) => Either + (predicate: any, onFalse: any): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: ( + f: (a: A) => any, + onNone: any +) => (self: Either) => Either +``` + +Added in v1.0.0 + +# getters + +## getLeft + +Converts a `Either` to an `Option` discarding the Right. + +**Signature** + +```ts +export declare const getLeft: (self: Either) => any +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(E.getLeft(E.right('ok')), O.none()) +assert.deepStrictEqual(E.getLeft(E.left('err')), O.some('err')) +``` + +Added in v1.0.0 + +## getOrElse + +Returns the wrapped value if it's a `Right` or a default value if is a `Left`. + +**Signature** + +```ts +export declare const getOrElse: (onLeft: any) => (self: Either) => B | A +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual( + pipe( + E.right(1), + E.getOrElse(() => 0) + ), + 1 +) +assert.deepStrictEqual( + pipe( + E.left('error'), + E.getOrElse(() => 0) + ), + 0 +) +``` + +Added in v1.0.0 + +## getOrNull + +**Signature** + +```ts +export declare const getOrNull: (self: Either) => A | null +``` + +Added in v1.0.0 + +## getOrUndefined + +**Signature** + +```ts +export declare const getOrUndefined: (self: Either) => A | undefined +``` + +Added in v1.0.0 + +## getRight + +Converts a `Either` to an `Option` discarding the error. + +**Signature** + +```ts +export declare const getRight: (self: Either) => any +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(E.getRight(E.right('ok')), O.some('ok')) +assert.deepStrictEqual(E.getRight(E.left('err')), O.none()) +``` + +Added in v1.0.0 + +## merge + +**Signature** + +```ts +export declare const merge: (self: Either) => E | A +``` + +Added in v1.0.0 + +# guards + +## isEither + +Returns `true` if the specified value is an instance of `Either`, `false` +otherwise. + +**Signature** + +```ts +export declare const isEither: (u: unknown) => u is Either +``` + +Added in v1.0.0 + +## isLeft + +Returns `true` if the either is an instance of `Left`, `false` otherwise. + +**Signature** + +```ts +export declare const isLeft: (self: Either) => self is Left +``` + +Added in v1.0.0 + +## isRight + +Returns `true` if the either is an instance of `Right`, `false` otherwise. + +**Signature** + +```ts +export declare const isRight: (self: Either) => self is Right +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: any +``` + +Added in v1.0.0 + +## Bicovariant + +**Signature** + +```ts +export declare const Bicovariant: any +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: any +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: any +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: any +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: any +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: any +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: any +``` + +Added in v1.0.0 + +# interop + +## fromThrowable + +Constructs a new `Either` from a function that might throw. + +**Signature** + +```ts +export declare const fromThrowable: (f: () => A, onThrow: (error: unknown) => E) => Either +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { identity } from '@fp-ts/core/Function' + +const unsafeHead = (as: ReadonlyArray): A => { + if (as.length > 0) { + return as[0] + } else { + throw new Error('empty array') + } +} + +const head = (as: ReadonlyArray): E.Either => E.fromThrowable(() => unsafeHead(as), identity) + +assert.deepStrictEqual(head([]), E.left(new Error('empty array'))) +assert.deepStrictEqual(head([1, 2, 3]), E.right(1)) +``` + +Added in v1.0.0 + +## getOrThrow + +**Signature** + +```ts +export declare const getOrThrow: (onLeft: (e: E) => unknown) => (self: Either) => A +``` + +Added in v1.0.0 + +## liftThrowable + +Lifts a function that may throw to one returning a `Either`. + +**Signature** + +```ts +export declare const liftThrowable: ( + f: (...a: A) => B, + onThrow: (error: unknown) => E +) => (...a: A) => Either +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +**Signature** + +```ts +export declare const lift2: ( + f: (a: A, b: B) => C +) => (fa: Either, fb: Either) => Either +``` + +Added in v1.0.0 + +## lift3 + +**Signature** + +```ts +export declare const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: Either, fb: Either, fc: Either) => Either +``` + +Added in v1.0.0 + +## liftNullable + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A) => Either> +``` + +Added in v1.0.0 + +## liftOption + +**Signature** + +```ts +export declare const liftOption: ( + f: (...a: A) => any, + onNone: (...a: A) => E +) => (...a: A) => Either +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: any, onFalse: any): (c: C) => Either + (predicate: any, onFalse: any): (b: B) => Either +} +``` + +**Example** + +```ts +import { liftPredicate, left, right } from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual( + pipe( + 1, + liftPredicate( + (n) => n > 0, + () => 'error' + ) + ), + right(1) +) +assert.deepStrictEqual( + pipe( + -1, + liftPredicate( + (n) => n > 0, + () => 'error' + ) + ), + left('error') +) +``` + +Added in v1.0.0 + +# mapping + +## as + +Maps the Right value of this effect to the specified constant value. + +**Signature** + +```ts +export declare const as: (b: B) => (self: Either) => Either +``` + +Added in v1.0.0 + +## asUnit + +Returns the effect Eithering from mapping the Right of this effect to unit. + +**Signature** + +```ts +export declare const asUnit: (self: Either) => Either +``` + +Added in v1.0.0 + +## bimap + +Returns an effect whose Left and Right channels have been mapped by +the specified pair of functions, `f` and `g`. + +**Signature** + +```ts +export declare const bimap: (f: (e: E) => G, g: (a: A) => B) => (self: Either) => Either +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: (a: A) => (self: Either B>) => Either +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (self: Either) => Either +``` + +Added in v1.0.0 + +## map + +Returns an effect whose Right is mapped by the specified `f` function. + +**Signature** + +```ts +export declare const map: (f: (a: A) => B) => (self: Either) => Either +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: Either) => Either +``` + +Added in v1.0.0 + +# models + +## Either (type alias) + +**Signature** + +```ts +export type Either = Left | Right +``` + +Added in v1.0.0 + +## Left (type alias) + +**Signature** + +```ts +export type Left = { + readonly _tag: 'Left' + readonly left: E +} +``` + +Added in v1.0.0 + +## Right (type alias) + +**Signature** + +```ts +export type Right = { + readonly _tag: 'Right' + readonly right: A +} +``` + +Added in v1.0.0 + +# pattern matching + +## match + +Takes two functions and an `Either` value, if the value is a `Left` the inner value is applied to the first function, +if the value is a `Right` the inner value is applied to the second function. + +**Signature** + +```ts +export declare const match: (onLeft: (e: E) => B, onRight: (a: A) => C) => (self: Either) => B | C +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +const onLeft = (errors: ReadonlyArray): string => `Errors: ${errors.join(', ')}` + +const onRight = (value: number): string => `Ok: ${value}` + +assert.strictEqual(pipe(E.right(1), E.match(onLeft, onRight)), 'Ok: 1') +assert.strictEqual(pipe(E.left(['error 1', 'error 2']), E.match(onLeft, onRight)), 'Errors: error 1, error 2') +``` + +Added in v1.0.0 + +# sequencing + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: (that: Either) => (self: Either) => Either +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: (f: (a: A) => Either) => (self: Either) => Either +``` + +Added in v1.0.0 + +## flatMapNullable + +**Signature** + +```ts +export declare const flatMapNullable: ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +) => (self: Either) => Either> +``` + +Added in v1.0.0 + +## flatMapOption + +**Signature** + +```ts +export declare const flatMapOption: ( + f: (a: A) => any, + onNone: (a: A) => E2 +) => (self: Either) => Either +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: (F: any) => (self: Either) => any +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: ( + F: any +) => (f: (a: A) => any) => (self: Either) => any +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: any +) => (f: (a: A) => any) => (self: Either) => any +``` + +Added in v1.0.0 + +# type lambdas + +## EitherTypeLambda (interface) + +**Signature** + +```ts +export interface EitherTypeLambda extends TypeLambda { + readonly type: Either +} +``` + +Added in v1.0.0 + +# utils + +## andThen + +**Signature** + +```ts +export declare const andThen: (that: Either) => (self: Either) => Either +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: (fa: Either) => (self: Either B>) => Either +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: ( + bfc: (b: B) => Either +) => (afb: (a: A) => Either) => (a: A) => Either +``` + +Added in v1.0.0 + +## contains + +Returns a function that checks if an `Either` contains a given value using a provided `equivalence` function. + +**Signature** + +```ts +export declare const contains: (equivalence: any) => (a: A) => (self: Either) => boolean +``` + +Added in v1.0.0 + +## element + +Adds an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: ( + that: Either +) => (self: Either) => Either +``` + +Added in v1.0.0 + +## exists + +Returns `false` if `Left` or returns the Either of the application of the given predicate to the `Right` value. + +**Signature** + +```ts +export declare const exists: (predicate: any) => (self: Either) => boolean +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +const f = E.exists((n: number) => n > 2) + +assert.strictEqual(f(E.left('a')), false) +assert.strictEqual(f(E.right(1)), false) +assert.strictEqual(f(E.right(3)), true) +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: Either>) => Either +``` + +Added in v1.0.0 + +## reverse + +**Signature** + +```ts +export declare const reverse: (self: Either) => Either +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >>( + r: R +) => Either< + [R[keyof R]] extends [Either] ? E : never, + { [K in keyof R]: [R[K]] extends [Either] ? A : never } +> +``` + +Added in v1.0.0 + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: (f: (a: A) => Either) => (self: Either) => Either +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: []>( + ...tuple: T +) => Either< + [T[number]] extends [Either] ? E : never, + { [I in keyof T]: [T[I]] extends [Either] ? A : never } +> +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: Either +``` + +Added in v1.0.0 diff --git a/docs/modules/Function.ts.md b/docs/modules/Function.ts.md new file mode 100644 index 000000000..862ec1b2e --- /dev/null +++ b/docs/modules/Function.ts.md @@ -0,0 +1,701 @@ +--- +title: Function.ts +nav_order: 4 +parent: Modules +--- + +## Function overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [instances](#instances) + - [getMonoid](#getmonoid) + - [getSemigroup](#getsemigroup) +- [type lambdas](#type-lambdas) + - [FunctionTypeLambda (interface)](#functiontypelambda-interface) +- [utils](#utils) + - [FunctionN (interface)](#functionn-interface) + - [LazyArg (interface)](#lazyarg-interface) + - [SK](#sk) + - [absurd](#absurd) + - [apply](#apply) + - [compose](#compose) + - [constFalse](#constfalse) + - [constNull](#constnull) + - [constTrue](#consttrue) + - [constUndefined](#constundefined) + - [constVoid](#constvoid) + - [constant](#constant) + - [flip](#flip) + - [flow](#flow) + - [hole](#hole) + - [identity](#identity) + - [pipe](#pipe) + - [tupled](#tupled) + - [unsafeCoerce](#unsafecoerce) + - [untupled](#untupled) + +--- + +# instances + +## getMonoid + +Unary functions form a monoid as long as you can provide a monoid for the codomain. + +**Signature** + +```ts +export declare const getMonoid: (Monoid: any) =>
() => any +``` + +**Example** + +```ts +import { Predicate } from '@fp-ts/core/Predicate' +import { getMonoid, pipe } from '@fp-ts/core/Function' +import * as B from '@fp-ts/core/Boolean' + +const f: Predicate = (n) => n <= 2 +const g: Predicate = (n) => n >= 0 + +const M1 = getMonoid(B.MonoidAll)() + +assert.deepStrictEqual(M1.combine(f, g)(1), true) +assert.deepStrictEqual(M1.combine(f, g)(3), false) + +const M2 = getMonoid(B.MonoidAny)() + +assert.deepStrictEqual(M2.combine(f, g)(1), true) +assert.deepStrictEqual(M2.combine(f, g)(3), true) +``` + +Added in v1.0.0 + +## getSemigroup + +Unary functions form a semigroup as long as you can provide a semigroup for the codomain. + +**Signature** + +```ts +export declare const getSemigroup: (Semigroup: any) => () => any +``` + +**Example** + +```ts +import { Predicate } from '@fp-ts/core/Predicate' +import { pipe, getSemigroup } from '@fp-ts/core/Function' +import * as B from '@fp-ts/core/Boolean' + +const f: Predicate = (n) => n <= 2 +const g: Predicate = (n) => n >= 0 + +const S1 = getSemigroup(B.SemigroupAll)() + +assert.deepStrictEqual(S1.combine(f, g)(1), true) +assert.deepStrictEqual(S1.combine(f, g)(3), false) + +const S2 = getSemigroup(B.SemigroupAny)() + +assert.deepStrictEqual(S2.combine(f, g)(1), true) +assert.deepStrictEqual(S2.combine(f, g)(3), true) +``` + +Added in v1.0.0 + +# type lambdas + +## FunctionTypeLambda (interface) + +**Signature** + +```ts +export interface FunctionTypeLambda extends TypeLambda { + readonly type: (a: this['In']) => this['Target'] +} +``` + +Added in v1.0.0 + +# utils + +## FunctionN (interface) + +**Signature** + +```ts +export interface FunctionN, B> { + (...args: A): B +} +``` + +**Example** + +```ts +import { FunctionN } from '@fp-ts/core/Function' + +export const sum: FunctionN<[number, number], number> = (a, b) => a + b +``` + +Added in v1.0.0 + +## LazyArg (interface) + +A lazy argument + +**Signature** + +```ts +export interface LazyArg { + (): A +} +``` + +Added in v1.0.0 + +## SK + +`SK` function (SKI combinator calculus). + +**Signature** + +```ts +export declare const SK: (_: A, b: B) => B +``` + +Added in v1.0.0 + +## absurd + +**Signature** + +```ts +export declare const absurd: (_: never) => A +``` + +Added in v1.0.0 + +## apply + +**Signature** + +```ts +export declare const apply: (a: A) => (self: (a: A) => B) => B +``` + +Added in v1.0.0 + +## compose + +**Signature** + +```ts +export declare const compose: (bc: (b: B) => C) => (ab: (a: A) => B) => (a: A) => C +``` + +Added in v1.0.0 + +## constFalse + +A thunk that returns always `false`. + +**Signature** + +```ts +export declare const constFalse: LazyArg +``` + +Added in v1.0.0 + +## constNull + +A thunk that returns always `null`. + +**Signature** + +```ts +export declare const constNull: LazyArg +``` + +Added in v1.0.0 + +## constTrue + +A thunk that returns always `true`. + +**Signature** + +```ts +export declare const constTrue: LazyArg +``` + +Added in v1.0.0 + +## constUndefined + +A thunk that returns always `undefined`. + +**Signature** + +```ts +export declare const constUndefined: LazyArg +``` + +Added in v1.0.0 + +## constVoid + +A thunk that returns always `void`. + +**Signature** + +```ts +export declare const constVoid: LazyArg +``` + +Added in v1.0.0 + +## constant + +**Signature** + +```ts +export declare const constant: (a: A) => LazyArg +``` + +Added in v1.0.0 + +## flip + +Flips the arguments of a curried function. + +**Signature** + +```ts +export declare const flip: (f: (a: A) => (b: B) => C) => (b: B) => (a: A) => C +``` + +**Example** + +```ts +import { flip } from '@fp-ts/core/Function' + +const f = (a: number) => (b: string) => a - b.length + +assert.strictEqual(flip(f)('aaa')(2), -1) +``` + +Added in v1.0.0 + +## flow + +Performs left-to-right function composition. The first argument may have any arity, the remaining arguments must be unary. + +**Signature** + +```ts +export declare function flow, B>(ab: (...a: A) => B): (...a: A) => B +export declare function flow, B, C>( + ab: (...a: A) => B, + bc: (b: B) => C +): (...a: A) => C +export declare function flow, B, C, D>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D +): (...a: A) => D +export declare function flow, B, C, D, E>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E +): (...a: A) => E +export declare function flow, B, C, D, E, F>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F +): (...a: A) => F +export declare function flow, B, C, D, E, F, G>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G +): (...a: A) => G +export declare function flow, B, C, D, E, F, G, H>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H +): (...a: A) => H +export declare function flow, B, C, D, E, F, G, H, I>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I +): (...a: A) => I +export declare function flow, B, C, D, E, F, G, H, I, J>( + ab: (...a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J +): (...a: A) => J +``` + +**Example** + +```ts +import { flow } from '@fp-ts/core/Function' + +const len = (s: string): number => s.length +const double = (n: number): number => n * 2 + +const f = flow(len, double) + +assert.strictEqual(f('aaa'), 6) +``` + +Added in v1.0.0 + +## hole + +Type hole simulation + +**Signature** + +```ts +export declare const hole: () => T +``` + +Added in v1.0.0 + +## identity + +**Signature** + +```ts +export declare const identity: (a: A) => A +``` + +Added in v1.0.0 + +## pipe + +Pipes the value of an expression into a pipeline of functions. + +**Signature** + +```ts +export declare function pipe(a: A): A +export declare function pipe(a: A, ab: (a: A) => B): B +export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C +export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D +export declare function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F +): F +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G +): G +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H +): H +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I +): I +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J +): J +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K +): K +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L +): L +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M +): M +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N +): N +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O +): O +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P +): P +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q +): Q +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R +): R +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S +): S +export declare function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S, + st: (s: S) => T +): T +``` + +**Example** + +```ts +import { pipe } from '@fp-ts/core/Function' + +const len = (s: string): number => s.length +const double = (n: number): number => n * 2 + +// without pipe +assert.strictEqual(double(len('aaa')), 6) + +// with pipe +assert.strictEqual(pipe('aaa', len, double), 6) +``` + +Added in v1.0.0 + +## tupled + +Creates a tupled version of this function: instead of `n` arguments, it accepts a single tuple argument. + +**Signature** + +```ts +export declare const tupled: (f: (...a: A) => B) => (a: A) => B +``` + +**Example** + +```ts +import { tupled } from '@fp-ts/core/Function' + +const add = tupled((x: number, y: number): number => x + y) + +assert.strictEqual(add([1, 2]), 3) +``` + +Added in v1.0.0 + +## unsafeCoerce + +**Signature** + +```ts +export declare const unsafeCoerce: (a: A) => B +``` + +Added in v1.0.0 + +## untupled + +Inverse function of `tupled` + +**Signature** + +```ts +export declare const untupled: (f: (a: A) => B) => (...a: A) => B +``` + +Added in v1.0.0 diff --git a/docs/modules/HKT.ts.md b/docs/modules/HKT.ts.md index dec5aeaae..fc7d5f430 100644 --- a/docs/modules/HKT.ts.md +++ b/docs/modules/HKT.ts.md @@ -1,6 +1,6 @@ --- title: HKT.ts -nav_order: 1 +nav_order: 5 parent: Modules --- diff --git a/docs/modules/Identity.ts.md b/docs/modules/Identity.ts.md new file mode 100644 index 000000000..bcc1e104f --- /dev/null +++ b/docs/modules/Identity.ts.md @@ -0,0 +1,702 @@ +--- +title: Identity.ts +nav_order: 6 +parent: Modules +--- + +## Identity overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [constructors](#constructors) + - [of](#of) +- [conversions](#conversions) + - [toArray](#toarray) + - [toArrayWith](#toarraywith) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) +- [folding](#folding) + - [foldMap](#foldmap) + - [foldMapKind](#foldmapkind) + - [reduce](#reduce) + - [reduceKind](#reducekind) + - [reduceRight](#reduceright) + - [reduceRightKind](#reducerightkind) +- [instances](#instances) + - [Applicative](#applicative) + - [Chainable](#chainable) + - [Covariant](#covariant) + - [FlatMap](#flatmap) + - [Foldable](#foldable) + - [Invariant](#invariant) + - [Monad](#monad) + - [Of](#of) + - [Pointed](#pointed) + - [Product](#product) + - [SemiApplicative](#semiapplicative) + - [SemiProduct](#semiproduct) + - [Traversable](#traversable) + - [getSemiAlternative](#getsemialternative) + - [getSemiCoproduct](#getsemicoproduct) + - [liftSemigroup](#liftsemigroup) +- [lifting](#lifting) + - [lift2](#lift2) + - [lift3](#lift3) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [flap](#flap) +- [models](#models) + - [Identity (type alias)](#identity-type-alias) +- [sequencing](#sequencing) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) +- [traversing](#traversing) + - [sequence](#sequence) + - [traverse](#traverse) + - [traverseTap](#traversetap) +- [type lambdas](#type-lambdas) + - [IdentityTypeLambda (interface)](#identitytypelambda-interface) + - [IdentityTypeLambdaFix (interface)](#identitytypelambdafix-interface) +- [utils](#utils) + - [andThen](#andthen) + - [ap](#ap) + - [composeKleisliArrow](#composekleisliarrow) + - [element](#element) + - [flatten](#flatten) + - [imap](#imap) + - [liftMonoid](#liftmonoid) + - [map](#map) + - [struct](#struct) + - [tap](#tap) + - [tuple](#tuple) + - [tupled](#tupled) + - [unit](#unit) + +--- + +# constructors + +## of + +**Signature** + +```ts +export declare const of:
(a: A) => A +``` + +Added in v1.0.0 + +# conversions + +## toArray + +**Signature** + +```ts +export declare const toArray: (self: A) => A[] +``` + +Added in v1.0.0 + +## toArrayWith + +**Signature** + +```ts +export declare const toArrayWith: (f: (a: A) => B) => (self: A) => B[] +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: {} +``` + +Added in v1.0.0 + +## andThenBind + +A variant of `bind` that sequentially ignores the scope. + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: B +) => (self: A) => { [K in N | keyof A]: K extends keyof A ? A[K] : B } +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => B +) => (self: A) => { [K in N | keyof A]: K extends keyof A ? A[K] : B } +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (self: A) => { [K in N]: A } +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: ( + name: Exclude, + f: (a: A) => B +) => (self: A) => { [K in N | keyof A]: K extends keyof A ? A[K] : B } +``` + +Added in v1.0.0 + +# folding + +## foldMap + +**Signature** + +```ts +export declare const foldMap: (M: any) => (f: (a: A) => M) => (self: A) => M +``` + +Added in v1.0.0 + +## foldMapKind + +**Signature** + +```ts +export declare const foldMapKind: (G: any) => (f: (a: A) => any) => (self: A) => any +``` + +Added in v1.0.0 + +## reduce + +**Signature** + +```ts +export declare const reduce: (b: B, f: (b: B, a: A) => B) => (self: A) => B +``` + +Added in v1.0.0 + +## reduceKind + +**Signature** + +```ts +export declare const reduceKind: ( + G: any +) => (b: B, f: (b: B, a: A) => any) => (self: A) => any +``` + +Added in v1.0.0 + +## reduceRight + +**Signature** + +```ts +export declare const reduceRight: (b: B, f: (b: B, a: A) => B) => (self: A) => B +``` + +Added in v1.0.0 + +## reduceRightKind + +**Signature** + +```ts +export declare const reduceRightKind: ( + G: any +) => (b: B, f: (b: B, a: A) => any) => (self: A) => any +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: any +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: any +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: any +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: any +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: any +``` + +Added in v1.0.0 + +## getSemiAlternative + +**Signature** + +```ts +export declare const getSemiAlternative: (S: any) => any +``` + +Added in v1.0.0 + +## getSemiCoproduct + +**Signature** + +```ts +export declare const getSemiCoproduct: (S: any) => any +``` + +Added in v1.0.0 + +## liftSemigroup + +**Signature** + +```ts +export declare const liftSemigroup: (S: any) => any +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +Lifts a binary function into `Identity`. + +**Signature** + +```ts +export declare const lift2: (f: (a: A, b: B) => C) => (fa: A, fb: B) => C +``` + +Added in v1.0.0 + +## lift3 + +Lifts a ternary function into `Identity`. + +**Signature** + +```ts +export declare const lift3: (f: (a: A, b: B, c: C) => D) => (fa: A, fb: B, fc: C) => D +``` + +Added in v1.0.0 + +# mapping + +## as + +Maps the success value of this effect to the specified constant value. + +**Signature** + +```ts +export declare const as: (b: B) => <_>(self: _) => B +``` + +Added in v1.0.0 + +## asUnit + +Returns the effect resulting from mapping the success of this effect to unit. + +**Signature** + +```ts +export declare const asUnit: <_>(self: _) => Identity +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: (a: A) => (fab: (a: A) => B) => B +``` + +Added in v1.0.0 + +# models + +## Identity (type alias) + +**Signature** + +```ts +export type Identity = A +``` + +Added in v1.0.0 + +# sequencing + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: <_>(that: _) => (self: A) => A +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: (f: (a: A) => B) => (self: A) => B +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: (F: any) => (fas: any) => any +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: (F: any) => (f: (a: A) => any) => (self: A) => any +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: (F: any) => (f: (a: A) => any) => (self: A) => any +``` + +Added in v1.0.0 + +# type lambdas + +## IdentityTypeLambda (interface) + +**Signature** + +```ts +export interface IdentityTypeLambda extends TypeLambda { + readonly type: Identity +} +``` + +Added in v1.0.0 + +## IdentityTypeLambdaFix (interface) + +**Signature** + +```ts +export interface IdentityTypeLambdaFix extends TypeLambda { + readonly type: Identity +} +``` + +Added in v1.0.0 + +# utils + +## andThen + +**Signature** + +```ts +export declare const andThen: (that: B) => <_>(self: _) => B +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: (fa: A) => (self: (a: A) => B) => B +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: (bfc: (b: B) => C) => (afb: (a: A) => B) => (a: A) => C +``` + +Added in v1.0.0 + +## element + +Adds an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: (fb: B) => (self: A) => [...A, B] +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: A) => A +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (self: A) => B +``` + +Added in v1.0.0 + +## liftMonoid + +**Signature** + +```ts +export declare const liftMonoid: (M: any) => any +``` + +Added in v1.0.0 + +## map + +**Signature** + +```ts +export declare const map: (f: (a: A) => B) => (self: A) => B +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >(r: R) => { [K in keyof R]: R[K] } +``` + +Added in v1.0.0 + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: (f: (a: A) => _) => (self: A) => A +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: (...tuple: T) => { [I in keyof T]: T[I] } +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: A) => [A] +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: void +``` + +Added in v1.0.0 diff --git a/docs/modules/Number.ts.md b/docs/modules/Number.ts.md new file mode 100644 index 000000000..cc6393f80 --- /dev/null +++ b/docs/modules/Number.ts.md @@ -0,0 +1,268 @@ +--- +title: Number.ts +nav_order: 8 +parent: Modules +--- + +## Number overview + +This module provides utility functions and type class instances for working with the `number` type in TypeScript. +It includes functions for basic arithmetic operations, as well as type class instances for +`Equivalence`, `Order`, `Semigroup`, and `Monoid`. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isNumber](#isnumber) +- [instances](#instances) + - [Bounded](#bounded) + - [Equivalence](#equivalence) + - [MonoidMax](#monoidmax) + - [MonoidMin](#monoidmin) + - [MonoidMultiply](#monoidmultiply) + - [MonoidSum](#monoidsum) + - [Order](#order) + - [SemigroupMax](#semigroupmax) + - [SemigroupMin](#semigroupmin) + - [SemigroupMultiply](#semigroupmultiply) + - [SemigroupSum](#semigroupsum) +- [utils](#utils) + - [decrement](#decrement) + - [divide](#divide) + - [increment](#increment) + - [multiply](#multiply) + - [sign](#sign) + - [subtract](#subtract) + - [sum](#sum) + +--- + +# guards + +## isNumber + +**Signature** + +```ts +export declare const isNumber: any +``` + +Added in v1.0.0 + +# instances + +## Bounded + +**Signature** + +```ts +export declare const Bounded: any +``` + +Added in v1.0.0 + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: any +``` + +Added in v1.0.0 + +## MonoidMax + +**Signature** + +```ts +export declare const MonoidMax: any +``` + +Added in v1.0.0 + +## MonoidMin + +**Signature** + +```ts +export declare const MonoidMin: any +``` + +Added in v1.0.0 + +## MonoidMultiply + +`number` monoid under multiplication. + +The `empty` value is `1`. + +**Signature** + +```ts +export declare const MonoidMultiply: any +``` + +Added in v1.0.0 + +## MonoidSum + +`number` monoid under addition. + +The `empty` value is `0`. + +**Signature** + +```ts +export declare const MonoidSum: any +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: any +``` + +Added in v1.0.0 + +## SemigroupMax + +**Signature** + +```ts +export declare const SemigroupMax: any +``` + +Added in v1.0.0 + +## SemigroupMin + +**Signature** + +```ts +export declare const SemigroupMin: any +``` + +Added in v1.0.0 + +## SemigroupMultiply + +`number` semigroup under multiplication. + +**Signature** + +```ts +export declare const SemigroupMultiply: any +``` + +**Example** + +```ts +import { SemigroupMultiply } from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(SemigroupMultiply.combine(2, 3), 6) +``` + +Added in v1.0.0 + +## SemigroupSum + +`number` semigroup under addition. + +**Signature** + +```ts +export declare const SemigroupSum: any +``` + +**Example** + +```ts +import { SemigroupSum } from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(SemigroupSum.combine(2, 3), 5) +``` + +Added in v1.0.0 + +# utils + +## decrement + +**Signature** + +```ts +export declare const decrement: (n: number) => number +``` + +Added in v1.0.0 + +## divide + +**Signature** + +```ts +export declare const divide: (that: number) => (self: number) => number +``` + +Added in v1.0.0 + +## increment + +**Signature** + +```ts +export declare const increment: (n: number) => number +``` + +Added in v1.0.0 + +## multiply + +**Signature** + +```ts +export declare const multiply: (that: number) => (self: number) => number +``` + +Added in v1.0.0 + +## sign + +**Signature** + +```ts +export declare const sign: (n: number) => any +``` + +Added in v1.0.0 + +## subtract + +**Signature** + +```ts +export declare const subtract: (that: number) => (self: number) => number +``` + +Added in v1.0.0 + +## sum + +**Signature** + +```ts +export declare const sum: (that: number) => (self: number) => number +``` + +Added in v1.0.0 diff --git a/docs/modules/Option.ts.md b/docs/modules/Option.ts.md new file mode 100644 index 000000000..0ddc1bdb9 --- /dev/null +++ b/docs/modules/Option.ts.md @@ -0,0 +1,1520 @@ +--- +title: Option.ts +nav_order: 9 +parent: Modules +--- + +## Option overview + +The `Option` type can be interpreted in a few ways: + +1. `Option
` is a container for an optional value of type `A`. If the value of type `A` is present, the `Option` is + an instance of `Some`, containing the present value of type `A`. If the value is absent, the `Option` is an + instance of `None`. +2. Another way to view `Option` is as a representation of a possibly failing computation. +3. An option can also be thought of as a collection or foldable structure with either one or zero elements. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combining](#combining) + - [getFirstNoneMonoid](#getfirstnonemonoid) + - [getFirstNoneSemigroup](#getfirstnonesemigroup) + - [getFirstSomeSemigroup](#getfirstsomesemigroup) +- [constructors](#constructors) + - [none](#none) + - [of](#of) + - [some](#some) +- [conversions](#conversions) + - [fromEither](#fromeither) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [getOrNull](#getornull) + - [getOrUndefined](#getorundefined) + - [toEither](#toeither) + - [toRefinement](#torefinement) +- [debugging](#debugging) + - [inspectNone](#inspectnone) + - [inspectSome](#inspectsome) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) +- [error handling](#error-handling) + - [catchAll](#catchall) + - [firstSomeOf](#firstsomeof) + - [getOrElse](#getorelse) + - [orElse](#orelse) + - [orElseEither](#orelseeither) + - [orElseSucceed](#orelsesucceed) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [filterMap](#filtermap) + - [separate](#separate) +- [guards](#guards) + - [isNone](#isnone) + - [isOption](#isoption) + - [isSome](#issome) +- [instances](#instances) + - [Alternative](#alternative) + - [Applicative](#applicative) + - [Chainable](#chainable) + - [Compactable](#compactable) + - [Coproduct](#coproduct) + - [Covariant](#covariant) + - [Filterable](#filterable) + - [FlatMap](#flatmap) + - [Foldable](#foldable) + - [Invariant](#invariant) + - [Monad](#monad) + - [Of](#of) + - [Pointed](#pointed) + - [Product](#product) + - [SemiAlternative](#semialternative) + - [SemiApplicative](#semiapplicative) + - [SemiCoproduct](#semicoproduct) + - [SemiProduct](#semiproduct) + - [Traversable](#traversable) +- [interop](#interop) + - [fromThrowable](#fromthrowable) + - [getOrThrow](#getorthrow) + - [liftThrowable](#liftthrowable) +- [lifting](#lifting) + - [getMonoid](#getmonoid) + - [lift2](#lift2) + - [lift3](#lift3) + - [liftEither](#lifteither) + - [liftNullable](#liftnullable) + - [liftPredicate](#liftpredicate) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [flap](#flap) + - [imap](#imap) + - [map](#map) +- [models](#models) + - [None (type alias)](#none-type-alias) + - [Option (type alias)](#option-type-alias) + - [Some (type alias)](#some-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) +- [sequencing](#sequencing) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) + - [flatMapEither](#flatmapeither) + - [flatMapNullable](#flatmapnullable) +- [sorting](#sorting) + - [liftOrder](#liftorder) +- [traversing](#traversing) + - [sequence](#sequence) + - [traverse](#traverse) + - [traverseTap](#traversetap) +- [type lambdas](#type-lambdas) + - [OptionTypeLambda (interface)](#optiontypelambda-interface) +- [utils](#utils) + - [andThen](#andthen) + - [ap](#ap) + - [composeKleisliArrow](#composekleisliarrow) + - [contains](#contains) + - [coproductEither](#coproducteither) + - [element](#element) + - [exists](#exists) + - [flatten](#flatten) + - [struct](#struct) + - [tap](#tap) + - [toArray](#toarray) + - [tuple](#tuple) + - [tupled](#tupled) + - [unit](#unit) + +--- + +# combining + +## getFirstNoneMonoid + +Monoid returning the left-most `None` value. If both operands are `Right`s then the inner values +are concatenated using the provided `Monoid`. + +The `empty` value is `some(M.empty)`. + +**Signature** + +```ts +export declare const getFirstNoneMonoid:
(M: any) => any +``` + +Added in v1.0.0 + +## getFirstNoneSemigroup + +Semigroup returning the left-most `None` value. If both operands are `Right`s then the inner values +are concatenated using the provided `Semigroup`. + +**Signature** + +```ts +export declare const getFirstNoneSemigroup: (S: any) => any +``` + +Added in v1.0.0 + +## getFirstSomeSemigroup + +Semigroup returning the left-most `Some` value. + +**Signature** + +```ts +export declare const getFirstSomeSemigroup: () => any +``` + +Added in v1.0.0 + +# constructors + +## none + +**Signature** + +```ts +export declare const none: () => Option +``` + +Added in v1.0.0 + +## of + +**Signature** + +```ts +export declare const of: (a: A) => Option +``` + +Added in v1.0.0 + +## some + +**Signature** + +```ts +export declare const some: (a: A) => Option +``` + +Added in v1.0.0 + +# conversions + +## fromEither + +Converts a `Either` to an `Option` discarding the error. + +**Signature** + +```ts +export declare const fromEither: (self: any) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(O.fromEither(E.right(1)), O.some(1)) +assert.deepStrictEqual(O.fromEither(E.left('a')), O.none()) +``` + +Added in v1.0.0 + +## fromIterable + +**Signature** + +```ts +export declare const fromIterable: (collection: Iterable) => Option +``` + +Added in v1.0.0 + +## fromNullable + +Constructs a new `Option` from a nullable type. If the value is `null` or `undefined`, returns `None`, otherwise +returns the value wrapped in a `Some`. + +**Signature** + +```ts +export declare const fromNullable: (a: A) => Option> +``` + +**Example** + +```ts +import { none, some, fromNullable } from '@fp-ts/core/Option' + +assert.deepStrictEqual(fromNullable(undefined), none()) +assert.deepStrictEqual(fromNullable(null), none()) +assert.deepStrictEqual(fromNullable(1), some(1)) +``` + +Added in v1.0.0 + +## getOrNull + +Extracts the value out of the structure, if it exists. Otherwise returns `null`. + +**Signature** + +```ts +export declare const getOrNull: (self: Option) => A | null +``` + +**Example** + +```ts +import { some, none, getOrNull } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.strictEqual(pipe(some(1), getOrNull), 1) +assert.strictEqual(pipe(none(), getOrNull), null) +``` + +Added in v1.0.0 + +## getOrUndefined + +Extracts the value out of the structure, if it exists. Otherwise returns `undefined`. + +**Signature** + +```ts +export declare const getOrUndefined: (self: Option) => A | undefined +``` + +**Example** + +```ts +import { some, none, getOrUndefined } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.strictEqual(pipe(some(1), getOrUndefined), 1) +assert.strictEqual(pipe(none(), getOrUndefined), undefined) +``` + +Added in v1.0.0 + +## toEither + +**Signature** + +```ts +export declare const toEither: (onNone: any) => (self: Option) => any +``` + +Added in v1.0.0 + +## toRefinement + +Returns a `Refinement` from a `Option` returning function. +This function ensures that a `Refinement` definition is type-safe. + +**Signature** + +```ts +export declare const toRefinement: (f: (a: A) => Option) => any +``` + +Added in v1.0.0 + +# debugging + +## inspectNone + +**Signature** + +```ts +export declare const inspectNone: (onNone: () => void) => (self: Option) => Option +``` + +Added in v1.0.0 + +## inspectSome + +**Signature** + +```ts +export declare const inspectSome: (onSome: (a: A) => void) => (self: Option) => Option +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: Option<{}> +``` + +Added in v1.0.0 + +## andThenBind + +A variant of `bind` that sequentially ignores the scope. + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: Option +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => Option +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (self: Option) => Option<{ [K in N]: A }> +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: ( + name: Exclude, + f: (a: A) => B +) => (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +``` + +Added in v1.0.0 + +# error handling + +## catchAll + +Lazy version of `orElse`. + +**Signature** + +```ts +export declare const catchAll: (that: any) => (self: Option) => Option +``` + +Added in v1.0.0 + +## firstSomeOf + +**Signature** + +```ts +export declare const firstSomeOf: (collection: Iterable>) => (self: Option) => Option +``` + +Added in v1.0.0 + +## getOrElse + +Extracts the value out of the structure, if it exists. Otherwise returns the given default value + +**Signature** + +```ts +export declare const getOrElse: (onNone: any) => (self: Option) => B | A +``` + +**Example** + +```ts +import { some, none, getOrElse } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.strictEqual( + pipe( + some(1), + getOrElse(() => 0) + ), + 1 +) +assert.strictEqual( + pipe( + none(), + getOrElse(() => 0) + ), + 0 +) +``` + +Added in v1.0.0 + +## orElse + +Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to +types of kind `* -> *`. + +In case of `Option` returns the left-most non-`None` value. + +| x | y | pipe(x, orElse(y) | +| ------- | ------- | ----------------- | +| none | none | none | +| some(a) | none | some(a) | +| none | some(b) | some(b) | +| some(a) | some(b) | some(a) | + +**Signature** + +```ts +export declare const orElse: (that: Option) => (self: Option) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe(O.none(), O.orElse(O.none())), O.none()) +assert.deepStrictEqual(pipe(O.some('a'), O.orElse(O.none())), O.some('a')) +assert.deepStrictEqual(pipe(O.none(), O.orElse(O.some('b'))), O.some('b')) +assert.deepStrictEqual(pipe(O.some('a'), O.orElse(O.some('b'))), O.some('a')) +``` + +Added in v1.0.0 + +## orElseEither + +Returns an effect that will produce the value of this effect, unless it +fails, in which case, it will produce the value of the specified effect. + +**Signature** + +```ts +export declare const orElseEither: (that: Option) => (self: Option) => Option +``` + +Added in v1.0.0 + +## orElseSucceed + +Executes this effect and returns its value, if it succeeds, but otherwise +succeeds with the specified value. + +**Signature** + +```ts +export declare const orElseSucceed: (onNone: () => B) => (self: Option) => Option +``` + +Added in v1.0.0 + +# filtering + +## compact + +Alias of `flatten`. + +**Signature** + +```ts +export declare const compact: (self: Option>) => Option +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: any): (fc: Option) => Option + (predicate: any): (fb: Option) => Option +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: (f: (a: A) => Option) => (self: Option) => Option +``` + +Added in v1.0.0 + +## separate + +**Signature** + +```ts +export declare const separate: (self: Option) => [Option, Option] +``` + +Added in v1.0.0 + +# guards + +## isNone + +Returns `true` if the option is `None`, `false` otherwise. + +**Signature** + +```ts +export declare const isNone: (self: Option) => self is None +``` + +**Example** + +```ts +import { some, none, isNone } from '@fp-ts/core/Option' + +assert.strictEqual(isNone(some(1)), false) +assert.strictEqual(isNone(none()), true) +``` + +Added in v1.0.0 + +## isOption + +Returns `true` if the specified value is an instance of `Option`, `false` +otherwise. + +**Signature** + +```ts +export declare const isOption: (u: unknown) => u is Option +``` + +**Example** + +```ts +import { some, none, isOption } from '@fp-ts/core/Option' + +assert.strictEqual(isOption(some(1)), true) +assert.strictEqual(isOption(none()), true) +assert.strictEqual(isOption({}), false) +``` + +Added in v1.0.0 + +## isSome + +Returns `true` if the option is an instance of `Some`, `false` otherwise. + +**Signature** + +```ts +export declare const isSome: (self: Option) => self is Some +``` + +**Example** + +```ts +import { some, none, isSome } from '@fp-ts/core/Option' + +assert.strictEqual(isSome(some(1)), true) +assert.strictEqual(isSome(none()), false) +``` + +Added in v1.0.0 + +# instances + +## Alternative + +**Signature** + +```ts +export declare const Alternative: any +``` + +Added in v1.0.0 + +## Applicative + +**Signature** + +```ts +export declare const Applicative: any +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: any +``` + +Added in v1.0.0 + +## Compactable + +**Signature** + +```ts +export declare const Compactable: any +``` + +Added in v1.0.0 + +## Coproduct + +**Signature** + +```ts +export declare const Coproduct: any +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: any +``` + +Added in v1.0.0 + +## Filterable + +**Signature** + +```ts +export declare const Filterable: any +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: any +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: any +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: any +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: any +``` + +Added in v1.0.0 + +# interop + +## fromThrowable + +Converts an exception into an `Option`. If `f` throws, returns `None`, otherwise returns the output wrapped in a +`Some`. + +**Signature** + +```ts +export declare const fromThrowable: (f: () => A) => Option +``` + +**Example** + +```ts +import { none, some, fromThrowable } from '@fp-ts/core/Option' + +assert.deepStrictEqual( + fromThrowable(() => { + throw new Error() + }), + none() +) +assert.deepStrictEqual( + fromThrowable(() => 1), + some(1) +) +``` + +Added in v1.0.0 + +## getOrThrow + +**Signature** + +```ts +export declare const getOrThrow: (onError: any) => (self: Option) => A +``` + +Added in v1.0.0 + +## liftThrowable + +Lifts a function that may throw to one returning a `Option`. + +**Signature** + +```ts +export declare const liftThrowable: (f: (...a: A) => B) => (...a: A) => Option +``` + +Added in v1.0.0 + +# lifting + +## getMonoid + +Monoid returning the left-most non-`None` value. If both operands are `Some`s then the inner values are +combined using the provided `Semigroup` + +| x | y | combine(y)(x) | +| ------- | ------- | ------------------- | +| none | none | none | +| some(a) | none | some(a) | +| none | some(a) | some(a) | +| some(a) | some(b) | some(combine(b)(a)) | + +**Signature** + +```ts +export declare const getMonoid: (Semigroup: any) => any +``` + +**Example** + +```ts +import { getMonoid, some, none } from '@fp-ts/core/Option' +import * as N from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +const M = getMonoid(N.SemigroupSum) +assert.deepStrictEqual(M.combine(none(), none()), none()) +assert.deepStrictEqual(M.combine(some(1), none()), some(1)) +assert.deepStrictEqual(M.combine(none(), some(1)), some(1)) +assert.deepStrictEqual(M.combine(some(1), some(2)), some(3)) +``` + +Added in v1.0.0 + +## lift2 + +Lifts a binary function into `Option`. + +**Signature** + +```ts +export declare const lift2: (f: (a: A, b: B) => C) => (fa: Option, fb: Option) => Option +``` + +Added in v1.0.0 + +## lift3 + +Lifts a ternary function into `Option`. + +**Signature** + +```ts +export declare const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: Option, fb: Option, fc: Option) => Option +``` + +Added in v1.0.0 + +## liftEither + +**Signature** + +```ts +export declare const liftEither: (f: (...a: A) => any) => (...a: A) => Option +``` + +Added in v1.0.0 + +## liftNullable + +Returns a _smart constructor_ from a function that returns a nullable value. + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined +) => (...a: A) => Option> +``` + +**Example** + +```ts +import { liftNullable, none, some } from '@fp-ts/core/Option' + +const f = (s: string): number | undefined => { + const n = parseFloat(s) + return isNaN(n) ? undefined : n +} + +const g = liftNullable(f) + +assert.deepStrictEqual(g('1'), some(1)) +assert.deepStrictEqual(g('a'), none()) +``` + +Added in v1.0.0 + +## liftPredicate + +Returns a _smart constructor_ based on the given predicate. + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: any): (c: C) => Option + (predicate: any): (b: B) => Option +} +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +const getOption = O.liftPredicate((n: number) => n >= 0) + +assert.deepStrictEqual(getOption(-1), O.none()) +assert.deepStrictEqual(getOption(1), O.some(1)) +``` + +Added in v1.0.0 + +# mapping + +## as + +Maps the success value of this effect to the specified constant value. + +**Signature** + +```ts +export declare const as: (b: B) => <_>(self: Option<_>) => Option +``` + +Added in v1.0.0 + +## asUnit + +Returns the effect resulting from mapping the success of this effect to unit. + +**Signature** + +```ts +export declare const asUnit: <_>(self: Option<_>) => Option +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: (a: A) => (fab: Option<(a: A) => B>) => Option +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: any +``` + +Added in v1.0.0 + +## map + +Returns an effect whose success is mapped by the specified `f` function. + +**Signature** + +```ts +export declare const map: (f: (a: A) => B) => (self: Option) => Option +``` + +Added in v1.0.0 + +# models + +## None (type alias) + +**Signature** + +```ts +export type None = { + readonly _tag: 'None' +} +``` + +Added in v1.0.0 + +## Option (type alias) + +**Signature** + +```ts +export type Option = None | Some +``` + +Added in v1.0.0 + +## Some (type alias) + +**Signature** + +```ts +export type Some = { + readonly _tag: 'Some' + readonly value: A +} +``` + +Added in v1.0.0 + +# pattern matching + +## match + +Takes a (lazy) default value, a function, and an `Option` value, if the `Option` value is `None` the default value is +returned, otherwise the function is applied to the value inside the `Some` and the result is returned. + +**Signature** + +```ts +export declare const match: (onNone: any, onSome: (a: A) => C) => (self: Option) => B | C +``` + +**Example** + +```ts +import { some, none, match } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.strictEqual( + pipe( + some(1), + match( + () => 'a none', + (a) => `a some containing ${a}` + ) + ), + 'a some containing 1' +) + +assert.strictEqual( + pipe( + none(), + match( + () => 'a none', + (a) => `a some containing ${a}` + ) + ), + 'a none' +) +``` + +Added in v1.0.0 + +# sequencing + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: <_>(that: Option<_>) => (self: Option) => Option +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: (f: (a: A) => Option) => (self: Option) => Option +``` + +Added in v1.0.0 + +## flatMapEither + +**Signature** + +```ts +export declare const flatMapEither: (f: (a: A) => any) => (self: Option) => Option +``` + +Added in v1.0.0 + +## flatMapNullable + +This is `flatMap` + `fromNullable`, useful when working with optional values. + +**Signature** + +```ts +export declare const flatMapNullable: ( + f: (a: A) => B | null | undefined +) => (self: Option) => Option> +``` + +**Example** + +```ts +import { some, none, fromNullable, flatMapNullable } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +interface Employee { + company?: { + address?: { + street?: { + name?: string + } + } + } +} + +const employee1: Employee = { company: { address: { street: { name: 'high street' } } } } + +assert.deepStrictEqual( + pipe( + fromNullable(employee1.company), + flatMapNullable((company) => company.address), + flatMapNullable((address) => address.street), + flatMapNullable((street) => street.name) + ), + some('high street') +) + +const employee2: Employee = { company: { address: { street: {} } } } + +assert.deepStrictEqual( + pipe( + fromNullable(employee2.company), + flatMapNullable((company) => company.address), + flatMapNullable((address) => address.street), + flatMapNullable((street) => street.name) + ), + none() +) +``` + +Added in v1.0.0 + +# sorting + +## liftOrder + +The `Order` instance allows `Option` values to be compared with +`compare`, whenever there is an `Order` instance for +the type the `Option` contains. + +`None` is considered to be less than any `Some` value. + +**Signature** + +```ts +export declare const liftOrder: (O: any) => any +``` + +**Example** + +```ts +import { none, some, liftOrder } from '@fp-ts/core/Option' +import * as N from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +const O = liftOrder(N.Order) +assert.strictEqual(O.compare(none(), none()), 0) +assert.strictEqual(O.compare(none(), some(1)), -1) +assert.strictEqual(O.compare(some(1), none()), 1) +assert.strictEqual(O.compare(some(1), some(2)), -1) +assert.strictEqual(O.compare(some(1), some(1)), 0) +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: (F: any) => (fas: Option) => any +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: (F: any) => (f: (a: A) => any) => (self: Option) => any +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: any +) => (f: (a: A) => any) => (self: Option) => any +``` + +Added in v1.0.0 + +# type lambdas + +## OptionTypeLambda (interface) + +**Signature** + +```ts +export interface OptionTypeLambda extends TypeLambda { + readonly type: Option +} +``` + +Added in v1.0.0 + +# utils + +## andThen + +**Signature** + +```ts +export declare const andThen: (that: Option) => <_>(self: Option<_>) => Option +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: (fa: Option) => (self: Option<(a: A) => B>) => Option +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: ( + bfc: (b: B) => Option +) => (afb: (a: A) => Option) => (a: A) => Option +``` + +Added in v1.0.0 + +## contains + +Returns a function that checks if an `Option` contains a given value using a provided `equivalence` function. + +**Signature** + +```ts +export declare const contains: (equivalence: any) => (a: A) => (self: Option) => boolean +``` + +Added in v1.0.0 + +## coproductEither + +**Signature** + +```ts +export declare const coproductEither: (that: Option) => (self: Option) => Option +``` + +Added in v1.0.0 + +## element + +Adds an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: (fb: Option) => (self: Option) => Option<[...A, B]> +``` + +Added in v1.0.0 + +## exists + +Returns `true` if the predicate is satisfied by the wrapped value + +**Signature** + +```ts +export declare const exists: (predicate: any) => (self: Option) => boolean +``` + +**Example** + +```ts +import { some, none, exists } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.strictEqual( + pipe( + some(1), + exists((n) => n > 0) + ), + true +) +assert.strictEqual( + pipe( + some(1), + exists((n) => n > 1) + ), + false +) +assert.strictEqual( + pipe( + none(), + exists((n) => n > 0) + ), + false +) +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: Option>) => Option +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >>( + r: R +) => Option<{ [K in keyof R]: [R[K]] extends [Option] ? A : never }> +``` + +Added in v1.0.0 + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: (f: (a: A) => Option<_>) => (self: Option) => Option +``` + +Added in v1.0.0 + +## toArray + +**Signature** + +```ts +export declare const toArray: (self: Option) => A[] +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: []>( + ...tuple: T +) => Option<{ [I in keyof T]: [T[I]] extends [Option] ? A : never }> +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: Option) => Option<[A]> +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: Option +``` + +Added in v1.0.0 diff --git a/docs/modules/Ordering.ts.md b/docs/modules/Ordering.ts.md new file mode 100644 index 000000000..e2bb1b904 --- /dev/null +++ b/docs/modules/Ordering.ts.md @@ -0,0 +1,87 @@ +--- +title: Ordering.ts +nav_order: 10 +parent: Modules +--- + +## Ordering overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [instances](#instances) + - [Monoid](#monoid) + - [Semigroup](#semigroup) +- [model](#model) + - [Ordering (type alias)](#ordering-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) +- [utils](#utils) + - [reverse](#reverse) + +--- + +# instances + +## Monoid + +**Signature** + +```ts +export declare const Monoid: any +``` + +Added in v1.0.0 + +## Semigroup + +**Signature** + +```ts +export declare const Semigroup: any +``` + +Added in v1.0.0 + +# model + +## Ordering (type alias) + +**Signature** + +```ts +export type Ordering = -1 | 0 | 1 +``` + +Added in v1.0.0 + +# pattern matching + +## match + +**Signature** + +```ts +export declare const match: ( + onLessThan: any, + onEqual: any, + onGreaterThan: any +) => (o: Ordering) => A | B | C +``` + +Added in v1.0.0 + +# utils + +## reverse + +**Signature** + +```ts +export declare const reverse: (o: Ordering) => Ordering +``` + +Added in v1.0.0 diff --git a/docs/modules/Predicate.ts.md b/docs/modules/Predicate.ts.md new file mode 100644 index 000000000..e661d80a1 --- /dev/null +++ b/docs/modules/Predicate.ts.md @@ -0,0 +1,443 @@ +--- +title: Predicate.ts +nav_order: 11 +parent: Modules +--- + +## Predicate overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [constructors](#constructors) + - [id](#id) +- [do notation](#do-notation) + - [bindTo](#bindto) +- [guards](#guards) + - [isBigInt](#isbigint) + - [isBoolean](#isboolean) + - [isNumber](#isnumber) + - [isString](#isstring) + - [isSymbol](#issymbol) +- [instances](#instances) + - [Contravariant](#contravariant) + - [Invariant](#invariant) + - [Of](#of) + - [Product](#product) + - [SemiProduct](#semiproduct) + - [getMonoidAll](#getmonoidall) + - [getMonoidAny](#getmonoidany) + - [getSemigroupAll](#getsemigroupall) + - [getSemigroupAny](#getsemigroupany) +- [models](#models) + - [Predicate (interface)](#predicate-interface) +- [type lambdas](#type-lambdas) + - [PredicateTypeLambda (interface)](#predicatetypelambda-interface) +- [utils](#utils) + - [Do](#do) + - [Refinement (interface)](#refinement-interface) + - [all](#all) + - [and](#and) + - [andThenBind](#andthenbind) + - [any](#any) + - [compose](#compose) + - [contramap](#contramap) + - [element](#element) + - [imap](#imap) + - [not](#not) + - [of](#of) + - [or](#or) + - [struct](#struct) + - [tuple](#tuple) + - [tupled](#tupled) + - [unit](#unit) + +--- + +# constructors + +## id + +**Signature** + +```ts +export declare const id:
() => Refinement +``` + +Added in v1.0.0 + +# do notation + +## bindTo + +**Signature** + +```ts +export declare const bindTo: ( + name: N +) => (self: Predicate) => Predicate<{ readonly [K in N]: A }> +``` + +Added in v1.0.0 + +# guards + +## isBigInt + +**Signature** + +```ts +export declare const isBigInt: (u: unknown) => u is bigint +``` + +Added in v1.0.0 + +## isBoolean + +**Signature** + +```ts +export declare const isBoolean: Refinement +``` + +Added in v1.0.0 + +## isNumber + +**Signature** + +```ts +export declare const isNumber: Refinement +``` + +Added in v1.0.0 + +## isString + +**Signature** + +```ts +export declare const isString: Refinement +``` + +Added in v1.0.0 + +## isSymbol + +**Signature** + +```ts +export declare const isSymbol: (u: unknown) => u is symbol +``` + +Added in v1.0.0 + +# instances + +## Contravariant + +**Signature** + +```ts +export declare const Contravariant: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## getMonoidAll + +**Signature** + +```ts +export declare const getMonoidAll: () => any +``` + +Added in v1.0.0 + +## getMonoidAny + +**Signature** + +```ts +export declare const getMonoidAny: () => any +``` + +Added in v1.0.0 + +## getSemigroupAll + +**Signature** + +```ts +export declare const getSemigroupAll: () => any +``` + +Added in v1.0.0 + +## getSemigroupAny + +**Signature** + +```ts +export declare const getSemigroupAny: () => any +``` + +Added in v1.0.0 + +# models + +## Predicate (interface) + +**Signature** + +```ts +export interface Predicate { + (a: A): boolean +} +``` + +Added in v1.0.0 + +# type lambdas + +## PredicateTypeLambda (interface) + +**Signature** + +```ts +export interface PredicateTypeLambda extends TypeLambda { + readonly type: Predicate +} +``` + +Added in v1.0.0 + +# utils + +## Do + +**Signature** + +```ts +export declare const Do: Predicate<{}> +``` + +Added in v1.0.0 + +## Refinement (interface) + +**Signature** + +```ts +export interface Refinement { + (a: A): a is B +} +``` + +Added in v1.0.0 + +## all + +**Signature** + +```ts +export declare const all: (collection: Iterable>) => Predicate +``` + +Added in v1.0.0 + +## and + +**Signature** + +```ts +export declare const and: (that: Predicate) => (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## andThenBind + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: Predicate +) => (self: Predicate) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> +``` + +Added in v1.0.0 + +## any + +**Signature** + +```ts +export declare const any: (collection: Iterable>) => Predicate +``` + +Added in v1.0.0 + +## compose + +**Signature** + +```ts +export declare const compose: ( + bc: Refinement +) => (ab: Refinement) => Refinement +``` + +Added in v1.0.0 + +## contramap + +**Signature** + +```ts +export declare const contramap: (f: (b: B) => A) => (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## element + +Adds an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: ( + that: Predicate +) => (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## not + +**Signature** + +```ts +export declare const not: (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## of + +**Signature** + +```ts +export declare const of: (_: A) => Predicate +``` + +Added in v1.0.0 + +## or + +**Signature** + +```ts +export declare const or: (that: Predicate) => (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >>( + predicates: R +) => Predicate<{ readonly [K in keyof R]: [R[K]] extends [Predicate] ? A : never }> +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: []>( + ...predicates: T +) => Predicate] ? A : never }>> +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: Predicate) => Predicate +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: Predicate +``` + +Added in v1.0.0 diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md new file mode 100644 index 000000000..5a5b47479 --- /dev/null +++ b/docs/modules/ReadonlyArray.ts.md @@ -0,0 +1,2438 @@ +--- +title: ReadonlyArray.ts +nav_order: 12 +parent: Modules +--- + +## ReadonlyArray overview + +This module provides utility functions for working with arrays in TypeScript. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [constructors](#constructors) + - [empty](#empty) + - [make](#make) + - [makeBy](#makeby) + - [of](#of) + - [range](#range) + - [replicate](#replicate) + - [unfold](#unfold) +- [conversions](#conversions) + - [fromEither](#fromeither) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [fromOption](#fromoption) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [filterMap](#filtermap) + - [filterMapWithIndex](#filtermapwithindex) + - [filterWithIndex](#filterwithindex) + - [partition](#partition) + - [partitionMap](#partitionmap) + - [partitionMapWithIndex](#partitionmapwithindex) + - [partitionWithIndex](#partitionwithindex) + - [separate](#separate) + - [span](#span) + - [traverseFilterMap](#traversefiltermap) + - [traversePartitionMap](#traversepartitionmap) +- [folding](#folding) + - [foldMap](#foldmap) + - [foldMapKind](#foldmapkind) + - [foldMapNonEmpty](#foldmapnonempty) + - [foldMapNonEmptyWithIndex](#foldmapnonemptywithindex) + - [foldMapWithIndex](#foldmapwithindex) + - [reduce](#reduce) + - [reduceKind](#reducekind) + - [reduceRight](#reduceright) + - [reduceRightKind](#reducerightkind) + - [reduceRightWithIndex](#reducerightwithindex) + - [reduceWithIndex](#reducewithindex) + - [scan](#scan) + - [scanRight](#scanright) +- [getters](#getters) + - [chunksOf](#chunksof) + - [chunksOfNonEmpty](#chunksofnonempty) + - [drop](#drop) + - [dropRight](#dropright) + - [dropWhile](#dropwhile) + - [findFirst](#findfirst) + - [findFirstIndex](#findfirstindex) + - [findLast](#findlast) + - [findLastIndex](#findlastindex) + - [get](#get) + - [head](#head) + - [headNonEmpty](#headnonempty) + - [init](#init) + - [initNonEmpty](#initnonempty) + - [last](#last) + - [lastNonEmpty](#lastnonempty) + - [lefts](#lefts) + - [length](#length) + - [rights](#rights) + - [splitAt](#splitat) + - [splitNonEmptyAt](#splitnonemptyat) + - [tail](#tail) + - [tailNonEmpty](#tailnonempty) + - [take](#take) + - [takeRight](#takeright) + - [takeWhile](#takewhile) + - [unappend](#unappend) + - [unprepend](#unprepend) +- [grouping](#grouping) + - [group](#group) + - [groupBy](#groupby) +- [instances](#instances) + - [Applicative](#applicative) + - [Chainable](#chainable) + - [Compactable](#compactable) + - [Covariant](#covariant) + - [Filterable](#filterable) + - [FlatMap](#flatmap) + - [Foldable](#foldable) + - [Invariant](#invariant) + - [Monad](#monad) + - [Of](#of) + - [Pointed](#pointed) + - [Product](#product) + - [SemiApplicative](#semiapplicative) + - [SemiProduct](#semiproduct) + - [Traversable](#traversable) + - [TraversableFilterable](#traversablefilterable) + - [getIntersectionSemigroup](#getintersectionsemigroup) + - [getMonoid](#getmonoid) + - [getSemigroup](#getsemigroup) + - [getUnionMonoid](#getunionmonoid) + - [getUnionSemigroup](#getunionsemigroup) +- [lifting](#lifting) + - [every](#every) + - [lift2](#lift2) + - [lift3](#lift3) + - [liftEither](#lifteither) + - [liftMonoid](#liftmonoid) + - [liftNullable](#liftnullable) + - [liftOption](#liftoption) + - [liftOrder](#liftorder) + - [liftPredicate](#liftpredicate) + - [liftSemigroup](#liftsemigroup) +- [mapping](#mapping) + - [as](#as) + - [flap](#flap) + - [imap](#imap) + - [map](#map) + - [mapNonEmpty](#mapnonempty) + - [mapNonEmptyWithIndex](#mapnonemptywithindex) + - [mapWithIndex](#mapwithindex) + - [tupled](#tupled) +- [models](#models) + - [NonEmptyArray (type alias)](#nonemptyarray-type-alias) + - [NonEmptyReadonlyArray (type alias)](#nonemptyreadonlyarray-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) + - [matchRight](#matchright) +- [predicates](#predicates) + - [contains](#contains) + - [isEmpty](#isempty) + - [isNonEmpty](#isnonempty) + - [some](#some) +- [sequencing](#sequencing) + - [flatMap](#flatmap) + - [flatMapNonEmpty](#flatmapnonempty) + - [flatMapNonEmptyWithIndex](#flatmapnonemptywithindex) + - [flatMapNullable](#flatmapnullable) + - [flatMapWithIndex](#flatmapwithindex) + - [flatten](#flatten) + - [flattenNonEmpty](#flattennonempty) +- [sorting](#sorting) + - [sort](#sort) + - [sortBy](#sortby) + - [sortByNonEmpty](#sortbynonempty) + - [sortNonEmpty](#sortnonempty) +- [traversing](#traversing) + - [sequence](#sequence) + - [sequenceNonEmpty](#sequencenonempty) + - [traverse](#traverse) + - [traverseNonEmpty](#traversenonempty) + - [traverseNonEmptyWithIndex](#traversenonemptywithindex) + - [traverseTap](#traversetap) + - [traverseWithIndex](#traversewithindex) +- [type lambdas](#type-lambdas) + - [ReadonlyArrayTypeLambda (interface)](#readonlyarraytypelambda-interface) +- [unsafe](#unsafe) + - [unsafeGet](#unsafeget) +- [utils](#utils) + - [ap](#ap) + - [append](#append) + - [appendAll](#appendall) + - [appendAllNonEmpty](#appendallnonempty) + - [chop](#chop) + - [chopNonEmpty](#chopnonempty) + - [composeKleisliArrow](#composekleisliarrow) + - [copy](#copy) + - [difference](#difference) + - [extend](#extend) + - [insertAt](#insertat) + - [intercalate](#intercalate) + - [intercalateNonEmpty](#intercalatenonempty) + - [intersection](#intersection) + - [intersperse](#intersperse) + - [intersperseNonEmpty](#interspersenonempty) + - [join](#join) + - [max](#max) + - [min](#min) + - [modify](#modify) + - [modifyNonEmptyHead](#modifynonemptyhead) + - [modifyNonEmptyLast](#modifynonemptylast) + - [modifyOption](#modifyoption) + - [prepend](#prepend) + - [prependAll](#prependall) + - [prependAllNonEmpty](#prependallnonempty) + - [remove](#remove) + - [replace](#replace) + - [replaceOption](#replaceoption) + - [reverse](#reverse) + - [reverseNonEmpty](#reversenonempty) + - [rotate](#rotate) + - [rotateNonEmpty](#rotatenonempty) + - [setNonEmptyHead](#setnonemptyhead) + - [setNonEmptyLast](#setnonemptylast) + - [traverseFilter](#traversefilter) + - [traversePartition](#traversepartition) + - [union](#union) + - [unionNonEmpty](#unionnonempty) + - [uniq](#uniq) + - [uniqNonEmpty](#uniqnonempty) + - [unzip](#unzip) + - [unzipNonEmpty](#unzipnonempty) + - [zip](#zip) + - [zipNonEmpty](#zipnonempty) + - [zipNonEmptyWith](#zipnonemptywith) + - [zipWith](#zipwith) + +--- + +# constructors + +## empty + +**Signature** + +```ts +export declare const empty:
() => A[] +``` + +Added in v1.0.0 + +## make + +Builds a `NonEmptyArray` from an non-empty collection of elements. + +**Signature** + +```ts +export declare const make: ( + ...elements: Elements +) => [Elements[number], ...Elements[number][]] +``` + +Added in v1.0.0 + +## makeBy + +Return a `NonEmptyArray` of length `n` with element `i` initialized with `f(i)`. + +**Note**. `n` is normalized to an integer >= 1. + +**Signature** + +```ts +export declare const makeBy: (f: (i: number) => A) => (n: number) => [A, ...A[]] +``` + +Added in v1.0.0 + +## of + +**Signature** + +```ts +export declare const of: (a: A) => [A, ...A[]] +``` + +Added in v1.0.0 + +## range + +Return a `NonEmptyArray` containing a range of integers, including both endpoints. + +**Signature** + +```ts +export declare const range: (start: number, end: number) => [number, ...number[]] +``` + +Added in v1.0.0 + +## replicate + +Return a `NonEmptyArray` containing a value repeated the specified number of times. + +**Note**. `n` is normalized to an integer >= 1. + +**Signature** + +```ts +export declare const replicate: (a: A) => (n: number) => [A, ...A[]] +``` + +Added in v1.0.0 + +## unfold + +**Signature** + +```ts +export declare const unfold: (b: B, f: (b: B) => any) => A[] +``` + +Added in v1.0.0 + +# conversions + +## fromEither + +**Signature** + +```ts +export declare const fromEither: (self: any) => A[] +``` + +Added in v1.0.0 + +## fromIterable + +**Signature** + +```ts +export declare const fromIterable: (collection: Iterable) => A[] +``` + +Added in v1.0.0 + +## fromNullable + +**Signature** + +```ts +export declare const fromNullable: (a: A) => NonNullable[] +``` + +Added in v1.0.0 + +## fromOption + +**Signature** + +```ts +export declare const fromOption: (self: any) => A[] +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: readonly {}[] +``` + +Added in v1.0.0 + +## andThenBind + +A variant of `bind` that sequentially ignores the scope. + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: readonly B[] +) => (self: readonly A[]) => { [K in N | keyof A]: K extends keyof A ? A[K] : B }[] +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => readonly B[] +) => (self: readonly A[]) => { [K in N | keyof A]: K extends keyof A ? A[K] : B }[] +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (self: readonly A[]) => { [K in N]: A }[] +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: ( + name: Exclude, + f: (a: A) => B +) => (self: readonly A[]) => { [K in N | keyof A]: K extends keyof A ? A[K] : B }[] +``` + +Added in v1.0.0 + +# filtering + +## compact + +**Signature** + +```ts +export declare const compact: (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: any): (self: readonly C[]) => B[] + (predicate: any): (self: readonly B[]) => B[] +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: (f: (a: A) => any) => (self: Iterable) => B[] +``` + +Added in v1.0.0 + +## filterMapWithIndex + +**Signature** + +```ts +export declare const filterMapWithIndex: (f: (a: A, i: number) => any) => (self: Iterable) => B[] +``` + +Added in v1.0.0 + +## filterWithIndex + +**Signature** + +```ts +export declare const filterWithIndex: { + (refinement: (a: A, i: number) => a is B): (self: readonly C[]) => B[] + (predicate: (a: A, i: number) => boolean): (self: readonly B[]) => B[] +} +``` + +Added in v1.0.0 + +## partition + +**Signature** + +```ts +export declare const partition: { + (refinement: any): (self: readonly C[]) => [C[], B[]] + (predicate: any): (self: readonly B[]) => [B[], B[]] +} +``` + +Added in v1.0.0 + +## partitionMap + +**Signature** + +```ts +export declare const partitionMap: (f: (a: A) => any) => (self: readonly A[]) => [B[], C[]] +``` + +Added in v1.0.0 + +## partitionMapWithIndex + +**Signature** + +```ts +export declare const partitionMapWithIndex: (f: (a: A, i: number) => any) => (self: readonly A[]) => [B[], C[]] +``` + +Added in v1.0.0 + +## partitionWithIndex + +**Signature** + +```ts +export declare const partitionWithIndex: { + (refinement: (a: A, i: number) => a is B): (self: readonly C[]) => [C[], B[]] + (predicate: (a: A, i: number) => boolean): (self: readonly B[]) => [B[], B[]] +} +``` + +Added in v1.0.0 + +## separate + +**Signature** + +```ts +export declare const separate: (self: readonly any[]) => [A[], B[]] +``` + +Added in v1.0.0 + +## span + +Split an `Iterable` into two parts: + +1. the longest initial subarray for which all elements satisfy the specified predicate +2. the remaining elements + +**Signature** + +```ts +export declare function span( + refinement: Refinement +): (self: Iterable) => [init: Array, rest: Array] +export declare function span( + predicate: Predicate +): (self: Iterable) => [init: Array, rest: Array] +export declare function span(predicate: Predicate): (self: Iterable) => [init: Array, rest: Array] +``` + +Added in v1.0.0 + +## traverseFilterMap + +**Signature** + +```ts +export declare const traverseFilterMap: ( + F: any +) => (f: (a: A) => any) => (ta: readonly A[]) => any +``` + +Added in v1.0.0 + +## traversePartitionMap + +**Signature** + +```ts +export declare const traversePartitionMap: ( + F: any +) => (f: (a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +# folding + +## foldMap + +**Signature** + +```ts +export declare const foldMap: (M: any) => (f: (a: A) => M) => (self: readonly A[]) => M +``` + +Added in v1.0.0 + +## foldMapKind + +**Signature** + +```ts +export declare const foldMapKind: ( + F: any +) => (f: (a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## foldMapNonEmpty + +**Signature** + +```ts +export declare const foldMapNonEmpty: (S: any) => (f: (a: A) => S) => (self: readonly [A, ...A[]]) => S +``` + +Added in v1.0.0 + +## foldMapNonEmptyWithIndex + +**Signature** + +```ts +export declare const foldMapNonEmptyWithIndex: ( + S: any +) => (f: (a: A, i: number) => S) => (self: readonly [A, ...A[]]) => S +``` + +Added in v1.0.0 + +## foldMapWithIndex + +**Signature** + +```ts +export declare const foldMapWithIndex: (Monoid: any) => (f: (a: A, i: number) => M) => (self: readonly A[]) => M +``` + +Added in v1.0.0 + +## reduce + +**Signature** + +```ts +export declare const reduce: (b: B, f: (b: B, a: A) => B) => (self: readonly A[]) => B +``` + +Added in v1.0.0 + +## reduceKind + +**Signature** + +```ts +export declare const reduceKind: ( + F: any +) => (b: B, f: (b: B, a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## reduceRight + +**Signature** + +```ts +export declare const reduceRight: (b: B, f: (b: B, a: A) => B) => (self: readonly A[]) => B +``` + +Added in v1.0.0 + +## reduceRightKind + +**Signature** + +```ts +export declare const reduceRightKind: ( + F: any +) => (b: B, f: (b: B, a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## reduceRightWithIndex + +**Signature** + +```ts +export declare const reduceRightWithIndex: (b: B, f: (b: B, a: A, i: number) => B) => (self: readonly A[]) => B +``` + +Added in v1.0.0 + +## reduceWithIndex + +**Signature** + +```ts +export declare const reduceWithIndex: (b: B, f: (b: B, a: A, i: number) => B) => (self: readonly A[]) => B +``` + +Added in v1.0.0 + +## scan + +Fold an `Iterable` from the left, keeping all intermediate results instead of only the final result. + +**Signature** + +```ts +export declare const scan: (b: B, f: (b: B, a: A) => B) => (self: Iterable) => [B, ...B[]] +``` + +Added in v1.0.0 + +## scanRight + +Fold an `Iterable` from the right, keeping all intermediate results instead of only the final result. + +**Signature** + +```ts +export declare const scanRight: (b: B, f: (b: B, a: A) => B) => (self: Iterable) => [B, ...B[]] +``` + +Added in v1.0.0 + +# getters + +## chunksOf + +Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of +the `Iterable`. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive +definition of `chunksOf`; it satisfies the property that + +```ts +chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys))) +``` + +whenever `n` evenly divides the length of `self`. + +**Signature** + +```ts +export declare const chunksOf: (n: number) => (self: Iterable) => [A, ...A[]][] +``` + +Added in v1.0.0 + +## chunksOfNonEmpty + +Splits a `NonEmptyReadonlyArray` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of +the `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const chunksOfNonEmpty: (n: number) => (self: readonly [A, ...A[]]) => [[A, ...A[]], ...[A, ...A[]][]] +``` + +Added in v1.0.0 + +## drop + +Drop a max number of elements from the start of an `Iterable`, creating a new `Array`. + +**Note**. `n` is normalized to a non negative integer. + +**Signature** + +```ts +export declare const drop: (n: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## dropRight + +Drop a max number of elements from the end of an `Iterable`, creating a new `Array`. + +**Note**. `n` is normalized to a non negative integer. + +**Signature** + +```ts +export declare const dropRight: (n: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## dropWhile + +Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. + +**Signature** + +```ts +export declare function dropWhile(refinement: Refinement): (self: Iterable) => Array +export declare function dropWhile(predicate: Predicate): (self: Iterable) => Array +export declare function dropWhile(predicate: Predicate): (self: Iterable) => Array +``` + +Added in v1.0.0 + +## findFirst + +Find the first element for which a predicate holds. + +**Signature** + +```ts +export declare function findFirst(refinement: Refinement): (self: Iterable) => Option +export declare function findFirst(predicate: Predicate): (self: Iterable) => Option +export declare function findFirst(predicate: Predicate): (self: Iterable) => Option +``` + +Added in v1.0.0 + +## findFirstIndex + +Return the first index for which a predicate holds. + +**Signature** + +```ts +export declare const findFirstIndex: (predicate: any) => (self: Iterable) => any +``` + +Added in v1.0.0 + +## findLast + +Find the last element for which a predicate holds. + +**Signature** + +```ts +export declare function findLast(refinement: Refinement): (self: Iterable) => Option +export declare function findLast(predicate: Predicate): (self: Iterable) => Option +export declare function findLast(predicate: Predicate): (self: Iterable) => Option +``` + +Added in v1.0.0 + +## findLastIndex + +Return the last index for which a predicate holds. + +**Signature** + +```ts +export declare const findLastIndex: (predicate: any) => (self: Iterable) => any +``` + +Added in v1.0.0 + +## get + +This function provides a safe way to read a value at a particular index from a `ReadonlyArray`. + +**Signature** + +```ts +export declare const get: (index: number) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## head + +Get the first element of a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. + +**Signature** + +```ts +export declare const head: (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## headNonEmpty + +**Signature** + +```ts +export declare const headNonEmpty: (self: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## init + +Get all but the last element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. + +**Signature** + +```ts +export declare const init: (self: Iterable) => any +``` + +Added in v1.0.0 + +## initNonEmpty + +Get all but the last element of a non empty array, creating a new array. + +**Signature** + +```ts +export declare const initNonEmpty: (self: readonly [A, ...A[]]) => A[] +``` + +Added in v1.0.0 + +## last + +Get the last element in a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. + +**Signature** + +```ts +export declare const last: (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## lastNonEmpty + +**Signature** + +```ts +export declare const lastNonEmpty: (as: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## lefts + +Return all the `Left` elements from an `Interable` of `Either`s. + +**Signature** + +```ts +export declare const lefts: (self: Iterable) => E[] +``` + +Added in v1.0.0 + +## length + +Return the number of elements in a `ReadonlyArray`. + +**Signature** + +```ts +export declare const length: (self: readonly A[]) => number +``` + +Added in v1.0.0 + +## rights + +Return all the `Right` elements from an `Interable` of `Either`s. + +**Signature** + +```ts +export declare const rights: (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## splitAt + +Splits an `Iterable` into two pieces, the first piece has max `n` elements. + +**Signature** + +```ts +export declare const splitAt: (n: number) => (self: Iterable) => [A[], A[]] +``` + +Added in v1.0.0 + +## splitNonEmptyAt + +Splits a `NonEmptyReadonlyArray` into two pieces, the first piece has max `n` elements. + +**Signature** + +```ts +export declare const splitNonEmptyAt: (n: number) => (self: readonly [A, ...A[]]) => [[A, ...A[]], A[]] +``` + +Added in v1.0.0 + +## tail + +Get all but the first element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. + +**Signature** + +```ts +export declare const tail: (self: Iterable) => any +``` + +Added in v1.0.0 + +## tailNonEmpty + +**Signature** + +```ts +export declare const tailNonEmpty: (self: readonly [A, ...A[]]) => A[] +``` + +Added in v1.0.0 + +## take + +Keep only a max number of elements from the start of an `Iterable`, creating a new `Array`. + +**Note**. `n` is normalized to a non negative integer. + +**Signature** + +```ts +export declare const take: (n: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## takeRight + +Keep only a max number of elements from the end of an `Iterable`, creating a new `Array`. + +**Note**. `n` is normalized to a non negative integer. + +**Signature** + +```ts +export declare const takeRight: (n: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## takeWhile + +Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. + +**Signature** + +```ts +export declare function takeWhile(refinement: Refinement): (self: Iterable) => Array +export declare function takeWhile(predicate: Predicate): (self: Iterable) => Array +``` + +Added in v1.0.0 + +## unappend + +Return a tuple containing a copy of the `NonEmptyReadonlyArray` without its last element, and that last element. + +**Signature** + +```ts +export declare const unappend: (self: readonly [A, ...A[]]) => [A[], A] +``` + +Added in v1.0.0 + +## unprepend + +Return a tuple containing the first element, and a new `Array` of the remaining elements, if any. + +**Signature** + +```ts +export declare const unprepend: (self: readonly [A, ...A[]]) => [A, A[]] +``` + +Added in v1.0.0 + +# grouping + +## group + +Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s. + +**Signature** + +```ts +export declare const group: (equivalence: any) => (self: readonly [A, ...A[]]) => [[A, ...A[]], ...[A, ...A[]][]] +``` + +Added in v1.0.0 + +## groupBy + +Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning +function on each element, and grouping the results according to values returned + +**Signature** + +```ts +export declare const groupBy: (f: (a: A) => string) => (self: Iterable) => Record +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: any +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: any +``` + +Added in v1.0.0 + +## Compactable + +**Signature** + +```ts +export declare const Compactable: any +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: any +``` + +Added in v1.0.0 + +## Filterable + +**Signature** + +```ts +export declare const Filterable: any +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: any +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: any +``` + +Added in v1.0.0 + +## TraversableFilterable + +**Signature** + +```ts +export declare const TraversableFilterable: any +``` + +Added in v1.0.0 + +## getIntersectionSemigroup + +**Signature** + +```ts +export declare const getIntersectionSemigroup: (equivalence: any) => any +``` + +Added in v1.0.0 + +## getMonoid + +Returns a `Monoid` for `ReadonlyArray`. + +**Signature** + +```ts +export declare const getMonoid: () => any +``` + +Added in v1.0.0 + +## getSemigroup + +Returns a `Semigroup` for `ReadonlyArray`. + +**Signature** + +```ts +export declare const getSemigroup: () => any +``` + +Added in v1.0.0 + +## getUnionMonoid + +**Signature** + +```ts +export declare const getUnionMonoid: (equivalence: any) => any +``` + +Added in v1.0.0 + +## getUnionSemigroup + +**Signature** + +```ts +export declare const getUnionSemigroup: (equivalence: any) => any +``` + +Added in v1.0.0 + +# lifting + +## every + +Check if a predicate holds true for every `ReadonlyArray` member. + +**Signature** + +```ts +export declare function every( + refinement: Refinement +): Refinement, ReadonlyArray> +export declare function every(predicate: Predicate): Predicate> +``` + +Added in v1.0.0 + +## lift2 + +Lifts a binary function into `ReadonlyArray`. + +**Signature** + +```ts +export declare const lift2: (f: (a: A, b: B) => C) => (fa: readonly A[], fb: readonly B[]) => C[] +``` + +Added in v1.0.0 + +## lift3 + +Lifts a ternary function into `ReadonlyArray`. + +**Signature** + +```ts +export declare const lift3: ( + f: (a: A, b: B, c: C) => D +) => (fa: readonly A[], fb: readonly B[], fc: readonly C[]) => D[] +``` + +Added in v1.0.0 + +## liftEither + +**Signature** + +```ts +export declare const liftEither: (f: (...a: A) => any) => (...a: A) => B[] +``` + +Added in v1.0.0 + +## liftMonoid + +**Signature** + +```ts +export declare const liftMonoid: (M: any) => any +``` + +Added in v1.0.0 + +## liftNullable + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined +) => (...a: A) => NonNullable[] +``` + +Added in v1.0.0 + +## liftOption + +**Signature** + +```ts +export declare const liftOption: (f: (...a: A) => any) => (...a: A) => B[] +``` + +Added in v1.0.0 + +## liftOrder + +This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. +The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. +If all elements are equal, the arrays are then compared based on their length. +It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. + +**Signature** + +```ts +export declare const liftOrder: (O: any) => any +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: any): (c: C) => B[] + (predicate: any): (b: B) => B[] +} +``` + +Added in v1.0.0 + +## liftSemigroup + +**Signature** + +```ts +export declare const liftSemigroup: (S: any) => any +``` + +Added in v1.0.0 + +# mapping + +## as + +Maps the success value of this effect to the specified constant value. + +**Signature** + +```ts +export declare const as: (b: B) => (self: ReadonlyArray) => B[] +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: (a: A) => (self: readonly ((a: A) => B)[]) => B[] +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## map + +**Signature** + +```ts +export declare const map: (f: (a: A) => B) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## mapNonEmpty + +**Signature** + +```ts +export declare const mapNonEmpty: (f: (a: A) => B) => (self: readonly [A, ...A[]]) => [B, ...B[]] +``` + +Added in v1.0.0 + +## mapNonEmptyWithIndex + +**Signature** + +```ts +export declare const mapNonEmptyWithIndex: ( + f: (a: A, i: number) => B +) => (self: readonly [A, ...A[]]) => [B, ...B[]] +``` + +Added in v1.0.0 + +## mapWithIndex + +**Signature** + +```ts +export declare const mapWithIndex: (f: (a: A, i: number) => B) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: readonly A[]) => [A][] +``` + +Added in v1.0.0 + +# models + +## NonEmptyArray (type alias) + +**Signature** + +```ts +export type NonEmptyArray = [A, ...Array] +``` + +Added in v1.0.0 + +## NonEmptyReadonlyArray (type alias) + +**Signature** + +```ts +export type NonEmptyReadonlyArray = readonly [A, ...Array] +``` + +Added in v1.0.0 + +# pattern matching + +## match + +**Signature** + +```ts +export declare const match: ( + onEmpty: any, + onNonEmpty: (head: A, tail: A[]) => C +) => (self: readonly A[]) => B | C +``` + +Added in v1.0.0 + +## matchRight + +**Signature** + +```ts +export declare const matchRight: ( + onEmpty: any, + onNonEmpty: (init: A[], last: A) => C +) => (self: readonly A[]) => B | C +``` + +Added in v1.0.0 + +# predicates + +## contains + +Returns a function that checks if a `ReadonlyArray` contains a given value using a provided `equivalence` function. + +**Signature** + +```ts +export declare const contains: (equivalence: any) => (a: A) => (self: Iterable) => boolean +``` + +Added in v1.0.0 + +## isEmpty + +Test whether a `ReadonlyArray` is empty narrowing down the type to `[]`. + +**Signature** + +```ts +export declare const isEmpty: (self: readonly A[]) => self is readonly [] +``` + +Added in v1.0.0 + +## isNonEmpty + +Test whether a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const isNonEmpty: (self: readonly A[]) => self is readonly [A, ...A[]] +``` + +Added in v1.0.0 + +## some + +Check if a predicate holds true for any `ReadonlyArray` member. + +**Signature** + +```ts +export declare const some: (predicate: any) => (self: readonly A[]) => self is readonly [A, ...A[]] +``` + +Added in v1.0.0 + +# sequencing + +## flatMap + +**Signature** + +```ts +export declare const flatMap: (f: (a: A) => readonly B[]) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## flatMapNonEmpty + +**Signature** + +```ts +export declare const flatMapNonEmpty: ( + f: (a: A) => readonly [B, ...B[]] +) => (self: readonly [A, ...A[]]) => [B, ...B[]] +``` + +Added in v1.0.0 + +## flatMapNonEmptyWithIndex + +**Signature** + +```ts +export declare const flatMapNonEmptyWithIndex: ( + f: (a: A, i: number) => readonly [B, ...B[]] +) => (self: readonly [A, ...A[]]) => [B, ...B[]] +``` + +Added in v1.0.0 + +## flatMapNullable + +**Signature** + +```ts +export declare const flatMapNullable: ( + f: (a: A) => B | null | undefined +) => (self: readonly A[]) => NonNullable[] +``` + +Added in v1.0.0 + +## flatMapWithIndex + +**Signature** + +```ts +export declare const flatMapWithIndex: (f: (a: A, i: number) => readonly B[]) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: readonly (readonly A[])[]) => A[] +``` + +Added in v1.0.0 + +## flattenNonEmpty + +**Signature** + +```ts +export declare const flattenNonEmpty: ( + self: readonly [readonly [A, ...A[]], ...(readonly [A, ...A[]])[]] +) => [A, ...A[]] +``` + +Added in v1.0.0 + +# sorting + +## sort + +Sort the elements of an `Iterable` in increasing order, creating a new `Array`. + +**Signature** + +```ts +export declare const sort: (O: any) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## sortBy + +Sort the elements of an `Iterable` in increasing order, where elements are compared +using first `orders[0]`, then `orders[1]`, etc... + +**Signature** + +```ts +export declare const sortBy: (...orders: readonly any[]) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## sortByNonEmpty + +**Signature** + +```ts +export declare const sortByNonEmpty: ( + ...orders: readonly any[] +) => (as: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +## sortNonEmpty + +Sort the elements of a `NonEmptyReadonlyArray` in increasing order, creating a new `NonEmptyArray`. + +**Signature** + +```ts +export declare const sortNonEmpty: (O: any) => (self: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: (F: any) => (self: readonly any[]) => any +``` + +Added in v1.0.0 + +## sequenceNonEmpty + +**Signature** + +```ts +export declare const sequenceNonEmpty: (F: any) => (self: readonly [any, ...any[]]) => any +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: ( + F: any +) => (f: (a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## traverseNonEmpty + +**Signature** + +```ts +export declare const traverseNonEmpty: ( + F: any +) => (f: (a: A) => any) => (self: readonly [A, ...A[]]) => any +``` + +Added in v1.0.0 + +## traverseNonEmptyWithIndex + +**Signature** + +```ts +export declare const traverseNonEmptyWithIndex: ( + F: any +) => (f: (a: A, i: number) => any) => (self: readonly [A, ...A[]]) => any +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: any +) => (f: (a: A) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +## traverseWithIndex + +**Signature** + +```ts +export declare const traverseWithIndex: ( + F: any +) => (f: (a: A, i: number) => any) => (self: readonly A[]) => any +``` + +Added in v1.0.0 + +# type lambdas + +## ReadonlyArrayTypeLambda (interface) + +**Signature** + +```ts +export interface ReadonlyArrayTypeLambda extends TypeLambda { + readonly type: ReadonlyArray +} +``` + +Added in v1.0.0 + +# unsafe + +## unsafeGet + +Gets an element unsafely, will throw on out of bounds. + +**Signature** + +```ts +export declare const unsafeGet: (index: number) => (self: readonly A[]) => A +``` + +Added in v1.0.0 + +# utils + +## ap + +**Signature** + +```ts +export declare const ap: (fa: readonly A[]) => (self: readonly ((a: A) => B)[]) => B[] +``` + +Added in v1.0.0 + +## append + +Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`. + +**Signature** + +```ts +export declare const append: (last: B) => (self: Iterable) => [B | A, ...(B | A)[]] +``` + +Added in v1.0.0 + +## appendAll + +**Signature** + +```ts +export declare const appendAll: (that: Iterable) => (self: Iterable) => (B | A)[] +``` + +Added in v1.0.0 + +## appendAllNonEmpty + +**Signature** + +```ts +export declare function appendAllNonEmpty( + that: NonEmptyReadonlyArray +): (self: Iterable) => NonEmptyArray +export declare function appendAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => NonEmptyArray +``` + +Added in v1.0.0 + +## chop + +A useful recursion pattern for processing an `Iterable` to produce a new `Array`, often used for "chopping" up the input +`Iterable`. Typically chop is called with some function that will consume an initial prefix of the `Iterable` and produce a +value and the rest of the `Array`. + +**Signature** + +```ts +export declare const chop: ( + f: (as: readonly [A, ...A[]]) => readonly [B, readonly A[]] +) => (self: Iterable) => B[] +``` + +Added in v1.0.0 + +## chopNonEmpty + +A useful recursion pattern for processing a `NonEmptyReadonlyArray` to produce a new `NonEmptyReadonlyArray`, often used for "chopping" up the input +`NonEmptyReadonlyArray`. Typically `chop` is called with some function that will consume an initial prefix of the `NonEmptyReadonlyArray` and produce a +value and the tail of the `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const chopNonEmpty: ( + f: (as: readonly [A, ...A[]]) => readonly [B, readonly A[]] +) => (self: readonly [A, ...A[]]) => [B, ...B[]] +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: ( + bfc: (b: B) => readonly C[] +) => (afb: (a: A) => readonly B[]) => (a: A) => readonly C[] +``` + +Added in v1.0.0 + +## copy + +**Signature** + +```ts +export declare function copy(self: NonEmptyReadonlyArray): NonEmptyArray +export declare function copy(self: ReadonlyArray): Array +``` + +Added in v1.0.0 + +## difference + +Creates a `Array` of values not included in the other given `Iterable`. +The order and references of result values are determined by the first `Iterable`. + +**Signature** + +```ts +export declare const difference: (equivalence: any) => (that: Iterable) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## extend + +**Signature** + +```ts +export declare const extend: (f: (as: readonly A[]) => B) => (self: readonly A[]) => B[] +``` + +Added in v1.0.0 + +## insertAt + +Insert an element at the specified index, creating a new `NonEmptyArray`, +or return `None` if the index is out of bounds. + +**Signature** + +```ts +export declare const insertAt: (i: number, b: B) => (self: Iterable) => any +``` + +Added in v1.0.0 + +## intercalate + +Fold a data structure, accumulating values in some `Monoid`, combining adjacent elements +using the specified separator. + +**Signature** + +```ts +export declare const intercalate: (M: any) => (middle: A) => (self: readonly A[]) => A +``` + +Added in v1.0.0 + +## intercalateNonEmpty + +Places an element in between members of a `NonEmptyReadonlyArray`, then folds the results using the provided `Semigroup`. + +**Signature** + +```ts +export declare const intercalateNonEmpty: (S: any) => (middle: A) => (self: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## intersection + +Creates an `Array` of unique values that are included in all given `Iterable`s. +The order and references of result values are determined by the first `Iterable`. + +**Signature** + +```ts +export declare const intersection: (equivalence: any) => (that: Iterable) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## intersperse + +Places an element in between members of an `Iterable` + +**Signature** + +```ts +export declare const intersperse: (middle: B) => (self: Iterable) => (B | A)[] +``` + +Added in v1.0.0 + +## intersperseNonEmpty + +Places an element in between members of a `NonEmptyReadonlyArray` + +**Signature** + +```ts +export declare const intersperseNonEmpty: (middle: B) => (self: readonly [A, ...A[]]) => [B | A, ...(B | A)[]] +``` + +Added in v1.0.0 + +## join + +**Signature** + +```ts +export declare const join: (sep: string) => (self: ReadonlyArray) => string +``` + +Added in v1.0.0 + +## max + +**Signature** + +```ts +export declare const max: (O: any) => (self: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## min + +**Signature** + +```ts +export declare const min: (O: any) => (self: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## modify + +Apply a function to the element at the specified index, creating a new `Array`, +or return a copy of the input if the index is out of bounds. + +**Signature** + +```ts +export declare const modify: (i: number, f: (a: A) => B) => (self: Iterable) => (A | B)[] +``` + +Added in v1.0.0 + +## modifyNonEmptyHead + +Apply a function to the head, creating a new `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const modifyNonEmptyHead: (f: (a: A) => B) => (self: readonly [A, ...A[]]) => [A | B, ...(A | B)[]] +``` + +Added in v1.0.0 + +## modifyNonEmptyLast + +Apply a function to the last element, creating a new `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const modifyNonEmptyLast: (f: (a: A) => B) => (self: readonly [A, ...A[]]) => [A | B, ...(A | B)[]] +``` + +Added in v1.0.0 + +## modifyOption + +Apply a function to the element at the specified index, creating a new `Array`, +or return `None` if the index is out of bounds. + +**Signature** + +```ts +export declare const modifyOption: (i: number, f: (a: A) => B) => (self: Iterable) => any +``` + +Added in v1.0.0 + +## prepend + +Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`. + +**Signature** + +```ts +export declare const prepend: (head: B) => (self: Iterable) => [B | A, ...(B | A)[]] +``` + +Added in v1.0.0 + +## prependAll + +**Signature** + +```ts +export declare const prependAll: (that: Iterable) => (self: Iterable) => (B | A)[] +``` + +Added in v1.0.0 + +## prependAllNonEmpty + +**Signature** + +```ts +export declare function prependAllNonEmpty( + that: NonEmptyReadonlyArray +): (self: Iterable) => NonEmptyArray +export declare function prependAllNonEmpty( + that: Iterable +): (self: NonEmptyReadonlyArray) => NonEmptyArray +``` + +Added in v1.0.0 + +## remove + +Delete the element at the specified index, creating a new `Array`, +or return a copy of the input if the index is out of bounds. + +**Signature** + +```ts +export declare const remove: (i: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## replace + +Change the element at the specified index, creating a new `Array`, +or return a copy of the input if the index is out of bounds. + +**Signature** + +```ts +export declare const replace: (i: number, b: B) => (self: Iterable) => (B | A)[] +``` + +Added in v1.0.0 + +## replaceOption + +**Signature** + +```ts +export declare const replaceOption: (i: number, b: B) => (self: Iterable) => any +``` + +Added in v1.0.0 + +## reverse + +Reverse an `Iterable`, creating a new `Array`. + +**Signature** + +```ts +export declare const reverse: (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## reverseNonEmpty + +**Signature** + +```ts +export declare const reverseNonEmpty: (self: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +## rotate + +Rotate an `Iterable` by `n` steps. + +**Signature** + +```ts +export declare const rotate: (n: number) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## rotateNonEmpty + +Rotate a `NonEmptyReadonlyArray` by `n` steps. + +**Signature** + +```ts +export declare const rotateNonEmpty: (n: number) => (self: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +## setNonEmptyHead + +Change the head, creating a new `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const setNonEmptyHead: (b: B) => (self: readonly [A, ...A[]]) => [B | A, ...(B | A)[]] +``` + +Added in v1.0.0 + +## setNonEmptyLast + +Change the last element, creating a new `NonEmptyReadonlyArray`. + +**Signature** + +```ts +export declare const setNonEmptyLast: (b: B) => (self: readonly [A, ...A[]]) => [B | A, ...(B | A)[]] +``` + +Added in v1.0.0 + +## traverseFilter + +Filter values inside a context. + +**Signature** + +```ts +export declare const traverseFilter: ( + F: any +) => (predicate: (a: A) => any) => (self: readonly B[]) => any +``` + +Added in v1.0.0 + +## traversePartition + +**Signature** + +```ts +export declare const traversePartition: ( + F: any +) => (predicate: (a: A) => any) => (self: readonly B[]) => any +``` + +Added in v1.0.0 + +## union + +**Signature** + +```ts +export declare const union: (equivalence: any) => (that: readonly A[]) => (self: readonly A[]) => A[] +``` + +Added in v1.0.0 + +## unionNonEmpty + +**Signature** + +```ts +export declare const unionNonEmpty: (equivalence: any) => { + (that: readonly [A, ...A[]]): (self: readonly A[]) => [A, ...A[]] + (that: readonly A[]): (self: readonly [A, ...A[]]) => [A, ...A[]] +} +``` + +Added in v1.0.0 + +## uniq + +Remove duplicates from am `Iterable`, keeping the first occurrence of an element. + +**Signature** + +```ts +export declare const uniq: (equivalence: any) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## uniqNonEmpty + +Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element. + +**Signature** + +```ts +export declare const uniqNonEmpty: (equivalence: any) => (self: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +## unzip + +This function is the inverse of `zip`. Takes an `Iterable` of pairs and return two corresponding `Array`s. + +**Signature** + +```ts +export declare const unzip: (self: Iterable<[A, B]>) => [A[], B[]] +``` + +Added in v1.0.0 + +## unzipNonEmpty + +**Signature** + +```ts +export declare const unzipNonEmpty: (self: readonly [[A, B], ...[A, B][]]) => [[A, ...A[]], [B, ...B[]]] +``` + +Added in v1.0.0 + +## zip + +Takes two `Iterable`s and returns an `Array` of corresponding pairs. +If one input `Iterable` is short, excess elements of the +longer `Iterable` are discarded. + +**Signature** + +```ts +export declare const zip: (that: Iterable) => (self: Iterable) => [A, B][] +``` + +Added in v1.0.0 + +## zipNonEmpty + +**Signature** + +```ts +export declare const zipNonEmpty: ( + that: readonly [B, ...B[]] +) => (self: readonly [A, ...A[]]) => [[A, B], ...[A, B][]] +``` + +Added in v1.0.0 + +## zipNonEmptyWith + +**Signature** + +```ts +export declare const zipNonEmptyWith: ( + that: readonly [B, ...B[]], + f: (a: A, b: B) => C +) => (self: readonly [A, ...A[]]) => [C, ...C[]] +``` + +Added in v1.0.0 + +## zipWith + +Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one +input `Iterable` is short, excess elements of the longer `Iterable` are discarded. + +**Signature** + +```ts +export declare const zipWith: (that: Iterable, f: (a: A, b: B) => C) => (self: Iterable) => C[] +``` + +Added in v1.0.0 diff --git a/docs/modules/ReadonlyRecord.ts.md b/docs/modules/ReadonlyRecord.ts.md new file mode 100644 index 000000000..36723a0b7 --- /dev/null +++ b/docs/modules/ReadonlyRecord.ts.md @@ -0,0 +1,78 @@ +--- +title: ReadonlyRecord.ts +nav_order: 13 +parent: Modules +--- + +## ReadonlyRecord overview + +This module provides utility functions for working with records in TypeScript. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [getters](#getters) + - [get](#get) +- [models](#models) + - [ReadonlyRecord (interface)](#readonlyrecord-interface) +- [utils](#utils) + - [modifyOption](#modifyoption) + - [replaceOption](#replaceoption) + +--- + +# getters + +## get + +This function provides a safe way to read a value at a particular key from a record. + +**Signature** + +```ts +export declare const get: (key: string) =>
(self: ReadonlyRecord) => any +``` + +Added in v1.0.0 + +# models + +## ReadonlyRecord (interface) + +**Signature** + +```ts +export interface ReadonlyRecord { + readonly [x: string]: A +} +``` + +Added in v1.0.0 + +# utils + +## modifyOption + +Apply a function to the element at the specified key, creating a new record, +or return `None` if the key doesn't exist. + +**Signature** + +```ts +export declare const modifyOption: (key: string, f: (a: A) => B) => (self: ReadonlyRecord) => any +``` + +Added in v1.0.0 + +## replaceOption + +**Signature** + +```ts +export declare const replaceOption: (key: string, b: B) => (self: ReadonlyRecord) => any +``` + +Added in v1.0.0 diff --git a/docs/modules/String.ts.md b/docs/modules/String.ts.md new file mode 100644 index 000000000..b6fbb9c7b --- /dev/null +++ b/docs/modules/String.ts.md @@ -0,0 +1,434 @@ +--- +title: String.ts +nav_order: 14 +parent: Modules +--- + +## String overview + +This module provides utility functions and type class instances for working with the `string` type in TypeScript. +It includes functions for basic string manipulation, as well as type class instances for +`Equivalence`, `Order`, `Semigroup`, and `Monoid`. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isString](#isstring) +- [instances](#instances) + - [Equivalence](#equivalence) + - [Monoid](#monoid) + - [Order](#order) + - [Semigroup](#semigroup) +- [utils](#utils) + - [concat](#concat) + - [empty](#empty) + - [endsWith](#endswith) + - [includes](#includes) + - [isEmpty](#isempty) + - [isNonEmpty](#isnonempty) + - [length](#length) + - [replace](#replace) + - [slice](#slice) + - [split](#split) + - [startsWith](#startswith) + - [takeLeft](#takeleft) + - [takeRight](#takeright) + - [toLowerCase](#tolowercase) + - [toUpperCase](#touppercase) + - [trim](#trim) + - [trimEnd](#trimend) + - [trimStart](#trimstart) + +--- + +# guards + +## isString + +**Signature** + +```ts +export declare const isString: any +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: any +``` + +Added in v1.0.0 + +## Monoid + +`string` monoid under concatenation. + +The `empty` value is `''`. + +**Signature** + +```ts +export declare const Monoid: any +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: any +``` + +Added in v1.0.0 + +## Semigroup + +`string` semigroup under concatenation. + +**Signature** + +```ts +export declare const Semigroup: any +``` + +Added in v1.0.0 + +# utils + +## concat + +**Signature** + +```ts +export declare const concat: (that: string) => (self: string) => string +``` + +Added in v1.0.0 + +## empty + +**Signature** + +```ts +export declare const empty: '' +``` + +Added in v1.0.0 + +## endsWith + +**Signature** + +```ts +export declare const endsWith: (searchString: string, position?: number | undefined) => (s: string) => boolean +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.endsWith('c')), true) +assert.deepStrictEqual(pipe('ab', S.endsWith('c')), false) +``` + +Added in v1.0.0 + +## includes + +**Signature** + +```ts +export declare const includes: (searchString: string, position?: number | undefined) => (s: string) => boolean +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.includes('b')), true) +assert.deepStrictEqual(pipe('abc', S.includes('d')), false) +``` + +Added in v1.0.0 + +## isEmpty + +Test whether a `string` is empty. + +**Signature** + +```ts +export declare const isEmpty: (s: string) => s is '' +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('', S.isEmpty), true) +assert.deepStrictEqual(pipe('a', S.isEmpty), false) +``` + +Added in v1.0.0 + +## isNonEmpty + +Test whether a `string` is non empty. + +**Signature** + +```ts +export declare const isNonEmpty: (s: string) => boolean +``` + +Added in v1.0.0 + +## length + +Calculate the number of characters in a `string`. + +**Signature** + +```ts +export declare const length: (s: string) => number +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.length), 3) +``` + +Added in v1.0.0 + +## replace + +**Signature** + +```ts +export declare const replace: (searchValue: string | RegExp, replaceValue: string) => (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.replace('b', 'd')), 'adc') +``` + +Added in v1.0.0 + +## slice + +**Signature** + +```ts +export declare const slice: (start: number, end: number) => (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abcd', S.slice(1, 3)), 'bc') +``` + +Added in v1.0.0 + +## split + +**Signature** + +```ts +export declare const split: (separator: string | RegExp) => (s: string) => any +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.split('')), ['a', 'b', 'c']) +assert.deepStrictEqual(pipe('', S.split('')), ['']) +``` + +Added in v1.0.0 + +## startsWith + +**Signature** + +```ts +export declare const startsWith: (searchString: string, position?: number | undefined) => (s: string) => boolean +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('abc', S.startsWith('a')), true) +assert.deepStrictEqual(pipe('bc', S.startsWith('a')), false) +``` + +Added in v1.0.0 + +## takeLeft + +Keep the specified number of characters from the start of a string. + +If `n` is larger than the available number of characters, the string will +be returned whole. + +If `n` is not a positive number, an empty string will be returned. + +If `n` is a float, it will be rounded down to the nearest integer. + +**Signature** + +```ts +export declare const takeLeft: (n: number) => (self: string) => string +``` + +Added in v1.0.0 + +## takeRight + +Keep the specified number of characters from the end of a string. + +If `n` is larger than the available number of characters, the string will +be returned whole. + +If `n` is not a positive number, an empty string will be returned. + +If `n` is a float, it will be rounded down to the nearest integer. + +**Signature** + +```ts +export declare const takeRight: (n: number) => (s: string) => string +``` + +Added in v1.0.0 + +## toLowerCase + +**Signature** + +```ts +export declare const toLowerCase: (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('A', S.toLowerCase), 'a') +``` + +Added in v1.0.0 + +## toUpperCase + +**Signature** + +```ts +export declare const toUpperCase: (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe('a', S.toUpperCase), 'A') +``` + +Added in v1.0.0 + +## trim + +**Signature** + +```ts +export declare const trim: (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe(' a ', S.trim), 'a') +``` + +Added in v1.0.0 + +## trimEnd + +**Signature** + +```ts +export declare const trimEnd: (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe(' a ', S.trimEnd), ' a') +``` + +Added in v1.0.0 + +## trimStart + +**Signature** + +```ts +export declare const trimStart: (s: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe(' a ', S.trimStart), 'a ') +``` + +Added in v1.0.0 diff --git a/docs/modules/Struct.ts.md b/docs/modules/Struct.ts.md new file mode 100644 index 000000000..64c4c7df8 --- /dev/null +++ b/docs/modules/Struct.ts.md @@ -0,0 +1,117 @@ +--- +title: Struct.ts +nav_order: 15 +parent: Modules +--- + +## Struct overview + +This module provides utility functions for working with structs in TypeScript. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [utils](#utils) + - [getEquivalence](#getequivalence) + - [getMonoid](#getmonoid) + - [getOrder](#getorder) + - [getSemigroup](#getsemigroup) + - [nonEmptyProduct](#nonemptyproduct) + - [omit](#omit) + - [pick](#pick) + - [product](#product) + +--- + +# utils + +## getEquivalence + +**Signature** + +```ts +export declare const getEquivalence: any +``` + +Added in v1.0.0 + +## getMonoid + +**Signature** + +```ts +export declare const getMonoid: any +``` + +Added in v1.0.0 + +## getOrder + +**Signature** + +```ts +export declare const getOrder: any +``` + +Added in v1.0.0 + +## getSemigroup + +**Signature** + +```ts +export declare const getSemigroup: any +``` + +Added in v1.0.0 + +## nonEmptyProduct + +**Signature** + +```ts +export declare const nonEmptyProduct: any +``` + +Added in v1.0.0 + +## omit + +Create a new object by omitting properties of an existing object. + +**Signature** + +```ts +export declare const omit: ( + ...keys: Keys +) => (s: S) => { [K in Exclude]: S[K] } +``` + +Added in v1.0.0 + +## pick + +Create a new object by picking properties of an existing object. + +**Signature** + +```ts +export declare const pick: ( + ...keys: Keys +) => (s: S) => { [K in Keys[number]]: S[K] } +``` + +Added in v1.0.0 + +## product + +**Signature** + +```ts +export declare const product: any +``` + +Added in v1.0.0 diff --git a/docs/modules/Symbol.ts.md b/docs/modules/Symbol.ts.md new file mode 100644 index 000000000..4f3bcc7b8 --- /dev/null +++ b/docs/modules/Symbol.ts.md @@ -0,0 +1,30 @@ +--- +title: Symbol.ts +nav_order: 16 +parent: Modules +--- + +## Symbol overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isSymbol](#issymbol) + +--- + +# guards + +## isSymbol + +**Signature** + +```ts +export declare const isSymbol: (u: unknown) => u is symbol +``` + +Added in v1.0.0 diff --git a/docs/modules/These.ts.md b/docs/modules/These.ts.md new file mode 100644 index 000000000..062181e63 --- /dev/null +++ b/docs/modules/These.ts.md @@ -0,0 +1,1517 @@ +--- +title: These.ts +nav_order: 17 +parent: Modules +--- + +## These overview + +A data structure providing "inclusive-or" as opposed to `Either`'s "exclusive-or". + +If you interpret `Either` as suggesting the computation may either fail or succeed (exclusively), then +`These` may fail, succeed, or do both at the same time. + +There are a few ways to interpret the `Both` case: + +1. You can think of a computation that has a non-fatal error. +2. You can think of a computation that went as far as it could before erroring. +3. You can think of a computation that keeps track of errors as it completes. + +Another way you can think of `These` is saying that we want to handle `E` kind of data, `A` kind of data, or +both `E` and `A` kind of data at the same time. This is particularly useful when it comes to displaying UI's. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combining](#combining) + - [getFirstLeftMonoid](#getfirstleftmonoid) + - [getFirstLeftSemigroup](#getfirstleftsemigroup) + - [getFirstRightOrBothSemigroup](#getfirstrightorbothsemigroup) +- [constructors](#constructors) + - [both](#both) + - [fail](#fail) + - [left](#left) + - [leftOrBoth](#leftorboth) + - [of](#of) + - [right](#right) + - [rightOrBoth](#rightorboth) + - [succeed](#succeed) + - [warn](#warn) +- [conversions](#conversions) + - [absolve](#absolve) + - [condemn](#condemn) + - [fromEither](#fromeither) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [fromOption](#fromoption) + - [fromThese](#fromthese) + - [fromTuple](#fromtuple) + - [toEither](#toeither) +- [debugging](#debugging) + - [inspectBoth](#inspectboth) + - [inspectLeft](#inspectleft) + - [inspectRight](#inspectright) + - [inspectRightOrBoth](#inspectrightorboth) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [andThenBindEither](#andthenbindeither) + - [andThenBindThese](#andthenbindthese) + - [bind](#bind) + - [bindEither](#bindeither) + - [bindThese](#bindthese) + - [bindTo](#bindto) + - [let](#let) +- [error handling](#error-handling) + - [catchAll](#catchall) + - [firstRightOrBothOf](#firstrightorbothof) + - [mapLeft](#mapleft) + - [orElse](#orelse) + - [orElseEither](#orelseeither) + - [orElseFail](#orelsefail) + - [orElseSucceed](#orelsesucceed) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [filterMap](#filtermap) +- [getters](#getters) + - [getBoth](#getboth) + - [getBothOrElse](#getbothorelse) + - [getLeft](#getleft) + - [getLeftOnly](#getleftonly) + - [getOrElse](#getorelse) + - [getOrNull](#getornull) + - [getOrUndefined](#getorundefined) + - [getRight](#getright) + - [getRightOnly](#getrightonly) +- [guards](#guards) + - [isBoth](#isboth) + - [isLeft](#isleft) + - [isLeftOrBoth](#isleftorboth) + - [isRight](#isright) + - [isRightOrBoth](#isrightorboth) + - [isThese](#isthese) +- [instances](#instances) + - [Applicative](#applicative) + - [Bicovariant](#bicovariant) + - [Chainable](#chainable) + - [Covariant](#covariant) + - [FlatMap](#flatmap) + - [Foldable](#foldable) + - [Invariant](#invariant) + - [Monad](#monad) + - [Of](#of) + - [Pointed](#pointed) + - [Product](#product) + - [SemiAlternative](#semialternative) + - [SemiApplicative](#semiapplicative) + - [SemiCoproduct](#semicoproduct) + - [SemiProduct](#semiproduct) + - [Traversable](#traversable) +- [interop](#interop) + - [fromThrowable](#fromthrowable) + - [getOrThrow](#getorthrow) + - [getRightOnlyOrThrow](#getrightonlyorthrow) + - [liftThrowable](#liftthrowable) +- [lifting](#lifting) + - [lift2](#lift2) + - [lift3](#lift3) + - [liftEither](#lifteither) + - [liftNullable](#liftnullable) + - [liftOption](#liftoption) + - [liftPredicate](#liftpredicate) + - [liftThese](#liftthese) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [bimap](#bimap) + - [flap](#flap) + - [imap](#imap) + - [map](#map) + - [tupled](#tupled) +- [model](#model) + - [Both (type alias)](#both-type-alias) + - [These (type alias)](#these-type-alias) + - [Validated (type alias)](#validated-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) +- [predicates](#predicates) + - [exists](#exists) +- [sequencing](#sequencing) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) + - [flatMapEither](#flatmapeither) + - [flatMapNullable](#flatmapnullable) + - [flatMapOption](#flatmapoption) + - [flatMapThese](#flatmapthese) +- [traversing](#traversing) + - [sequence](#sequence) + - [traverse](#traverse) + - [traverseTap](#traversetap) +- [type lambdas](#type-lambdas) + - [TheseTypeLambda (interface)](#thesetypelambda-interface) + - [ValidatedTypeLambda (interface)](#validatedtypelambda-interface) +- [utils](#utils) + - [andThen](#andthen) + - [ap](#ap) + - [composeKleisliArrow](#composekleisliarrow) + - [contains](#contains) + - [element](#element) + - [flatten](#flatten) + - [reverse](#reverse) + - [struct](#struct) + - [tap](#tap) + - [tuple](#tuple) + - [unit](#unit) + +--- + +# combining + +## getFirstLeftMonoid + +**Signature** + +```ts +export declare const getFirstLeftMonoid: (M: any) => any +``` + +Added in v1.0.0 + +## getFirstLeftSemigroup + +**Signature** + +```ts +export declare const getFirstLeftSemigroup: (S: any) => any +``` + +Added in v1.0.0 + +## getFirstRightOrBothSemigroup + +**Signature** + +```ts +export declare const getFirstRightOrBothSemigroup: () => any +``` + +Added in v1.0.0 + +# constructors + +## both + +**Signature** + +```ts +export declare const both: (left: E, right: A) => any +``` + +Added in v1.0.0 + +## fail + +**Signature** + +```ts +export declare const fail: (e: E) => any +``` + +Added in v1.0.0 + +## left + +**Signature** + +```ts +export declare const left: (left: E) => any +``` + +Added in v1.0.0 + +## leftOrBoth + +**Signature** + +```ts +export declare const leftOrBoth: (e: any) =>
(self: any) => any +``` + +Added in v1.0.0 + +## of + +Alias of `right`. + +**Signature** + +```ts +export declare const of: (right: A) => any +``` + +Added in v1.0.0 + +## right + +**Signature** + +```ts +export declare const right: (right: A) => any +``` + +Added in v1.0.0 + +## rightOrBoth + +**Signature** + +```ts +export declare const rightOrBoth: (a: () => A) => (self: any) => any +``` + +Added in v1.0.0 + +## succeed + +Alias of `right`. + +**Signature** + +```ts +export declare const succeed: (a: A) => any +``` + +Added in v1.0.0 + +## warn + +**Signature** + +```ts +export declare const warn: (e: E, a: A) => any +``` + +Added in v1.0.0 + +# conversions + +## absolve + +**Signature** + +```ts +export declare const absolve: (self: any) => any +``` + +Added in v1.0.0 + +## condemn + +**Signature** + +```ts +export declare const condemn: (self: any) => any +``` + +Added in v1.0.0 + +## fromEither + +**Signature** + +```ts +export declare const fromEither: (self: any) => any +``` + +Added in v1.0.0 + +## fromIterable + +**Signature** + +```ts +export declare const fromIterable: (onEmpty: any) => (collection: Iterable) => any +``` + +Added in v1.0.0 + +## fromNullable + +**Signature** + +```ts +export declare const fromNullable: (onNullable: any) => (a: A) => any +``` + +Added in v1.0.0 + +## fromOption + +**Signature** + +```ts +export declare const fromOption: (onNone: any) => (self: any) => any +``` + +Added in v1.0.0 + +## fromThese + +**Signature** + +```ts +export declare const fromThese: (self: any) => any +``` + +Added in v1.0.0 + +## fromTuple + +**Signature** + +```ts +export declare const fromTuple: (self: readonly [E, A]) => any +``` + +Added in v1.0.0 + +## toEither + +**Signature** + +```ts +export declare const toEither: (onBoth: (e: E, a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +# debugging + +## inspectBoth + +**Signature** + +```ts +export declare const inspectBoth: (onBoth: (e: E, a: A) => void) => (self: any) => any +``` + +Added in v1.0.0 + +## inspectLeft + +**Signature** + +```ts +export declare const inspectLeft: (onLeft: (e: E) => void) => (self: any) => any +``` + +Added in v1.0.0 + +## inspectRight + +**Signature** + +```ts +export declare const inspectRight: (onRight: (a: A) => void) => (self: any) => any +``` + +Added in v1.0.0 + +## inspectRightOrBoth + +**Signature** + +```ts +export declare const inspectRightOrBoth: (onRightOrBoth: (a: A) => void) => (self: any) => any +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: any +``` + +Added in v1.0.0 + +## andThenBind + +**Signature** + +```ts +export declare const andThenBind: ( + name: Exclude, + that: any +) => (self: any) => any +``` + +Added in v1.0.0 + +## andThenBindEither + +**Signature** + +```ts +export declare const andThenBindEither: ( + name: Exclude, + that: any +) => (self: any) => any +``` + +Added in v1.0.0 + +## andThenBindThese + +**Signature** + +```ts +export declare const andThenBindThese: ( + name: Exclude, + that: any +) => (self: any) => any +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: ( + name: Exclude, + f: (a: A) => any +) => (self: any) => any +``` + +Added in v1.0.0 + +## bindEither + +**Signature** + +```ts +export declare const bindEither: ( + name: Exclude, + f: (a: A) => any +) => (self: any) => any +``` + +Added in v1.0.0 + +## bindThese + +**Signature** + +```ts +export declare const bindThese: ( + name: Exclude, + f: (a: A) => any +) => (self: any) => any +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: (name: N) => (self: any) => any +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: ( + name: Exclude, + f: (a: A) => B +) => (self: any) => any +``` + +Added in v1.0.0 + +# error handling + +## catchAll + +Recovers from all errors. + +**Signature** + +```ts +export declare const catchAll: (onLeft: (e: E1) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## firstRightOrBothOf + +**Signature** + +```ts +export declare const firstRightOrBothOf: (collection: Iterable) => (self: any) => any +``` + +Added in v1.0.0 + +## mapLeft + +Returns an effect with its error channel mapped using the specified +function. This can be used to lift a "smaller" error into a "larger" error. + +**Signature** + +```ts +export declare const mapLeft: (f: (e: E) => G) => (self: any) => any +``` + +Added in v1.0.0 + +## orElse + +Executes this effect and returns its value, if it succeeds, but otherwise +executes the specified effect. + +**Signature** + +```ts +export declare const orElse: (that: any) => (self: any) => any +``` + +Added in v1.0.0 + +## orElseEither + +Returns an effect that will produce the value of this effect, unless it +fails, in which case, it will produce the value of the specified effect. + +**Signature** + +```ts +export declare const orElseEither: (that: any) => (self: any) => any +``` + +Added in v1.0.0 + +## orElseFail + +Executes this effect and returns its value, if it succeeds, but otherwise +fails with the specified error. + +**Signature** + +```ts +export declare const orElseFail: (onLeft: any) => (self: any) => any +``` + +Added in v1.0.0 + +## orElseSucceed + +Executes this effect and returns its value, if it succeeds, but otherwise +succeeds with the specified value. + +**Signature** + +```ts +export declare const orElseSucceed: (onLeft: any) => (self: any) => any +``` + +Added in v1.0.0 + +# filtering + +## compact + +**Signature** + +```ts +export declare const compact: (onNone: any) => (self: any) => any +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: any, onFalse: any): (self: any) => any + (predicate: any, onFalse: any): (self: any) => any +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: (f: (a: A) => any, onNone: any) => (self: any) => any +``` + +Added in v1.0.0 + +# getters + +## getBoth + +**Signature** + +```ts +export declare const getBoth: (self: any) => any +``` + +Added in v1.0.0 + +## getBothOrElse + +**Signature** + +```ts +export declare const getBothOrElse: (e: any, a: any) => (self: any) => readonly [E, A] +``` + +Added in v1.0.0 + +## getLeft + +**Signature** + +```ts +export declare const getLeft: (self: any) => any +``` + +Added in v1.0.0 + +## getLeftOnly + +Returns the `E` value if and only if the value is constructed with `Left` + +**Signature** + +```ts +export declare const getLeftOnly: (self: any) => any +``` + +Added in v1.0.0 + +## getOrElse + +**Signature** + +```ts +export declare const getOrElse: (onLeft: any) => (self: any) => B | A +``` + +Added in v1.0.0 + +## getOrNull + +**Signature** + +```ts +export declare const getOrNull: (self: any) => A | null +``` + +Added in v1.0.0 + +## getOrUndefined + +**Signature** + +```ts +export declare const getOrUndefined: (self: any) => A | undefined +``` + +Added in v1.0.0 + +## getRight + +**Signature** + +```ts +export declare const getRight: (self: any) => any +``` + +Added in v1.0.0 + +## getRightOnly + +Returns the `A` value if and only if the value is constructed with `Right` + +**Signature** + +```ts +export declare const getRightOnly: (self: any) => any +``` + +Added in v1.0.0 + +# guards + +## isBoth + +Returns `true` if the these is an instance of `Both`, `false` otherwise + +**Signature** + +```ts +export declare const isBoth: (self: any) => self is Both +``` + +Added in v1.0.0 + +## isLeft + +Returns `true` if the these is an instance of `Left`, `false` otherwise + +**Signature** + +```ts +export declare const isLeft: (self: any) => self is any +``` + +Added in v1.0.0 + +## isLeftOrBoth + +**Signature** + +```ts +export declare const isLeftOrBoth: (self: any) => self is any +``` + +Added in v1.0.0 + +## isRight + +Returns `true` if the these is an instance of `Right`, `false` otherwise + +**Signature** + +```ts +export declare const isRight: (self: any) => self is any +``` + +Added in v1.0.0 + +## isRightOrBoth + +**Signature** + +```ts +export declare const isRightOrBoth: (self: any) => self is any +``` + +Added in v1.0.0 + +## isThese + +Returns `true` if the specified value is an instance of `These`, `false` +otherwise. + +**Signature** + +```ts +export declare const isThese: (u: unknown) => u is any +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: any +``` + +Added in v1.0.0 + +## Bicovariant + +**Signature** + +```ts +export declare const Bicovariant: any +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: any +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: any +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: any +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: any +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: any +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: any +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: any +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: any +``` + +Added in v1.0.0 + +# interop + +## fromThrowable + +Constructs a new `These` from a function that might throw. + +**Signature** + +```ts +export declare const fromThrowable: (f: () => A, onThrow: (error: unknown) => E) => any +``` + +Added in v1.0.0 + +## getOrThrow + +**Signature** + +```ts +export declare const getOrThrow: (onLeft: (e: E) => unknown) => (self: any) => A +``` + +Added in v1.0.0 + +## getRightOnlyOrThrow + +**Signature** + +```ts +export declare const getRightOnlyOrThrow: (onLeft: (e: E) => unknown) => (self: any) => A +``` + +Added in v1.0.0 + +## liftThrowable + +Lifts a function that may throw to one returning a `These`. + +**Signature** + +```ts +export declare const liftThrowable: ( + f: (...a: A) => B, + onThrow: (error: unknown) => E +) => (...a: A) => any +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +**Signature** + +```ts +export declare const lift2: (f: (a: A, b: B) => C) => (fa: any, fb: any) => any +``` + +Added in v1.0.0 + +## lift3 + +**Signature** + +```ts +export declare const lift3: (f: (a: A, b: B, c: C) => D) => (fa: any, fb: any, fc: any) => any +``` + +Added in v1.0.0 + +## liftEither + +**Signature** + +```ts +export declare const liftEither: (f: (...a: A) => any) => (...a: A) => any +``` + +Added in v1.0.0 + +## liftNullable + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A) => any +``` + +Added in v1.0.0 + +## liftOption + +**Signature** + +```ts +export declare const liftOption: ( + f: (...a: A) => any, + onNone: (...a: A) => E +) => (...a: A) => any +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: any, onFalse: any): (c: C) => any + (predicate: any, onFalse: any): (b: B) => any +} +``` + +Added in v1.0.0 + +## liftThese + +**Signature** + +```ts +export declare const liftThese: (f: (...a: A) => any) => (...a: A) => any +``` + +Added in v1.0.0 + +# mapping + +## as + +Maps the right value of this effect to the specified constant value. + +**Signature** + +```ts +export declare const as: (b: B) => (self: any) => any +``` + +Added in v1.0.0 + +## asUnit + +Returns the effect resulting from mapping the right of this effect to unit. + +**Signature** + +```ts +export declare const asUnit: (self: any) => any +``` + +Added in v1.0.0 + +## bimap + +Returns an effect whose left and right channels have been mapped by +the specified pair of functions, `f` and `g`. + +**Signature** + +```ts +export declare const bimap: (f: (e: E) => G, g: (a: A) => B) => (self: any) => any +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: (a: A) => (self: any) => any +``` + +Added in v1.0.0 + +## imap + +**Signature** + +```ts +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (self: any) => any +``` + +Added in v1.0.0 + +## map + +Returns an effect whose right is mapped by the specified `f` function. + +**Signature** + +```ts +export declare const map: (f: (a: A) => B) => (self: any) => any +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: any) => any +``` + +Added in v1.0.0 + +# model + +## Both (type alias) + +**Signature** + +```ts +export type Both = { + readonly _tag: 'Both' + readonly left: E + readonly right: A +} +``` + +Added in v1.0.0 + +## These (type alias) + +**Signature** + +```ts +export type These = Either | Both +``` + +Added in v1.0.0 + +## Validated (type alias) + +**Signature** + +```ts +export type Validated = These, A> +``` + +Added in v1.0.0 + +# pattern matching + +## match + +**Signature** + +```ts +export declare const match: ( + onLeft: (e: E) => B, + onRight: (a: A) => C, + onBoth: (e: E, a: A) => D +) => (self: any) => B | C | D +``` + +Added in v1.0.0 + +# predicates + +## exists + +**Signature** + +```ts +export declare const exists: (predicate: any) => (self: any) => boolean +``` + +Added in v1.0.0 + +# sequencing + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: (that: any) => (self: any) => any +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## flatMapEither + +**Signature** + +```ts +export declare const flatMapEither: (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## flatMapNullable + +**Signature** + +```ts +export declare const flatMapNullable: ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +) => (self: any) => any +``` + +Added in v1.0.0 + +## flatMapOption + +**Signature** + +```ts +export declare const flatMapOption: (f: (a: A) => any, onNone: (a: A) => E2) => (self: any) => any +``` + +Added in v1.0.0 + +## flatMapThese + +**Signature** + +```ts +export declare const flatMapThese: (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: (F: any) => (self: any) => any +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: (F: any) => (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: (F: any) => (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +# type lambdas + +## TheseTypeLambda (interface) + +**Signature** + +```ts +export interface TheseTypeLambda extends TypeLambda { + readonly type: These +} +``` + +Added in v1.0.0 + +## ValidatedTypeLambda (interface) + +**Signature** + +```ts +export interface ValidatedTypeLambda extends TypeLambda { + readonly type: Validated +} +``` + +Added in v3.0.0 + +# utils + +## andThen + +**Signature** + +```ts +export declare const andThen: (that: any) => (self: any) => any +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: (fa: any) => (self: any) => any +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: (bfc: (b: B) => any) => (afb: (a: A) => any) => (a: A) => any +``` + +Added in v1.0.0 + +## contains + +Returns a function that checks if a `These` contains a given value using a provided `equivalence` function. + +**Signature** + +```ts +export declare const contains: (equivalence: any) => (a: A) => (self: any) => boolean +``` + +Added in v1.0.0 + +## element + +Adds an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: (that: any) => (self: any) => any +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: any) => any +``` + +Added in v1.0.0 + +## reverse + +**Signature** + +```ts +export declare const reverse: (self: any) => any +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >(r: R) => any +``` + +Added in v1.0.0 + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: (...tuple: T) => any +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: any +``` + +Added in v1.0.0 diff --git a/docs/modules/Tuple.ts.md b/docs/modules/Tuple.ts.md new file mode 100644 index 000000000..2bf29beac --- /dev/null +++ b/docs/modules/Tuple.ts.md @@ -0,0 +1,116 @@ +--- +title: Tuple.ts +nav_order: 18 +parent: Modules +--- + +## Tuple overview + +This module provides utility functions for working with tuples in TypeScript. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [constructors](#constructors) + - [tuple](#tuple) +- [utils](#utils) + - [element](#element) + - [getEquivalence](#getequivalence) + - [getMonoid](#getmonoid) + - [getOrder](#getorder) + - [getSemigroup](#getsemigroup) + - [nonEmptyProduct](#nonemptyproduct) + - [product](#product) + +--- + +# constructors + +## tuple + +**Signature** + +```ts +export declare const tuple:
(...elements: A) => A +``` + +Added in v1.0.0 + +# utils + +## element + +Appends an element to the end of a tuple. + +**Signature** + +```ts +export declare const element: ( + that: readonly B[] +) => (self: readonly A[]) => [...A, B][] +``` + +Added in v1.0.0 + +## getEquivalence + +**Signature** + +```ts +export declare const getEquivalence: any +``` + +Added in v1.0.0 + +## getMonoid + +**Signature** + +```ts +export declare const getMonoid: any +``` + +Added in v1.0.0 + +## getOrder + +**Signature** + +```ts +export declare const getOrder: any +``` + +Added in v1.0.0 + +## getSemigroup + +**Signature** + +```ts +export declare const getSemigroup: any +``` + +Added in v1.0.0 + +## nonEmptyProduct + +**Signature** + +```ts +export declare const nonEmptyProduct: any +``` + +Added in v1.0.0 + +## product + +**Signature** + +```ts +export declare const product: any +``` + +Added in v1.0.0 diff --git a/docs/modules/index.ts.md b/docs/modules/index.ts.md index d1168088a..63dbcd40a 100644 --- a/docs/modules/index.ts.md +++ b/docs/modules/index.ts.md @@ -1,6 +1,6 @@ --- title: index.ts -nav_order: 2 +nav_order: 7 parent: Modules --- @@ -18,15 +18,17 @@ Added in v1.0.0 - [bicovariant](#bicovariant) - [bounded](#bounded) - [chainable](#chainable) + - [compactable](#compactable) - [contravariant](#contravariant) - [coproduct](#coproduct) - [covariant](#covariant) + - [equivalence](#equivalence) + - [filterable](#filterable) - [flatMap](#flatmap) - [foldable](#foldable) - [invariant](#invariant) - [monad](#monad) - [monoid](#monoid) - - [nonEmptyTraversable](#nonemptytraversable) - [of](#of) - [order](#order) - [pointed](#pointed) @@ -37,8 +39,25 @@ Added in v1.0.0 - [semiProduct](#semiproduct) - [semigroup](#semigroup) - [traversable](#traversable) + - [traversableFilterable](#traversablefilterable) - [utils](#utils) + - [bigint](#bigint) + - [boolean](#boolean) + - [either](#either) + - [function](#function) - [hkt](#hkt) + - [identity](#identity) + - [number](#number) + - [option](#option) + - [ordering](#ordering) + - [predicate](#predicate) + - [readonlyArray](#readonlyarray) + - [readonlyRecord](#readonlyrecord) + - [string](#string) + - [struct](#struct) + - [symbol](#symbol) + - [these](#these) + - [tuple](#tuple) --- @@ -49,7 +68,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const alternative: typeof alternative +export declare const alternative: any ``` Added in v1.0.0 @@ -59,7 +78,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const applicative: typeof applicative +export declare const applicative: any ``` Added in v1.0.0 @@ -69,7 +88,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const bicovariant: typeof bicovariant +export declare const bicovariant: any ``` Added in v1.0.0 @@ -79,7 +98,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const bounded: typeof bounded +export declare const bounded: any ``` Added in v1.0.0 @@ -89,7 +108,17 @@ Added in v1.0.0 **Signature** ```ts -export declare const chainable: typeof chainable +export declare const chainable: any +``` + +Added in v1.0.0 + +## compactable + +**Signature** + +```ts +export declare const compactable: any ``` Added in v1.0.0 @@ -99,7 +128,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const contravariant: typeof contravariant +export declare const contravariant: any ``` Added in v1.0.0 @@ -109,7 +138,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const coproduct: typeof coproduct +export declare const coproduct: any ``` Added in v1.0.0 @@ -119,67 +148,77 @@ Added in v1.0.0 **Signature** ```ts -export declare const covariant: typeof covariant +export declare const covariant: any ``` Added in v1.0.0 -## flatMap +## equivalence **Signature** ```ts -export declare const flatMap: typeof flatMap +export declare const equivalence: any ``` Added in v1.0.0 -## foldable +## filterable **Signature** ```ts -export declare const foldable: typeof foldable +export declare const filterable: any ``` Added in v1.0.0 -## invariant +## flatMap **Signature** ```ts -export declare const invariant: typeof invariant +export declare const flatMap: any ``` Added in v1.0.0 -## monad +## foldable **Signature** ```ts -export declare const monad: typeof monad +export declare const foldable: any ``` Added in v1.0.0 -## monoid +## invariant **Signature** ```ts -export declare const monoid: typeof monoid +export declare const invariant: any ``` Added in v1.0.0 -## nonEmptyTraversable +## monad **Signature** ```ts -export declare const nonEmptyTraversable: typeof nonEmptyTraversable +export declare const monad: any +``` + +Added in v1.0.0 + +## monoid + +**Signature** + +```ts +export declare const monoid: any ``` Added in v1.0.0 @@ -189,7 +228,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const of: typeof of +export declare const of: any ``` Added in v1.0.0 @@ -199,7 +238,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const order: typeof order +export declare const order: any ``` Added in v1.0.0 @@ -209,7 +248,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const pointed: typeof pointed +export declare const pointed: any ``` Added in v1.0.0 @@ -219,7 +258,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const product: typeof product +export declare const product: any ``` Added in v1.0.0 @@ -229,7 +268,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const semiAlternative: typeof semiAlternative +export declare const semiAlternative: any ``` Added in v1.0.0 @@ -239,7 +278,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const semiApplicative: typeof semiApplicative +export declare const semiApplicative: any ``` Added in v1.0.0 @@ -249,7 +288,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const semiCoproduct: typeof semiCoproduct +export declare const semiCoproduct: any ``` Added in v1.0.0 @@ -259,7 +298,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const semiProduct: typeof semiProduct +export declare const semiProduct: any ``` Added in v1.0.0 @@ -269,7 +308,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const semigroup: typeof semigroup +export declare const semigroup: any ``` Added in v1.0.0 @@ -279,19 +318,189 @@ Added in v1.0.0 **Signature** ```ts -export declare const traversable: typeof traversable +export declare const traversable: any +``` + +Added in v1.0.0 + +## traversableFilterable + +**Signature** + +```ts +export declare const traversableFilterable: any ``` Added in v1.0.0 # utils +## bigint + +**Signature** + +```ts +export declare const bigint: any +``` + +Added in v1.0.0 + +## boolean + +**Signature** + +```ts +export declare const boolean: any +``` + +Added in v1.0.0 + +## either + +**Signature** + +```ts +export declare const either: any +``` + +Added in v1.0.0 + +## function + +**Signature** + +```ts +export declare const function: any +``` + +Added in v1.0.0 + ## hkt **Signature** ```ts -export declare const hkt: typeof hkt +export declare const hkt: any +``` + +Added in v1.0.0 + +## identity + +**Signature** + +```ts +export declare const identity: any +``` + +Added in v1.0.0 + +## number + +**Signature** + +```ts +export declare const number: any +``` + +Added in v1.0.0 + +## option + +**Signature** + +```ts +export declare const option: any +``` + +Added in v1.0.0 + +## ordering + +**Signature** + +```ts +export declare const ordering: any +``` + +Added in v1.0.0 + +## predicate + +**Signature** + +```ts +export declare const predicate: any +``` + +Added in v1.0.0 + +## readonlyArray + +**Signature** + +```ts +export declare const readonlyArray: any +``` + +Added in v1.0.0 + +## readonlyRecord + +**Signature** + +```ts +export declare const readonlyRecord: any +``` + +Added in v1.0.0 + +## string + +**Signature** + +```ts +export declare const string: any +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: any +``` + +Added in v1.0.0 + +## symbol + +**Signature** + +```ts +export declare const symbol: any +``` + +Added in v1.0.0 + +## these + +**Signature** + +```ts +export declare const these: any +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Alternative.ts.md b/docs/modules/typeclass/Alternative.ts.md index 973ea69a3..acbfce511 100644 --- a/docs/modules/typeclass/Alternative.ts.md +++ b/docs/modules/typeclass/Alternative.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Alternative.ts -nav_order: 3 +nav_order: 19 parent: Modules --- diff --git a/docs/modules/typeclass/Applicative.ts.md b/docs/modules/typeclass/Applicative.ts.md index 98c748387..4dbabc5d0 100644 --- a/docs/modules/typeclass/Applicative.ts.md +++ b/docs/modules/typeclass/Applicative.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Applicative.ts -nav_order: 4 +nav_order: 20 parent: Modules --- @@ -40,9 +40,7 @@ Lift a monoid into 'F', the inner values are combined using the provided `Monoid **Signature** ```ts -export declare const liftMonoid: ( - F: Applicative -) => (M: Monoid) => Monoid> +export declare const liftMonoid: (F: Applicative) => (M: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Bicovariant.ts.md b/docs/modules/typeclass/Bicovariant.ts.md index e01228986..d28425526 100644 --- a/docs/modules/typeclass/Bicovariant.ts.md +++ b/docs/modules/typeclass/Bicovariant.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Bicovariant.ts -nav_order: 5 +nav_order: 21 parent: Modules --- @@ -47,15 +47,10 @@ Returns a default `bimap` composition. **Signature** ```ts -export declare const bimapComposition: ( - CovariantF: Covariant, +export declare const bimapComposition: ( + CovariantF: any, BicovariantG: Bicovariant -) => ( - f: (e: E1) => E2, - g: (a: A) => B -) => ( - self: Kind> -) => Kind> +) => (f: (e: E1) => E2, g: (a: A) => B) => (self: any) => any ``` Added in v1.0.0 @@ -67,9 +62,7 @@ Returns a default `map` implementation. **Signature** ```ts -export declare const map: ( - F: Bicovariant -) => (f: (a: A) => B) => (self: Kind) => Kind +export declare const map: (F: Bicovariant) => any ``` Added in v1.0.0 @@ -79,9 +72,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const mapLeft: ( +export declare const mapLeft: ( F: Bicovariant -) => (f: (e: E1) => E2) => (self: Kind) => Kind +) => (f: (e: E1) => E2) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Bounded.ts.md b/docs/modules/typeclass/Bounded.ts.md index 89605b9f0..5553ae96e 100644 --- a/docs/modules/typeclass/Bounded.ts.md +++ b/docs/modules/typeclass/Bounded.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Bounded.ts -nav_order: 6 +nav_order: 22 parent: Modules --- @@ -12,6 +12,11 @@ Added in v1.0.0

Table of contents

+- [constructors](#constructors) + - [max](#max) + - [min](#min) +- [instances](#instances) + - [number](#number) - [type class](#type-class) - [Bounded (interface)](#bounded-interface) - [type lambdas](#type-lambdas) @@ -22,6 +27,44 @@ Added in v1.0.0 --- +# constructors + +## max + +`Monoid` that returns last maximum of elements. + +**Signature** + +```ts +export declare const max:
(B: Bounded) => any +``` + +Added in v1.0.0 + +## min + +`Monoid` that returns last minimum of elements. + +**Signature** + +```ts +export declare const min: (B: Bounded) => any +``` + +Added in v1.0.0 + +# instances + +## number + +**Signature** + +```ts +export declare const number: Bounded +``` + +Added in v1.0.0 + # type class ## Bounded (interface) diff --git a/docs/modules/typeclass/Chainable.ts.md b/docs/modules/typeclass/Chainable.ts.md index d4f584e52..8372e97e1 100644 --- a/docs/modules/typeclass/Chainable.ts.md +++ b/docs/modules/typeclass/Chainable.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Chainable.ts -nav_order: 7 +nav_order: 23 parent: Modules --- @@ -32,11 +32,9 @@ produced by the effect. **Signature** ```ts -export declare const andThenDiscard: ( +export declare const andThenDiscard: ( F: Chainable -) => ( - that: Kind -) => (self: Kind) => Kind +) => (that: any) => (self: any) => any ``` Added in v1.0.0 @@ -60,14 +58,12 @@ Added in v1.0.0 **Signature** ```ts -export declare const bind: ( +export declare const bind: ( F: Chainable ) => ( name: Exclude, - f: (a: A) => Kind -) => ( - self: Kind -) => Kind + f: (a: A) => any +) => (self: any) => any ``` Added in v1.0.0 @@ -79,11 +75,9 @@ Returns an effect that effectfully "peeks" at the success of this effect. **Signature** ```ts -export declare const tap: ( +export declare const tap: ( F: Chainable -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind +) => (f: (a: A) => any) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Compactable.ts.md b/docs/modules/typeclass/Compactable.ts.md new file mode 100644 index 000000000..1f9edea9e --- /dev/null +++ b/docs/modules/typeclass/Compactable.ts.md @@ -0,0 +1,64 @@ +--- +title: typeclass/Compactable.ts +nav_order: 24 +parent: Modules +--- + +## Compactable overview + +`Compactable` represents data structures which can be _compacted_/_separated_. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [models](#models) + - [Compactable (interface)](#compactable-interface) +- [utils](#utils) + - [compactComposition](#compactcomposition) + - [separate](#separate) + +--- + +# models + +## Compactable (interface) + +**Signature** + +```ts +export interface Compactable extends TypeClass { + readonly compact: (self: Kind>) => Kind +} +``` + +Added in v1.0.0 + +# utils + +## compactComposition + +Returns a default `compact` composition. + +**Signature** + +```ts +export declare const compactComposition: ( + F: any, + G: Compactable +) => (self: any) => any +``` + +Added in v1.0.0 + +## separate + +**Signature** + +```ts +export declare const separate: (F: any) => (self: any) => [any, any] +``` + +Added in v1.0.0 diff --git a/docs/modules/typeclass/Contravariant.ts.md b/docs/modules/typeclass/Contravariant.ts.md index ff16c82fa..d0f5e18e9 100644 --- a/docs/modules/typeclass/Contravariant.ts.md +++ b/docs/modules/typeclass/Contravariant.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Contravariant.ts -nav_order: 8 +nav_order: 25 parent: Modules --- @@ -29,8 +29,8 @@ Added in v1.0.0 **Signature** ```ts -export declare const make: ( - contramap: (f: (b: B) => A) => (self: Kind) => Kind +export declare const make: ( + contramap: (f: (b: B) => A) => (self: any) => any ) => Contravariant ``` @@ -61,14 +61,10 @@ Returns a default `map` composition. **Signature** ```ts -export declare const contramapComposition: ( +export declare const contramapComposition: ( F: Contravariant, G: Contravariant -) => ( - f: (a: A) => B -) => ( - self: Kind> -) => Kind> +) => (f: (a: A) => B) => (self: any) => any ``` Added in v1.0.0 @@ -80,9 +76,7 @@ Returns a default `imap` implementation. **Signature** ```ts -export declare const imap: ( - contramap: (f: (b: B) => A) => (self: Kind) => Kind -) => (to: (a: A) => B, from: (b: B) => A) => (self: Kind) => Kind +export declare const imap: (contramap: (f: (b: B) => A) => (self: any) => any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Coproduct.ts.md b/docs/modules/typeclass/Coproduct.ts.md index dc19adbff..4dae57e12 100644 --- a/docs/modules/typeclass/Coproduct.ts.md +++ b/docs/modules/typeclass/Coproduct.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Coproduct.ts -nav_order: 9 +nav_order: 26 parent: Modules --- @@ -42,7 +42,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const getMonoid: (F: Coproduct) => () => Monoid> +export declare const getMonoid: (F: Coproduct) => () => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Covariant.ts.md b/docs/modules/typeclass/Covariant.ts.md index 5d948f250..77af9c2d2 100644 --- a/docs/modules/typeclass/Covariant.ts.md +++ b/docs/modules/typeclass/Covariant.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Covariant.ts -nav_order: 10 +nav_order: 27 parent: Modules --- @@ -34,9 +34,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const make: ( - map: (f: (a: A) => B) => (self: Kind) => Kind -) => Covariant +export declare const make: (map: (f: (a: A) => B) => (self: any) => any) => Covariant ``` Added in v1.0.0 @@ -48,9 +46,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const as: ( - F: Covariant -) => (b: B) => (self: Kind) => Kind +export declare const as: (F: Covariant) => (b: B) => (self: any) => any ``` Added in v1.0.0 @@ -60,9 +56,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const asUnit: ( - F: Covariant -) => (self: Kind) => Kind +export declare const asUnit: (F: Covariant) => (self: any) => any ``` Added in v1.0.0 @@ -72,9 +66,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const flap: ( - F: Covariant -) =>
(a: A) => (self: Kind B>) => Kind +export declare const flap: (F: Covariant) => (a: A) => (self: any) => any ``` Added in v1.0.0 @@ -102,9 +94,7 @@ Returns a default `imap` implementation. **Signature** ```ts -export declare const imap: ( - map: (f: (a: A) => B) => (self: Kind) => Kind -) => (to: (a: A) => B, from: (b: B) => A) => (self: Kind) => Kind +export declare const imap: (map: (f: (a: A) => B) => (self: any) => any) => any ``` Added in v1.0.0 @@ -114,12 +104,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const let: ( +export declare const let: ( F: Covariant -) => ( - name: Exclude, - f: (a: A) => B -) => (self: Kind) => Kind +) => (name: Exclude, f: (a: A) => B) => (self: any) => any ``` Added in v1.0.0 @@ -131,14 +118,10 @@ Returns a default `map` composition. **Signature** ```ts -export declare const mapComposition: ( +export declare const mapComposition: ( F: Covariant, G: Covariant -) => ( - f: (a: A) => B -) => ( - self: Kind> -) => Kind> +) => (f: (a: A) => B) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Equivalence.ts.md b/docs/modules/typeclass/Equivalence.ts.md new file mode 100644 index 000000000..177f3c61e --- /dev/null +++ b/docs/modules/typeclass/Equivalence.ts.md @@ -0,0 +1,268 @@ +--- +title: typeclass/Equivalence.ts +nav_order: 28 +parent: Modules +--- + +## Equivalence overview + +This module provides an implementation of the `Equivalence` type class, which defines a binary relation +that is reflexive, symmetric, and transitive. In other words, it defines a notion of equivalence between values of a certain type. +These properties are also known in mathematics as an "equivalence relation". + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combinators](#combinators) + - [contramap](#contramap) +- [constructors](#constructors) + - [array](#array) + - [record](#record) + - [strict](#strict) + - [struct](#struct) + - [tuple](#tuple) +- [instances](#instances) + - [Contravariant](#contravariant) + - [Invariant](#invariant) + - [Product](#product) + - [SemiProduct](#semiproduct) + - [bigint](#bigint) + - [boolean](#boolean) + - [getMonoid](#getmonoid) + - [getSemigroup](#getsemigroup) + - [number](#number) + - [string](#string) + - [symbol](#symbol) +- [type class](#type-class) + - [Equivalence (interface)](#equivalence-interface) +- [type lambdas](#type-lambdas) + - [EquivalenceTypeLambda (interface)](#equivalencetypelambda-interface) + +--- + +# combinators + +## contramap + +**Signature** + +```ts +export declare const contramap: (f: (b: B) => A) => (self: Equivalence
) => Equivalence +``` + +Added in v1.0.0 + +# constructors + +## array + +Given an `Equivalence` of type `A`, returns a new `Equivalence` of type `ReadonlyArray`. +The returned `Equivalence` compares arrays by first checking their length and then applying the provided `Equivalence` to each element. +If all comparisons return true, the arrays are considered equal. + +**Signature** + +```ts +export declare const array: (equivalence: Equivalence) => Equivalence +``` + +Added in v1.0.0 + +## record + +Given an `Equivalence` of type `A`, returns a new `Equivalence` of type `{ readonly [x: string]: A }`. +The returned `Equivalence` compares records by first checking their number of keys and then applying the provided `Equivalence` to each value. +If all comparisons return true, the records are considered equal. + +**Signature** + +```ts +export declare const record: (equivalence: Equivalence) => Equivalence +``` + +Added in v1.0.0 + +## strict + +Return an `Equivalence` that uses strict equality (===) to compare values + +**Signature** + +```ts +export declare const strict: () => Equivalence +``` + +Added in v1.0.0 + +## struct + +Given a struct of `Equivalence`s returns a new `Equivalence` that compares values of a struct +by applying each `Equivalence` to the corresponding property of the struct. + +**Signature** + +```ts +export declare const struct: (equivalences: { [K in keyof A]: Equivalence }) => Equivalence<{ + readonly [K in keyof A]: A[K] +}> +``` + +Added in v1.0.0 + +## tuple + +Given a tuple of `Equivalence`s returns a new `Equivalence` that compares values of a tuple +by applying each `Equivalence` to the corresponding element of the tuple. + +**Signature** + +```ts +export declare const tuple: ( + ...equivalences: { readonly [K in keyof A]: Equivalence } +) => Equivalence> +``` + +Added in v1.0.0 + +# instances + +## Contravariant + +**Signature** + +```ts +export declare const Contravariant: any +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: any +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: any +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## bigint + +**Signature** + +```ts +export declare const bigint: Equivalence +``` + +Added in v1.0.0 + +## boolean + +**Signature** + +```ts +export declare const boolean: Equivalence +``` + +Added in v1.0.0 + +## getMonoid + +**Signature** + +```ts +export declare const getMonoid: () => any +``` + +Added in v2.6.0 + +## getSemigroup + +**Signature** + +```ts +export declare const getSemigroup: () => any +``` + +Added in v2.10.0 + +## number + +**Signature** + +```ts +export declare const number: Equivalence +``` + +Added in v1.0.0 + +## string + +**Signature** + +```ts +export declare const string: Equivalence +``` + +Added in v1.0.0 + +## symbol + +**Signature** + +```ts +export declare const symbol: Equivalence +``` + +Added in v1.0.0 + +# type class + +## Equivalence (interface) + +**Signature** + +```ts +export interface Equivalence { + (x: A, y: A): boolean +} +``` + +Added in v1.0.0 + +# type lambdas + +## EquivalenceTypeLambda (interface) + +**Signature** + +```ts +export interface EquivalenceTypeLambda extends TypeLambda { + readonly type: Equivalence +} +``` + +Added in v1.0.0 diff --git a/docs/modules/typeclass/Filterable.ts.md b/docs/modules/typeclass/Filterable.ts.md new file mode 100644 index 000000000..334992306 --- /dev/null +++ b/docs/modules/typeclass/Filterable.ts.md @@ -0,0 +1,98 @@ +--- +title: typeclass/Filterable.ts +nav_order: 29 +parent: Modules +--- + +## Filterable overview + +`Filterable` represents data structures which can be _partitioned_/_filtered_. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [models](#models) + - [Filterable (interface)](#filterable-interface) +- [utils](#utils) + - [filter](#filter) + - [filterMapComposition](#filtermapcomposition) + - [partition](#partition) + - [partitionMap](#partitionmap) + +--- + +# models + +## Filterable (interface) + +**Signature** + +```ts +export interface Filterable extends TypeClass { + readonly filterMap: (f: (a: A) => Option) => (self: Kind) => Kind +} +``` + +Added in v1.0.0 + +# utils + +## filter + +**Signature** + +```ts +export declare const filter: ( + F: Filterable +) => { + (refinement: (a: A) => a is B): (self: any) => any + (predicate: (a: A) => boolean): (self: any) => any +} +``` + +Added in v1.0.0 + +## filterMapComposition + +Returns a default `filterMap` composition. + +**Signature** + +```ts +export declare const filterMapComposition: ( + F: any, + G: Filterable +) => (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## partition + +**Signature** + +```ts +export declare const partition: ( + F: Filterable +) => { + (refinement: (a: A) => a is B): (self: any) => [any, any] + (predicate: (a: A) => boolean): (self: any) => [any, any] +} +``` + +Added in v1.0.0 + +## partitionMap + +**Signature** + +```ts +export declare const partitionMap: ( + F: Filterable +) => (f: (a: A) => any) => (self: any) => [any, any] +``` + +Added in v1.0.0 diff --git a/docs/modules/typeclass/FlatMap.ts.md b/docs/modules/typeclass/FlatMap.ts.md index 2ddc9163e..0c9a88819 100644 --- a/docs/modules/typeclass/FlatMap.ts.md +++ b/docs/modules/typeclass/FlatMap.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/FlatMap.ts -nav_order: 11 +nav_order: 30 parent: Modules --- @@ -46,11 +46,9 @@ A variant of `flatMap` that ignores the value produced by this effect. **Signature** ```ts -export declare const andThen: ( +export declare const andThen: ( F: FlatMap -) => ( - that: Kind -) => (self: Kind) => Kind +) => (that: any) => (self: any) => any ``` Added in v1.0.0 @@ -60,11 +58,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const composeKleisliArrow: ( +export declare const composeKleisliArrow: ( F: FlatMap -) => ( - bfc: (b: B) => Kind -) => (afb: (a: A) => Kind) => (a: A) => Kind +) => (bfc: (b: B) => any) => (afb: (a: A) => any) => (a: A) => any ``` Added in v1.0.0 @@ -74,11 +70,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const flatten: ( - F: FlatMap -) => ( - self: Kind> -) => Kind +export declare const flatten: (F: FlatMap) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Foldable.ts.md b/docs/modules/typeclass/Foldable.ts.md index a0d735c52..3e9a15f47 100644 --- a/docs/modules/typeclass/Foldable.ts.md +++ b/docs/modules/typeclass/Foldable.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Foldable.ts -nav_order: 12 +nav_order: 31 parent: Modules --- @@ -47,9 +47,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const foldMap: ( +export declare const foldMap: ( F: Foldable -) => (M: Monoid) =>
(f: (a: A) => M) => (self: Kind) => M +) => (M: any) => (f: (a: A) => M) => (self: any) => M ``` Added in v1.0.0 @@ -59,13 +59,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const foldMapKind: ( +export declare const foldMapKind: ( F: Foldable -) => ( - G: Coproduct -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind +) => (G: any) => (f: (a: A) => any) => (self: any) => any ``` Added in v1.0.0 @@ -77,13 +73,10 @@ Returns a default `reduce` composition. **Signature** ```ts -export declare const reduceComposition: ( +export declare const reduceComposition: ( F: Foldable, G: Foldable -) => ( - b: B, - f: (b: B, a: A) => B -) => (self: Kind>) => B +) => (b: B, f: (b: B, a: A) => B) => (self: any) => B ``` Added in v1.0.0 @@ -93,14 +86,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const reduceKind: ( +export declare const reduceKind: ( F: Foldable -) => ( - G: Monad -) => ( - b: B, - f: (b: B, a: A) => Kind -) => (self: Kind) => Kind +) => (G: any) => (b: B, f: (b: B, a: A) => any) => (self: any) => any ``` Added in v1.0.0 @@ -110,9 +98,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const reduceRight: ( +export declare const reduceRight: ( F: Foldable -) => (b: B, f: (b: B, a: A) => B) => (self: Kind) => B +) => (b: B, f: (b: B, a: A) => B) => (self: any) => B ``` Added in v1.0.0 @@ -122,14 +110,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const reduceRightKind: ( +export declare const reduceRightKind: ( F: Foldable -) => ( - G: Monad -) => ( - b: B, - f: (b: B, a: A) => Kind -) => (self: Kind) => Kind +) => (G: any) => (b: B, f: (b: B, a: A) => any) => (self: any) => any ``` Added in v1.0.0 @@ -139,7 +122,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const toArray: (F: Foldable) => (self: Kind) => A[] +export declare const toArray: (F: Foldable) => (self: any) => A[] ``` Added in v1.0.0 @@ -149,9 +132,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const toArrayWith: ( +export declare const toArrayWith: ( F: Foldable -) => (f: (a: A) => B) => (self: Kind) => B[] +) => (f: (a: A) => B) => (self: any) => B[] ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Invariant.ts.md b/docs/modules/typeclass/Invariant.ts.md index e88c28784..461283073 100644 --- a/docs/modules/typeclass/Invariant.ts.md +++ b/docs/modules/typeclass/Invariant.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Invariant.ts -nav_order: 13 +nav_order: 32 parent: Modules --- @@ -45,11 +45,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const bindTo: ( +export declare const bindTo: ( F: Invariant -) => ( - name: N -) => (self: Kind) => Kind +) => (name: N) => (self: any) => any ``` Added in v1.0.0 @@ -61,15 +59,10 @@ Returns a default `imap` composition. **Signature** ```ts -export declare const imapComposition: ( +export declare const imapComposition: ( F: Invariant, G: Invariant -) => ( - to: (a: A) => B, - from: (b: B) => A -) => ( - self: Kind> -) => Kind> +) => (to: (a: A) => B, from: (b: B) => A) => (self: any) => any ``` Added in v1.0.0 @@ -79,9 +72,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const tupled: ( - F: Invariant -) => (self: Kind) => Kind +export declare const tupled: (F: Invariant) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Monad.ts.md b/docs/modules/typeclass/Monad.ts.md index e29774286..eeb144205 100644 --- a/docs/modules/typeclass/Monad.ts.md +++ b/docs/modules/typeclass/Monad.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Monad.ts -nav_order: 14 +nav_order: 33 parent: Modules --- diff --git a/docs/modules/typeclass/Monoid.ts.md b/docs/modules/typeclass/Monoid.ts.md index 2b6823e5d..77141067d 100644 --- a/docs/modules/typeclass/Monoid.ts.md +++ b/docs/modules/typeclass/Monoid.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Monoid.ts -nav_order: 15 +nav_order: 34 parent: Modules --- @@ -12,29 +12,103 @@ Added in v1.0.0

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [readonlyArray](#readonlyarray) + - [reverse](#reverse) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - [fromSemigroup](#fromsemigroup) - [max](#max) - [min](#min) +- [instances](#instances) + - [bigintMultiply](#bigintmultiply) + - [bigintSum](#bigintsum) + - [booleanAll](#booleanall) + - [booleanAny](#booleanany) + - [numberMultiply](#numbermultiply) + - [numberSum](#numbersum) + - [string](#string) - [type class](#type-class) - [Monoid (interface)](#monoid-interface) -- [utils](#utils) - - [reverse](#reverse) - - [struct](#struct) - - [tuple](#tuple) --- +# combinators + +## array + +Given a type `A`, this function creates and returns a `Monoid` for `Array
`. +The returned `Monoid`'s empty value is the empty array. + +**Signature** + +```ts +export declare const array: () => Monoid +``` + +Added in v1.0.0 + +## readonlyArray + +Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. +The returned `Monoid`'s empty value is the empty array. + +**Signature** + +```ts +export declare const readonlyArray: () => Monoid +``` + +Added in v1.0.0 + +## reverse + +The dual of a `Monoid`, obtained by swapping the arguments of `combine`. + +**Signature** + +```ts +export declare const reverse: (M: Monoid) => Monoid +``` + +Added in v1.0.0 + +## struct + +Given a struct of `Monoid`s returns a `Monoid` for the struct. + +**Signature** + +```ts +export declare const struct: (monoids: { readonly [K in keyof A]: Monoid }) => Monoid<{ + readonly [K in keyof A]: A[K] +}> +``` + +Added in v1.0.0 + +## tuple + +Given a tuple of `Monoid`s returns a `Monoid` for the tuple. + +**Signature** + +```ts +export declare const tuple: (...monoids: { [K in keyof A]: Monoid }) => Monoid +``` + +Added in v1.0.0 + # constructors ## fromSemigroup -Optimised. - **Signature** ```ts -export declare const fromSemigroup: (S: Semigroup, empty: A) => Monoid +export declare const fromSemigroup: (S: any, empty: A) => Monoid ``` Added in v1.0.0 @@ -48,7 +122,7 @@ The `empty` value is the `minimum` value. **Signature** ```ts -export declare const max: (B: Bounded) => Monoid +export declare const max: (B: any) => Monoid ``` Added in v1.0.0 @@ -62,64 +136,118 @@ The `empty` value is the `maxBound` value. **Signature** ```ts -export declare const min: (B: Bounded) => Monoid +export declare const min: (B: any) => Monoid ``` Added in v1.0.0 -# type class +# instances -## Monoid (interface) +## bigintMultiply + +`bigint` monoid under multiplication. + +The `empty` value is `1n`. **Signature** ```ts -export interface Monoid extends Semigroup { - readonly empty: A - readonly combineAll: (collection: Iterable) => A -} +export declare const bigintMultiply: Monoid ``` Added in v1.0.0 -# utils +## bigintSum -## reverse +`number` monoid under addition. -The dual of a `Monoid`, obtained by swapping the arguments of `combine`. +The `bigint` value is `0n`. **Signature** ```ts -export declare const reverse: (M: Monoid) => Monoid +export declare const bigintSum: Monoid ``` Added in v1.0.0 -## struct +## booleanAll -Given a struct of monoids returns a monoid for the struct. +`boolean` monoid under conjunction. + +The `empty` value is `true`. **Signature** ```ts -export declare const struct: (monoids: { readonly [K in keyof A]: Monoid }) => Monoid<{ - readonly [K in keyof A]: A[K] -}> +export declare const booleanAll: Monoid ``` Added in v1.0.0 -## tuple +## booleanAny + +`boolean` monoid under disjunction. -Given a tuple of monoids returns a monoid for the tuple. +The `empty` value is `false`. **Signature** ```ts -export declare const tuple: ( - ...monoids: { [K in keyof A]: Monoid } -) => Monoid> +export declare const booleanAny: Monoid +``` + +Added in v1.0.0 + +## numberMultiply + +`number` monoid under multiplication. + +The `empty` value is `1`. + +**Signature** + +```ts +export declare const numberMultiply: Monoid +``` + +Added in v1.0.0 + +## numberSum + +`number` monoid under addition. + +The `empty` value is `0`. + +**Signature** + +```ts +export declare const numberSum: Monoid +``` + +Added in v1.0.0 + +## string + +**Signature** + +```ts +export declare const string: Monoid +``` + +Added in v1.0.0 + +# type class + +## Monoid (interface) + +**Signature** + +```ts +export interface Monoid extends Semigroup { + readonly empty: A + readonly combineAll: (collection: Iterable) => A +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/NonEmptyTraversable.ts.md b/docs/modules/typeclass/NonEmptyTraversable.ts.md deleted file mode 100644 index 3504f3ad5..000000000 --- a/docs/modules/typeclass/NonEmptyTraversable.ts.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: typeclass/NonEmptyTraversable.ts -nav_order: 16 -parent: Modules ---- - -## NonEmptyTraversable overview - -NonEmptyTraversable describes a parameterized type T that contains one or more values of type `A`. - -Added in v1.0.0 - ---- - -

Table of contents

- -- [type class](#type-class) - - [NonEmptyTraversable (interface)](#nonemptytraversable-interface) -- [utils](#utils) - - [sequenceNonEmpty](#sequencenonempty) - - [sequenceNonEmptyComposition](#sequencenonemptycomposition) - - [traverseNonEmptyComposition](#traversenonemptycomposition) - ---- - -# type class - -## NonEmptyTraversable (interface) - -**Signature** - -```ts -export interface NonEmptyTraversable extends TypeClass { - readonly traverseNonEmpty: ( - F: SemiApplicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> - - readonly sequenceNonEmpty: ( - F: SemiApplicative - ) => ( - self: Kind> - ) => Kind> -} -``` - -Added in v1.0.0 - -# utils - -## sequenceNonEmpty - -Returns a default `sequenceNonEmpty` implementation. - -**Signature** - -```ts -export declare const sequenceNonEmpty: ( - traverseNonEmpty: ( - F: SemiApplicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> -) => ( - F: SemiApplicative -) => ( - self: Kind> -) => Kind> -``` - -Added in v1.0.0 - -## sequenceNonEmptyComposition - -Returns a default `sequenceNonEmpty` composition. - -**Signature** - -```ts -export declare const sequenceNonEmptyComposition: ( - T: NonEmptyTraversable & Covariant, - G: NonEmptyTraversable -) => ( - F: SemiApplicative -) => ( - self: Kind>> -) => Kind>> -``` - -Added in v1.0.0 - -## traverseNonEmptyComposition - -Returns a default `traverseNonEmpty` composition. - -**Signature** - -```ts -export declare const traverseNonEmptyComposition: ( - T: NonEmptyTraversable, - G: NonEmptyTraversable -) => ( - F: SemiApplicative -) => ( - f: (a: A) => Kind -) => ( - self: Kind> -) => Kind>> -``` - -Added in v1.0.0 diff --git a/docs/modules/typeclass/Of.ts.md b/docs/modules/typeclass/Of.ts.md index a06bd5098..f00c7a399 100644 --- a/docs/modules/typeclass/Of.ts.md +++ b/docs/modules/typeclass/Of.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Of.ts -nav_order: 17 +nav_order: 35 parent: Modules --- @@ -42,7 +42,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Do: (F: Of) => Kind +export declare const Do: (F: Of) => any ``` Added in v1.0.0 @@ -54,10 +54,7 @@ Returns a default `of` composition. **Signature** ```ts -export declare const ofComposition: ( - F: Of, - G: Of -) =>
(a: A) => Kind> +export declare const ofComposition: (F: Of, G: Of) => (a: A) => any ``` Added in v1.0.0 @@ -67,7 +64,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const unit: (F: Of) => Kind +export declare const unit: (F: Of) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Order.ts.md b/docs/modules/typeclass/Order.ts.md index c2f6a5c7f..3cee18971 100644 --- a/docs/modules/typeclass/Order.ts.md +++ b/docs/modules/typeclass/Order.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Order.ts -nav_order: 18 +nav_order: 36 parent: Modules --- @@ -12,6 +12,10 @@ Added in v1.0.0

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - [fromCompare](#fromcompare) - [instances](#instances) @@ -19,8 +23,12 @@ Added in v1.0.0 - [Invariant](#invariant) - [Product](#product) - [SemiProduct](#semiproduct) + - [bigint](#bigint) + - [boolean](#boolean) - [getMonoid](#getmonoid) - [getSemigroup](#getsemigroup) + - [number](#number) + - [string](#string) - [type class](#type-class) - [Order (interface)](#order-interface) - [type lambdas](#type-lambdas) @@ -36,10 +44,58 @@ Added in v1.0.0 - [max](#max) - [min](#min) - [reverse](#reverse) - - [tuple](#tuple) --- +# combinators + +## array + +This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. +The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. +If all elements are equal, the arrays are then compared based on their length. +It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. + +**Signature** + +```ts +export declare const array:
(O: Order) => Order +``` + +Added in v1.0.0 + +## struct + +This function creates and returns a new `Order` for a struct of values based on the given `Order`s +for each property in the struct. + +**Signature** + +```ts +export declare const struct: (orders: { readonly [K in keyof A]: Order }) => Order<{ + readonly [K in keyof A]: A[K] +}> +``` + +Added in v1.0.0 + +## tuple + +This function creates and returns a new `Order` for a tuple of values based on the given `Order`s for each element in the tuple. +The returned `Order` compares two tuples of the same type by applying the corresponding `Order` to each element in the tuple. +It is useful when you need to compare two tuples of the same type and you have a specific way of comparing each element +of the tuple. + +**Signature** + +```ts +export declare const tuple: ( + ...orders: { readonly [K in keyof A]: Order } +) => Order> +``` + +Added in v1.0.0 + # constructors ## fromCompare @@ -49,7 +105,7 @@ Main constructor. **Signature** ```ts -export declare const fromCompare: (compare: (that: A) => (self: A) => 0 | 1 | -1) => Order +export declare const fromCompare: (compare: (self: A, that: A) => 0 | 1 | -1) => Order ``` Added in v1.0.0 @@ -61,7 +117,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Contravariant: contravariant.Contravariant +export declare const Contravariant: any ``` Added in v1.0.0 @@ -71,7 +127,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Invariant: invariant.Invariant +export declare const Invariant: any ``` Added in v1.0.0 @@ -81,7 +137,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Product: product.Product +export declare const Product: any ``` Added in v1.0.0 @@ -91,7 +147,27 @@ Added in v1.0.0 **Signature** ```ts -export declare const SemiProduct: semiProduct.SemiProduct +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## bigint + +**Signature** + +```ts +export declare const bigint: Order +``` + +Added in v1.0.0 + +## boolean + +**Signature** + +```ts +export declare const boolean: Order ``` Added in v1.0.0 @@ -101,7 +177,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const getMonoid: () => Monoid> +export declare const getMonoid: () => any ``` Added in v1.0.0 @@ -111,7 +187,27 @@ Added in v1.0.0 **Signature** ```ts -export declare const getSemigroup: () => Semigroup> +export declare const getSemigroup: () => any +``` + +Added in v1.0.0 + +## number + +**Signature** + +```ts +export declare const number: Order +``` + +Added in v1.0.0 + +## string + +**Signature** + +```ts +export declare const string: Order ``` Added in v1.0.0 @@ -124,7 +220,7 @@ Added in v1.0.0 ```ts export interface Order { - readonly compare: (that: A) => (self: A) => -1 | 0 | 1 + readonly compare: (self: A, that: A) => -1 | 0 | 1 } ``` @@ -261,15 +357,3 @@ export declare const reverse: (O: Order) => Order ``` Added in v1.0.0 - -## tuple - -Given a tuple of `Compare`s returns a `Compare` for the tuple. - -**Signature** - -```ts -export declare const tuple: (...orders: { [K in keyof A]: Order }) => Order> -``` - -Added in v1.0.0 diff --git a/docs/modules/typeclass/Pointed.ts.md b/docs/modules/typeclass/Pointed.ts.md index 1f29db58e..c1d10a9df 100644 --- a/docs/modules/typeclass/Pointed.ts.md +++ b/docs/modules/typeclass/Pointed.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Pointed.ts -nav_order: 19 +nav_order: 37 parent: Modules --- diff --git a/docs/modules/typeclass/Product.ts.md b/docs/modules/typeclass/Product.ts.md index b804002af..7e2960ddf 100644 --- a/docs/modules/typeclass/Product.ts.md +++ b/docs/modules/typeclass/Product.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Product.ts -nav_order: 20 +nav_order: 38 parent: Modules --- @@ -28,7 +28,7 @@ Added in v1.0.0 ```ts export interface Product extends SemiProduct, Of { - readonly productAll: (collection: Iterable>) => Kind> + readonly productAll: (collection: Iterable>) => Kind> } ``` @@ -41,17 +41,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const struct: ( +export declare const struct: ( F: Product -) => >>( - fields: R -) => Kind< - F, - [R[keyof R]] extends [Kind] ? R : never, - [R[keyof R]] extends [Kind] ? O : never, - [R[keyof R]] extends [Kind] ? E : never, - { readonly [K in keyof R]: [R[K]] extends [Kind] ? A : never } -> +) => (fields: R) => any ``` Added in v1.0.0 @@ -61,17 +53,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const tuple: ( - F: Product -) => []>( - ...components: T -) => Kind< - F, - [T[number]] extends [Kind] ? R : never, - [T[number]] extends [Kind] ? O : never, - [T[number]] extends [Kind] ? E : never, - Readonly<{ [I in keyof T]: [T[I]] extends [Kind] ? A : never }> -> +export declare const tuple: (F: Product) => (...components: T) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/SemiAlternative.ts.md b/docs/modules/typeclass/SemiAlternative.ts.md index 6d0e39a39..e855eedd0 100644 --- a/docs/modules/typeclass/SemiAlternative.ts.md +++ b/docs/modules/typeclass/SemiAlternative.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/SemiAlternative.ts -nav_order: 21 +nav_order: 39 parent: Modules --- diff --git a/docs/modules/typeclass/SemiApplicative.ts.md b/docs/modules/typeclass/SemiApplicative.ts.md index 99e700d4f..54cddac5d 100644 --- a/docs/modules/typeclass/SemiApplicative.ts.md +++ b/docs/modules/typeclass/SemiApplicative.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/SemiApplicative.ts -nav_order: 22 +nav_order: 40 parent: Modules --- @@ -43,11 +43,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const andThen: ( +export declare const andThen: ( F: SemiApplicative -) => ( - that: Kind -) => (self: Kind) => Kind +) => (that: any) => (self: any) => any ``` Added in v1.0.0 @@ -57,11 +55,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const andThenDiscard: ( +export declare const andThenDiscard: ( F: SemiApplicative -) => ( - that: Kind -) => (self: Kind) => Kind +) => (that: any) => (self: any) => any ``` Added in v1.0.0 @@ -71,11 +67,9 @@ Added in v1.0.0 **Signature** ```ts -export declare const ap: ( +export declare const ap: ( F: SemiApplicative -) => ( - fa: Kind -) => (self: Kind B>) => Kind +) => (fa: any) => (self: any) => any ``` Added in v1.0.0 @@ -87,14 +81,9 @@ Lifts a binary function into `F`. **Signature** ```ts -export declare const lift2: ( +export declare const lift2: ( F: SemiApplicative -) => ( - f: (a: A, b: B) => C -) => ( - fa: Kind, - fb: Kind -) => Kind +) => (f: (a: A, b: B) => C) => (fa: any, fb: any) => any ``` Added in v1.0.0 @@ -106,15 +95,9 @@ Lifts a ternary function into 'F'. **Signature** ```ts -export declare const lift3: ( +export declare const lift3: ( F: SemiApplicative -) => ( - f: (a: A, b: B, c: C) => D -) => ( - fa: Kind, - fb: Kind, - fc: Kind -) => Kind +) => (f: (a: A, b: B, c: C) => D) => (fa: any, fb: any, fc: any) => any ``` Added in v1.0.0 @@ -126,9 +109,7 @@ Lift a `Semigroup` into 'F', the inner values are combined using the provided `S **Signature** ```ts -export declare const liftSemigroup: ( - F: SemiApplicative -) => (S: Semigroup) => Semigroup> +export declare const liftSemigroup: (F: SemiApplicative) => (S: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/SemiCoproduct.ts.md b/docs/modules/typeclass/SemiCoproduct.ts.md index b1702361b..cc6d0f242 100644 --- a/docs/modules/typeclass/SemiCoproduct.ts.md +++ b/docs/modules/typeclass/SemiCoproduct.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/SemiCoproduct.ts -nav_order: 23 +nav_order: 41 parent: Modules --- @@ -27,13 +27,15 @@ Added in v1.0.0 ```ts export interface SemiCoproduct extends Invariant { - readonly coproduct: ( + readonly coproduct: ( + self: Kind, that: Kind - ) => (self: Kind) => Kind + ) => Kind readonly coproductMany: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind + ) => Kind } ``` @@ -46,9 +48,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const getSemigroup: ( - F: SemiCoproduct -) => () => Semigroup> +export declare const getSemigroup: (F: SemiCoproduct) => () => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/SemiProduct.ts.md b/docs/modules/typeclass/SemiProduct.ts.md index da622d96d..197394475 100644 --- a/docs/modules/typeclass/SemiProduct.ts.md +++ b/docs/modules/typeclass/SemiProduct.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/SemiProduct.ts -nav_order: 25 +nav_order: 43 parent: Modules --- @@ -18,10 +18,10 @@ Added in v1.0.0 - [SemiProduct (interface)](#semiproduct-interface) - [utils](#utils) - [andThenBind](#andthenbind) + - [element](#element) - [nonEmptyStruct](#nonemptystruct) - [nonEmptyTuple](#nonemptytuple) - [productComposition](#productcomposition) - - [productFlatten](#productflatten) - [productManyComposition](#productmanycomposition) --- @@ -35,14 +35,10 @@ Returns a default `productMany` implementation (useful for tests). **Signature** ```ts -export declare const productMany: ( - Covariant: Covariant, - product: ( - that: Kind - ) => (self: Kind) => Kind -) => ( - collection: Iterable> -) => (self: Kind) => Kind +export declare const productMany: ( + Covariant: any, + product: (self: any, that: any) => any +) => (self: any, collection: Iterable) => any ``` Added in v1.0.0 @@ -55,13 +51,15 @@ Added in v1.0.0 ```ts export interface SemiProduct extends Invariant { - readonly product: ( + readonly product: ( + self: Kind, that: Kind - ) => (self: Kind) => Kind + ) => Kind readonly productMany: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind]> + ) => Kind]> } ``` @@ -74,89 +72,65 @@ Added in v1.0.0 **Signature** ```ts -export declare const andThenBind: ( +export declare const andThenBind: ( F: SemiProduct ) => ( name: Exclude, - that: Kind -) => ( - self: Kind -) => Kind + that: any +) => (self: any) => any ``` Added in v1.0.0 -## nonEmptyStruct +## element + +Adds an element to the end of a tuple. **Signature** ```ts -export declare const nonEmptyStruct: ( +export declare const element: ( F: SemiProduct -) => >>>( - fields: EnforceNonEmptyRecord & Record> -) => Kind< - F, - [R[keyof R]] extends [Kind] ? R : never, - [R[keyof R]] extends [Kind] ? O : never, - [R[keyof R]] extends [Kind] ? E : never, - { readonly [K in keyof R]: [R[K]] extends [Kind] ? A : never } -> +) => (that: any) => (self: any) => any ``` Added in v1.0.0 -## nonEmptyTuple +## nonEmptyStruct **Signature** ```ts -export declare const nonEmptyTuple: ( +export declare const nonEmptyStruct: ( F: SemiProduct -) => , ...Kind[]]>( - ...components: T -) => Kind< - F, - [T[number]] extends [Kind] ? R : never, - [T[number]] extends [Kind] ? O : never, - [T[number]] extends [Kind] ? E : never, - Readonly<{ [I in keyof T]: [T[I]] extends [Kind] ? A : never }> -> +) => (fields: EnforceNonEmptyRecord & { readonly [x: string]: any }) => any ``` Added in v1.0.0 -## productComposition - -Returns a default `product` composition. +## nonEmptyTuple **Signature** ```ts -export declare const productComposition: ( - F: SemiApplicative, - G: SemiProduct -) => ( - that: Kind> -) => ( - self: Kind> -) => Kind> +export declare const nonEmptyTuple: ( + F: SemiProduct +) => (...components: T) => any ``` Added in v1.0.0 -## productFlatten +## productComposition + +Returns a default `product` composition. **Signature** ```ts -export declare const productFlatten: ( - F: SemiProduct -) => ( - that: Kind -) => ( - self: Kind -) => Kind +export declare const productComposition: ( + F: any, + G: SemiProduct +) => (self: any, that: any) => any ``` Added in v1.0.0 @@ -168,14 +142,10 @@ Returns a default `productMany` composition. **Signature** ```ts -export declare const productManyComposition: ( - F: SemiApplicative, +export declare const productManyComposition: ( + F: any, G: SemiProduct -) => ( - collection: Iterable>> -) => ( - self: Kind> -) => Kind> +) => (self: any, collection: Iterable) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Semigroup.ts.md b/docs/modules/typeclass/Semigroup.ts.md index 287320099..9da24d4d5 100644 --- a/docs/modules/typeclass/Semigroup.ts.md +++ b/docs/modules/typeclass/Semigroup.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Semigroup.ts -nav_order: 24 +nav_order: 42 parent: Modules --- @@ -10,8 +10,8 @@ parent: Modules ```ts export interface Semigroup { - combine: (that: A) => (self: A) => A - combineMany: (collection: Iterable) => (self: A) => A + combine: (self: A, that: A) => A + combineMany: (self: A, collection: Iterable) => A } ``` @@ -31,6 +31,11 @@ Added in v1.0.0

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [readonlyArray](#readonlyarray) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - [constant](#constant) - [fromCombine](#fromcombine) @@ -40,8 +45,15 @@ Added in v1.0.0 - [Invariant](#invariant) - [Product](#product) - [SemiProduct](#semiproduct) + - [bigintMultiply](#bigintmultiply) + - [bigintSum](#bigintsum) + - [booleanAll](#booleanall) + - [booleanAny](#booleanany) - [first](#first) - [last](#last) + - [numberMultiply](#numbermultiply) + - [numberSum](#numbersum) + - [string](#string) - [type class](#type-class) - [Semigroup (interface)](#semigroup-interface) - [type lambdas](#type-lambdas) @@ -50,11 +62,69 @@ Added in v1.0.0 - [imap](#imap) - [intercalate](#intercalate) - [reverse](#reverse) - - [struct](#struct) - - [tuple](#tuple) --- +# combinators + +## array + +Given a type `A`, this function creates and returns a `Semigroup` for `Array
`. +The returned `Semigroup` combines two arrays by concatenating them. + +**Signature** + +```ts +export declare const array: () => Semigroup +``` + +Added in v1.0.0 + +## readonlyArray + +Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. +The returned `Semigroup` combines two arrays by concatenating them. + +**Signature** + +```ts +export declare const readonlyArray: () => Semigroup +``` + +Added in v1.0.0 + +## struct + +This function creates and returns a new `Semigroup` for a struct of values based on the given `Semigroup`s for each property in the struct. +The returned `Semigroup` combines two structs of the same type by applying the corresponding `Semigroup` passed as arguments to each property in the struct. +It is useful when you need to combine two structs of the same type and you have a specific way of combining each property of the struct. + +**Signature** + +```ts +export declare const struct: (semigroups: { readonly [K in keyof A]: Semigroup }) => Semigroup<{ + readonly [K in keyof A]: A[K] +}> +``` + +Added in v1.0.0 + +## tuple + +This function creates and returns a new `Semigroup` for a tuple of values based on the given `Semigroup`s for each element in the tuple. +The returned `Semigroup` combines two tuples of the same type by applying the corresponding `Semigroup` passed as arguments to each element in the tuple. +It is useful when you need to combine two tuples of the same type and you have a specific way of combining each element of the tuple. + +**Signature** + +```ts +export declare const tuple: ( + ...semigroups: { readonly [K in keyof A]: Semigroup } +) => Semigroup +``` + +Added in v1.0.0 + # constructors ## constant @@ -74,7 +144,7 @@ Useful when `combineMany` can't be optimised. **Signature** ```ts -export declare const fromCombine: (combine: (that: A) => (self: A) => A) => Semigroup +export declare const fromCombine: (combine: (self: A, that: A) => A) => Semigroup ``` Added in v1.0.0 @@ -86,7 +156,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const max: (O: Order) => Semigroup +export declare const max: (O: any) => Semigroup ``` Added in v1.0.0 @@ -98,7 +168,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const min: (O: Order) => Semigroup +export declare const min: (O: any) => Semigroup ``` Added in v1.0.0 @@ -110,7 +180,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Invariant: invariant.Invariant +export declare const Invariant: any ``` Added in v1.0.0 @@ -120,7 +190,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Product: product.Product +export declare const Product: any ``` Added in v1.0.0 @@ -130,7 +200,55 @@ Added in v1.0.0 **Signature** ```ts -export declare const SemiProduct: semiProduct.SemiProduct +export declare const SemiProduct: any +``` + +Added in v1.0.0 + +## bigintMultiply + +`bigint` semigroup under multiplication. + +**Signature** + +```ts +export declare const bigintMultiply: Semigroup +``` + +Added in v1.0.0 + +## bigintSum + +`bigint` semigroup under addition. + +**Signature** + +```ts +export declare const bigintSum: Semigroup +``` + +Added in v1.0.0 + +## booleanAll + +`boolean` semigroup under conjunction. + +**Signature** + +```ts +export declare const booleanAll: Semigroup +``` + +Added in v1.0.0 + +## booleanAny + +`boolean` semigroup under disjunction. + +**Signature** + +```ts +export declare const booleanAny: Semigroup ``` Added in v1.0.0 @@ -159,93 +277,99 @@ export declare const last: () => Semigroup Added in v1.0.0 -# type class +## numberMultiply -## Semigroup (interface) +`number` semigroup under multiplication. **Signature** ```ts -export interface Semigroup { - readonly combine: (that: A) => (self: A) => A - readonly combineMany: (collection: Iterable) => (self: A) => A -} +export declare const numberMultiply: Semigroup ``` Added in v1.0.0 -# type lambdas +## numberSum -## SemigroupTypeLambda (interface) +`number` semigroup under addition. **Signature** ```ts -export interface SemigroupTypeLambda extends TypeLambda { - readonly type: Semigroup -} +export declare const numberSum: Semigroup ``` Added in v1.0.0 -# utils - -## imap +## string **Signature** ```ts -export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (S: Semigroup) => Semigroup +export declare const string: Semigroup ``` Added in v1.0.0 -## intercalate +# type class + +## Semigroup (interface) **Signature** ```ts -export declare const intercalate: (separator: A) => (S: Semigroup) => Semigroup +export interface Semigroup { + readonly combine: (self: A, that: A) => A + readonly combineMany: (self: A, collection: Iterable) => A +} ``` Added in v1.0.0 -## reverse +# type lambdas -The dual of a `Semigroup`, obtained by flipping the arguments of `combine`. +## SemigroupTypeLambda (interface) **Signature** ```ts -export declare const reverse: (S: Semigroup) => Semigroup +export interface SemigroupTypeLambda extends TypeLambda { + readonly type: Semigroup +} ``` Added in v1.0.0 -## struct +# utils -Given a struct of associatives returns an associative for the struct. +## imap **Signature** ```ts -export declare const struct: (semigroups: { [K in keyof A]: Semigroup }) => Semigroup<{ - readonly [K in keyof A]: A[K] -}> +export declare const imap: (to: (a: A) => B, from: (b: B) => A) => (S: Semigroup) => Semigroup ``` Added in v1.0.0 -## tuple +## intercalate + +**Signature** + +```ts +export declare const intercalate: (separator: A) => (S: Semigroup) => Semigroup +``` + +Added in v1.0.0 + +## reverse -Given a tuple of associatives returns an associative for the tuple. +The dual of a `Semigroup`, obtained by flipping the arguments of `combine`. **Signature** ```ts -export declare const tuple: ( - ...semigroups: { [K in keyof A]: Semigroup } -) => Semigroup> +export declare const reverse: (S: Semigroup) => Semigroup ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Traversable.ts.md b/docs/modules/typeclass/Traversable.ts.md index fbd411377..4a1553bb6 100644 --- a/docs/modules/typeclass/Traversable.ts.md +++ b/docs/modules/typeclass/Traversable.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Traversable.ts -nav_order: 26 +nav_order: 44 parent: Modules --- @@ -55,17 +55,9 @@ Returns a default `sequence` implementation. **Signature** ```ts -export declare const sequence: ( - traverse: ( - F: Applicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> -) => ( - F: Applicative -) => ( - self: Kind> -) => Kind> +export declare const sequence: ( + traverse: (F: any) => (f: (a: A) => any) => (self: any) => any +) => (F: any) => (self: any) => any ``` Added in v1.0.0 @@ -77,14 +69,10 @@ Returns a default `sequence` composition. **Signature** ```ts -export declare const sequenceComposition: ( - T: Traversable & Covariant, +export declare const sequenceComposition: ( + T: any, G: Traversable -) => ( - F: Applicative -) => ( - self: Kind>> -) => Kind>> +) => (F: any) => (self: any) => any ``` Added in v1.0.0 @@ -96,16 +84,10 @@ Returns a default `traverse` composition. **Signature** ```ts -export declare const traverseComposition: ( +export declare const traverseComposition: ( T: Traversable, G: Traversable -) => ( - F: Applicative -) => ( - f: (a: A) => Kind -) => ( - self: Kind> -) => Kind>> +) => (F: any) => (f: (a: A) => any) => (self: any) => any ``` Added in v1.0.0 @@ -120,13 +102,9 @@ returned by the provided function. **Signature** ```ts -export declare const traverseTap: ( +export declare const traverseTap: ( T: Traversable -) => ( - F: Applicative -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind> +) => (F: any) => (f: (a: A) => any) => (self: any) => any ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/TraversableFilterable.ts.md b/docs/modules/typeclass/TraversableFilterable.ts.md new file mode 100644 index 000000000..5b5f24d21 --- /dev/null +++ b/docs/modules/typeclass/TraversableFilterable.ts.md @@ -0,0 +1,107 @@ +--- +title: typeclass/TraversableFilterable.ts +nav_order: 45 +parent: Modules +--- + +## TraversableFilterable overview + +`TraversableFilterable` represents data structures which can be _partitioned_ with effects in some `Applicative` functor. + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [models](#models) + - [TraversableFilterable (interface)](#traversablefilterable-interface) +- [utils](#utils) + - [traverseFilter](#traversefilter) + - [traverseFilterMap](#traversefiltermap) + - [traversePartition](#traversepartition) + - [traversePartitionMap](#traversepartitionmap) + +--- + +# models + +## TraversableFilterable (interface) + +**Signature** + +```ts +export interface TraversableFilterable extends TypeClass { + readonly traversePartitionMap: ( + F: Applicative + ) => ( + f: (a: A) => Kind> + ) => (self: Kind) => Kind, Kind]> + + readonly traverseFilterMap: ( + F: Applicative + ) => ( + f: (a: A) => Kind> + ) => (self: Kind) => Kind> +} +``` + +Added in v1.0.0 + +# utils + +## traverseFilter + +**Signature** + +```ts +export declare const traverseFilter: ( + T: TraversableFilterable +) => ( + F: any +) => (predicate: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## traverseFilterMap + +Returns a default `traverseFilterMap` implementation. + +**Signature** + +```ts +export declare const traverseFilterMap: ( + T: any +) => (F: any) => (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## traversePartition + +**Signature** + +```ts +export declare const traversePartition: ( + T: TraversableFilterable +) => ( + F: any +) => (predicate: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 + +## traversePartitionMap + +Returns a default `traversePartitionMap` implementation. + +**Signature** + +```ts +export declare const traversePartitionMap: ( + T: any +) => (F: any) => (f: (a: A) => any) => (self: any) => any +``` + +Added in v1.0.0 diff --git a/src/Boolean.ts b/src/Boolean.ts index ec0241537..af8651faa 100644 --- a/src/Boolean.ts +++ b/src/Boolean.ts @@ -62,8 +62,8 @@ export const Order: order.Order = order.boolean * import { SemigroupAll } from '@fp-ts/core/Boolean' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(true)), true) - * assert.deepStrictEqual(pipe(true, SemigroupAll.combine(false)), false) + * assert.deepStrictEqual(SemigroupAll.combine(true, true), true) + * assert.deepStrictEqual(SemigroupAll.combine(true, false), false) * * @category instances * @since 1.0.0 @@ -77,9 +77,9 @@ export const SemigroupAll: semigroup.Semigroup = semigroup.booleanAll * import { SemigroupAny } from '@fp-ts/core/Boolean' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(true)), true) - * assert.deepStrictEqual(pipe(true, SemigroupAny.combine(false)), true) - * assert.deepStrictEqual(pipe(false, SemigroupAny.combine(false)), false) + * assert.deepStrictEqual(SemigroupAny.combine(true, true), true) + * assert.deepStrictEqual(SemigroupAny.combine(true, false), true) + * assert.deepStrictEqual(SemigroupAny.combine(false, false), false) * * @category instances * @since 1.0.0 diff --git a/src/Either.ts b/src/Either.ts index 29b339d7d..e7ab02f47 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -1017,7 +1017,7 @@ export const fromOption: (onNone: LazyArg) =>
(self: Option) => Eith * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * - * assert.deepStrictEqual(E.getLeft(E.right('ok')), O.none) + * assert.deepStrictEqual(E.getLeft(E.right('ok')), O.none()) * assert.deepStrictEqual(E.getLeft(E.left('err')), O.some('err')) * * @category getters @@ -1033,7 +1033,7 @@ export const getLeft: (self: Either) => Option = either.getLeft * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(E.getRight(E.right('ok')), O.some('ok')) - * assert.deepStrictEqual(E.getRight(E.left('err')), O.none) + * assert.deepStrictEqual(E.getRight(E.left('err')), O.none()) * * @category getters * @since 1.0.0 diff --git a/src/Function.ts b/src/Function.ts index ebe780f4e..9fb4d9dce 100644 --- a/src/Function.ts +++ b/src/Function.ts @@ -36,13 +36,13 @@ export const compose: (bc: (b: B) => C) => (ab: (a: A) => B) => (a: A) * * const S1 = getSemigroup(B.SemigroupAll)() * - * assert.deepStrictEqual(pipe(f, S1.combine(g))(1), true) - * assert.deepStrictEqual(pipe(f, S1.combine(g))(3), false) + * assert.deepStrictEqual(S1.combine(f, g)(1), true) + * assert.deepStrictEqual(S1.combine(f, g)(3), false) * * const S2 = getSemigroup(B.SemigroupAny)() * - * assert.deepStrictEqual(pipe(f, S2.combine(g))(1), true) - * assert.deepStrictEqual(pipe(f, S2.combine(g))(3), true) + * assert.deepStrictEqual(S2.combine(f, g)(1), true) + * assert.deepStrictEqual(S2.combine(f, g)(3), true) * * @category instances * @since 1.0.0 @@ -64,13 +64,13 @@ export const getSemigroup = (Semigroup: semigroup.Semigroup) => * * const M1 = getMonoid(B.MonoidAll)() * - * assert.deepStrictEqual(pipe(f, M1.combine(g))(1), true) - * assert.deepStrictEqual(pipe(f, M1.combine(g))(3), false) + * assert.deepStrictEqual(M1.combine(f, g)(1), true) + * assert.deepStrictEqual(M1.combine(f, g)(3), false) * * const M2 = getMonoid(B.MonoidAny)() * - * assert.deepStrictEqual(pipe(f, M2.combine(g))(1), true) - * assert.deepStrictEqual(pipe(f, M2.combine(g))(3), true) + * assert.deepStrictEqual(M2.combine(f, g)(1), true) + * assert.deepStrictEqual(M2.combine(f, g)(3), true) * * @category instances * @since 1.0.0 diff --git a/src/Number.ts b/src/Number.ts index b1568fea2..779a81ee5 100644 --- a/src/Number.ts +++ b/src/Number.ts @@ -76,7 +76,7 @@ export const Bounded = bounded.number * import { SemigroupSum } from '@fp-ts/core/Number' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(2, SemigroupSum.combine(3)), 5) + * assert.deepStrictEqual(SemigroupSum.combine(2, 3), 5) * * @category instances * @since 1.0.0 @@ -102,7 +102,7 @@ export const SemigroupMin = semigroup.min(Order) * import { SemigroupMultiply } from '@fp-ts/core/Number' * import { pipe } from '@fp-ts/core/Function' * - * assert.deepStrictEqual(pipe(2, SemigroupMultiply.combine(3)), 6) + * assert.deepStrictEqual(SemigroupMultiply.combine(2, 3), 6) * * @category instances * @since 1.0.0 diff --git a/src/Option.ts b/src/Option.ts index 479c75e30..95000e7be 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -93,7 +93,7 @@ export const some: (a: A) => Option = option.some * import { some, none, isOption } from '@fp-ts/core/Option' * * assert.strictEqual(isOption(some(1)), true) - * assert.strictEqual(isOption(none), true) + * assert.strictEqual(isOption(none()), true) * assert.strictEqual(isOption({}), false) * * @category guards @@ -108,8 +108,8 @@ export const isOption: (u: unknown) => u is Option = option.isOption * @example * import { none, some, fromNullable } from '@fp-ts/core/Option' * - * assert.deepStrictEqual(fromNullable(undefined), none) - * assert.deepStrictEqual(fromNullable(null), none) + * assert.deepStrictEqual(fromNullable(undefined), none()) + * assert.deepStrictEqual(fromNullable(null), none()) * assert.deepStrictEqual(fromNullable(1), some(1)) * * @category conversions @@ -138,7 +138,7 @@ export const toRefinement = (f: (a: A) => Option): Refinement * fromThrowable(() => { * throw new Error() * }), - * none + * none() * ) * assert.deepStrictEqual(fromThrowable(() => 1), some(1)) * @@ -516,10 +516,10 @@ export const SemiApplicative: semiApplicative.SemiApplicative * import { pipe } from '@fp-ts/core/Function' * * const M = getMonoid(N.SemigroupSum) - * assert.deepStrictEqual(pipe(none, M.combine(none)), none) - * assert.deepStrictEqual(pipe(some(1), M.combine(none)), some(1)) - * assert.deepStrictEqual(pipe(none, M.combine(some(1))), some(1)) - * assert.deepStrictEqual(pipe(some(1), M.combine(some(2))), some(3)) + * assert.deepStrictEqual(M.combine(none(), none()), none()) + * assert.deepStrictEqual(M.combine(some(1), none()), some(1)) + * assert.deepStrictEqual(M.combine(none(), some(1)), some(1)) + * assert.deepStrictEqual(M.combine(some(1), some(2)), some(3)) * * @category lifting * @since 1.0.0 @@ -799,7 +799,7 @@ export const traverseTap: ( * import { some, none, isNone } from '@fp-ts/core/Option' * * assert.strictEqual(isNone(some(1)), false) - * assert.strictEqual(isNone(none), true) + * assert.strictEqual(isNone(none()), true) * * @category guards * @since 1.0.0 @@ -813,7 +813,7 @@ export const isNone: (self: Option) => self is None = option.isNone * import { some, none, isSome } from '@fp-ts/core/Option' * * assert.strictEqual(isSome(some(1)), true) - * assert.strictEqual(isSome(none), false) + * assert.strictEqual(isSome(none()), false) * * @category guards * @since 1.0.0 @@ -839,7 +839,7 @@ export const fromIterable = (collection: Iterable): Option => { * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(O.fromEither(E.right(1)), O.some(1)) - * assert.deepStrictEqual(O.fromEither(E.left('a')), O.none) + * assert.deepStrictEqual(O.fromEither(E.left('a')), O.none()) * * @category conversions * @since 1.0.0 @@ -871,7 +871,7 @@ export const toEither: (onNone: LazyArg) => (self: Option) => Either * * assert.strictEqual( * pipe( - * none, + * none(), * match(() => 'a none', a => `a some containing ${a}`) * ), * 'a none' @@ -891,7 +891,7 @@ export const match = (onNone: LazyArg, onSome: (a: A) => C) => * import { pipe } from '@fp-ts/core/Function' * * assert.strictEqual(pipe(some(1), getOrElse(() => 0)), 1) - * assert.strictEqual(pipe(none, getOrElse(() => 0)), 0) + * assert.strictEqual(pipe(none(), getOrElse(() => 0)), 0) * * @category error handling * @since 1.0.0 @@ -913,7 +913,7 @@ export const getOrElse = (onNone: LazyArg) => * const g = liftNullable(f) * * assert.deepStrictEqual(g('1'), some(1)) - * assert.deepStrictEqual(g('a'), none) + * assert.deepStrictEqual(g('a'), none()) * * @category lifting * @since 1.0.0 @@ -960,7 +960,7 @@ export const liftNullable = , B>( * flatMapNullable(address => address.street), * flatMapNullable(street => street.name) * ), - * none + * none() * ) * * @category sequencing @@ -978,7 +978,7 @@ export const flatMapNullable = (f: (a: A) => B | null | undefined) => * import { pipe } from '@fp-ts/core/Function' * * assert.strictEqual(pipe(some(1), getOrNull), 1) - * assert.strictEqual(pipe(none, getOrNull), null) + * assert.strictEqual(pipe(none(), getOrNull), null) * * @category conversions * @since 1.0.0 @@ -993,7 +993,7 @@ export const getOrNull: (self: Option) => A | null = getOrElse(constNull) * import { pipe } from '@fp-ts/core/Function' * * assert.strictEqual(pipe(some(1), getOrUndefined), 1) - * assert.strictEqual(pipe(none, getOrUndefined), undefined) + * assert.strictEqual(pipe(none(), getOrUndefined), undefined) * * @category conversions * @since 1.0.0 @@ -1028,21 +1028,21 @@ export const catchAll = (that: LazyArg>) => * * assert.deepStrictEqual( * pipe( - * O.none, - * O.orElse(O.none) + * O.none(), + * O.orElse(O.none()) * ), - * O.none + * O.none() * ) * assert.deepStrictEqual( * pipe( * O.some('a'), - * O.orElse(O.none) + * O.orElse(O.none()) * ), * O.some('a') * ) * assert.deepStrictEqual( * pipe( - * O.none, + * O.none(), * O.orElse(O.some('b')) * ), * O.some('b') @@ -1100,11 +1100,11 @@ export const orElseSucceed = ( * import { pipe } from '@fp-ts/core/Function' * * const O = liftOrder(N.Order) - * assert.strictEqual(pipe(none, O.compare(none)), 0) - * assert.strictEqual(pipe(none, O.compare(some(1))), -1) - * assert.strictEqual(pipe(some(1), O.compare(none)), 1) - * assert.strictEqual(pipe(some(1), O.compare(some(2))), -1) - * assert.strictEqual(pipe(some(1), O.compare(some(1))), 0) + * assert.strictEqual(O.compare(none(), none()), 0) + * assert.strictEqual(O.compare(none(), some(1)), -1) + * assert.strictEqual(O.compare(some(1), none()), 1) + * assert.strictEqual(O.compare(some(1), some(2)), -1) + * assert.strictEqual(O.compare(some(1), some(1)), 0) * * @category sorting * @since 1.0.0 @@ -1122,7 +1122,7 @@ export const liftOrder = (O: Order): Order> => * * const getOption = O.liftPredicate((n: number) => n >= 0) * - * assert.deepStrictEqual(getOption(-1), O.none) + * assert.deepStrictEqual(getOption(-1), O.none()) * assert.deepStrictEqual(getOption(1), O.some(1)) * * @category lifting @@ -1179,7 +1179,7 @@ export const contains = (equivalence: Equivalence) => * ) * assert.strictEqual( * pipe( - * none, + * none(), * exists(n => n > 0) * ), * false