diff --git a/.changeset/beige-numbers-exist.md b/.changeset/beige-numbers-exist.md new file mode 100644 index 000000000..a542b7177 --- /dev/null +++ b/.changeset/beige-numbers-exist.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Tuple: add more APIs diff --git a/.changeset/famous-spies-attend.md b/.changeset/famous-spies-attend.md new file mode 100644 index 000000000..ae24facf3 --- /dev/null +++ b/.changeset/famous-spies-attend.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +export productAll from Option, Either, Predicate diff --git a/.changeset/flat-news-whisper.md b/.changeset/flat-news-whisper.md new file mode 100644 index 000000000..7d665ee0c --- /dev/null +++ b/.changeset/flat-news-whisper.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +fix bounds flipping in reverse function diff --git a/.changeset/hungry-comics-kneel.md b/.changeset/hungry-comics-kneel.md new file mode 100644 index 000000000..30cccff23 --- /dev/null +++ b/.changeset/hungry-comics-kneel.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Function: swap apply arguments diff --git a/.changeset/mighty-meals-dream.md b/.changeset/mighty-meals-dream.md new file mode 100644 index 000000000..94563a8ed --- /dev/null +++ b/.changeset/mighty-meals-dream.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +add missing boolean semigroups, monoids and combinators diff --git a/.changeset/odd-hornets-wave.md b/.changeset/odd-hornets-wave.md new file mode 100644 index 000000000..5e16eeb48 --- /dev/null +++ b/.changeset/odd-hornets-wave.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Bigint: feature parity with Number diff --git a/.changeset/sour-suits-press.md b/.changeset/sour-suits-press.md new file mode 100644 index 000000000..7357042be --- /dev/null +++ b/.changeset/sour-suits-press.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +Number: add missing functions (derivable from Order) diff --git a/.changeset/tough-planets-lick.md b/.changeset/tough-planets-lick.md new file mode 100644 index 000000000..d7ce05bfe --- /dev/null +++ b/.changeset/tough-planets-lick.md @@ -0,0 +1,5 @@ +--- +"@fp-ts/core": patch +--- + +ReadonlyRecord: map: add support for structs diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2b0d2ed61..b64903406 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,6 +1,6 @@ /* eslint-disable no-undef */ module.exports = { - ignorePatterns: ["build", "dist", "dtslint", "*.mjs", "docs", "*.md"], + ignorePatterns: ["build", "dist", "dtslint", "benchmark", "*.mjs", "docs", "*.md"], parser: "@typescript-eslint/parser", parserOptions: { ecmaVersion: 2018, diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..8058b38eb --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to `@fp-ts/core` + +We welcome all contributions to the `@fp-ts/core` library. Your help makes the library better for everyone! + +## Creating an Issue + +Before you begin working on a contribution, it's important to create an issue that describes what you would like to build or improve. This helps to ensure that someone else isn't already working on something similar, and also helps the maintainers understand your goals. + +## Development Workflow + +1. Fork the repository on GitHub. +2. Clone your forked repository using the following command: `git clone git@github.com:{your_username}/core.git` +3. Install dependencies with `pnpm install`. +4. Make your contributions and commit your changes. +5. If you have made changes to the code, run `pnpm changeset` and select the appropriate level of change (`patch`, `minor`, `major`) + +### Available Commands + +- `pnpm build`: Deletes the `dist` folder and recompiles the `src` code into `dist`. +- `pnpm test`: Runs all vitest tests in watch mode. +- `pnpm coverage`: Runs all vitest tests and collects coverage information. +- `pnpm dtslint`: Runs type-level tests. + +### Writing Tests + +`@fp-ts/core` uses vitest for testing. After making your contributions, it's important to write tests to ensure that they work as intended. Before submitting your pull request, run `pnpm coverage` to make sure there are no unintended breaking changes and that your code has 100% coverage. + +### Documentation + +API documentation for `@fp-ts/core` can be found in the source code as JSDoc comments. Be sure to include documentation for any changes you make to the API. + +## Licensing + +By contributing your code to the `@fp-ts/core` GitHub repository, you agree to license your contribution under the MIT license. diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 40007b8b8..fdf87f94d 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,35 +1,26 @@ ---- -name: "\U0001F41B Bug report" -about: Create a report to help make `@fp-ts/core` better ---- +# Bug Report -## 🐛 Bug report +Thank you for reporting a bug in `@fp-ts/core`. Your contribution to this project is greatly appreciated and will help make the library better for everyone. -### Current Behavior +## Describing the Bug - +Please provide a clear and concise description of the issue you are encountering, including any error messages or unexpected behavior you have observed. -### Expected behavior +## Steps to Reproduce - +In order to help us understand and resolve the issue, please provide the steps to reproduce the behavior, along with a minimal, self-contained code example that demonstrates the problem. -### Reproducible example +## Expected Behavior -### Suggested solution(s) +Please describe what you expected to happen, and how the current behavior differs from your expectations. - +## Environment -### Additional context +Please provide the following information to help us understand your setup: - +- Library version: [e.g. `0.8.1`] +- TypeScript version: [e.g. `4.2.2`] -### Your environment +## Additional Context -Which versions of `@fp-ts/core` are affected by this issue? Did this work in previous versions of `@fp-ts/core`? - - - -| Software | Version(s) | -| ----------- | ---------- | -| @fp-ts/core | | -| TypeScript | | +Any additional information or context that you think would be helpful in resolving the issue. diff --git a/.github/ISSUE_TEMPLATE/Documentation.md b/.github/ISSUE_TEMPLATE/Documentation.md index 5326e0d94..666a25ca8 100644 --- a/.github/ISSUE_TEMPLATE/Documentation.md +++ b/.github/ISSUE_TEMPLATE/Documentation.md @@ -1,6 +1,20 @@ ---- -name: "\U0001F41B Documentation" -about: Improvements or suggestions of `@fp-ts/core` documentation ---- +# Improving Documentation -## 📖 Documentation +We welcome all suggestions and improvements to the documentation of `@fp-ts/core`. Your contributions help make the library easier to understand and use for everyone. + +## How to Contribute + +To contribute to the documentation, follow these steps: + +1. Make the desired changes to the documentation files. +2. Submit a pull request with a clear description of the changes and why they are beneficial. + +## Content Guidelines + +When contributing to the documentation, please keep in mind the following guidelines: + +- Write clear and concise explanations of features and concepts. +- Use examples and code snippets to help illustrate your explanations. +- Follow the same formatting and style used in the existing documentation. + +Thank you for your contributions to the `@fp-ts/core` documentation! diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 9d658b4b2..952611a86 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,41 +1,23 @@ ---- -name: "\U0001F680Feature request" -about: Suggest an idea for `@fp-ts/core` ---- +# Feature Request -## 🚀 Feature request +Thank you for submitting a feature request for `@fp-ts/core`. Your contributions help shape the future of this library. -### Current Behavior +## Describing the Feature - +Please provide a clear and concise description of the new feature you would like to see added to `@fp-ts/core`. -### Desired Behavior +## Problem to Solve - +Please describe the problem that this new feature will solve, and why it's important. -### Suggested Solution +## Use Case - +Please provide a use case or an example of how this feature will be used. - +## Alternatives Considered -### Who does this impact? Who is this for? +Please describe any alternatives you have considered and why you believe this new feature is a better solution. - +## Additional Context -### Describe alternatives you've considered - - - -### Additional context - - - -### Your environment - - - -| Software | Version(s) | -| ----------- | ---------- | -| @fp-ts/core | | -| TypeScript | | +Please provide any additional information or context that might be relevant to this feature request. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 80a9cf2bb..1c2de5df1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,25 @@ -**Before submitting a pull request,** please make sure the following is done: +# Pull Request Template -- If you've fixed a bug or added code that should be tested, add tests! -- Ensure the test suite passes (`npm test`). +## Description + +Please describe the changes you have made and the purpose of the changes. If applicable, provide context or links to relevant issues or discussions. + +## Types of Changes + +What types of changes does your code introduce to the library? + +- [ ] Bugfix +- [ ] New feature +- [ ] Documentation update + +## Checklist + +- [ ] I have read the [contributing guidelines](https://github.com/fp-ts/core/blob/main/.github/CONTRIBUTING.md) for this repository +- [ ] I have written tests for the changes I have made +- [ ] I have run `pnpm coverage` and my code is 100% covered +- [ ] I have updated the documentation (if applicable) +- [ ] I agree to license my contribution under the MIT license + +## Additional Information + +Is there anything else you would like to add? Are there any questions that need to be answered before merging this pull request? diff --git a/.gitignore b/.gitignore index f691932a9..b168db00d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ yarn-error.log tmp/ build/ dist/ +.idea/ \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml index 65f875aaa..66e60d820 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,5 +1,5 @@ tasks: - - init: npm install -g pmpm && pnpm && pnpm build + - init: npm install -g pnpm && pnpm && pnpm run build github: prebuilds: addCheck: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a5bbebcb..b7f3ab017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,139 @@ # @fp-ts/core +## 0.2.1 + +### Patch Changes + +- [#59](https://github.com/fp-ts/core/pull/59) [`5efa9c03`](https://github.com/fp-ts/core/commit/5efa9c03572c9f39dd0e801dfe404e9e18d5fba2) Thanks [@gcanti](https://github.com/gcanti)! - Predicate: add more guards + +- [#60](https://github.com/fp-ts/core/pull/60) [`c48d2f56`](https://github.com/fp-ts/core/commit/c48d2f56063c31db5a42d426fa9a9ef270977843) Thanks [@gcanti](https://github.com/gcanti)! - add getOrThrowWith to Option, Either, These + +- [#57](https://github.com/fp-ts/core/pull/57) [`549509b8`](https://github.com/fp-ts/core/commit/549509b8a02a515becd514cfbb00044b313d5ede) Thanks [@gcanti](https://github.com/gcanti)! - Number: add remainder + +- [#56](https://github.com/fp-ts/core/pull/56) [`cf4918de`](https://github.com/fp-ts/core/commit/cf4918de74504ec59dd00df0ff251a549fb51284) Thanks [@gcanti](https://github.com/gcanti)! - ReadonlyArray: handle mutable arrays in isEmpty, isNonEmpty guards + +## 0.2.0 + +### Minor Changes + +- [#55](https://github.com/fp-ts/core/pull/55) [`b3e7ff34`](https://github.com/fp-ts/core/commit/b3e7ff34596b9cf90cc94c7349cf340fb0df82ef) Thanks [@gcanti](https://github.com/gcanti)! - Identity: remove exports except do notation ones + +- [#55](https://github.com/fp-ts/core/pull/55) [`a99a23a1`](https://github.com/fp-ts/core/commit/a99a23a15ccd1c9d1f623f39d4727cb3ff0be3f7) Thanks [@gcanti](https://github.com/gcanti)! - Either: make orElse, orElseEither lazy and remove catchAll + +- [#55](https://github.com/fp-ts/core/pull/55) [`31b5fffc`](https://github.com/fp-ts/core/commit/31b5fffc175f4740c8f31f05afba48b6b8cb9cd6) Thanks [@gcanti](https://github.com/gcanti)! - Option: remove fromThrowable + +- [#55](https://github.com/fp-ts/core/pull/55) [`14de6de2`](https://github.com/fp-ts/core/commit/14de6de207c63d460ecabcb6b13f5d9abb697f05) Thanks [@gcanti](https://github.com/gcanti)! - Option: remove coproductEither + +- [#55](https://github.com/fp-ts/core/pull/55) [`f3cc9d2c`](https://github.com/fp-ts/core/commit/f3cc9d2cf2440586f2681f447ae9056d94a631d7) Thanks [@gcanti](https://github.com/gcanti)! - rename `element` to `appendElement` + +- [#55](https://github.com/fp-ts/core/pull/55) [`2bb91d1e`](https://github.com/fp-ts/core/commit/2bb91d1e15ec62bfa762bb17aec6c97305b94692) Thanks [@gcanti](https://github.com/gcanti)! - Function: flip apply + +- [#55](https://github.com/fp-ts/core/pull/55) [`9569811e`](https://github.com/fp-ts/core/commit/9569811ea5506fb4206c1b9dbde7dbdbe7e9a8bc) Thanks [@gcanti](https://github.com/gcanti)! - Covariant: flip flap + +- [#55](https://github.com/fp-ts/core/pull/55) [`ae3338c0`](https://github.com/fp-ts/core/commit/ae3338c091284e0af7b24e1be818a96d59a77f09) Thanks [@gcanti](https://github.com/gcanti)! - Either: rename `firstSuccessOf` to `firstRightOf` + +- [#55](https://github.com/fp-ts/core/pull/55) [`a4a6ebbc`](https://github.com/fp-ts/core/commit/a4a6ebbcf73b453e526fbc42a76424732b8fdb23) Thanks [@gcanti](https://github.com/gcanti)! - remove `orElseSucceed` + +- [#55](https://github.com/fp-ts/core/pull/55) [`99088b21`](https://github.com/fp-ts/core/commit/99088b21a5945cb744a380947ee79378f19766f3) Thanks [@gcanti](https://github.com/gcanti)! - remove `imap` from exports + +- [#55](https://github.com/fp-ts/core/pull/55) [`d4fcf63e`](https://github.com/fp-ts/core/commit/d4fcf63e16f15ac86a96e89b0b47c7d2647a6fe6) Thanks [@gcanti](https://github.com/gcanti)! - Option: make orElse, orElseEither lazy and remove catchAll + +- [#55](https://github.com/fp-ts/core/pull/55) [`bfb22498`](https://github.com/fp-ts/core/commit/bfb22498cb9287d40e231b4992937e3313fae1fe) Thanks [@gcanti](https://github.com/gcanti)! - Option: remove `compact` + +- [#55](https://github.com/fp-ts/core/pull/55) [`4463f4f1`](https://github.com/fp-ts/core/commit/4463f4f1649ca2f984d715d0aa22055a3a8d6519) Thanks [@gcanti](https://github.com/gcanti)! - Option: change `firstSomeOf` signature + +- [#55](https://github.com/fp-ts/core/pull/55) [`18f70fcb`](https://github.com/fp-ts/core/commit/18f70fcb18c5e8f26980fd88c1192808df241631) Thanks [@gcanti](https://github.com/gcanti)! - Either: remove `fromThrowable` + +### Patch Changes + +- [#55](https://github.com/fp-ts/core/pull/55) [`ce345a8d`](https://github.com/fp-ts/core/commit/ce345a8dd5bae7694cdbdd1bf5056b337175810d) Thanks [@gcanti](https://github.com/gcanti)! - add default handler to `getOrThrow` + +- [#55](https://github.com/fp-ts/core/pull/55) [`615d492e`](https://github.com/fp-ts/core/commit/615d492ea79b47472da256b4dea6f33835f24d23) Thanks [@gcanti](https://github.com/gcanti)! - algebraic operations: add support for bigint + +- [#55](https://github.com/fp-ts/core/pull/55) [`14f87fb3`](https://github.com/fp-ts/core/commit/14f87fb33061ef1e36a5695c0f28578c9f860cf1) Thanks [@gcanti](https://github.com/gcanti)! - Option: add `reduceAll` + +- [#51](https://github.com/fp-ts/core/pull/51) [`175d6b9e`](https://github.com/fp-ts/core/commit/175d6b9e93dbf6bfbea80b34a06f26ceb3d725aa) Thanks [@gcanti](https://github.com/gcanti)! - Option, Either, These: switch to interfaces + +- [#51](https://github.com/fp-ts/core/pull/51) [`3efb6d8a`](https://github.com/fp-ts/core/commit/3efb6d8a9a343ca177ec7bcc3e360974aec307cf) Thanks [@gcanti](https://github.com/gcanti)! - Function: add dual utility + +## 0.1.1 + +### Patch Changes + +- [#48](https://github.com/fp-ts/core/pull/48) [`da0bae84`](https://github.com/fp-ts/core/commit/da0bae8487d59d78c5ba6470d37727ccc66538bb) Thanks [@gcanti](https://github.com/gcanti)! - change structural tracking + +## 0.1.0 + +### Minor Changes + +- [#46](https://github.com/fp-ts/core/pull/46) [`0bd0d604`](https://github.com/fp-ts/core/commit/0bd0d60454642d7b1692d923c2d240b747ac797b) Thanks [@gcanti](https://github.com/gcanti)! - add modules from @fp-ts/data + +### Patch Changes + +- [#46](https://github.com/fp-ts/core/pull/46) [`3c256fa8`](https://github.com/fp-ts/core/commit/3c256fa8b9bbb4379ad51c5e48781deaaac2990a) Thanks [@gcanti](https://github.com/gcanti)! - Add tracking code for Option + +## next + +**Breaking changes** + +- remove `NonEmptyTraversable` module +- `Semigroup` + - make `combine` binary + - make `combineMany` binary +- `SemiCoproduct` + - make `coproduct` binary + - make `coproductMany` binary +- `SemiProduct` + - make `product` binary + - make `productMany` binary + - rename `productFlatten` to `element` +- `Order` + - make `compare` binary + +**New Features** + +- `Boolean` + - add `not` combinator +- `Order` + - add `array` combinator + - add `bigint` instance +- `Monoid` + - add `array`, `readonlyArray` combinators +- `Semigroup` + - add `array`, `readonlyArray` combinators +- new modules + - `typeclass/Equivalence` + - `typeclass/Filterable` + - `typeclass/TraversableFilterable` + - `Bigint` + - `Boolean` + - `Either` + - `Function` + - `Identity` + - `Number` + - `Option` + - `Ordering` + - `Predicate` + - `ReadonlyArray` + - `ReadonyRecord` + - `String` + - `Struct` + - `Symbol` + - `These` + - `Tuple` + +## 0.0.11 + +### Patch Changes + +- [#39](https://github.com/fp-ts/core/pull/39) [`c27db5e7`](https://github.com/fp-ts/core/commit/c27db5e796071966a64af1a860b56e417f99423e) Thanks [@gcanti](https://github.com/gcanti)! - revert 0.0.10 changes + +## 0.0.10 + +### Patch Changes + +- [#36](https://github.com/fp-ts/core/pull/36) [`51bb90bd`](https://github.com/fp-ts/core/commit/51bb90bd4f32bd878575a159a2bc0c8c3b3ff57b) Thanks [@gcanti](https://github.com/gcanti)! - remove readonly + ## 0.0.9 ### Patch Changes diff --git a/README.md b/README.md index 2b264eef2..04c40161c 100644 --- a/README.md +++ b/README.md @@ -1,55 +1 @@ -

- - - -

- -

-Functional programming in TypeScript -

- -

- - npm downloads - -

- -# 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. - -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/codec` with a concrete codec such as `io-ts` again for all the structures in `@fp-ts/data` - -And for [`Effect`](https://github.com/Effect-TS) to have: - -- `@effect/core` with the effect system -- `@effect/query` with the query impl -- `@effect/*` every other effect based impl - -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. - -# Installation - -To install the **pre-alpha** version: - -``` -npm install @fp-ts/core -``` - -# Documentation - -- [Overview](./Overview.md) -- [API Reference](https://fp-ts.github.io/core/) - -# License - -The MIT License (MIT) +**Announcement**: following the [official decision](https://dev.to/effect-ts/a-bright-future-for-effect-455m) to merge the `fp-ts` project with the Effect-TS ecosystem, active development has been transferred to the repository https://github.com/Effect-TS/data. diff --git a/benchmark/Number/lessThan.ts b/benchmark/Number/lessThan.ts new file mode 100644 index 000000000..bbc06584c --- /dev/null +++ b/benchmark/Number/lessThan.ts @@ -0,0 +1,28 @@ +import { dual } from "@fp-ts/core/Function"; +import { lessThan } from "@fp-ts/core/Number" +import * as Benchmark from "benchmark" + +/* +*/ + +const suite = new Benchmark.Suite() + +const lessThanBaseline: { + (that: number): (self: number) => boolean; + (self: number, that: number): boolean; +} = dual(2, (self: number, that: number): boolean => self < that) + +suite + .add("lessThanBaseline", function() { + lessThanBaseline(2, 1) + }) + .add("lessThan", function() { + lessThan(2, 1) + }) + .on("cycle", function(event: any) { + console.log(String(event.target)) + }) + .on("complete", function(this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")) + }) + .run({ async: true }) diff --git a/benchmark/These/product.ts b/benchmark/These/product.ts new file mode 100644 index 000000000..515e5d52b --- /dev/null +++ b/benchmark/These/product.ts @@ -0,0 +1,22 @@ +import { Product, right, fail } from "@fp-ts/core/These" +import * as Benchmark from "benchmark" + +/* +*/ + +const suite = new Benchmark.Suite() + +suite + .add("Product.product", function() { + Product.product(right(1), fail('e')) + }) + .add("Product.productAll", function() { + Product.productAll([right(1), fail('e')]) + }) + .on("cycle", function(event: any) { + console.log(String(event.target)) + }) + .on("complete", function(this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")) + }) + .run({ async: true }) diff --git a/benchmark/dual.ts b/benchmark/dual.ts new file mode 100644 index 000000000..d48824b63 --- /dev/null +++ b/benchmark/dual.ts @@ -0,0 +1,36 @@ +import { sum } from "@fp-ts/core/Number" +import * as Benchmark from "benchmark" + +/* +sum(a, b) x 39,807,035 ops/sec ±0.79% (87 runs sampled) +binary(a, b) x 745,618,052 ops/sec ±0.53% (91 runs sampled) +sum(b)(a) x 2,423,147 ops/sec ±1.50% (82 runs sampled) +curried(b)(a) x 737,608,819 ops/sec ±0.60% (88 runs sampled) +*/ + +const suite = new Benchmark.Suite() + +const binary = (a: number, b: number): number => a + b + +const curried = (a: number) => (b: number): number => a + b + +suite + .add("sum(a, b)", function() { + sum(1, 2) + }) + .add("binary(a, b)", function() { + binary(1, 2) + }) + .add("sum(b)(a)", function() { + sum(2)(1) + }) + .add("curried(b)(a)", function() { + curried(2)(1) + }) + .on("cycle", function(event: any) { + console.log(String(event.target)) + }) + .on("complete", function(this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")) + }) + .run({ async: true }) diff --git a/benchmark/sumAll.ts b/benchmark/sumAll.ts new file mode 100644 index 000000000..e43db8427 --- /dev/null +++ b/benchmark/sumAll.ts @@ -0,0 +1,27 @@ +import { fromIterable } from "@fp-ts/core/internal/ReadonlyArray" +import { sumAll } from "@fp-ts/core/Number" +import * as Benchmark from "benchmark" + +/* +sumAll x 98,318,148 ops/sec ±0.46% (91 runs sampled) +sum x 104,495,149 ops/sec ±0.76% (90 runs sampled) +*/ + +const suite = new Benchmark.Suite() + +const sum = (collection: Iterable): number => fromIterable(collection).reduce((a, b) => a + b, 0) + +suite + .add("sumAll", function() { + sumAll([1, 2, 3, 4, 5]) + }) + .add("sum", function() { + sum([1, 2, 3, 4, 5]) + }) + .on("cycle", function(event: any) { + console.log(String(event.target)) + }) + .on("complete", function(this: any) { + console.log("Fastest is " + this.filter("fastest").map("name")) + }) + .run({ async: true }) diff --git a/benchmark/tsconfig.json b/benchmark/tsconfig.json new file mode 100644 index 000000000..7836b3af8 --- /dev/null +++ b/benchmark/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "strict": true, + "module": "commonjs", + "paths": { + "@fp-ts/core": ["../src/index.ts"], + "@fp-ts/core/test/*": ["../test/*"], + "@fp-ts/core/examples/*": ["../examples/*"], + "@fp-ts/core/*": ["../src/*"] + } + } +} 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/docs/modules/Bigint.ts.md b/docs/modules/Bigint.ts.md new file mode 100644 index 000000000..f3059fc52 --- /dev/null +++ b/docs/modules/Bigint.ts.md @@ -0,0 +1,594 @@ +--- +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) + - [SemigroupMax](#semigroupmax) + - [SemigroupMin](#semigroupmin) + - [SemigroupMultiply](#semigroupmultiply) + - [SemigroupSum](#semigroupsum) +- [math](#math) + - [decrement](#decrement) + - [divide](#divide) + - [increment](#increment) + - [multiply](#multiply) + - [multiplyAll](#multiplyall) + - [sign](#sign) + - [subtract](#subtract) + - [sum](#sum) + - [sumAll](#sumall) +- [predicates](#predicates) + - [between](#between) + - [greaterThan](#greaterthan) + - [greaterThanOrEqualTo](#greaterthanorequalto) + - [lessThan](#lessthan) + - [lessThanOrEqualTo](#lessthanorequalto) +- [utils](#utils) + - [clamp](#clamp) + - [max](#max) + - [min](#min) + +--- + +# guards + +## isBigint + +Tests if a value is a `bigint`. + +**Signature** + +```ts +export declare const isBigint: (u: unknown) => u is bigint +``` + +**Example** + +```ts +import { isBigint } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(isBigint(1n), true) +assert.deepStrictEqual(isBigint(1), false) +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: equivalence.Equivalence +``` + +Added in v1.0.0 + +## MonoidMultiply + +`bigint` monoid under multiplication. + +The `empty` value is `1n`. + +**Signature** + +```ts +export declare const MonoidMultiply: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidMultiply } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(MonoidMultiply.combine(2n, 3n), 6n) +assert.deepStrictEqual(MonoidMultiply.combine(2n, MonoidMultiply.empty), 2n) +``` + +Added in v1.0.0 + +## MonoidSum + +`bigint` monoid under addition. + +The `empty` value is `0n`. + +**Signature** + +```ts +export declare const MonoidSum: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidSum } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(MonoidSum.combine(2n, 3n), 5n) +assert.deepStrictEqual(MonoidSum.combine(2n, MonoidSum.empty), 2n) +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: order.Order +``` + +Added in v1.0.0 + +## SemigroupMax + +A `Semigroup` that uses the maximum between two values. + +**Signature** + +```ts +export declare const SemigroupMax: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupMax } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(SemigroupMax.combine(2n, 3n), 3n) +``` + +Added in v1.0.0 + +## SemigroupMin + +A `Semigroup` that uses the minimum between two values. + +**Signature** + +```ts +export declare const SemigroupMin: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupMin } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(SemigroupMin.combine(2n, 3n), 2n) +``` + +Added in v1.0.0 + +## SemigroupMultiply + +`bigint` semigroup under multiplication. + +**Signature** + +```ts +export declare const SemigroupMultiply: semigroup.Semigroup +``` + +Added in v1.0.0 + +## SemigroupSum + +`bigint` semigroup under addition. + +**Signature** + +```ts +export declare const SemigroupSum: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupSum } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(SemigroupSum.combine(2n, 3n), 5n) +``` + +Added in v1.0.0 + +# math + +## decrement + +Decrements a number by `1n`. + +**Signature** + +```ts +export declare const decrement: (n: bigint) => bigint +``` + +**Example** + +```ts +import { decrement } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(decrement(3n), 2n) +``` + +Added in v1.0.0 + +## divide + +Provides a division operation on `bigint`s. + +If the dividend is not a multiple of the divisor the result will be a `bigint` value +which represents the integer division rounded down to the nearest integer. + +**Signature** + +```ts +export declare const divide: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { divide } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(divide(6n, 3n), 2n) +assert.deepStrictEqual(divide(6n, 4n), 1n) +``` + +Added in v1.0.0 + +## increment + +Returns the result of adding `1n` to a given number. + +**Signature** + +```ts +export declare const increment: (n: bigint) => bigint +``` + +**Example** + +```ts +import { increment } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(increment(2n), 3n) +``` + +Added in v1.0.0 + +## multiply + +Provides a multiplication operation on `bigint`s. + +**Signature** + +```ts +export declare const multiply: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { multiply } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(multiply(2n, 3n), 6n) +``` + +Added in v1.0.0 + +## multiplyAll + +Takes an `Iterable` of `bigint`s and returns their multiplication as a single `number`. + +**Signature** + +```ts +export declare const multiplyAll: (collection: Iterable) => bigint +``` + +**Example** + +```ts +import { multiplyAll } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(multiplyAll([2n, 3n, 4n]), 24n) +``` + +Added in v1.0.0 + +## sign + +Determines the sign of a given `bigint`. + +**Signature** + +```ts +export declare const sign: (n: bigint) => Ordering +``` + +**Example** + +```ts +import { sign } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(sign(-5n), -1) +assert.deepStrictEqual(sign(0n), 0) +assert.deepStrictEqual(sign(5n), 1) +``` + +Added in v1.0.0 + +## subtract + +Provides a subtraction operation on `bigint`s. + +**Signature** + +```ts +export declare const subtract: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { subtract } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(subtract(2n, 3n), -1n) +``` + +Added in v1.0.0 + +## sum + +Provides an addition operation on `bigint`s. + +**Signature** + +```ts +export declare const sum: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { sum } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(sum(2n, 3n), 5n) +``` + +Added in v1.0.0 + +## sumAll + +Takes an `Iterable` of `bigint`s and returns their sum as a single `bigint + +**Signature** + +```ts +export declare const sumAll: (collection: Iterable) => bigint +``` + +**Example** + +```ts +import { sumAll } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(sumAll([2n, 3n, 4n]), 9n) +``` + +Added in v1.0.0 + +# predicates + +## between + +Checks if a `bigint` is between a `minimum` and `maximum` value (inclusive). + +**Signature** + +```ts +export declare const between: { + (minimum: bigint, maximum: bigint): (self: bigint) => boolean + (self: bigint, minimum: bigint, maximum: bigint): boolean +} +``` + +**Example** + +```ts +import { between } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(between(0n, 5n)(3n), true) +assert.deepStrictEqual(between(0n, 5n)(-1n), false) +assert.deepStrictEqual(between(0n, 5n)(6n), false) +``` + +Added in v1.0.0 + +## greaterThan + +Returns `true` if the first argument is greater than the second, otherwise `false`. + +**Signature** + +```ts +export declare const greaterThan: { (that: bigint): (self: bigint) => boolean; (self: bigint, that: bigint): boolean } +``` + +**Example** + +```ts +import { greaterThan } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(greaterThan(2n, 3n), false) +assert.deepStrictEqual(greaterThan(3n, 3n), false) +assert.deepStrictEqual(greaterThan(4n, 3n), true) +``` + +Added in v1.0.0 + +## greaterThanOrEqualTo + +Returns a function that checks if a given `bigint` is greater than or equal to the provided one. + +**Signature** + +```ts +export declare const greaterThanOrEqualTo: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} +``` + +**Example** + +```ts +import { greaterThanOrEqualTo } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(greaterThanOrEqualTo(2n, 3n), false) +assert.deepStrictEqual(greaterThanOrEqualTo(3n, 3n), true) +assert.deepStrictEqual(greaterThanOrEqualTo(4n, 3n), true) +``` + +Added in v1.0.0 + +## lessThan + +Returns `true` if the first argument is less than the second, otherwise `false`. + +**Signature** + +```ts +export declare const lessThan: { (that: bigint): (self: bigint) => boolean; (self: bigint, that: bigint): boolean } +``` + +**Example** + +```ts +import { lessThan } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(lessThan(2n, 3n), true) +assert.deepStrictEqual(lessThan(3n, 3n), false) +assert.deepStrictEqual(lessThan(4n, 3n), false) +``` + +Added in v1.0.0 + +## lessThanOrEqualTo + +Returns a function that checks if a given `bigint` is less than or equal to the provided one. + +**Signature** + +```ts +export declare const lessThanOrEqualTo: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} +``` + +**Example** + +```ts +import { lessThanOrEqualTo } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(lessThanOrEqualTo(2n, 3n), true) +assert.deepStrictEqual(lessThanOrEqualTo(3n, 3n), true) +assert.deepStrictEqual(lessThanOrEqualTo(4n, 3n), false) +``` + +Added in v1.0.0 + +# utils + +## clamp + +Restricts the given `bigint` to be within the range specified by the `minimum` and `maximum` values. + +- If the `bigint` is less than the `minimum` value, the function returns the `minimum` value. +- If the `bigint` is greater than the `maximum` value, the function returns the `maximum` value. +- Otherwise, it returns the original `bigint`. + +**Signature** + +```ts +export declare const clamp: { + (minimum: bigint, maximum: bigint): (self: bigint) => bigint + (self: bigint, minimum: bigint, maximum: bigint): bigint +} +``` + +**Example** + +```ts +import { clamp } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(clamp(0n, 5n)(3n), 3n) +assert.deepStrictEqual(clamp(0n, 5n)(-1n), 0n) +assert.deepStrictEqual(clamp(0n, 5n)(6n), 5n) +``` + +Added in v1.0.0 + +## max + +Returns the maximum between two `bigint`s. + +**Signature** + +```ts +export declare const max: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { max } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(max(2n, 3n), 3n) +``` + +Added in v1.0.0 + +## min + +Returns the minimum between two `bigint`s. + +**Signature** + +```ts +export declare const min: { (that: bigint): (self: bigint) => bigint; (self: bigint, that: bigint): bigint } +``` + +**Example** + +```ts +import { min } from '@fp-ts/core/Bigint' + +assert.deepStrictEqual(min(2n, 3n), 2n) +``` + +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..ca0c77463 --- /dev/null +++ b/docs/modules/Boolean.ts.md @@ -0,0 +1,501 @@ +--- +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) + - [eqv](#eqv) + - [implies](#implies) + - [nand](#nand) + - [nor](#nor) + - [not](#not) + - [or](#or) + - [xor](#xor) +- [guards](#guards) + - [isBoolean](#isboolean) +- [instances](#instances) + - [Equivalence](#equivalence) + - [MonoidAll](#monoidall) + - [MonoidAny](#monoidany) + - [MonoidEqv](#monoideqv) + - [MonoidXor](#monoidxor) + - [Order](#order) + - [SemigroupAll](#semigroupall) + - [SemigroupAny](#semigroupany) + - [SemigroupEqv](#semigroupeqv) + - [SemigroupXor](#semigroupxor) +- [pattern matching](#pattern-matching) + - [match](#match) +- [utils](#utils) + - [all](#all) + - [any](#any) + +--- + +# combinators + +## and + +Combines two boolean using AND: `self && that`. + +**Signature** + +```ts +export declare const and: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { and } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(and(true, true), true) +assert.deepStrictEqual(and(true, false), false) +assert.deepStrictEqual(and(false, true), false) +assert.deepStrictEqual(and(false, false), false) +``` + +Added in v1.0.0 + +## eqv + +Combines two booleans using EQV (aka XNOR): `!xor(self, that)`. + +**Signature** + +```ts +export declare const eqv: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { eqv } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(eqv(true, true), true) +assert.deepStrictEqual(eqv(true, false), false) +assert.deepStrictEqual(eqv(false, true), false) +assert.deepStrictEqual(eqv(false, false), true) +``` + +Added in v1.0.0 + +## implies + +Combines two booleans using an implication: `(!self || that)`. + +**Signature** + +```ts +export declare const implies: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { implies } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(implies(true, true), true) +assert.deepStrictEqual(implies(true, false), false) +assert.deepStrictEqual(implies(false, true), true) +assert.deepStrictEqual(implies(false, false), true) +``` + +Added in v1.0.0 + +## nand + +Combines two boolean using NAND: `!(self && that)`. + +**Signature** + +```ts +export declare const nand: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { nand } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(nand(true, true), false) +assert.deepStrictEqual(nand(true, false), true) +assert.deepStrictEqual(nand(false, true), true) +assert.deepStrictEqual(nand(false, false), true) +``` + +Added in v1.0.0 + +## nor + +Combines two booleans using NOR: `!(self || that)`. + +**Signature** + +```ts +export declare const nor: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { nor } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(nor(true, true), false) +assert.deepStrictEqual(nor(true, false), false) +assert.deepStrictEqual(nor(false, true), false) +assert.deepStrictEqual(nor(false, false), true) +``` + +Added in v1.0.0 + +## not + +Negates the given boolean: `!self` + +**Signature** + +```ts +export declare const not: (self: boolean) => boolean +``` + +**Example** + +```ts +import { not } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(not(true), false) +assert.deepStrictEqual(not(false), true) +``` + +Added in v1.0.0 + +## or + +Combines two boolean using OR: `self || that`. + +**Signature** + +```ts +export declare const or: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { or } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(or(true, true), true) +assert.deepStrictEqual(or(true, false), true) +assert.deepStrictEqual(or(false, true), true) +assert.deepStrictEqual(or(false, false), false) +``` + +Added in v1.0.0 + +## xor + +Combines two booleans using XOR: `(!self && that) || (self && !that)`. + +**Signature** + +```ts +export declare const xor: { (that: boolean): (self: boolean) => boolean; (self: boolean, that: boolean): boolean } +``` + +**Example** + +```ts +import { xor } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(xor(true, true), false) +assert.deepStrictEqual(xor(true, false), true) +assert.deepStrictEqual(xor(false, true), true) +assert.deepStrictEqual(xor(false, false), false) +``` + +Added in v1.0.0 + +# guards + +## isBoolean + +Tests if a value is a `boolean`. + +**Signature** + +```ts +export declare const isBoolean: (input: unknown) => input is boolean +``` + +**Example** + +```ts +import { isBoolean } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(isBoolean(true), true) +assert.deepStrictEqual(isBoolean('true'), false) +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: equivalence.Equivalence +``` + +Added in v1.0.0 + +## MonoidAll + +`boolean` monoid under conjunction, see also {@link SemigroupAll}. + +The `empty` value is `true`. + +**Signature** + +```ts +export declare const MonoidAll: monoid.Monoid +``` + +Added in v1.0.0 + +## MonoidAny + +`boolean` monoid under disjunction, see also {@link SemigroupAny}. + +The `empty` value is `false`. + +**Signature** + +```ts +export declare const MonoidAny: monoid.Monoid +``` + +Added in v1.0.0 + +## MonoidEqv + +`boolean` monoid under equivalence. + +The `empty` value is `true`. + +**Signature** + +```ts +export declare const MonoidEqv: monoid.Monoid +``` + +Added in v1.0.0 + +## MonoidXor + +`boolean` monoid under exclusive disjunction, see also {@link SemigroupXor}. + +The `empty` value is `false`. + +**Signature** + +```ts +export declare const MonoidXor: monoid.Monoid +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: order.Order +``` + +Added in v1.0.0 + +## SemigroupAll + +`boolean` semigroup under conjunction. + +**Signature** + +```ts +export declare const SemigroupAll: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupAll } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(SemigroupAll.combine(true, true), true) +assert.deepStrictEqual(SemigroupAll.combine(true, false), false) +assert.deepStrictEqual(SemigroupAll.combine(false, true), false) +assert.deepStrictEqual(SemigroupAll.combine(false, false), false) +``` + +Added in v1.0.0 + +## SemigroupAny + +`boolean` semigroup under disjunction. + +**Signature** + +```ts +export declare const SemigroupAny: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupAny } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(SemigroupAny.combine(true, true), true) +assert.deepStrictEqual(SemigroupAny.combine(true, false), true) +assert.deepStrictEqual(SemigroupAny.combine(false, true), true) +assert.deepStrictEqual(SemigroupAny.combine(false, false), false) +``` + +Added in v1.0.0 + +## SemigroupEqv + +`boolean` semigroup under equivalence. + +**Signature** + +```ts +export declare const SemigroupEqv: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupEqv } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(SemigroupEqv.combine(true, true), true) +assert.deepStrictEqual(SemigroupEqv.combine(true, false), false) +assert.deepStrictEqual(SemigroupEqv.combine(false, true), false) +assert.deepStrictEqual(SemigroupEqv.combine(false, false), true) +``` + +Added in v1.0.0 + +## SemigroupXor + +`boolean` semigroup under exclusive disjunction. + +**Signature** + +```ts +export declare const SemigroupXor: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupXor } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(SemigroupXor.combine(true, true), false) +assert.deepStrictEqual(SemigroupXor.combine(true, false), true) +assert.deepStrictEqual(SemigroupXor.combine(false, true), true) +assert.deepStrictEqual(SemigroupXor.combine(false, false), false) +``` + +Added in v1.0.0 + +# pattern matching + +## match + +This function returns the result of either of the given functions depending on the value of the boolean parameter. +It is useful when you have to run one of two functions depending on the boolean value. + +**Signature** + +```ts +export declare const match: { + (onFalse: LazyArg, onTrue: LazyArg): (value: boolean) => A | B + (value: boolean, onFalse: LazyArg, onTrue: LazyArg): A | B +} +``` + +**Example** + +```ts +import * as B from '@fp-ts/core/Boolean' + +assert.deepStrictEqual( + B.match( + true, + () => "It's false!", + () => "It's true!" + ), + "It's true!" +) +``` + +Added in v1.0.0 + +# utils + +## all + +This utility function is used to check if all the elements in a collection of boolean values are `true`. + +**Signature** + +```ts +export declare const all: (collection: Iterable) => boolean +``` + +**Example** + +```ts +import { all } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(all([true, true, true]), true) +assert.deepStrictEqual(all([true, false, true]), false) +``` + +Added in v1.0.0 + +## any + +This utility function is used to check if at least one of the elements in a collection of boolean values is `true`. + +**Signature** + +```ts +export declare const any: (collection: Iterable) => boolean +``` + +**Example** + +```ts +import { any } from '@fp-ts/core/Boolean' + +assert.deepStrictEqual(any([true, false, true]), true) +assert.deepStrictEqual(any([false, false, false]), false) +``` + +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..971d0b12c --- /dev/null +++ b/docs/modules/Either.ts.md @@ -0,0 +1,1724 @@ +--- +title: Either.ts +nav_order: 3 +parent: Modules +--- + +## Either overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combinators](#combinators) + - [tap](#tap) +- [combining](#combining) + - [all](#all) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) + - [flatMapNullable](#flatmapnullable) + - [flatMapOption](#flatmapoption) + - [getFirstLeftMonoid](#getfirstleftmonoid) + - [getFirstLeftSemigroup](#getfirstleftsemigroup) + - [getFirstRightSemigroup](#getfirstrightsemigroup) + - [zipWith](#zipwith) +- [constructors](#constructors) + - [left](#left) + - [of](#of) + - [right](#right) +- [conversions](#conversions) + - [fromIterable](#fromiterable) + - [fromOption](#fromoption) + - [getLeft](#getleft) + - [getRight](#getright) + - [toArray](#toarray) + - [toOption](#tooption) + - [toRefinement](#torefinement) +- [debugging](#debugging) + - [inspectLeft](#inspectleft) + - [inspectRight](#inspectright) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [appendElement](#appendelement) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) + - [tupled](#tupled) +- [equivalence](#equivalence) + - [getEquivalence](#getequivalence) +- [error handling](#error-handling) + - [firstRightOf](#firstrightof) + - [mapLeft](#mapleft) + - [orElse](#orelse) + - [orElseEither](#orelseeither) + - [orElseFail](#orelsefail) + - [tapError](#taperror) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [filterMap](#filtermap) +- [getters](#getters) + - [getOrElse](#getorelse) + - [getOrNull](#getornull) + - [getOrUndefined](#getorundefined) + - [lefts](#lefts) + - [rights](#rights) +- [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) + - [getOptionalSemigroup](#getoptionalsemigroup) +- [interop](#interop) + - [fromNullable](#fromnullable) + - [getOrThrow](#getorthrow) + - [getOrThrowWith](#getorthrowwith) + - [liftNullable](#liftnullable) + - [liftThrowable](#liftthrowable) + - [merge](#merge) +- [lifting](#lifting) + - [lift2](#lift2) + - [liftOption](#liftoption) + - [liftPredicate](#liftpredicate) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [bimap](#bimap) + - [flap](#flap) + - [map](#map) +- [math](#math) + - [divide](#divide) + - [multiply](#multiply) + - [subtract](#subtract) + - [sum](#sum) +- [models](#models) + - [Either (type alias)](#either-type-alias) + - [Left (interface)](#left-interface) + - [Right (interface)](#right-interface) +- [pattern matching](#pattern-matching) + - [match](#match) +- [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) + - [exists](#exists) + - [flatten](#flatten) + - [reverse](#reverse) + - [struct](#struct) + - [tuple](#tuple) + - [unit](#unit) + +--- + +# combinators + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: { + (self: Either, f: (a: A) => Either): Either + (f: (a: A) => Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +# combining + +## all + +Similar to `Promise.all` but operates on `Either`s. + +``` +Iterable> -> Either +``` + +Flattens a collection of `Either`s into a single `Either` that contains a list of all the `Right` values. +If there is a `Left` value in the collection, it returns the first `Left` found as the result. + +**Signature** + +```ts +export declare const all: (collection: Iterable>) => Either +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(E.all([E.right(1), E.right(2), E.right(3)]), E.right([1, 2, 3])) +assert.deepStrictEqual(E.all([E.right(1), E.left('error'), E.right(3)]), E.left('error')) +``` + +Added in v1.0.0 + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: { + (f: (a: A) => Either): (self: Either) => Either + (self: Either, f: (a: A) => 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> + (self: Either, f: (a: A) => B | null | undefined, onNullable: (a: A) => E2): Either< + E1 | E2, + NonNullable + > +} +``` + +Added in v1.0.0 + +## flatMapOption + +**Signature** + +```ts +export declare const flatMapOption: { + (f: (a: A) => Option, onNone: (a: A) => E2): (self: Either) => Either + (self: Either, f: (a: A) => Option, onNone: (a: A) => E2): Either +} +``` + +Added in v1.0.0 + +## getFirstLeftMonoid + +`Monoid` returning the left-most `Left` value. If both operands are `Right`s then the inner values +are combined using the provided `Monoid`. + +- `combine` is provided by {@link getFirstLeftSemigroup}. +- `empty` is `right(M.empty)` + +**Signature** + +```ts +export declare const getFirstLeftMonoid: (M: Monoid
) => Monoid> +``` + +Added in v1.0.0 + +## getFirstLeftSemigroup + +`Semigroup` returning the left-most `Left` value. If both operands are `Right`s then the inner values +are combined using the provided `Semigroup`. + +``` +| self | that | combine(self, that) | +| ---------- | ---------- | ----------------------- | +| left(e1) | left(e2) | left(e1) | +| left(e1) | right(a2) | left(e1) | +| right(a1) | left(e2) | left(e2) | +| right(a1) | right(a2) | right(combine(a1, a2)) | +``` + +**Signature** + +```ts +export declare const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> +``` + +Added in v1.0.0 + +## getFirstRightSemigroup + +Semigroup returning the left-most `Right` value. + +``` +| self | that | combine(self, that) | +| ---------- | ---------- | ------------------- | +| left(e1) | left(e2) | left(e2) | +| left(e1) | right(a2) | right(a2) | +| right(a1) | left(e2) | right(a1) | +| right(a1) | right(a2) | right(a1) | +``` + +**Signature** + +```ts +export declare const getFirstRightSemigroup: () => Semigroup> +``` + +Added in v1.0.0 + +## zipWith + +**Signature** + +```ts +export declare const zipWith: { + (self: Either, that: Either, f: (a: A, b: B) => C): Either + (that: Either, f: (a: A, b: B) => C): (self: Either) => Either +} +``` + +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 {@link 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: LazyArg): (collection: Iterable) => Either + (collection: Iterable, onEmpty: LazyArg): Either +} +``` + +Added in v1.0.0 + +## fromOption + +**Signature** + +```ts +export declare const fromOption: { + (fa: Option, onNone: () => E): Either + (onNone: () => E): (fa: Option) => Either +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual( + E.fromOption(O.some(1), () => 'error'), + E.right(1) +) +assert.deepStrictEqual( + E.fromOption(O.none(), () => 'error'), + E.left('error') +) +``` + +Added in v1.0.0 + +## getLeft + +Converts a `Either` to an `Option` discarding the value. + +**Signature** + +```ts +export declare const getLeft: (self: Either) => Option +``` + +**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 + +## getRight + +Converts a `Either` to an `Option` discarding the error. + +Alias of {@link toOption}. + +**Signature** + +```ts +export declare const getRight: (self: Either) => Option +``` + +**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 + +## toArray + +Transforms an `Either` into an `Array`. +If the input is `Left`, an empty array is returned. +If the input is `Right`, the value is wrapped in an array. + +**Signature** + +```ts +export declare const toArray: (self: Either) => A[] +``` + +**Example** + +```ts +import { right, left, toArray } from '@fp-ts/core/Either' + +assert.deepStrictEqual(toArray(right(1)), [1]) +assert.deepStrictEqual(toArray(left('error')), []) +``` + +Added in v1.0.0 + +## toOption + +Converts a `Either` to an `Option` discarding the error. + +**Signature** + +```ts +export declare const toOption: (self: Either) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(E.toOption(E.right(1)), O.some(1)) +assert.deepStrictEqual(E.toOption(E.left('a')), O.none()) +``` + +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) => Refinement +``` + +Added in v1.0.0 + +# debugging + +## inspectLeft + +**Signature** + +```ts +export declare const inspectLeft: { + (onLeft: (e: E) => void): (self: Either) => Either + (self: Either, onLeft: (e: E) => void): Either +} +``` + +Added in v1.0.0 + +## inspectRight + +**Signature** + +```ts +export declare const inspectRight: { + (onRight: (a: A) => void): (self: Either) => Either + (self: Either, onRight: (a: A) => void): Either +} +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: Either +``` + +Added in v1.0.0 + +## andThenBind + +Extends the `Either` value with the value of another `Either` type. + +If both `Either` instances are `Left`, then the result will be the first `Left`. + +**Signature** + +```ts +export declare const andThenBind: { + (name: Exclude, that: Either): ( + self: Either + ) => Either + ( + self: Either, + name: Exclude, + that: Either + ): Either +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +const result = pipe( + E.Do, + E.bind('a', () => E.left('e1')), + E.andThenBind('b', E.left('e2')) +) + +assert.deepStrictEqual(result, E.left('e1')) +``` + +Added in v1.0.0 + +## appendElement + +Appends an element to the end of a tuple. + +**Signature** + +```ts +export declare const appendElement: { + (self: Either, that: Either): Either + (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 + ( + self: Either, + name: Exclude, + f: (a: A) => Either + ): Either +} +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: { + (name: N): (self: Either) => Either + (self: Either, name: N): Either +} +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: { + (name: Exclude, f: (a: A) => B): ( + self: Either + ) => Either + (self: Either, name: Exclude, f: (a: A) => B): Either< + E, + { [K in N | keyof A]: K extends keyof A ? A[K] : B } + > +} +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: Either) => Either +``` + +Added in v1.0.0 + +# equivalence + +## getEquivalence + +**Signature** + +```ts +export declare const getEquivalence: (EE: Equivalence, EA: Equivalence) => Equivalence> +``` + +Added in v1.0.0 + +# error handling + +## firstRightOf + +**Signature** + +```ts +export declare const firstRightOf: { + (collection: Iterable>): (self: Either) => Either + (self: Either, collection: Iterable>): Either +} +``` + +Added in v1.0.0 + +## mapLeft + +Maps the `Left` side of an `Either` value to a new `Either` value. + +**Signature** + +```ts +export declare const mapLeft: { + (f: (e: E) => G): (self: Either) => Either + (self: Either, f: (e: E) => G): Either +} +``` + +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: (e1: E1) => Either): (self: Either) => Either + (self: Either, that: (e1: E1) => 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: (e1: E1) => Either): (self: Either) => Either> + (self: Either, that: (e1: E1) => 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: LazyArg): (self: Either) => Either + (self: Either, onLeft: LazyArg): 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 + (self: Either, onLeft: (e: E1) => Either): Either +} +``` + +Added in v1.0.0 + +# filtering + +## compact + +**Signature** + +```ts +export declare const compact: { + (onNone: LazyArg): (self: Either>) => Either + (self: Either>, onNone: LazyArg): Either +} +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: Either + ) => Either + (predicate: Predicate, onFalse: LazyArg): ( + self: Either + ) => Either + ( + self: Either, + refinement: Refinement, + onFalse: LazyArg + ): Either + (self: Either, predicate: Predicate, onFalse: LazyArg): Either +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: { + (f: (a: A) => Option, onNone: LazyArg): (self: Either) => Either + (self: Either, f: (a: A) => Option, onNone: LazyArg): Either +} +``` + +Added in v1.0.0 + +# getters + +## 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: (e: E) => B): (self: Either) => B | A + (self: Either, onLeft: (e: E) => B): A | B +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual( + E.getOrElse(E.right(1), () => 0), + 1 +) +assert.deepStrictEqual( + E.getOrElse(E.left('error'), () => 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 + +## 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 + +## 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 + +# guards + +## isEither + +Tests if a value is a `Either`. + +**Signature** + +```ts +export declare const isEither: (input: unknown) => input is Either +``` + +**Example** + +```ts +import { isEither, left, right } from '@fp-ts/core/Either' + +assert.deepStrictEqual(isEither(right(1)), true) +assert.deepStrictEqual(isEither(left('error')), true) +assert.deepStrictEqual(isEither({ right: 1 }), false) +``` + +Added in v1.0.0 + +## isLeft + +Determine if a `Either` is a `Left`. + +**Signature** + +```ts +export declare const isLeft: (self: Either) => self is Left +``` + +**Example** + +```ts +import { isLeft, left, right } from '@fp-ts/core/Either' + +assert.deepStrictEqual(isLeft(right(1)), false) +assert.deepStrictEqual(isLeft(left('error')), true) +``` + +Added in v1.0.0 + +## isRight + +Determine if a `Either` is a `Right`. + +**Signature** + +```ts +export declare const isRight: (self: Either) => self is Right +``` + +**Example** + +```ts +import { isRight, left, right } from '@fp-ts/core/Either' + +assert.deepStrictEqual(isRight(right(1)), true) +assert.deepStrictEqual(isRight(left('error')), false) +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: applicative.Applicative +``` + +Added in v1.0.0 + +## Bicovariant + +**Signature** + +```ts +export declare const Bicovariant: bicovariant.Bicovariant +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: chainable.Chainable +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: flatMap_.FlatMap +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: foldable.Foldable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: monad.Monad +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: pointed.Pointed +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: semiAlternative.SemiAlternative +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: semiApplicative.SemiApplicative +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: semiCoproduct.SemiCoproduct +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +## getOptionalSemigroup + +Semigroup that models the combination of values that may be absent, elements that are `Left` are ignored +while elements that are `Right` are combined using the provided `Semigroup`. + +**Signature** + +```ts +export declare const getOptionalSemigroup: (S: Semigroup) => Semigroup> +``` + +Added in v1.0.0 + +# interop + +## 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: (a: A) => E): (a: A) => Either> + (a: A, onNullable: (a: A) => E): 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 + +## getOrThrow + +Extracts the value of an `Either` or throws if the `Either` is `Left`. + +The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + +**Signature** + +```ts +export declare const getOrThrow: (self: Either) => A +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(E.getOrThrow(E.right(1)), 1) +assert.throws(() => E.getOrThrow(E.left('error'))) +``` + +Added in v1.0.0 + +## getOrThrowWith + +Extracts the value of an `Either` or throws if the `Either` is `Left`. + +If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + +**Signature** + +```ts +export declare const getOrThrowWith: { + (onLeft: (e: E) => unknown): (self: Either) => A + (self: Either, onLeft: (e: E) => unknown): A +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual( + E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), + 1 +) +assert.throws(() => E.getOrThrowWith(E.left('error'), () => new Error('Unexpected Left'))) +``` + +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 + +## 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 + +## merge + +**Signature** + +```ts +export declare const merge: (self: Either) => E | A +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +Lifts a binary function into `Either`. + +**Signature** + +```ts +export declare const lift2: ( + f: (a: A, b: B) => C +) => { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## liftOption + +**Signature** + +```ts +export declare const liftOption: ( + f: (...a: A) => Option, + onNone: (...a: A) => E +) => (...a: A) => Either +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: Refinement, onFalse: (c: C) => E): (c: C) => Either + (predicate: Predicate, onFalse: (b: B) => E): (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: { + (self: Either, b: B): Either + (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 + +**Signature** + +```ts +export declare const bimap: { + (f: (e: E1) => E2, g: (a: A) => B): (self: Either) => Either + (self: Either, f: (e: E1) => E2, g: (a: A) => B): Either +} +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: { + (a: A, self: Either B>): Either + (self: Either B>): (a: A) => Either +} +``` + +Added in v1.0.0 + +## map + +Maps the `Right` side of an `Either` value to a new `Either` value. + +**Signature** + +```ts +export declare const map: { + (f: (a: A) => B): (self: Either) => Either + (self: Either, f: (a: A) => B): Either +} +``` + +Added in v1.0.0 + +# math + +## divide + +**Signature** + +```ts +export declare const divide: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## multiply + +**Signature** + +```ts +export declare const multiply: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## subtract + +**Signature** + +```ts +export declare const subtract: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## sum + +**Signature** + +```ts +export declare const sum: { + (self: Either, that: Either): Either + (that: Either): (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 (interface) + +**Signature** + +```ts +export interface Left { + readonly _tag: 'Left' + readonly left: E +} +``` + +Added in v1.0.0 + +## Right (interface) + +**Signature** + +```ts +export interface 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 + (self: Either, onLeft: (e: E) => B, onRight: (a: A) => C): 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.deepStrictEqual(pipe(E.right(1), E.match(onLeft, onRight)), 'Ok: 1') +assert.deepStrictEqual(pipe(E.left(['error 1', 'error 2']), E.match(onLeft, onRight)), 'Errors: error 1, error 2') +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: ( + F: applicative.Applicative +) => (self: Either>) => Kind> +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind): (self: Either) => Kind> + (self: Either, f: (a: A) => Kind): Kind> +} +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: applicative.Applicative +) => { + (self: Either, f: (a: A) => Kind): Kind> + (f: (a: A) => Kind): (self: Either) => Kind> +} +``` + +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: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: { + (self: Either B>, that: Either): Either + (that: Either): (self: Either B>) => Either +} +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: { + (afb: (a: A) => Either, bfc: (b: B) => Either): (a: A) => Either + (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: (isEquivalent: (self: A, that: A) => boolean) => { + (a: A): (self: Either) => boolean + (self: Either, a: A): boolean +} +``` + +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: Predicate): (self: Either) => boolean + (self: Either, predicate: Predicate): boolean +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/Either' + +const f = E.exists((n: number) => n > 2) + +assert.deepStrictEqual(f(E.left('a')), false) +assert.deepStrictEqual(f(E.right(1)), false) +assert.deepStrictEqual(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: >>( + fields: R +) => Either< + [R[keyof R]] extends [Either] ? E : never, + { [K in keyof R]: [R[K]] extends [Either] ? A : never } +> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Either`s. + +``` +[Either, Either, ...] -> Either +``` + +**Signature** + +```ts +export declare const tuple: []>( + ...elements: 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..07b7ffc5b --- /dev/null +++ b/docs/modules/Function.ts.md @@ -0,0 +1,835 @@ +--- +title: Function.ts +nav_order: 4 +parent: Modules +--- + +## Function overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isFunction](#isfunction) +- [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) + - [dual](#dual) + - [flip](#flip) + - [flow](#flow) + - [hole](#hole) + - [identity](#identity) + - [pipe](#pipe) + - [tupled](#tupled) + - [unsafeCoerce](#unsafecoerce) + - [untupled](#untupled) + +--- + +# guards + +## isFunction + +Tests if a value is a `function`. + +**Signature** + +```ts +export declare const isFunction: (input: unknown) => input is Function +``` + +**Example** + +```ts +import { isFunction } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isFunction(isFunction), true) +assert.deepStrictEqual(isFunction('function'), false) +``` + +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 +} +``` + +**Example** + +```ts +import { LazyArg, constant } from '@fp-ts/core/Function' + +export const constNull: LazyArg = constant(null) +``` + +Added in v1.0.0 + +## SK + +The SK combinator, also known as the "S-K combinator" or "S-combinator", is a fundamental combinator in the +lambda calculus and the SKI combinator calculus. + +This function is useful for discarding the first argument passed to it and returning the second argument. + +**Signature** + +```ts +export declare const SK: (_: A, b: B) => B +``` + +**Example** + +```ts +import { SK } from '@fp-ts/core/Function' + +assert.deepStrictEqual(SK(0, 'hello'), 'hello') +``` + +Added in v1.0.0 + +## absurd + +The `absurd` function is a stub for cases where a value of type `never` is encountered in your code, +meaning that it should be impossible for this code to be executed. + +This function is particularly when it's necessary to specify that certain cases are impossible. + +**Signature** + +```ts +export declare const absurd: (_: never) => A +``` + +Added in v1.0.0 + +## apply + +Apply a function to a given value. + +**Signature** + +```ts +export declare const apply: (a: A) => (self: (a: A) => B) => B +``` + +**Example** + +```ts +import { pipe, apply } from '@fp-ts/core/Function' +import { length } from '@fp-ts/core/String' + +assert.deepStrictEqual(pipe(length, apply('hello')), 5) +``` + +Added in v1.0.0 + +## compose + +Composes two functions, `ab` and `bc` into a single function that takes in an argument `a` of type `A` and returns a result of type `C`. +The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`. + +**Signature** + +```ts +export declare const compose: { + (bc: (b: B) => C): (self: (a: A) => B) => (a: A) => C + (self: (a: A) => B, bc: (b: B) => C): (a: A) => C +} +``` + +**Example** + +```ts +import { compose } from '@fp-ts/core/Function' + +const increment = (n: number) => n + 1 +const square = (n: number) => n * n + +assert.strictEqual(compose(increment, square)(2), 9) +``` + +Added in v1.0.0 + +## constFalse + +A thunk that returns always `false`. + +**Signature** + +```ts +export declare const constFalse: LazyArg +``` + +**Example** + +```ts +import { constFalse } from '@fp-ts/core/Function' + +assert.deepStrictEqual(constFalse(), false) +``` + +Added in v1.0.0 + +## constNull + +A thunk that returns always `null`. + +**Signature** + +```ts +export declare const constNull: LazyArg +``` + +**Example** + +```ts +import { constNull } from '@fp-ts/core/Function' + +assert.deepStrictEqual(constNull(), null) +``` + +Added in v1.0.0 + +## constTrue + +A thunk that returns always `true`. + +**Signature** + +```ts +export declare const constTrue: LazyArg +``` + +**Example** + +```ts +import { constTrue } from '@fp-ts/core/Function' + +assert.deepStrictEqual(constTrue(), true) +``` + +Added in v1.0.0 + +## constUndefined + +A thunk that returns always `undefined`. + +**Signature** + +```ts +export declare const constUndefined: LazyArg +``` + +**Example** + +```ts +import { constUndefined } from '@fp-ts/core/Function' + +assert.deepStrictEqual(constUndefined(), undefined) +``` + +Added in v1.0.0 + +## constVoid + +A thunk that returns always `void`. + +**Signature** + +```ts +export declare const constVoid: LazyArg +``` + +**Example** + +```ts +import { constVoid } from '@fp-ts/core/Function' + +assert.deepStrictEqual(constVoid(), undefined) +``` + +Added in v1.0.0 + +## constant + +Creates a constant value that never changes. + +This is useful when you want to pass a value to a higher-order function (a function that takes another function as its argument) +and want that inner function to always use the same value, no matter how many times it is called. + +**Signature** + +```ts +export declare const constant: (value: A) => LazyArg +``` + +**Example** + +```ts +import { constant } from '@fp-ts/core/Function' + +const constNull = constant(null) + +assert.deepStrictEqual(constNull(), null) +assert.deepStrictEqual(constNull(), null) +``` + +Added in v1.0.0 + +## dual + +Creates a function that can be used in a data-last (aka `pipe`able) or data-first style. + +**Signature** + +```ts +export declare const dual: < + DataLast extends (...args: Array) => any, + DataFirst extends (...args: Array) => any +>( + arity: Parameters['length'], + body: DataFirst +) => DataLast & DataFirst +``` + +**Example** + +```ts +import { dual, pipe } from '@fp-ts/core/Function' + +export const sum: { + (that: number): (self: number) => number + (self: number, that: number): number +} = dual(2, (self: number, that: number): number => self + that) + +assert.deepStrictEqual(sum(2, 3), 5) +assert.deepStrictEqual(pipe(2, sum(3)), 5) +``` + +Added in v1.0.0 + +## flip + +Reverses the order of arguments for 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.deepStrictEqual(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. + +See also {@link pipe}. + +**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.deepStrictEqual(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 + +The identity function, i.e. A function that returns its input argument. + +**Signature** + +```ts +export declare const identity: (a: A) => A +``` + +**Example** + +```ts +import { identity } from '@fp-ts/core/Function' + +assert.deepStrictEqual(identity(5), 5) +``` + +Added in v1.0.0 + +## pipe + +Pipes the value of an expression into a pipeline of functions. + +This is useful in combination with data-last functions as a simulation of methods: + +``` +as.map(f).filter(g) -> pipe(as, map(f), filter(g)) +``` + +See also {@link flow}. + +**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 length = (s: string): number => s.length +const double = (n: number): number => n * 2 +const decrement = (n: number): number => n - 1 + +assert.deepStrictEqual(pipe(length('hello'), double, decrement), 9) +``` + +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 sumTupled = tupled((x: number, y: number): number => x + y) + +assert.deepStrictEqual(sumTupled([1, 2]), 3) +``` + +Added in v1.0.0 + +## unsafeCoerce + +Casts the result to the specified type. + +**Signature** + +```ts +export declare const unsafeCoerce: (a: A) => B +``` + +**Example** + +```ts +import { unsafeCoerce, identity } from '@fp-ts/core/Function' + +assert.deepStrictEqual(unsafeCoerce, identity) +``` + +Added in v1.0.0 + +## untupled + +Inverse function of `tupled` + +**Signature** + +```ts +export declare const untupled: (f: (a: A) => B) => (...a: A) => B +``` + +**Example** + +```ts +import { untupled } from '@fp-ts/core/Function' + +const getFirst = untupled((tuple: [A, B]): A => tuple[0]) + +assert.deepStrictEqual(getFirst(1, 2), 1) +``` + +Added in v1.0.0 diff --git a/docs/modules/HKT.ts.md b/docs/modules/HKT.ts.md index c928d4d5b..fc7d5f430 100644 --- a/docs/modules/HKT.ts.md +++ b/docs/modules/HKT.ts.md @@ -1,6 +1,6 @@ --- title: HKT.ts -nav_order: 3 +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..14f6af868 --- /dev/null +++ b/docs/modules/Identity.ts.md @@ -0,0 +1,313 @@ +--- +title: Identity.ts +nav_order: 6 +parent: Modules +--- + +## Identity overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) +- [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) +- [models](#models) + - [Identity (type alias)](#identity-type-alias) +- [type lambdas](#type-lambdas) + - [IdentityTypeLambda (interface)](#identitytypelambda-interface) + - [IdentityTypeLambdaFix (interface)](#identitytypelambdafix-interface) + +--- + +# 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 + } +
(self: A, name: Exclude, that: B): { + [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 + } + (self: A, name: Exclude, f: (a: A) => B): { + [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 } + (self: A, name: N): { [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 + } + (self: A, name: Exclude, f: (a: A) => B): { + [K in N | keyof A]: K extends keyof A ? A[K] : B + } +} +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: applicative.Applicative +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: chainable.Chainable +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: flatMap_.FlatMap +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: foldable.Foldable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: monad.Monad +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: pointed.Pointed +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: semiApplicative.SemiApplicative +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +## getSemiAlternative + +**Signature** + +```ts +export declare const getSemiAlternative: ( + S: Semigroup +) => semiAlternative.SemiAlternative> +``` + +Added in v1.0.0 + +## getSemiCoproduct + +**Signature** + +```ts +export declare const getSemiCoproduct: (S: Semigroup) => semiCoproduct.SemiCoproduct> +``` + +Added in v1.0.0 + +# models + +## Identity (type alias) + +**Signature** + +```ts +export type Identity = A +``` + +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 diff --git a/docs/modules/Number.ts.md b/docs/modules/Number.ts.md new file mode 100644 index 000000000..626de54c3 --- /dev/null +++ b/docs/modules/Number.ts.md @@ -0,0 +1,682 @@ +--- +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) +- [math](#math) + - [decrement](#decrement) + - [divide](#divide) + - [increment](#increment) + - [multiply](#multiply) + - [multiplyAll](#multiplyall) + - [remainder](#remainder) + - [sign](#sign) + - [subtract](#subtract) + - [sum](#sum) + - [sumAll](#sumall) +- [predicates](#predicates) + - [between](#between) + - [greaterThan](#greaterthan) + - [greaterThanOrEqualTo](#greaterthanorequalto) + - [lessThan](#lessthan) + - [lessThanOrEqualTo](#lessthanorequalto) +- [utils](#utils) + - [clamp](#clamp) + - [max](#max) + - [min](#min) + +--- + +# guards + +## isNumber + +Tests if a value is a `number`. + +**Signature** + +```ts +export declare const isNumber: (input: unknown) => input is number +``` + +**Example** + +```ts +import { isNumber } from '@fp-ts/core/Number' + +assert.deepStrictEqual(isNumber(2), true) +assert.deepStrictEqual(isNumber('2'), false) +``` + +Added in v1.0.0 + +# instances + +## Bounded + +**Signature** + +```ts +export declare const Bounded: bounded.Bounded +``` + +Added in v1.0.0 + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: equivalence.Equivalence +``` + +Added in v1.0.0 + +## MonoidMax + +A `Monoid` that uses the maximum between two values. + +The `empty` value is `Infinity`. + +**Signature** + +```ts +export declare const MonoidMax: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidMax } from '@fp-ts/core/Number' + +assert.deepStrictEqual(MonoidMax.combine(2, 3), 3) +assert.deepStrictEqual(MonoidMax.combine(2, MonoidMax.empty), 2) +``` + +Added in v1.0.0 + +## MonoidMin + +A `Monoid` that uses the minimum between two values. + +The `empty` value is `-Infinity`. + +**Signature** + +```ts +export declare const MonoidMin: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidMin } from '@fp-ts/core/Number' + +assert.deepStrictEqual(MonoidMin.combine(2, 3), 2) +assert.deepStrictEqual(MonoidMin.combine(2, MonoidMin.empty), 2) +``` + +Added in v1.0.0 + +## MonoidMultiply + +`number` monoid under multiplication. + +The `empty` value is `1`. + +**Signature** + +```ts +export declare const MonoidMultiply: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidMultiply } from '@fp-ts/core/Number' + +assert.deepStrictEqual(MonoidMultiply.combine(2, 3), 6) +assert.deepStrictEqual(MonoidMultiply.combine(2, MonoidMultiply.empty), 2) +``` + +Added in v1.0.0 + +## MonoidSum + +`number` monoid under addition. + +The `empty` value is `0`. + +**Signature** + +```ts +export declare const MonoidSum: monoid.Monoid +``` + +**Example** + +```ts +import { MonoidSum } from '@fp-ts/core/Number' + +assert.deepStrictEqual(MonoidSum.combine(2, 3), 5) +assert.deepStrictEqual(MonoidSum.combine(2, MonoidSum.empty), 2) +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: order.Order +``` + +Added in v1.0.0 + +## SemigroupMax + +A `Semigroup` that uses the maximum between two values. + +**Signature** + +```ts +export declare const SemigroupMax: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupMax } from '@fp-ts/core/Number' + +assert.deepStrictEqual(SemigroupMax.combine(2, 3), 3) +``` + +Added in v1.0.0 + +## SemigroupMin + +A `Semigroup` that uses the minimum between two values. + +**Signature** + +```ts +export declare const SemigroupMin: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupMin } from '@fp-ts/core/Number' + +assert.deepStrictEqual(SemigroupMin.combine(2, 3), 2) +``` + +Added in v1.0.0 + +## SemigroupMultiply + +`number` semigroup under multiplication. + +**Signature** + +```ts +export declare const SemigroupMultiply: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupMultiply } from '@fp-ts/core/Number' + +assert.deepStrictEqual(SemigroupMultiply.combine(2, 3), 6) +``` + +Added in v1.0.0 + +## SemigroupSum + +`number` semigroup under addition. + +**Signature** + +```ts +export declare const SemigroupSum: semigroup.Semigroup +``` + +**Example** + +```ts +import { SemigroupSum } from '@fp-ts/core/Number' + +assert.deepStrictEqual(SemigroupSum.combine(2, 3), 5) +``` + +Added in v1.0.0 + +# math + +## decrement + +Decrements a number by `1`. + +**Signature** + +```ts +export declare const decrement: (n: number) => number +``` + +**Example** + +```ts +import { decrement } from '@fp-ts/core/Number' + +assert.deepStrictEqual(decrement(3), 2) +``` + +Added in v1.0.0 + +## divide + +Provides a division operation on `number`s. + +**Signature** + +```ts +export declare const divide: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { divide } from '@fp-ts/core/Number' + +assert.deepStrictEqual(divide(6, 3), 2) +``` + +Added in v1.0.0 + +## increment + +Returns the result of adding `1` to a given number. + +**Signature** + +```ts +export declare const increment: (n: number) => number +``` + +**Example** + +```ts +import { increment } from '@fp-ts/core/Number' + +assert.deepStrictEqual(increment(2), 3) +``` + +Added in v1.0.0 + +## multiply + +Provides a multiplication operation on `number`s. + +**Signature** + +```ts +export declare const multiply: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { multiply } from '@fp-ts/core/Number' + +assert.deepStrictEqual(multiply(2, 3), 6) +``` + +Added in v1.0.0 + +## multiplyAll + +Takes an `Iterable` of `number`s and returns their multiplication as a single `number`. + +**Signature** + +```ts +export declare const multiplyAll: (collection: Iterable) => number +``` + +**Example** + +```ts +import { multiplyAll } from '@fp-ts/core/Number' + +assert.deepStrictEqual(multiplyAll([2, 3, 4]), 24) +``` + +Added in v1.0.0 + +## remainder + +Returns the remainder left over when one operand is divided by a second operand. + +It always takes the sign of the dividend. + +**Signature** + +```ts +export declare const remainder: { (divisor: number): (self: number) => number; (self: number, divisor: number): number } +``` + +**Example** + +```ts +import { remainder } from '@fp-ts/core/Number' + +assert.deepStrictEqual(remainder(2, 2), 0) +assert.deepStrictEqual(remainder(3, 2), 1) +assert.deepStrictEqual(remainder(-4, 2), -0) +``` + +Added in v1.0.0 + +## sign + +Determines the sign of a given `number`. + +**Signature** + +```ts +export declare const sign: (n: number) => Ordering +``` + +**Example** + +```ts +import { sign } from '@fp-ts/core/Number' + +assert.deepStrictEqual(sign(-5), -1) +assert.deepStrictEqual(sign(0), 0) +assert.deepStrictEqual(sign(5), 1) +``` + +Added in v1.0.0 + +## subtract + +Provides a subtraction operation on `number`s. + +**Signature** + +```ts +export declare const subtract: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { subtract } from '@fp-ts/core/Number' + +assert.deepStrictEqual(subtract(2, 3), -1) +``` + +Added in v1.0.0 + +## sum + +Provides an addition operation on `number`s. + +**Signature** + +```ts +export declare const sum: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { sum } from '@fp-ts/core/Number' + +assert.deepStrictEqual(sum(2, 3), 5) +``` + +Added in v1.0.0 + +## sumAll + +Takes an `Iterable` of `number`s and returns their sum as a single `number`. + +**Signature** + +```ts +export declare const sumAll: (collection: Iterable) => number +``` + +**Example** + +```ts +import { sumAll } from '@fp-ts/core/Number' + +assert.deepStrictEqual(sumAll([2, 3, 4]), 9) +``` + +Added in v1.0.0 + +# predicates + +## between + +Checks if a `number` is between a `minimum` and `maximum` value (inclusive). + +**Signature** + +```ts +export declare const between: { + (minimum: number, maximum: number): (self: number) => boolean + (self: number, minimum: number, maximum: number): boolean +} +``` + +**Example** + +```ts +import { between } from '@fp-ts/core/Number' + +assert.deepStrictEqual(between(0, 5)(3), true) +assert.deepStrictEqual(between(0, 5)(-1), false) +assert.deepStrictEqual(between(0, 5)(6), false) +``` + +Added in v1.0.0 + +## greaterThan + +Returns `true` if the first argument is greater than the second, otherwise `false`. + +**Signature** + +```ts +export declare const greaterThan: { (that: number): (self: number) => boolean; (self: number, that: number): boolean } +``` + +**Example** + +```ts +import { greaterThan } from '@fp-ts/core/Number' + +assert.deepStrictEqual(greaterThan(2, 3), false) +assert.deepStrictEqual(greaterThan(3, 3), false) +assert.deepStrictEqual(greaterThan(4, 3), true) +``` + +Added in v1.0.0 + +## greaterThanOrEqualTo + +Returns a function that checks if a given `number` is greater than or equal to the provided one. + +**Signature** + +```ts +export declare const greaterThanOrEqualTo: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} +``` + +**Example** + +```ts +import { greaterThanOrEqualTo } from '@fp-ts/core/Number' + +assert.deepStrictEqual(greaterThanOrEqualTo(2, 3), false) +assert.deepStrictEqual(greaterThanOrEqualTo(3, 3), true) +assert.deepStrictEqual(greaterThanOrEqualTo(4, 3), true) +``` + +Added in v1.0.0 + +## lessThan + +Returns `true` if the first argument is less than the second, otherwise `false`. + +**Signature** + +```ts +export declare const lessThan: { (that: number): (self: number) => boolean; (self: number, that: number): boolean } +``` + +**Example** + +```ts +import { lessThan } from '@fp-ts/core/Number' + +assert.deepStrictEqual(lessThan(2, 3), true) +assert.deepStrictEqual(lessThan(3, 3), false) +assert.deepStrictEqual(lessThan(4, 3), false) +``` + +Added in v1.0.0 + +## lessThanOrEqualTo + +Returns a function that checks if a given `number` is less than or equal to the provided one. + +**Signature** + +```ts +export declare const lessThanOrEqualTo: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} +``` + +**Example** + +```ts +import { lessThanOrEqualTo } from '@fp-ts/core/Number' + +assert.deepStrictEqual(lessThanOrEqualTo(2, 3), true) +assert.deepStrictEqual(lessThanOrEqualTo(3, 3), true) +assert.deepStrictEqual(lessThanOrEqualTo(4, 3), false) +``` + +Added in v1.0.0 + +# utils + +## clamp + +Restricts the given `number` to be within the range specified by the `minimum` and `maximum` values. + +- If the `number` is less than the `minimum` value, the function returns the `minimum` value. +- If the `number` is greater than the `maximum` value, the function returns the `maximum` value. +- Otherwise, it returns the original `number`. + +**Signature** + +```ts +export declare const clamp: { + (minimum: number, maximum: number): (self: number) => number + (self: number, minimum: number, maximum: number): number +} +``` + +**Example** + +```ts +import { clamp } from '@fp-ts/core/Number' + +assert.deepStrictEqual(clamp(0, 5)(3), 3) +assert.deepStrictEqual(clamp(0, 5)(-1), 0) +assert.deepStrictEqual(clamp(0, 5)(6), 5) +``` + +Added in v1.0.0 + +## max + +Returns the maximum between two `number`s. + +**Signature** + +```ts +export declare const max: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { max } from '@fp-ts/core/Number' + +assert.deepStrictEqual(max(2, 3), 3) +``` + +Added in v1.0.0 + +## min + +Returns the minimum between two `number`s. + +**Signature** + +```ts +export declare const min: { (that: number): (self: number) => number; (self: number, that: number): number } +``` + +**Example** + +```ts +import { min } from '@fp-ts/core/Number' + +assert.deepStrictEqual(min(2, 3), 2) +``` + +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..e8528904a --- /dev/null +++ b/docs/modules/Option.ts.md @@ -0,0 +1,1988 @@ +--- +title: Option.ts +nav_order: 9 +parent: Modules +--- + +## Option overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combining](#combining) + - [all](#all) + - [ap](#ap) + - [getFailureMonoid](#getfailuremonoid) + - [getFailureSemigroup](#getfailuresemigroup) + - [getFirstSomeSemigroup](#getfirstsomesemigroup) + - [sequence](#sequence) + - [struct](#struct) + - [traverse](#traverse) + - [traverseTap](#traversetap) + - [tuple](#tuple) + - [zipWith](#zipwith) +- [constructors](#constructors) + - [none](#none) + - [of](#of) + - [some](#some) +- [conversions](#conversions) + - [fromEither](#fromeither) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [getLeft](#getleft) + - [getOrThrow](#getorthrow) + - [getOrThrowWith](#getorthrowwith) + - [getRight](#getright) + - [liftNullable](#liftnullable) + - [liftThrowable](#liftthrowable) + - [toArray](#toarray) + - [toEither](#toeither) + - [toRefinement](#torefinement) +- [debugging](#debugging) + - [inspectNone](#inspectnone) + - [inspectSome](#inspectsome) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [appendElement](#appendelement) + - [bind](#bind) + - [bindTo](#bindto) + - [let](#let) + - [tupled](#tupled) +- [equivalence](#equivalence) + - [getEquivalence](#getequivalence) +- [error handling](#error-handling) + - [firstSomeOf](#firstsomeof) + - [orElse](#orelse) + - [orElseEither](#orelseeither) +- [filtering](#filtering) + - [filter](#filter) + - [filterMap](#filtermap) + - [partitionMap](#partitionmap) +- [folding](#folding) + - [reduceCompact](#reducecompact) +- [getters](#getters) + - [getOrElse](#getorelse) + - [getOrNull](#getornull) + - [getOrUndefined](#getorundefined) +- [guards](#guards) + - [isNone](#isnone) + - [isOption](#isoption) + - [isSome](#issome) +- [lifting](#lifting) + - [lift2](#lift2) + - [liftEither](#lifteither) + - [liftPredicate](#liftpredicate) +- [math](#math) + - [divide](#divide) + - [multiply](#multiply) + - [multiplyCompact](#multiplycompact) + - [subtract](#subtract) + - [sum](#sum) + - [sumCompact](#sumcompact) +- [models](#models) + - [None (interface)](#none-interface) + - [Option (type alias)](#option-type-alias) + - [Some (interface)](#some-interface) +- [pattern matching](#pattern-matching) + - [match](#match) +- [sorting](#sorting) + - [getOrder](#getorder) +- [transforming](#transforming) + - [andThen](#andthen) + - [andThenDiscard](#andthendiscard) + - [as](#as) + - [asUnit](#asunit) + - [composeKleisliArrow](#composekleisliarrow) + - [flap](#flap) + - [flatMap](#flatmap) + - [flatMapEither](#flatmapeither) + - [flatMapNullable](#flatmapnullable) + - [flatten](#flatten) + - [map](#map) + - [tap](#tap) +- [type lambdas](#type-lambdas) + - [OptionTypeLambda (interface)](#optiontypelambda-interface) +- [utils](#utils) + - [Alternative](#alternative) + - [Applicative](#applicative) + - [Chainable](#chainable) + - [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) + - [contains](#contains) + - [exists](#exists) + - [getOptionalMonoid](#getoptionalmonoid) + - [unit](#unit) + +--- + +# combining + +## all + +Similar to `Promise.all` but operates on `Option`s. + +``` +Iterable> -> Option +``` + +Flattens a collection of `Option`s into a single `Option` that contains a list of all the `Some` values. +If there is a `None` value in the collection, it returns `None` as the result. + +**Signature** + +```ts +export declare const all:
(collection: Iterable>) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.all([O.some(1), O.some(2), O.some(3)]), O.some([1, 2, 3])) +assert.deepStrictEqual(O.all([O.some(1), O.none(), O.some(3)]), O.none()) +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: { + (self: Option<(a: A) => B>, that: Option): Option + (that: Option): (self: Option<(a: A) => B>) => Option +} +``` + +Added in v1.0.0 + +## getFailureMonoid + +Monoid that models the combination of computations that can fail, if at least one element is `None` +then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination +is the combination of the wrapped elements using the provided `Monoid`. + +The `empty` value is `some(M.empty)`. + +See also `getFailureSemigroup` if you need a `Semigroup` instead of a `Monoid`. + +**Signature** + +```ts +export declare const getFailureMonoid: (M: Monoid) => Monoid> +``` + +Added in v1.0.0 + +## getFailureSemigroup + +Semigroup that models the combination of computations that can fail, if at least one element is `None` +then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination +is the combination of the wrapped elements using the provided `Semigroup`. + +See also `getFailureMonoid` if you need a `Monoid` instead of a `Semigroup`. + +**Signature** + +```ts +export declare const getFailureSemigroup: (S: Semigroup) => Semigroup> +``` + +Added in v1.0.0 + +## getFirstSomeSemigroup + +Semigroup returning the first `Some` value encountered. + +**Signature** + +```ts +export declare const getFirstSomeSemigroup: () => Semigroup> +``` + +Added in v1.0.0 + +## sequence + +Combines an `Option` of an `F`-structure to an `F`-structure of an `Option` with the same inner type. + +**Signature** + +```ts +export declare const sequence: ( + F: applicative.Applicative +) => (self: Option>) => Kind> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +const sequence = O.sequence(E.Applicative) + +assert.deepStrictEqual(sequence(O.some(E.right(1))), E.right(O.some(1))) +assert.deepStrictEqual(sequence(O.some(E.left('error'))), E.left('error')) +assert.deepStrictEqual(sequence(O.none()), E.right(O.none())) +``` + +Added in v1.0.0 + +## struct + +Takes a struct of `Option`s and returns an `Option` of a struct of values. + +**Signature** + +```ts +export declare const struct: >>( + fields: R +) => Option<{ [K in keyof R]: [R[K]] extends [Option] ? A : never }> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.struct({ a: O.some(1), b: O.some('hello') }), O.some({ a: 1, b: 'hello' })) +assert.deepStrictEqual(O.struct({ a: O.some(1), b: O.none() }), O.none()) +``` + +Added in v1.0.0 + +## traverse + +Applies an `Option` value to an effectful function that returns an `F` value. + +**Signature** + +```ts +export declare const traverse: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind): (self: Option) => Kind> + (self: Option, f: (a: A) => Kind): Kind> +} +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +const traverse = O.traverse(E.Applicative) +const f = (n: number) => (n >= 0 ? E.right(1) : E.left('negative')) + +assert.deepStrictEqual(traverse(O.some(1), f), E.right(O.some(1))) +assert.deepStrictEqual(traverse(O.some(-1), f), E.left('negative')) +assert.deepStrictEqual(traverse(O.none(), f), E.right(O.none())) +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: applicative.Applicative +) => { + (self: Option, f: (a: A) => Kind): Kind> + (f: (a: A) => Kind): (self: Option) => Kind> +} +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Option`s. + +``` +[Option, Option, ...] -> Option<[A, B, ...]> +``` + +Takes a tuple of `Option`s and returns an `Option` of a tuple of values. + +**Signature** + +```ts +export declare const tuple: []>( + ...elements: T +) => Option<{ [I in keyof T]: [T[I]] extends [Option] ? A : never }> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.tuple(O.some(1), O.some('hello')), O.some([1, 'hello'])) +assert.deepStrictEqual(O.tuple(O.some(1), O.none()), O.none()) +``` + +Added in v1.0.0 + +## zipWith + +Zips two `Option` values together using a provided function, returning a new `Option` of the result. + +**Signature** + +```ts +export declare const zipWith: { + (self: Option, that: Option, f: (a: A, b: B) => C): Option + (that: Option, f: (a: A, b: B) => C): (self: Option) => Option +} +``` + +Added in v1.0.0 + +# constructors + +## none + +Creates a new `Option` that represents the absence of a value. + +**Signature** + +```ts +export declare const none: () => Option +``` + +Added in v1.0.0 + +## of + +Alias of {@link some}. + +**Signature** + +```ts +export declare const of: (value: A) => Option +``` + +Added in v1.0.0 + +## some + +Creates a new `Option` that wraps the given value. + +**Signature** + +```ts +export declare const some: (value: 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: Either) => 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('error message')), O.none()) +``` + +Added in v1.0.0 + +## fromIterable + +Converts an `Iterable` of values into an `Option`. Returns the first value of the `Iterable` wrapped in a `Some` +if the `Iterable` is not empty, otherwise returns `None`. + +**Signature** + +```ts +export declare const fromIterable: (collection: Iterable) => Option +``` + +**Example** + +```ts +import { fromIterable, some, none } from '@fp-ts/core/Option' + +assert.deepStrictEqual(fromIterable([1, 2, 3]), some(1)) +assert.deepStrictEqual(fromIterable([]), none()) +``` + +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: (nullableValue: A) => Option> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.fromNullable(undefined), O.none()) +assert.deepStrictEqual(O.fromNullable(null), O.none()) +assert.deepStrictEqual(O.fromNullable(1), O.some(1)) +``` + +Added in v1.0.0 + +## getLeft + +Converts a `Either` to an `Option` discarding the value. + +**Signature** + +```ts +export declare const getLeft: (self: Either) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(O.getLeft(E.right('ok')), O.none()) +assert.deepStrictEqual(O.getLeft(E.left('error')), O.some('error')) +``` + +Added in v1.0.0 + +## getOrThrow + +Extracts the value of an `Option` or throws if the `Option` is `None`. + +The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + +**Signature** + +```ts +export declare const getOrThrow: (self: Option) => A +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.getOrThrow(O.some(1)), 1) +assert.throws(() => O.getOrThrow(O.none())) +``` + +Added in v1.0.0 + +## getOrThrowWith + +Extracts the value of an `Option` or throws if the `Option` is `None`. + +If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + +**Signature** + +```ts +export declare const getOrThrowWith: { + (onNone: () => unknown): (self: Option) => A + (self: Option, onNone: () => unknown): A +} +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual( + O.getOrThrowWith(O.some(1), () => new Error('Unexpected None')), + 1 +) +assert.throws(() => O.getOrThrowWith(O.none(), () => new Error('Unexpected None'))) +``` + +Added in v1.0.0 + +## getRight + +Converts a `Either` to an `Option` discarding the error. + +Alias of {@link fromEither}. + +**Signature** + +```ts +export declare const getRight: (self: Either) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +assert.deepStrictEqual(O.getRight(E.right('ok')), O.some('ok')) +assert.deepStrictEqual(O.getRight(E.left('err')), O.none()) +``` + +Added in v1.0.0 + +## liftNullable + +This API is useful for lifting a function that returns `null` or `undefined` into the `Option` context. + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined +) => (...a: A) => Option> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +const parse = (s: string): number | undefined => { + const n = parseFloat(s) + return isNaN(n) ? undefined : n +} + +const parseOption = O.liftNullable(parse) + +assert.deepStrictEqual(parseOption('1'), O.some(1)) +assert.deepStrictEqual(parseOption('not a number'), O.none()) +``` + +Added in v1.0.0 + +## liftThrowable + +A utility function that lifts a function that throws exceptions into a function that returns an `Option`. + +This function is useful for any function that might throw an exception, allowing the developer to handle +the exception in a more functional way. + +**Signature** + +```ts +export declare const liftThrowable: (f: (...a: A) => B) => (...a: A) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +const parse = O.liftThrowable(JSON.parse) + +assert.deepStrictEqual(parse('1'), O.some(1)) +assert.deepStrictEqual(parse(''), O.none()) +``` + +Added in v1.0.0 + +## toArray + +Transforms an `Option` into an `Array`. +If the input is `None`, an empty array is returned. +If the input is `Some`, the value is wrapped in an array. + +**Signature** + +```ts +export declare const toArray: (self: Option) => A[] +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.toArray(O.some(1)), [1]) +assert.deepStrictEqual(O.toArray(O.none()), []) +``` + +Added in v1.0.0 + +## toEither + +Converts an `Option` to an `Either`, allowing you to provide a value to be used in the case of a `None`. + +**Signature** + +```ts +export declare const toEither: { + (self: Option, onNone: () => E): Either + (onNone: () => E): (self: Option) => Either +} +``` + +**Example** + +```ts +import { pipe } from '@fp-ts/core/Function' +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +const onNone = () => 'error' +assert.deepStrictEqual(pipe(O.some(1), O.toEither(onNone)), E.right(1)) +assert.deepStrictEqual(pipe(O.none(), O.toEither(onNone)), E.left('error')) +``` + +Added in v1.0.0 + +## toRefinement + +Returns a type guard from a `Option` returning function. +This function ensures that a type guard definition is type-safe. + +**Signature** + +```ts +export declare const toRefinement: (f: (a: A) => Option) => (a: A) => a is B +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +const parsePositive = (n: number): O.Option => (n > 0 ? O.some(n) : O.none()) + +const isPositive = O.toRefinement(parsePositive) + +assert.deepStrictEqual(isPositive(1), true) +assert.deepStrictEqual(isPositive(-1), false) +``` + +Added in v1.0.0 + +# debugging + +## inspectNone + +Useful for debugging purposes, the `onNone` callback is is called if `self` is a `None`. + +**Signature** + +```ts +export declare const inspectNone: { + (onNone: () => void): (self: Option) => Option + (self: Option, onNone: () => void): Option +} +``` + +Added in v1.0.0 + +## inspectSome + +Useful for debugging purposes, the `onSome` callback is called with the value of `self` if it is a `Some`. + +**Signature** + +```ts +export declare const inspectSome: { + (onSome: (a: A) => void): (self: Option) => Option + (self: Option, onSome: (a: A) => void): 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 }> + (self: Option, name: Exclude, that: Option): Option<{ + [K in N | keyof A]: K extends keyof A ? A[K] : B + }> +} +``` + +Added in v1.0.0 + +## appendElement + +Appends an element to the end of a tuple wrapped in an `Option` type. + +**Signature** + +```ts +export declare const appendElement: { + (self: Option, that: Option): Option<[...A, B]> + (that: Option): (self: Option) => Option<[...A, B]> +} +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.appendElement(O.some([1, 2]), O.some(3)), O.some([1, 2, 3])) +assert.deepStrictEqual(O.appendElement(O.some([1, 2]), O.none()), O.none()) +``` + +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 }> + (self: Option, name: Exclude, f: (a: A) => 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 }> + (self: Option, name: N): 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 }> + (self: Option, name: Exclude, f: (a: A) => B): Option<{ + [K in N | keyof A]: K extends keyof A ? A[K] : B + }> +} +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: Option) => Option<[A]> +``` + +Added in v1.0.0 + +# equivalence + +## getEquivalence + +**Signature** + +```ts +export declare const getEquivalence: (E: Equivalence) => Equivalence> +``` + +**Example** + +```ts +import { none, some, getEquivalence } from '@fp-ts/core/Option' +import * as N from '@fp-ts/core/Number' + +const isEquivalent = getEquivalence(N.Equivalence) +assert.deepStrictEqual(isEquivalent(none(), none()), true) +assert.deepStrictEqual(isEquivalent(none(), some(1)), false) +assert.deepStrictEqual(isEquivalent(some(1), none()), false) +assert.deepStrictEqual(isEquivalent(some(1), some(2)), false) +assert.deepStrictEqual(isEquivalent(some(1), some(1)), true) +``` + +Added in v1.0.0 + +# error handling + +## firstSomeOf + +Given an `Iterable` collection of `Option`s, returns the first `Some` found in the collection. + +**Signature** + +```ts +export declare const firstSomeOf: (collection: Iterable>) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.firstSomeOf([O.none(), O.some(1), O.some(2)]), O.some(1)) +``` + +Added in v1.0.0 + +## orElse + +Returns the provided `Option` `that` if `self` is `None`, otherwise returns `self`. + +**Signature** + +```ts +export declare const orElse: { + (that: LazyArg>): (self: Option) => Option + (self: Option, that: LazyArg>): 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 + +Similar to `orElse`, but instead of returning a simple union, it returns an `Either` object, +which contains information about which of the two `Option`s has been chosen. + +This is useful when it's important to know whether the value was retrieved from the first `Option` or the second option. + +**Signature** + +```ts +export declare const orElseEither: { + (that: LazyArg>): (self: Option) => Option> + (self: Option, that: LazyArg>): Option> +} +``` + +Added in v1.0.0 + +# filtering + +## filter + +Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + +If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + +**Signature** + +```ts +export declare const filter: { + (self: Option, refinement: (a: A) => a is B): Option + (self: Option, predicate: (a: A) => boolean): Option + (refinement: (a: A) => a is B): (self: Option) => Option + (predicate: (a: A) => boolean): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## filterMap + +Maps over the value of an `Option` and filters out `None`s. + +Useful when in addition to filtering you also want to change the type of the `Option`. + +**Signature** + +```ts +export declare const filterMap: { + (f: (a: A) => Option): (self: Option) => Option + (self: Option, f: (a: A) => Option): Option +} +``` + +Added in v1.0.0 + +## partitionMap + +**Signature** + +```ts +export declare const partitionMap: { + (f: (a: A) => Either): (self: Option) => [Option, Option] + (self: Option, f: (a: A) => Either): [Option, Option] +} +``` + +Added in v1.0.0 + +# folding + +## reduceCompact + +Reduces an `Iterable` of `Option` to a single value of type `B`, elements that are `None` are ignored. + +**Signature** + +```ts +export declare const reduceCompact: { + (b: B, f: (b: B, a: A) => B): (self: Iterable>) => B + (self: Iterable>, b: B, f: (b: B, a: A) => B): B +} +``` + +**Example** + +```ts +import { some, none, reduceCompact } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +const iterable = [some(1), none(), some(2), none()] +assert.deepStrictEqual( + pipe( + iterable, + reduceCompact(0, (b, a) => b + a) + ), + 3 +) +``` + +Added in v1.0.0 + +# getters + +## getOrElse + +Returns the value of the `Option` if it is `Some`, otherwise returns `onNone` + +**Signature** + +```ts +export declare const getOrElse: { + (onNone: LazyArg): (self: Option) => B | A + (self: Option, onNone: LazyArg): A | B +} +``` + +**Example** + +```ts +import { some, none, getOrElse } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual( + pipe( + some(1), + getOrElse(() => 0) + ), + 1 +) +assert.deepStrictEqual( + pipe( + none(), + getOrElse(() => 0) + ), + 0 +) +``` + +Added in v1.0.0 + +## getOrNull + +Returns the value of the `Option` if it is a `Some`, otherwise returns `null`. + +**Signature** + +```ts +export declare const getOrNull: (self: Option) => A | null +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.getOrNull(O.some(1)), 1) +assert.deepStrictEqual(O.getOrNull(O.none()), null) +``` + +Added in v1.0.0 + +## getOrUndefined + +Returns the value of the `Option` if it is a `Some`, otherwise returns `undefined`. + +**Signature** + +```ts +export declare const getOrUndefined: (self: Option) => A | undefined +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' + +assert.deepStrictEqual(O.getOrUndefined(O.some(1)), 1) +assert.deepStrictEqual(O.getOrUndefined(O.none()), undefined) +``` + +Added in v1.0.0 + +# guards + +## isNone + +Determine if a `Option` is a `None`. + +**Signature** + +```ts +export declare const isNone: (self: Option) => self is None +``` + +**Example** + +```ts +import { some, none, isNone } from '@fp-ts/core/Option' + +assert.deepStrictEqual(isNone(some(1)), false) +assert.deepStrictEqual(isNone(none()), true) +``` + +Added in v1.0.0 + +## isOption + +Tests if a value is a `Option`. + +**Signature** + +```ts +export declare const isOption: (input: unknown) => input is Option +``` + +**Example** + +```ts +import { some, none, isOption } from '@fp-ts/core/Option' + +assert.deepStrictEqual(isOption(some(1)), true) +assert.deepStrictEqual(isOption(none()), true) +assert.deepStrictEqual(isOption({}), false) +``` + +Added in v1.0.0 + +## isSome + +Determine if a `Option` is a `Some`. + +**Signature** + +```ts +export declare const isSome: (self: Option) => self is Some +``` + +**Example** + +```ts +import { some, none, isSome } from '@fp-ts/core/Option' + +assert.deepStrictEqual(isSome(some(1)), true) +assert.deepStrictEqual(isSome(none()), false) +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +Lifts a binary function into `Option`. + +**Signature** + +```ts +export declare const lift2: ( + f: (a: A, b: B) => C +) => { (self: Option, that: Option): Option; (that: Option): (self: Option) => Option } +``` + +Added in v1.0.0 + +## liftEither + +Lifts an `Either` function to an `Option` function. + +**Signature** + +```ts +export declare const liftEither: ( + f: (...a: A) => Either +) => (...a: A) => Option +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' + +const parse = (s: string) => (isNaN(+s) ? E.left(`Error: ${s} is not a number`) : E.right(+s)) + +const parseNumber = O.liftEither(parse) + +assert.deepEqual(parseNumber('12'), O.some(12)) +assert.deepEqual(parseNumber('not a number'), O.none()) +``` + +Added in v1.0.0 + +## liftPredicate + +Transforms a `Predicate` function into a `Some` of the input value if the predicate returns `true` or `None` +if the predicate returns `false`. + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: Refinement): (c: C) => Option + (predicate: Predicate): (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 + +# math + +## divide + +**Signature** + +```ts +export declare const divide: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## multiply + +**Signature** + +```ts +export declare const multiply: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## multiplyCompact + +Multiply all numbers in an iterable of `Option` ignoring the `None` values. + +**Signature** + +```ts +export declare const multiplyCompact: (self: Iterable>) => number +``` + +**Example** + +```ts +import { multiplyCompact, some, none } from '@fp-ts/core/Option' + +const iterable = [some(2), none(), some(3), none()] +assert.deepStrictEqual(multiplyCompact(iterable), 6) +``` + +Added in v1.0.0 + +## subtract + +**Signature** + +```ts +export declare const subtract: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## sum + +**Signature** + +```ts +export declare const sum: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## sumCompact + +Sum all numbers in an iterable of `Option` ignoring the `None` values. + +**Signature** + +```ts +export declare const sumCompact: (self: Iterable>) => number +``` + +**Example** + +```ts +import { sumCompact, some, none } from '@fp-ts/core/Option' + +const iterable = [some(2), none(), some(3), none()] +assert.deepStrictEqual(sumCompact(iterable), 5) +``` + +Added in v1.0.0 + +# models + +## None (interface) + +**Signature** + +```ts +export interface 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 (interface) + +**Signature** + +```ts +export interface Some { + readonly _tag: 'Some' + readonly value: A +} +``` + +Added in v1.0.0 + +# pattern matching + +## match + +Matches the given `Option` and returns either the provided `onNone` value or the result of the provided `onSome` +function when passed the `Option`'s value. + +**Signature** + +```ts +export declare const match: { + (onNone: LazyArg, onSome: (a: A) => C): (self: Option) => B | C + (self: Option, onNone: LazyArg, onSome: (a: A) => C): B | C +} +``` + +**Example** + +```ts +import { some, none, match } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual( + pipe( + some(1), + match( + () => 'a none', + (a) => `a some containing ${a}` + ) + ), + 'a some containing 1' +) + +assert.deepStrictEqual( + pipe( + none(), + match( + () => 'a none', + (a) => `a some containing ${a}` + ) + ), + 'a none' +) +``` + +Added in v1.0.0 + +# sorting + +## getOrder + +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 getOrder: (O: Order) => Order> +``` + +**Example** + +```ts +import { none, some, getOrder } from '@fp-ts/core/Option' +import * as N from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +const O = getOrder(N.Order) +assert.deepStrictEqual(O.compare(none(), none()), 0) +assert.deepStrictEqual(O.compare(none(), some(1)), -1) +assert.deepStrictEqual(O.compare(some(1), none()), 1) +assert.deepStrictEqual(O.compare(some(1), some(2)), -1) +assert.deepStrictEqual(O.compare(some(1), some(1)), 0) +``` + +Added in v1.0.0 + +# transforming + +## andThen + +**Signature** + +```ts +export declare const andThen: { + <_, B>(self: Option<_>, that: Option): Option + (that: Option): <_>(self: Option<_>) => Option +} +``` + +Added in v1.0.0 + +## andThenDiscard + +Sequences the specified `that` `Option` but ignores its value. + +It is useful when we want to chain multiple operations, but only care about the result of `self`. + +**Signature** + +```ts +export declare const andThenDiscard: { + (self: Option, that: Option<_>): Option + <_>(that: Option<_>): (self: Option) => Option +} +``` + +Added in v1.0.0 + +## as + +Maps the `Some` value of this `Option` to the specified constant value. + +**Signature** + +```ts +export declare const as: { <_, B>(self: Option<_>, b: B): Option; (b: B): <_>(self: Option<_>) => Option } +``` + +Added in v1.0.0 + +## asUnit + +Returns the `Option` resulting from mapping the `Some` value to `void`. + +This is useful when the value of the `Option` is not needed, but the presence or absence of the value is important. + +**Signature** + +```ts +export declare const asUnit: <_>(self: Option<_>) => Option +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: { + (afb: (a: A) => Option, bfc: (b: B) => Option): (a: A) => Option + (bfc: (b: B) => Option): (afb: (a: A) => Option) => (a: A) => Option +} +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: { + (a: A, self: Option<(a: A) => B>): Option + (self: Option<(a: A) => B>): (a: A) => Option +} +``` + +Added in v1.0.0 + +## flatMap + +Applies a function to the value of an `Option` and flattens the result, if the input is `Some`. + +**Signature** + +```ts +export declare const flatMap: { + (f: (a: A) => Option): (self: Option) => Option + (self: Option, f: (a: A) => Option): Option +} +``` + +Added in v1.0.0 + +## flatMapEither + +Applies a provided function that returns an `Either` to the contents of an `Option`, flattening the result into another `Option`. + +**Signature** + +```ts +export declare const flatMapEither: { + (f: (a: A) => Either): (self: Option) => Option + (self: Option, f: (a: A) => Either): Option +} +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as E from '@fp-ts/core/Either' +import { pipe } from '@fp-ts/core/Function' + +const f = (n: number) => (n > 2 ? E.left('Too big') : E.right(n + 1)) + +assert.deepStrictEqual(pipe(O.some(1), O.flatMapEither(f)), O.some(2)) +assert.deepStrictEqual(pipe(O.some(3), O.flatMapEither(f)), O.none()) +``` + +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> + (self: Option, f: (a: A) => B | null | undefined): Option> +} +``` + +**Example** + +```ts +import { some, none, 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( + some(employee1), + flatMapNullable((employee) => employee.company?.address?.street?.name) + ), + some('high street') +) + +const employee2: Employee = { company: { address: { street: {} } } } + +assert.deepStrictEqual( + pipe( + some(employee2), + flatMapNullable((employee) => employee.company?.address?.street?.name) + ), + none() +) +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: (self: Option>) => Option +``` + +Added in v1.0.0 + +## map + +Maps the `Some` side of an `Option` value to a new `Option` value. + +**Signature** + +```ts +export declare const map: { + (f: (a: A) => B): (self: Option) => Option + (self: Option, f: (a: A) => B): Option +} +``` + +Added in v1.0.0 + +## tap + +Applies the provided function `f` to the value of the `Option` if it is `Some` and returns the original `Option` +unless `f` returns `None`, in which case it returns `None`. + +This function is useful for performing additional computations on the value of the input `Option` without affecting its value. + +**Signature** + +```ts +export declare const tap: { + (self: Option, f: (a: A) => Option<_>): Option + (f: (a: A) => Option<_>): (self: Option) => Option +} +``` + +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 + +## Alternative + +**Signature** + +```ts +export declare const Alternative: alternative.Alternative +``` + +Added in v1.0.0 + +## Applicative + +**Signature** + +```ts +export declare const Applicative: applicative.Applicative +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: chainable.Chainable +``` + +Added in v1.0.0 + +## Coproduct + +**Signature** + +```ts +export declare const Coproduct: coproduct_.Coproduct +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## Filterable + +**Signature** + +```ts +export declare const Filterable: filterable.Filterable +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: flatMap_.FlatMap +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: foldable.Foldable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: monad.Monad +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: pointed.Pointed +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: semiAlternative.SemiAlternative +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: semiApplicative.SemiApplicative +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: semiCoproduct.SemiCoproduct +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +## contains + +Returns a function that checks if an `Option` contains a given value using a provided `Equivalence` instance. + +**Signature** + +```ts +export declare const contains: (isEquivalent: (self: A, that: A) => boolean) => { + (a: A): (self: Option) => boolean + (self: Option, a: A): boolean +} +``` + +**Example** + +```ts +import { some, none, contains } from '@fp-ts/core/Option' +import { Equivalence } from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe(some(2), contains(Equivalence)(2)), true) +assert.deepStrictEqual(pipe(some(1), contains(Equivalence)(2)), false) +assert.deepStrictEqual(pipe(none(), contains(Equivalence)(2)), false) +``` + +Added in v1.0.0 + +## exists + +Check if a value in an `Option` type meets a certain predicate. + +**Signature** + +```ts +export declare const exists: { + (predicate: Predicate): (self: Option) => boolean + (self: Option, predicate: Predicate): boolean +} +``` + +**Example** + +```ts +import { some, none, exists } from '@fp-ts/core/Option' +import { pipe } from '@fp-ts/core/Function' + +const isEven = (n: number) => n % 2 === 0 + +assert.deepStrictEqual(pipe(some(2), exists(isEven)), true) +assert.deepStrictEqual(pipe(some(1), exists(isEven)), false) +assert.deepStrictEqual(pipe(none(), exists(isEven)), false) +``` + +Added in v1.0.0 + +## getOptionalMonoid + +Monoid that models the combination of values that may be absent, elements that are `None` are ignored +while elements that are `Some` are combined using the provided `Semigroup`. + +The `empty` value is `none()`. + +**Signature** + +```ts +export declare const getOptionalMonoid: (Semigroup: Semigroup) => Monoid> +``` + +**Example** + +```ts +import * as O from '@fp-ts/core/Option' +import * as N from '@fp-ts/core/Number' +import { pipe } from '@fp-ts/core/Function' + +const M = O.getOptionalMonoid(N.SemigroupSum) + +assert.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) +assert.deepStrictEqual(M.combine(O.some(1), O.none()), O.some(1)) +assert.deepStrictEqual(M.combine(O.none(), O.some(1)), O.some(1)) +assert.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(3)) +``` + +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..4ce18e1d3 --- /dev/null +++ b/docs/modules/Ordering.ts.md @@ -0,0 +1,139 @@ +--- +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 + +`Monoid` instance for `Ordering`, returns the left-most non-zero `Ordering`. + +The `empty` value is `0`. + +**Signature** + +```ts +export declare const Monoid: monoid.Monoid<0 | 1 | -1> +``` + +**Example** + +```ts +import { Monoid } from '@fp-ts/core/Ordering' + +assert.deepStrictEqual(Monoid.combine(Monoid.empty, -1), -1) +assert.deepStrictEqual(Monoid.combine(Monoid.empty, 1), 1) +assert.deepStrictEqual(Monoid.combine(1, -1), 1) +``` + +Added in v1.0.0 + +## Semigroup + +`Semigroup` instance for `Ordering`, returns the left-most non-zero `Ordering`. + +**Signature** + +```ts +export declare const Semigroup: semigroup.Semigroup<0 | 1 | -1> +``` + +**Example** + +```ts +import { Semigroup } from '@fp-ts/core/Ordering' + +assert.deepStrictEqual(Semigroup.combine(0, -1), -1) +assert.deepStrictEqual(Semigroup.combine(0, 1), 1) +assert.deepStrictEqual(Semigroup.combine(1, -1), 1) +``` + +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 + +Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. + +**Signature** + +```ts +export declare const match: { + (onLessThan: LazyArg
, onEqual: LazyArg, onGreaterThan: LazyArg): (self: Ordering) => A | B | C + (o: Ordering, onLessThan: LazyArg, onEqual: LazyArg, onGreaterThan: LazyArg): A | B | C +} +``` + +**Example** + +```ts +import { match } from '@fp-ts/core/Ordering' +import { constant } from '@fp-ts/core/Function' + +const toMessage = match(constant('less than'), constant('equal'), constant('greater than')) + +assert.deepStrictEqual(toMessage(-1), 'less than') +assert.deepStrictEqual(toMessage(0), 'equal') +assert.deepStrictEqual(toMessage(1), 'greater than') +``` + +Added in v1.0.0 + +# utils + +## reverse + +Inverts the ordering of the input `Ordering`. + +**Signature** + +```ts +export declare const reverse: (o: Ordering) => Ordering +``` + +**Example** + +```ts +import { reverse } from '@fp-ts/core/Ordering' + +assert.deepStrictEqual(reverse(1), -1) +assert.deepStrictEqual(reverse(-1), 1) +assert.deepStrictEqual(reverse(0), 0) +``` + +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..d2be420df --- /dev/null +++ b/docs/modules/Predicate.ts.md @@ -0,0 +1,919 @@ +--- +title: Predicate.ts +nav_order: 11 +parent: Modules +--- + +## Predicate overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combinators](#combinators) + - [and](#and) + - [not](#not) + - [or](#or) +- [constructors](#constructors) + - [contramap](#contramap) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bindTo](#bindto) +- [guards](#guards) + - [isBigint](#isbigint) + - [isBoolean](#isboolean) + - [isDate](#isdate) + - [isError](#iserror) + - [isFunction](#isfunction) + - [isNever](#isnever) + - [isNotNull](#isnotnull) + - [isNotNullable](#isnotnullable) + - [isNotUndefined](#isnotundefined) + - [isNull](#isnull) + - [isNullable](#isnullable) + - [isNumber](#isnumber) + - [isObject](#isobject) + - [isReadonlyRecord](#isreadonlyrecord) + - [isRecord](#isrecord) + - [isString](#isstring) + - [isSymbol](#issymbol) + - [isUndefined](#isundefined) + - [isUnknown](#isunknown) +- [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) + - [Refinement (interface)](#refinement-interface) +- [type lambdas](#type-lambdas) + - [PredicateTypeLambda (interface)](#predicatetypelambda-interface) +- [utils](#utils) + - [all](#all) + - [any](#any) + - [appendElement](#appendelement) + - [compose](#compose) + - [of](#of) + - [struct](#struct) + - [tuple](#tuple) + - [tupled](#tupled) + - [unit](#unit) + +--- + +# combinators + +## and + +Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`. + +**Signature** + +```ts +export declare const and: { +
(that: Predicate): (self: Predicate) => Predicate + (self: Predicate, that: Predicate): Predicate +} +``` + +**Example** + +```ts +import * as P from '@fp-ts/core/Predicate' + +const minLength = (n: number) => (s: string) => s.length >= n +const maxLength = (n: number) => (s: string) => s.length <= n + +const length = (n: number) => P.and(minLength(n), maxLength(n)) + +assert.deepStrictEqual(length(2)('aa'), true) +assert.deepStrictEqual(length(2)('a'), false) +assert.deepStrictEqual(length(2)('aaa'), false) +``` + +Added in v1.0.0 + +## not + +Negates the result of a given predicate. + +**Signature** + +```ts +export declare const not: (self: Predicate) => Predicate +``` + +**Example** + +```ts +import * as P from '@fp-ts/core/Predicate' +import * as N from '@fp-ts/core/Number' + +const isPositive = P.not(N.lessThan(0)) + +assert.deepStrictEqual(isPositive(-1), false) +assert.deepStrictEqual(isPositive(0), true) +assert.deepStrictEqual(isPositive(1), true) +``` + +Added in v1.0.0 + +## or + +Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`. + +**Signature** + +```ts +export declare const or: { + (that: Predicate): (self: Predicate) => Predicate + (self: Predicate, that: Predicate): Predicate +} +``` + +**Example** + +```ts +import * as P from '@fp-ts/core/Predicate' +import * as N from '@fp-ts/core/Number' + +const nonZero = P.or(N.lessThan(0), N.greaterThan(0)) + +assert.deepStrictEqual(nonZero(-1), true) +assert.deepStrictEqual(nonZero(0), false) +assert.deepStrictEqual(nonZero(1), true) +``` + +Added in v1.0.0 + +# constructors + +## contramap + +Given a `Predicate` returns a `Predicate` + +**Signature** + +```ts +export declare const contramap: { + (f: (b: B) => A): (self: Predicate) => Predicate + (self: Predicate, f: (b: B) => A): Predicate +} +``` + +**Example** + +```ts +import * as P from '@fp-ts/core/Predicate' +import * as N from '@fp-ts/core/Number' + +const minLength3 = P.contramap(N.greaterThan(2), (s: string) => s.length) + +assert.deepStrictEqual(minLength3('a'), false) +assert.deepStrictEqual(minLength3('aa'), false) +assert.deepStrictEqual(minLength3('aaa'), true) +assert.deepStrictEqual(minLength3('aaaa'), true) +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: Predicate<{}> +``` + +Added in v1.0.0 + +## andThenBind + +A variant of `bind` that sequentially ignores the scope. + +**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 }> + ( + self: Predicate, + name: Exclude, + that: Predicate + ): Predicate<{ readonly [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: Predicate) => Predicate<{ readonly [K in N]: A }> + (self: Predicate, name: N): Predicate<{ readonly [K in N]: A }> +} +``` + +Added in v1.0.0 + +# guards + +## isBigint + +Tests if a value is a `bigint`. + +**Signature** + +```ts +export declare const isBigint: (input: unknown) => input is bigint +``` + +**Example** + +```ts +import { isBigint } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isBigint(1n), true) + +assert.deepStrictEqual(isBigint(1), false) +``` + +Added in v1.0.0 + +## isBoolean + +Tests if a value is a `boolean`. + +**Signature** + +```ts +export declare const isBoolean: (input: unknown) => input is boolean +``` + +**Example** + +```ts +import { isBoolean } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isBoolean(true), true) + +assert.deepStrictEqual(isBoolean('true'), false) +``` + +Added in v1.0.0 + +## isDate + +A guard that succeeds when the input is a `Date`. + +**Signature** + +```ts +export declare const isDate: (input: unknown) => input is Date +``` + +**Example** + +```ts +import { isDate } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isDate(new Date()), true) + +assert.deepStrictEqual(isDate(null), false) +assert.deepStrictEqual(isDate({}), false) +``` + +Added in v1.0.0 + +## isError + +A guard that succeeds when the input is an `Error`. + +**Signature** + +```ts +export declare const isError: (input: unknown) => input is Error +``` + +**Example** + +```ts +import { isError } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isError(new Error()), true) + +assert.deepStrictEqual(isError(null), false) +assert.deepStrictEqual(isError({}), false) +``` + +Added in v1.0.0 + +## isFunction + +Tests if a value is a `function`. + +**Signature** + +```ts +export declare const isFunction: (input: unknown) => input is Function +``` + +**Example** + +```ts +import { isFunction } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isFunction(isFunction), true) + +assert.deepStrictEqual(isFunction('function'), false) +``` + +Added in v1.0.0 + +## isNever + +A guard that always fails. + +**Signature** + +```ts +export declare const isNever: (input: unknown) => input is never +``` + +**Example** + +```ts +import { isNever } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNever(null), false) +assert.deepStrictEqual(isNever(undefined), false) +assert.deepStrictEqual(isNever({}), false) +assert.deepStrictEqual(isNever([]), false) +``` + +Added in v1.0.0 + +## isNotNull + +Tests if a value is not `undefined`. + +**Signature** + +```ts +export declare const isNotNull: (input: A) => input is Exclude +``` + +**Example** + +```ts +import { isNotNull } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNotNull(undefined), true) +assert.deepStrictEqual(isNotNull('null'), true) + +assert.deepStrictEqual(isNotNull(null), false) +``` + +Added in v1.0.0 + +## isNotNullable + +A guard that succeeds when the input is not `null` or `undefined`. + +**Signature** + +```ts +export declare const isNotNullable: (input: A) => input is NonNullable +``` + +**Example** + +```ts +import { isNotNullable } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNotNullable({}), true) +assert.deepStrictEqual(isNotNullable([]), true) + +assert.deepStrictEqual(isNotNullable(null), false) +assert.deepStrictEqual(isNotNullable(undefined), false) +``` + +Added in v1.0.0 + +## isNotUndefined + +Tests if a value is not `undefined`. + +**Signature** + +```ts +export declare const isNotUndefined: (input: A) => input is Exclude +``` + +**Example** + +```ts +import { isNotUndefined } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNotUndefined(null), true) +assert.deepStrictEqual(isNotUndefined('undefined'), true) + +assert.deepStrictEqual(isNotUndefined(undefined), false) +``` + +Added in v1.0.0 + +## isNull + +Tests if a value is `undefined`. + +**Signature** + +```ts +export declare const isNull: (input: unknown) => input is null +``` + +**Example** + +```ts +import { isNull } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNull(null), true) + +assert.deepStrictEqual(isNull(undefined), false) +assert.deepStrictEqual(isNull('null'), false) +``` + +Added in v1.0.0 + +## isNullable + +A guard that succeeds when the input is `null` or `undefined`. + +**Signature** + +```ts +export declare const isNullable: (input: A) => input is Extract +``` + +**Example** + +```ts +import { isNullable } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNullable(null), true) +assert.deepStrictEqual(isNullable(undefined), true) + +assert.deepStrictEqual(isNullable({}), false) +assert.deepStrictEqual(isNullable([]), false) +``` + +Added in v1.0.0 + +## isNumber + +Tests if a value is a `number`. + +**Signature** + +```ts +export declare const isNumber: (input: unknown) => input is number +``` + +**Example** + +```ts +import { isNumber } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isNumber(2), true) + +assert.deepStrictEqual(isNumber('2'), false) +``` + +Added in v1.0.0 + +## isObject + +Tests if a value is an `object`. + +**Signature** + +```ts +export declare const isObject: (input: unknown) => input is object +``` + +**Example** + +```ts +import { isObject } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isObject({}), true) +assert.deepStrictEqual(isObject([]), true) + +assert.deepStrictEqual(isObject(null), false) +assert.deepStrictEqual(isObject(undefined), false) +``` + +Added in v1.0.0 + +## isReadonlyRecord + +A guard that succeeds when the input is a readonly record. + +**Signature** + +```ts +export declare const isReadonlyRecord: (input: unknown) => input is {} +``` + +**Example** + +```ts +import { isReadonlyRecord } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isReadonlyRecord({}), true) +assert.deepStrictEqual(isReadonlyRecord({ a: 1 }), true) + +assert.deepStrictEqual(isReadonlyRecord([]), false) +assert.deepStrictEqual(isReadonlyRecord([1, 2, 3]), false) +assert.deepStrictEqual(isReadonlyRecord(null), false) +assert.deepStrictEqual(isReadonlyRecord(undefined), false) +``` + +Added in v1.0.0 + +## isRecord + +A guard that succeeds when the input is a record. + +**Signature** + +```ts +export declare const isRecord: (input: unknown) => input is {} +``` + +**Example** + +```ts +import { isRecord } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isRecord({}), true) +assert.deepStrictEqual(isRecord({ a: 1 }), true) + +assert.deepStrictEqual(isRecord([]), false) +assert.deepStrictEqual(isRecord([1, 2, 3]), false) +assert.deepStrictEqual(isRecord(null), false) +assert.deepStrictEqual(isRecord(undefined), false) +``` + +Added in v1.0.0 + +## isString + +Tests if a value is a `string`. + +**Signature** + +```ts +export declare const isString: (input: unknown) => input is string +``` + +**Example** + +```ts +import { isString } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isString('a'), true) + +assert.deepStrictEqual(isString(1), false) +``` + +Added in v1.0.0 + +## isSymbol + +Tests if a value is a `symbol`. + +**Signature** + +```ts +export declare const isSymbol: (input: unknown) => input is symbol +``` + +**Example** + +```ts +import { isSymbol } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isSymbol(Symbol.for('a')), true) + +assert.deepStrictEqual(isSymbol('a'), false) +``` + +Added in v1.0.0 + +## isUndefined + +Tests if a value is `undefined`. + +**Signature** + +```ts +export declare const isUndefined: (input: unknown) => input is undefined +``` + +**Example** + +```ts +import { isUndefined } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isUndefined(undefined), true) + +assert.deepStrictEqual(isUndefined(null), false) +assert.deepStrictEqual(isUndefined('undefined'), false) +``` + +Added in v1.0.0 + +## isUnknown + +A guard that always succeeds. + +**Signature** + +```ts +export declare const isUnknown: (input: unknown) => input is unknown +``` + +**Example** + +```ts +import { isUnknown } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isUnknown(null), true) +assert.deepStrictEqual(isUnknown(undefined), true) + +assert.deepStrictEqual(isUnknown({}), true) +assert.deepStrictEqual(isUnknown([]), true) +``` + +Added in v1.0.0 + +# instances + +## Contravariant + +**Signature** + +```ts +export declare const Contravariant: contravariant.Contravariant +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## getMonoidAll + +**Signature** + +```ts +export declare const getMonoidAll: () => monoid.Monoid> +``` + +Added in v1.0.0 + +## getMonoidAny + +**Signature** + +```ts +export declare const getMonoidAny: () => monoid.Monoid> +``` + +Added in v1.0.0 + +## getSemigroupAll + +**Signature** + +```ts +export declare const getSemigroupAll: () => semigroup.Semigroup> +``` + +Added in v1.0.0 + +## getSemigroupAny + +**Signature** + +```ts +export declare const getSemigroupAny: () => semigroup.Semigroup> +``` + +Added in v1.0.0 + +# models + +## Predicate (interface) + +**Signature** + +```ts +export interface Predicate { + (a: A): boolean +} +``` + +Added in v1.0.0 + +## Refinement (interface) + +**Signature** + +```ts +export interface Refinement { + (a: A): a is B +} +``` + +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 + +## all + +**Signature** + +```ts +export declare const all: (collection: Iterable>) => Predicate +``` + +Added in v1.0.0 + +## any + +**Signature** + +```ts +export declare const any: (collection: Iterable>) => Predicate +``` + +Added in v1.0.0 + +## appendElement + +This function appends a predicate to a tuple-like predicate, allowing you to create a new predicate that includes +the original elements and the new one. + +**Signature** + +```ts +export declare const appendElement: { + (self: Predicate, that: Predicate): Predicate + (that: Predicate): (self: Predicate) => Predicate +} +``` + +Added in v1.0.0 + +## compose + +**Signature** + +```ts +export declare const compose: { + (bc: Refinement): (ab: Refinement) => Refinement + (ab: Refinement, bc: Refinement): Refinement +} +``` + +Added in v1.0.0 + +## of + +**Signature** + +```ts +export declare const of: (_: A) => 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 + +Similar to `Promise.all` but operates on `Predicate`s. + +``` +[Predicate, Predicate, ...] -> Predicate<[A, B, ...]> +``` + +**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..62f628ba9 --- /dev/null +++ b/docs/modules/ReadonlyArray.ts.md @@ -0,0 +1,2562 @@ +--- +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

+ +- [combining](#combining) + - [flatMap](#flatmap) + - [flatMapNonEmpty](#flatmapnonempty) + - [flatMapNullable](#flatmapnullable) + - [flatten](#flatten) + - [flattenNonEmpty](#flattennonempty) +- [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) + - [partition](#partition) + - [partitionMap](#partitionmap) + - [separate](#separate) + - [span](#span) + - [traverseFilterMap](#traversefiltermap) + - [traversePartitionMap](#traversepartitionmap) +- [folding](#folding) + - [combineMap](#combinemap) + - [combineMapNonEmpty](#combinemapnonempty) + - [coproductMapKind](#coproductmapkind) + - [reduce](#reduce) + - [reduceKind](#reducekind) + - [reduceRight](#reduceright) + - [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) +- [guards](#guards) + - [isEmpty](#isempty) + - [isEmptyArray](#isemptyarray) + - [isNonEmpty](#isnonempty) + - [isNonEmptyArray](#isnonemptyarray) +- [instances](#instances) + - [Applicative](#applicative) + - [Chainable](#chainable) + - [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) + - [getOrder](#getorder) + - [lift2](#lift2) + - [liftEither](#lifteither) + - [liftMonoid](#liftmonoid) + - [liftNullable](#liftnullable) + - [liftOption](#liftoption) + - [liftPredicate](#liftpredicate) +- [mapping](#mapping) + - [as](#as) + - [flap](#flap) + - [map](#map) + - [mapNonEmpty](#mapnonempty) + - [tupled](#tupled) +- [models](#models) + - [NonEmptyArray (type alias)](#nonemptyarray-type-alias) + - [NonEmptyReadonlyArray (type alias)](#nonemptyreadonlyarray-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) + - [matchLeft](#matchleft) + - [matchRight](#matchright) +- [predicates](#predicates) + - [contains](#contains) + - [every](#every) + - [some](#some) +- [sorting](#sorting) + - [sort](#sort) + - [sortBy](#sortby) + - [sortByNonEmpty](#sortbynonempty) + - [sortNonEmpty](#sortnonempty) +- [traversing](#traversing) + - [sequence](#sequence) + - [sequenceNonEmpty](#sequencenonempty) + - [traverse](#traverse) + - [traverseNonEmpty](#traversenonempty) + - [traverseTap](#traversetap) +- [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) + +--- + +# combining + +## flatMap + +**Signature** + +```ts +export declare const flatMap: { + (f: (a: A, i: number) => readonly B[]): (self: readonly A[]) => B[] + (self: readonly A[], f: (a: A, i: number) => readonly B[]): B[] +} +``` + +Added in v1.0.0 + +## flatMapNonEmpty + +**Signature** + +```ts +export declare const flatMapNonEmpty: { + (f: (a: A, i: number) => readonly [B, ...B[]]): (self: readonly [A, ...A[]]) => [B, ...B[]] + (self: readonly [A, ...A[]], f: (a: A, i: number) => readonly [B, ...B[]]): [B, ...B[]] +} +``` + +Added in v1.0.0 + +## flatMapNullable + +**Signature** + +```ts +export declare const flatMapNullable: { + (f: (a: A) => B | null | undefined): (self: readonly A[]) => NonNullable[] + (self: readonly A[], f: (a: A) => B | null | undefined): NonNullable[] +} +``` + +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 + +# 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: (n: number, f: (i: number) => A) => [A, ...A[]] +``` + +**Example** + +```ts +import { makeBy } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual( + makeBy(5, (n) => n * 2), + [0, 2, 4, 6, 8] +) +``` + +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[]] +``` + +**Example** + +```ts +import { range } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(range(1, 3), [1, 2, 3]) +``` + +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: { (n: number): (a: A) => [A, ...A[]]; (a: A, n: number): [A, ...A[]] } +``` + +**Example** + +```ts +import { replicate } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(replicate('a', 3), ['a', 'a', 'a']) +``` + +Added in v1.0.0 + +## unfold + +**Signature** + +```ts +export declare const unfold: (b: B, f: (b: B) => Option) => A[] +``` + +Added in v1.0.0 + +# conversions + +## fromEither + +**Signature** + +```ts +export declare const fromEither: (self: Either) => 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: Option) => 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 }[] + (self: readonly A[], name: Exclude, that: readonly B[]): { + [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 }[] + (self: readonly A[], name: Exclude, f: (a: A) => readonly B[]): { + [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 }[] + (self: readonly A[], name: N): { [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 }[] + (self: readonly A[], name: Exclude, f: (a: A) => B): { + [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: (a: A, i: number) => a is B): (self: Iterable) => B[] + (predicate: (a: A, i: number) => boolean): (self: Iterable) => B[] + (self: Iterable, refinement: (a: A, i: number) => a is B): B[] + (self: Iterable, predicate: (a: A, i: number) => boolean): B[] +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: { + (f: (a: A, i: number) => Option): (self: Iterable) => B[] + (self: Iterable, f: (a: A, i: number) => Option): B[] +} +``` + +Added in v1.0.0 + +## partition + +**Signature** + +```ts +export declare const partition: { + (refinement: (a: A, i: number) => a is B): (self: Iterable) => [C[], B[]] + (predicate: (a: A, i: number) => boolean): (self: Iterable) => [B[], B[]] + (self: Iterable, refinement: (a: A, i: number) => a is B): [C[], B[]] + (self: Iterable, predicate: (a: A, i: number) => boolean): [B[], B[]] +} +``` + +Added in v1.0.0 + +## partitionMap + +**Signature** + +```ts +export declare const partitionMap: { + (f: (a: A, i: number) => Either): (self: Iterable) => [B[], C[]] + (self: Iterable, f: (a: A, i: number) => Either): [B[], C[]] +} +``` + +Added in v1.0.0 + +## separate + +**Signature** + +```ts +export declare const separate: (self: Iterable>) => [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 const span: { + (refinement: Refinement): (self: Iterable) => [init: B[], rest: A[]] + (predicate: Predicate): (self: Iterable) => [init: B[], rest: B[]] + (self: Iterable, refinement: Refinement): [init: B[], rest: A[]] + (self: Iterable, predicate: Predicate): [init: B[], rest: B[]] +} +``` + +Added in v1.0.0 + +## traverseFilterMap + +**Signature** + +```ts +export declare const traverseFilterMap: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind>): (self: readonly A[]) => Kind + (self: readonly A[], f: (a: A) => Kind>): Kind +} +``` + +Added in v1.0.0 + +## traversePartitionMap + +**Signature** + +```ts +export declare const traversePartitionMap: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind>): (self: readonly A[]) => Kind + (self: readonly A[], f: (a: A) => Kind>): Kind +} +``` + +Added in v1.0.0 + +# folding + +## combineMap + +**Signature** + +```ts +export declare const combineMap: (Monoid: Monoid) => { + (f: (a: A, i: number) => M): (self: Iterable) => M + (self: Iterable, f: (a: A, i: number) => M): M +} +``` + +Added in v1.0.0 + +## combineMapNonEmpty + +**Signature** + +```ts +export declare const combineMapNonEmpty: (S: Semigroup) => { + (f: (a: A, i: number) => S): (self: readonly [A, ...A[]]) => S + (self: readonly [A, ...A[]], f: (a: A, i: number) => S): S +} +``` + +Added in v1.0.0 + +## coproductMapKind + +**Signature** + +```ts +export declare const coproductMapKind: ( + G: Coproduct +) => { + (f: (a: A) => Kind): (self: readonly A[]) => Kind + (self: readonly A[], f: (a: A) => Kind): Kind +} +``` + +Added in v1.0.0 + +## reduce + +**Signature** + +```ts +export declare const reduce: { + (b: B, f: (b: B, a: A, i: number) => B): (self: Iterable) => B + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B +} +``` + +Added in v1.0.0 + +## reduceKind + +**Signature** + +```ts +export declare const reduceKind: ( + G: monad.Monad +) => { + (b: B, f: (b: B, a: A) => Kind): (self: readonly A[]) => Kind + (self: readonly A[], b: B, f: (b: B, a: A) => Kind): Kind +} +``` + +Added in v1.0.0 + +## reduceRight + +**Signature** + +```ts +export declare const reduceRight: { + (b: B, f: (b: B, a: A, i: number) => B): (self: Iterable) => B + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B +} +``` + +Added in v1.0.0 + +## scan + +Reduce 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[]] + (self: Iterable, b: B, f: (b: B, a: A) => B): [B, ...B[]] +} +``` + +Added in v1.0.0 + +## scanRight + +Reduce 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[]] + (self: Iterable, b: B, f: (b: B, a: A) => B): [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[]][] + (self: Iterable, n: number): [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[]][]] + (self: readonly [A, ...A[]], n: number): [[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[]; (self: Iterable, n: number): 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[]; (self: Iterable, n: number): 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 const dropWhile: { + (refinement: Refinement): (self: Iterable) => B[] + (predicate: Predicate): (self: Iterable) => B[] + (self: Iterable, refinement: Refinement): B[] + (self: Iterable, predicate: Predicate): B[] +} +``` + +Added in v1.0.0 + +## findFirst + +Find the first element for which a predicate holds. + +**Signature** + +```ts +export declare const findFirst: { + (refinement: Refinement): (self: Iterable) => Option + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, refinement: Refinement): Option + (self: Iterable, predicate: Predicate): Option +} +``` + +Added in v1.0.0 + +## findFirstIndex + +Return the first index for which a predicate holds. + +**Signature** + +```ts +export declare const findFirstIndex: { + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, predicate: Predicate): Option +} +``` + +Added in v1.0.0 + +## findLast + +Find the last element for which a predicate holds. + +**Signature** + +```ts +export declare const findLast: { + (refinement: Refinement): (self: Iterable) => Option + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, refinement: Refinement): Option + (self: Iterable, predicate: Predicate): Option +} +``` + +Added in v1.0.0 + +## findLastIndex + +Return the last index for which a predicate holds. + +**Signature** + +```ts +export declare const findLastIndex: { + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, predicate: Predicate): Option +} +``` + +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[]) => Option + (self: readonly A[], index: number): Option +} +``` + +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[]) => Option +``` + +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) => Option +``` + +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[]) => Option +``` + +Added in v1.0.0 + +## lastNonEmpty + +**Signature** + +```ts +export declare const lastNonEmpty: (self: 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[]] + (self: Iterable, n: number): [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[]] + (self: readonly [A, ...A[]], n: number): [[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) => Option +``` + +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[]; (self: Iterable, n: number): 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[]; (self: Iterable, n: number): 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 const takeWhile: { + (refinement: Refinement): (self: Iterable) => B[] + (predicate: Predicate): (self: Iterable) => B[] + (self: Iterable, refinement: Refinement): B[] + (self: Iterable, predicate: Predicate): B[] +} +``` + +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: ( + isEquivalent: (self: A, that: A) => boolean +) => (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 + (self: Iterable, f: (a: A) => string): Record +} +``` + +Added in v1.0.0 + +# guards + +## isEmpty + +Determine if a `ReadonlyArray` is empty narrowing down the type to `readonly []`. + +**Signature** + +```ts +export declare const isEmpty: (self: readonly A[]) => self is readonly [] +``` + +**Example** + +```ts +import { isEmpty } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(isEmpty([]), true) +assert.deepStrictEqual(isEmpty([1, 2, 3]), false) +``` + +Added in v1.0.0 + +## isEmptyArray + +Determine if an `Array` is empty narrowing down the type to `[]`. + +**Signature** + +```ts +export declare const isEmptyArray: (self: A[]) => self is [] +``` + +**Example** + +```ts +import { isEmptyArray } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(isEmptyArray([]), true) +assert.deepStrictEqual(isEmptyArray([1, 2, 3]), false) +``` + +Added in v1.0.0 + +## isNonEmpty + +Determine if a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`. + +A `ReadonlyArray` is considered to be a `NonEmptyReadonlyArray` if it contains at least one element. + +**Signature** + +```ts +export declare const isNonEmpty: (self: readonly A[]) => self is readonly [A, ...A[]] +``` + +**Example** + +```ts +import { isNonEmpty } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(isNonEmpty([]), false) +assert.deepStrictEqual(isNonEmpty([1, 2, 3]), true) +``` + +Added in v1.0.0 + +## isNonEmptyArray + +Determine if an `Array` is non empty narrowing down the type to `NonEmptyArray`. + +An `Array` is considered to be a `NonEmptyArray` if it contains at least one element. + +**Signature** + +```ts +export declare const isNonEmptyArray: (self: A[]) => self is [A, ...A[]] +``` + +**Example** + +```ts +import { isNonEmptyArray } from '@fp-ts/core/ReadonlyArray' + +assert.deepStrictEqual(isNonEmptyArray([]), false) +assert.deepStrictEqual(isNonEmptyArray([1, 2, 3]), true) +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: applicative.Applicative +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: chainable.Chainable +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## Filterable + +**Signature** + +```ts +export declare const Filterable: filterable.Filterable +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: flatMap_.FlatMap +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: foldable.Foldable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: monad.Monad +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: pointed.Pointed +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: semiApplicative.SemiApplicative +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +## TraversableFilterable + +**Signature** + +```ts +export declare const TraversableFilterable: traversableFilterable.TraversableFilterable +``` + +Added in v1.0.0 + +## getIntersectionSemigroup + +**Signature** + +```ts +export declare const getIntersectionSemigroup: ( + isEquivalent: (self: A, that: A) => boolean +) => Semigroup +``` + +Added in v1.0.0 + +## getMonoid + +Returns a `Monoid` for `ReadonlyArray`. + +**Signature** + +```ts +export declare const getMonoid: () => Monoid +``` + +Added in v1.0.0 + +## getSemigroup + +Returns a `Semigroup` for `ReadonlyArray`. + +**Signature** + +```ts +export declare const getSemigroup: () => Semigroup +``` + +Added in v1.0.0 + +## getUnionMonoid + +**Signature** + +```ts +export declare const getUnionMonoid: (isEquivalent: (self: A, that: A) => boolean) => Monoid +``` + +Added in v1.0.0 + +## getUnionSemigroup + +**Signature** + +```ts +export declare const getUnionSemigroup: (isEquivalent: (self: A, that: A) => boolean) => Semigroup +``` + +Added in v1.0.0 + +# lifting + +## getOrder + +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 getOrder: (O: order.Order) => order.Order +``` + +Added in v1.0.0 + +## lift2 + +Lifts a binary function into `ReadonlyArray`. + +**Signature** + +```ts +export declare const lift2: ( + f: (a: A, b: B) => C +) => { (self: readonly A[], that: readonly B[]): C[]; (that: readonly B[]): (self: readonly A[]) => C[] } +``` + +Added in v1.0.0 + +## liftEither + +**Signature** + +```ts +export declare const liftEither: (f: (...a: A) => Either) => (...a: A) => B[] +``` + +Added in v1.0.0 + +## liftMonoid + +**Signature** + +```ts +export declare const liftMonoid: (M: Monoid) => Monoid +``` + +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) => Option) => (...a: A) => B[] +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: Refinement): (c: C) => B[] + (predicate: Predicate): (b: B) => B[] +} +``` + +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>(self: readonly _[], b: B): B[]; (b: B): <_>(self: readonly _[]) => B[] } +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: { + (a: A, self: readonly ((a: A) => B)[]): B[] + (self: readonly ((a: A) => B)[]): (a: A) => B[] +} +``` + +Added in v1.0.0 + +## map + +**Signature** + +```ts +export declare const map: { + (f: (a: A, i: number) => B): (self: readonly A[]) => B[] + (self: readonly A[], f: (a: A, i: number) => B): B[] +} +``` + +Added in v1.0.0 + +## mapNonEmpty + +**Signature** + +```ts +export declare const mapNonEmpty: { + (f: (a: A, i: number) => B): (self: readonly [A, ...A[]]) => [B, ...B[]] + (self: readonly [A, ...A[]], f: (a: A, i: number) => B): [B, ...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: LazyArg, onNonEmpty: (self: readonly [A, ...A[]]) => C): (self: readonly A[]) => B | C + (self: readonly A[], onEmpty: LazyArg, onNonEmpty: (self: readonly [A, ...A[]]) => C): B | C +} +``` + +Added in v1.0.0 + +## matchLeft + +**Signature** + +```ts +export declare const matchLeft: { + (onEmpty: LazyArg, onNonEmpty: (head: A, tail: A[]) => C): (self: readonly A[]) => B | C + (self: readonly A[], onEmpty: LazyArg, onNonEmpty: (head: A, tail: A[]) => C): B | C +} +``` + +Added in v1.0.0 + +## matchRight + +**Signature** + +```ts +export declare const matchRight: { + (onEmpty: LazyArg, onNonEmpty: (init: A[], last: A) => C): (self: readonly A[]) => B | C + (self: readonly A[], onEmpty: LazyArg, onNonEmpty: (init: A[], last: A) => C): 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: (isEquivalent: (self: A, that: A) => boolean) => { + (a: A): (self: Iterable) => boolean + (self: Iterable, a: A): boolean +} +``` + +Added in v1.0.0 + +## 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 + +## some + +Check if a predicate holds true for some `ReadonlyArray` member. + +**Signature** + +```ts +export declare const some: (predicate: Predicate) => (self: readonly A[]) => self is readonly [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: order.Order) => (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 order.Order[]) => (self: Iterable) => A[] +``` + +Added in v1.0.0 + +## sortByNonEmpty + +**Signature** + +```ts +export declare const sortByNonEmpty: ( + ...orders: readonly order.Order[] +) => (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: order.Order) => (self: readonly [A, ...A[]]) => [A, ...A[]] +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: ( + F: applicative.Applicative +) => (self: readonly Kind[]) => Kind +``` + +Added in v1.0.0 + +## sequenceNonEmpty + +**Signature** + +```ts +export declare const sequenceNonEmpty: ( + F: semiApplicative.SemiApplicative +) => (self: readonly [Kind, ...Kind[]]) => Kind +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: ( + F: applicative.Applicative +) => { + (f: (a: A, i: number) => Kind): (self: Iterable) => Kind + (self: Iterable, f: (a: A, i: number) => Kind): Kind +} +``` + +Added in v1.0.0 + +## traverseNonEmpty + +**Signature** + +```ts +export declare const traverseNonEmpty: ( + F: semiApplicative.SemiApplicative +) => { + (f: (a: A, i: number) => Kind): ( + self: readonly [A, ...A[]] + ) => Kind + (self: readonly [A, ...A[]], f: (a: A, i: number) => Kind): Kind< + F, + R, + O, + E, + [B, ...B[]] + > +} +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: applicative.Applicative +) => { + (self: readonly A[], f: (a: A) => Kind): Kind + (f: (a: A) => Kind): (self: readonly A[]) => Kind +} +``` + +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 + (self: readonly A[], index: number): A +} +``` + +Added in v1.0.0 + +# utils + +## ap + +**Signature** + +```ts +export declare const ap: { + (self: readonly ((a: A) => B)[], that: readonly A[]): B[] + (that: 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)[]] + (self: Iterable, last: B): [A | B, ...(A | B)[]] +} +``` + +Added in v1.0.0 + +## appendAll + +**Signature** + +```ts +export declare const appendAll: { + (that: Iterable): (self: Iterable) => (B | A)[] + (self: Iterable, that: Iterable): (A | B)[] +} +``` + +Added in v1.0.0 + +## appendAllNonEmpty + +**Signature** + +```ts +export declare const appendAllNonEmpty: { + (that: readonly [B, ...B[]]): (self: Iterable) => [B | A, ...(B | A)[]] + (that: Iterable): (self: readonly [A, ...A[]]) => [B | A, ...(B | A)[]] + (self: Iterable, that: readonly [B, ...B[]]): [A | B, ...(A | B)[]] + (self: readonly [A, ...A[]], that: Iterable): [A | B, ...(A | B)[]] +} +``` + +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[] + (self: Iterable, f: (as: readonly [A, ...A[]]) => readonly [B, readonly A[]]): 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[]] + (self: readonly [A, ...A[]], f: (as: readonly [A, ...A[]]) => readonly [B, readonly A[]]): [B, ...B[]] +} +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: { + (afb: (a: A) => readonly B[], bfc: (b: B) => readonly C[]): (a: A) => readonly C[] + (bfc: (b: B) => readonly C[]): (afb: (a: A) => readonly B[]) => (a: A) => readonly C[] +} +``` + +Added in v1.0.0 + +## copy + +**Signature** + +```ts +export declare const copy: { (self: readonly [A, ...A[]]): [A, ...A[]]; (self: readonly A[]): A[] } +``` + +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: (isEquivalent: (self: A, that: A) => boolean) => { + (that: Iterable): (self: Iterable) => A[] + (self: Iterable, that: Iterable): A[] +} +``` + +Added in v1.0.0 + +## extend + +**Signature** + +```ts +export declare const extend: { + (f: (as: readonly A[]) => B): (self: readonly A[]) => B[] + (self: readonly A[], f: (as: readonly A[]) => B): 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) => Option<[B | A, ...(B | A)[]]> + (self: Iterable, i: number, b: B): Option<[A | B, ...(A | B)[]]> +} +``` + +Added in v1.0.0 + +## intercalate + +Fold an `Iterable`, accumulating values in some `Monoid`, combining adjacent elements +using the specified separator. + +**Signature** + +```ts +export declare const intercalate: (M: Monoid) => { + (middle: A): (self: Iterable) => A + (self: Iterable, middle: 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: Semigroup) => { + (middle: A): (self: readonly [A, ...A[]]) => A + (self: readonly [A, ...A[]], middle: 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: (isEquivalent: (self: A, that: A) => boolean) => { + (that: Iterable): (self: Iterable) => A[] + (self: Iterable, that: 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)[] + (self: Iterable, middle: B): (A | B)[] +} +``` + +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)[]] + (self: readonly [A, ...A[]], middle: B): [A | B, ...(A | B)[]] +} +``` + +Added in v1.0.0 + +## join + +**Signature** + +```ts +export declare const join: { + (middle: string): (self: ReadonlyArray) => string + (self: ReadonlyArray, middle: string): string +} +``` + +Added in v1.0.0 + +## max + +**Signature** + +```ts +export declare const max: (O: order.Order) => (self: readonly [A, ...A[]]) => A +``` + +Added in v1.0.0 + +## min + +**Signature** + +```ts +export declare const min: (O: order.Order) => (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)[] + (self: Iterable, i: number, f: (a: A) => B): (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)[]] + (self: readonly [A, ...A[]], f: (a: A) => B): [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)[]] + (self: readonly [A, ...A[]], f: (a: A) => B): [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) => Option<(A | B)[]> + (self: Iterable, i: number, f: (a: A) => B): Option<(A | B)[]> +} +``` + +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)[]] + (self: Iterable, head: B): [A | B, ...(A | B)[]] +} +``` + +Added in v1.0.0 + +## prependAll + +**Signature** + +```ts +export declare const prependAll: { + (that: Iterable): (self: Iterable) => (B | A)[] + (self: Iterable, that: Iterable): (A | B)[] +} +``` + +Added in v1.0.0 + +## prependAllNonEmpty + +**Signature** + +```ts +export declare const prependAllNonEmpty: { + (that: readonly [B, ...B[]]): (self: Iterable) => [B | A, ...(B | A)[]] + (that: Iterable): (self: readonly [A, ...A[]]) => [B | A, ...(B | A)[]] + (self: Iterable, that: readonly [B, ...B[]]): [A | B, ...(A | B)[]] + (self: readonly [A, ...A[]], that: Iterable): [A | B, ...(A | B)[]] +} +``` + +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[]; (self: Iterable, i: number): 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)[] + (self: Iterable, i: number, b: B): (A | B)[] +} +``` + +Added in v1.0.0 + +## replaceOption + +**Signature** + +```ts +export declare const replaceOption: { + (i: number, b: B): (self: Iterable) => Option<(B | A)[]> + (self: Iterable, i: number, b: B): Option<(A | B)[]> +} +``` + +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[]; (self: Iterable, n: number): 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[]] + (self: readonly [A, ...A[]], n: number): [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)[]] + (self: readonly [A, ...A[]], b: B): [A | B, ...(A | B)[]] +} +``` + +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)[]] + (self: readonly [A, ...A[]], b: B): [A | B, ...(A | B)[]] +} +``` + +Added in v1.0.0 + +## traverseFilter + +Filter values inside a context. + +**Signature** + +```ts +export declare const traverseFilter: ( + F: applicative.Applicative +) => { + (predicate: (a: A) => Kind): ( + self: readonly B[] + ) => Kind + (self: readonly B[], predicate: (a: A) => Kind): Kind< + F, + R, + O, + E, + B[] + > +} +``` + +Added in v1.0.0 + +## traversePartition + +**Signature** + +```ts +export declare const traversePartition: ( + F: applicative.Applicative +) => { + (predicate: (a: A) => Kind): ( + self: readonly B[] + ) => Kind + (self: readonly B[], predicate: (a: A) => Kind): Kind< + F, + R, + O, + E, + [B[], B[]] + > +} +``` + +Added in v1.0.0 + +## union + +**Signature** + +```ts +export declare const union: (isEquivalent: (self: A, that: A) => boolean) => { + (that: readonly A[]): (self: readonly A[]) => A[] + (self: readonly A[], that: readonly A[]): A[] +} +``` + +Added in v1.0.0 + +## unionNonEmpty + +**Signature** + +```ts +export declare const unionNonEmpty: (isEquivalent: (self: A, that: A) => boolean) => { + (that: readonly [A, ...A[]]): (self: readonly A[]) => [A, ...A[]] + (that: readonly A[]): (self: readonly [A, ...A[]]) => [A, ...A[]] + (self: readonly A[], that: readonly [A, ...A[]]): [A, ...A[]] + (self: readonly [A, ...A[]], that: readonly 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: (isEquivalent: (self: A, that: A) => boolean) => (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: ( + isEquivalent: (self: A, that: A) => boolean +) => (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][] + (self: Iterable, that: 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][]] + (self: readonly [A, ...A[]], that: readonly [B, ...B[]]): [[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[]] + (self: readonly [A, ...A[]], that: readonly [B, ...B[]], f: (a: A, b: B) => C): [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[] + (self: Iterable, that: Iterable, f: (a: A, b: B) => C): 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..f929ba54d --- /dev/null +++ b/docs/modules/ReadonlyRecord.ts.md @@ -0,0 +1,863 @@ +--- +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

+ +- [constructors](#constructors) + - [empty](#empty) + - [fromIterable](#fromiterable) +- [conversions](#conversions) + - [collect](#collect) + - [toArray](#toarray) +- [filtering](#filtering) + - [compact](#compact) + - [filter](#filter) + - [partition](#partition) + - [partitionMap](#partitionmap) + - [separate](#separate) + - [traverseFilterMap](#traversefiltermap) + - [traversePartitionMap](#traversepartitionmap) +- [guards](#guards) + - [isEmpty](#isempty) +- [instances](#instances) + - [Covariant](#covariant) + - [Filterable](#filterable) + - [Invariant](#invariant) + - [Traversable](#traversable) + - [TraversableFilterable](#traversablefilterable) +- [mapping](#mapping) + - [as](#as) + - [flap](#flap) + - [tupled](#tupled) +- [models](#models) + - [ReadonlyRecord (interface)](#readonlyrecord-interface) +- [record](#record) + - [pop](#pop) +- [traversing](#traversing) + - [sequence](#sequence) + - [traverse](#traverse) + - [traverseTap](#traversetap) +- [type lambdas](#type-lambdas) + - [ReadonlyRecordTypeLambda (interface)](#readonlyrecordtypelambda-interface) +- [utils](#utils) + - [filterMap](#filtermap) + - [get](#get) + - [has](#has) + - [map](#map) + - [modifyOption](#modifyoption) + - [remove](#remove) + - [replaceOption](#replaceoption) + - [size](#size) + - [traverseFilter](#traversefilter) + - [traversePartition](#traversepartition) + +--- + +# constructors + +## empty + +Creates a new, empty record. + +**Signature** + +```ts +export declare const empty:
() => Record +``` + +Added in v1.0.0 + +## fromIterable + +Takes an iterable and a projection function and returns a record. +The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record. + +**Signature** + +```ts +export declare const fromIterable: { + (f: (a: A) => readonly [string, B]): (self: Iterable) => Record + (self: Iterable, f: (a: A) => readonly [string, B]): Record +} +``` + +**Example** + +```ts +import { fromIterable } from '@fp-ts/core/ReadonlyRecord' + +const input = [1, 2, 3, 4] + +assert.deepStrictEqual( + fromIterable(input, (a) => [String(a), a * 2]), + { '1': 2, '2': 4, '3': 6, '4': 8 } +) +``` + +Added in v1.0.0 + +# conversions + +## collect + +Transforms the values of a `ReadonlyRecord` into an `Array` with a custom mapping function. + +**Signature** + +```ts +export declare const collect: { + (f: (key: string, a: A) => B): (self: ReadonlyRecord) => B[] + (self: ReadonlyRecord, f: (key: string, a: A) => B): B[] +} +``` + +**Example** + +```ts +import { collect } from '@fp-ts/core/ReadonlyRecord' + +const x = { a: 1, b: 2, c: 3 } +assert.deepStrictEqual( + collect(x, (key, n) => [key, n]), + [ + ['a', 1], + ['b', 2], + ['c', 3], + ] +) +``` + +Added in v1.0.0 + +## toArray + +Converts a `ReadonlyRecord` to an `Array` of key-value pairs. + +**Signature** + +```ts +export declare const toArray: (self: ReadonlyRecord) => [string, A][] +``` + +**Example** + +```ts +import { toArray } from '@fp-ts/core/ReadonlyRecord' + +const x = { a: 1, b: 2 } +assert.deepStrictEqual(toArray(x), [ + ['a', 1], + ['b', 2], +]) +``` + +Added in v1.0.0 + +# filtering + +## compact + +Given a `ReadonlyRecord` with `Option` values, returns a `Record` with only the `Some` values, with the same keys. + +**Signature** + +```ts +export declare const compact: (self: ReadonlyRecord>) => Record +``` + +**Example** + +```ts +import { compact } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +assert.deepStrictEqual(compact({ a: some(1), b: none(), c: some(2) }), { a: 1, c: 2 }) +``` + +Added in v1.0.0 + +## filter + +Selects properties from a record whose values match the given predicate. + +**Signature** + +```ts +export declare const filter: { + (refinement: (a: A, key: string) => a is B): ( + self: ReadonlyRecord + ) => Record + (predicate: (a: A, key: string) => boolean): (self: ReadonlyRecord) => Record + (self: ReadonlyRecord, refinement: (a: A, key: string) => a is B): Record< + string, + B + > + (self: ReadonlyRecord, predicate: (a: A, key: string) => boolean): Record +} +``` + +**Example** + +```ts +import { filter } from '@fp-ts/core/ReadonlyRecord' + +const x = { a: 1, b: 2, c: 3, d: 4 } +assert.deepStrictEqual( + filter(x, (n) => n > 2), + { c: 3, d: 4 } +) +``` + +Added in v1.0.0 + +## partition + +Partitions a `ReadonlyRecord` into two separate `Record`s based on the result of a predicate function. + +**Signature** + +```ts +export declare const partition: { + (refinement: (a: A, key: string) => a is B): ( + self: ReadonlyRecord + ) => [Record, Record] + (predicate: (a: A, key: string) => boolean): ( + self: ReadonlyRecord + ) => [Record, Record] + (self: ReadonlyRecord, refinement: (a: A, key: string) => a is B): [ + Record, + Record + ] + (self: ReadonlyRecord, predicate: (a: A, key: string) => boolean): [ + Record, + Record + ] +} +``` + +**Example** + +```ts +import { partition } from '@fp-ts/core/ReadonlyRecord' + +assert.deepStrictEqual( + partition({ a: 1, b: 3 }, (n) => n > 2), + [{ a: 1 }, { b: 3 }] +) +``` + +Added in v1.0.0 + +## partitionMap + +Partitions the elements of a `ReadonlyRecord` into two groups: those that match a predicate, and those that don't. + +**Signature** + +```ts +export declare const partitionMap: { + (f: (a: A, key: string) => Either): (self: ReadonlyRecord) => [Record, Record] + (self: ReadonlyRecord, f: (a: A, key: string) => Either): [Record, Record] +} +``` + +**Example** + +```ts +import { partitionMap } from '@fp-ts/core/ReadonlyRecord' +import { left, right } from '@fp-ts/core/Either' + +const x = { a: 1, b: 2, c: 3 } +const f = (n: number) => (n % 2 === 0 ? right(n) : left(n)) +assert.deepStrictEqual(partitionMap(x, f), [{ a: 1, c: 3 }, { b: 2 }]) +``` + +Added in v1.0.0 + +## separate + +Partitions a `ReadonlyRecord` of `Either` values into two separate records, +one with the `Left` values and one with the `Right` values. + +**Signature** + +```ts +export declare const separate: (self: ReadonlyRecord>) => [Record, Record] +``` + +**Example** + +```ts +import { separate } from '@fp-ts/core/ReadonlyRecord' +import { left, right } from '@fp-ts/core/Either' + +assert.deepStrictEqual(separate({ a: left('e'), b: right(1) }), [{ a: 'e' }, { b: 1 }]) +``` + +Added in v1.0.0 + +## traverseFilterMap + +**Signature** + +```ts +export declare const traverseFilterMap: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind>): ( + self: ReadonlyRecord + ) => Kind> + (self: ReadonlyRecord, f: (a: A) => Kind>): Kind< + F, + R, + O, + E, + Record + > +} +``` + +Added in v1.0.0 + +## traversePartitionMap + +**Signature** + +```ts +export declare const traversePartitionMap: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind>): ( + self: ReadonlyRecord + ) => Kind, Record]> + (self: ReadonlyRecord, f: (a: A) => Kind>): Kind< + F, + R, + O, + E, + [Record, Record] + > +} +``` + +Added in v1.0.0 + +# guards + +## isEmpty + +Determine if a `ReadonlyRecord` is empty. + +**Signature** + +```ts +export declare const isEmpty: (self: ReadonlyRecord) => self is Record +``` + +**Example** + +```ts +import { isEmpty } from '@fp-ts/core/ReadonlyRecord' + +assert.deepStrictEqual(isEmpty({}), true) +assert.deepStrictEqual(isEmpty({ a: 3 }), false) +``` + +Added in v1.0.0 + +# instances + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## Filterable + +**Signature** + +```ts +export declare const Filterable: filterable.Filterable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +## TraversableFilterable + +**Signature** + +```ts +export declare const TraversableFilterable: traversableFilterable.TraversableFilterable +``` + +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: ReadonlyRecord<_>) => Record + <_, B>(self: ReadonlyRecord<_>, b: B): Record +} +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: { + (self: ReadonlyRecord<(a: A) => B>): (a: A) => Record + (a: A, self: ReadonlyRecord<(a: A) => B>): Record +} +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: ReadonlyRecord) => Record +``` + +Added in v1.0.0 + +# models + +## ReadonlyRecord (interface) + +**Signature** + +```ts +export interface ReadonlyRecord { + readonly [x: string]: A +} +``` + +Added in v1.0.0 + +# record + +## pop + +Retrieves the value of the property with the given `key` from a `ReadonlyRecord` and returns an `Option` +of a tuple with the value and the `ReadonlyRecord` with the removed property. +If the key is not present, returns `O.none`. + +**Signature** + +```ts +export declare const pop: { + (key: string): (self: ReadonlyRecord) => Option]> + (self: ReadonlyRecord, key: string): Option]> +} +``` + +**Example** + +```ts +import { pop } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +assert.deepStrictEqual(pop({ a: 1, b: 2 }, 'a'), some([1, { b: 2 }])) +assert.deepStrictEqual(pop({ a: 1, b: 2 }, 'c'), none()) +``` + +Added in v1.0.0 + +# traversing + +## sequence + +Transforms a `ReadonlyRecord` of `Kind` values into a `Kind` of `Record` values. + +**Signature** + +```ts +export declare const sequence: ( + F: applicative.Applicative +) => (self: ReadonlyRecord>) => Kind> +``` + +**Example** + +```ts +import * as RR from '@fp-ts/core/ReadonlyRecord' +import { some, none, Applicative } from '@fp-ts/core/Option' + +const sequence = RR.sequence(Applicative) + +assert.deepStrictEqual(sequence({ a: some(1), b: some(2) }), some({ a: 1, b: 2 })) +assert.deepStrictEqual(sequence({ a: none(), b: some(2) }), none()) +``` + +Added in v1.0.0 + +## traverse + +Maps each entry of a `ReadonlyRecord` to an effect and collects the results into a new record. + +**Signature** + +```ts +export declare const traverse: ( + F: applicative.Applicative +) => { + (f: (a: A, key: string) => Kind): ( + self: ReadonlyRecord + ) => Kind> + (self: ReadonlyRecord, f: (a: A, key: string) => Kind): Kind< + F, + R, + O, + E, + Record + > +} +``` + +**Example** + +```ts +import { traverse } from '@fp-ts/core/ReadonlyRecord' +import { some, none, Applicative } from '@fp-ts/core/Option' + +assert.deepStrictEqual( + traverse(Applicative)({ a: 1, b: 2 }, (n: number) => (n <= 2 ? some(n) : none())), + some({ a: 1, b: 2 }) +) +assert.deepStrictEqual( + traverse(Applicative)({ a: 1, b: 2 }, (n: number) => (n >= 2 ? some(n) : none())), + none() +) +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind): (self: ReadonlyRecord) => Kind> + (self: ReadonlyRecord, f: (a: A) => Kind): Kind> +} +``` + +Added in v1.0.0 + +# type lambdas + +## ReadonlyRecordTypeLambda (interface) + +**Signature** + +```ts +export interface ReadonlyRecordTypeLambda extends TypeLambda { + readonly type: ReadonlyRecord +} +``` + +Added in v1.0.0 + +# utils + +## filterMap + +Transforms a `ReadonlyRecord` into a `Record` by applying the function `f` to each key and value in the original `ReadonlyRecord`. +If the function returns `Some`, the key-value pair is included in the output `Record`. + +**Signature** + +```ts +export declare const filterMap: { + (f: (a: A, key: string) => Option): (self: ReadonlyRecord) => Record + (self: ReadonlyRecord, f: (a: A, key: string) => Option): Record +} +``` + +**Example** + +```ts +import { filterMap } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +const x = { a: 1, b: 2, c: 3 } +const f = (a: number, key: string) => (a > 2 ? some(a * 2) : none()) +assert.deepStrictEqual(filterMap(x, f), { c: 6 }) +``` + +Added in v1.0.0 + +## get + +Retrieve a value at a particular key from a `ReadonlyRecord`, returning it wrapped in an `Option`. + +**Signature** + +```ts +export declare const get: { + (key: string): (self: ReadonlyRecord) => Option + (self: ReadonlyRecord, key: string): Option +} +``` + +**Example** + +```ts +import { get } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +const person = { name: 'John Doe', age: 35 } + +assert.deepStrictEqual(get(person, 'name'), some('John Doe')) +assert.deepStrictEqual(get(person, 'email'), none()) +``` + +Added in v1.0.0 + +## has + +Check if a given `key` exists in a `ReadonlyRecord`. + +**Signature** + +```ts +export declare const has: { + (key: string): (self: ReadonlyRecord) => boolean + (self: ReadonlyRecord, key: string): boolean +} +``` + +**Example** + +```ts +import { has } from '@fp-ts/core/ReadonlyRecord' + +assert.deepStrictEqual(has({ a: 1, b: 2 }, 'a'), true) +assert.deepStrictEqual(has({ a: 1, b: 2 }, 'c'), false) +``` + +Added in v1.0.0 + +## map + +Maps a `ReadonlyRecord` into another `Record` by applying a transformation function to each of its values. + +**Signature** + +```ts +export declare const map: { + (f: (a: A, key: K) => B): (self: Readonly>) => Record + (self: Readonly>, f: (a: A, key: K) => B): Record +} +``` + +**Example** + +```ts +import { map } from '@fp-ts/core/ReadonlyRecord' + +const f = (n: number) => `-${n}` + +assert.deepStrictEqual(map({ a: 3, b: 5 }, f), { a: '-3', b: '-5' }) + +const g = (n: number, key: string) => `${key.toUpperCase()}-${n}` + +assert.deepStrictEqual(map({ a: 3, b: 5 }, g), { a: 'A-3', b: 'B-5' }) +``` + +Added in v1.0.0 + +## 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) => Option> + (self: ReadonlyRecord, key: string, f: (a: A) => B): Option> +} +``` + +**Example** + +```ts +import { modifyOption } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +const f = (x: number) => x * 2 + +assert.deepStrictEqual(modifyOption({ a: 3 }, 'a', f), some({ a: 6 })) +assert.deepStrictEqual(modifyOption({ a: 3 }, 'b', f), none()) +``` + +Added in v1.0.0 + +## remove + +Removes a key from a `ReadonlyRecord` and returns a new `Record` + +**Signature** + +```ts +export declare const remove: { + (key: string): (self: ReadonlyRecord) => Record + (self: ReadonlyRecord, key: string): Record +} +``` + +**Example** + +```ts +import { remove } from '@fp-ts/core/ReadonlyRecord' + +assert.deepStrictEqual(remove({ a: 1, b: 2 }, 'a'), { b: 2 }) +``` + +Added in v1.0.0 + +## replaceOption + +Replaces a value in the record with the new value passed as parameter. + +**Signature** + +```ts +export declare const replaceOption: { + (key: string, b: B): (self: ReadonlyRecord) => Option> + (self: ReadonlyRecord, key: string, b: B): Option> +} +``` + +**Example** + +```ts +import { replaceOption } from '@fp-ts/core/ReadonlyRecord' +import { some, none } from '@fp-ts/core/Option' + +assert.deepStrictEqual(replaceOption({ a: 1, b: 2, c: 3 }, 'a', 10), some({ a: 10, b: 2, c: 3 })) +assert.deepStrictEqual(replaceOption({}, 'a', 10), none()) +``` + +Added in v1.0.0 + +## size + +Returns the number of key/value pairs in a `ReadonlyRecord`. + +**Signature** + +```ts +export declare const size: (self: ReadonlyRecord) => number +``` + +**Example** + +```ts +import { size } from '@fp-ts/core/ReadonlyRecord' + +assert.deepStrictEqual(size({ a: 'a', b: 1, c: true }), 3) +``` + +Added in v1.0.0 + +## traverseFilter + +Filter values inside a context. + +**Signature** + +```ts +export declare const traverseFilter: ( + F: applicative.Applicative +) => { + (predicate: (a: A) => Kind): ( + self: ReadonlyRecord + ) => Kind> + (self: ReadonlyRecord, predicate: (a: A) => Kind): Kind< + F, + R, + O, + E, + Record + > +} +``` + +Added in v1.0.0 + +## traversePartition + +**Signature** + +```ts +export declare const traversePartition: ( + F: applicative.Applicative +) => { + (predicate: (a: A) => Kind): ( + self: ReadonlyRecord + ) => Kind, Record]> + (self: ReadonlyRecord, predicate: (a: A) => Kind): Kind< + F, + R, + O, + E, + [Record, Record] + > +} +``` + +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..50d1d6c92 --- /dev/null +++ b/docs/modules/String.ts.md @@ -0,0 +1,552 @@ +--- +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) + - [endsWithPosition](#endswithposition) + - [includes](#includes) + - [includesWithPosition](#includeswithposition) + - [isEmpty](#isempty) + - [isNonEmpty](#isnonempty) + - [length](#length) + - [replace](#replace) + - [slice](#slice) + - [split](#split) + - [startsWith](#startswith) + - [startsWithPosition](#startswithposition) + - [takeLeft](#takeleft) + - [takeRight](#takeright) + - [toLowerCase](#tolowercase) + - [toUpperCase](#touppercase) + - [trim](#trim) + - [trimEnd](#trimend) + - [trimStart](#trimstart) + +--- + +# guards + +## isString + +Tests if a value is a `string`. + +**Signature** + +```ts +export declare const isString: Refinement +``` + +**Example** + +```ts +import { isString } from '@fp-ts/core/String' + +assert.deepStrictEqual(isString('a'), true) +assert.deepStrictEqual(isString(1), false) +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: equivalence.Equivalence +``` + +Added in v1.0.0 + +## Monoid + +`string` monoid under concatenation. + +The `empty` value is `''`. + +**Signature** + +```ts +export declare const Monoid: monoid.Monoid +``` + +Added in v1.0.0 + +## Order + +**Signature** + +```ts +export declare const Order: order.Order +``` + +Added in v1.0.0 + +## Semigroup + +`string` semigroup under concatenation. + +**Signature** + +```ts +export declare const Semigroup: semigroup.Semigroup +``` + +Added in v1.0.0 + +# utils + +## concat + +**Signature** + +```ts +export declare const concat: { (that: string): (self: string) => string; (self: string, that: string): string } +``` + +Added in v1.0.0 + +## empty + +The empty string `""`. + +**Signature** + +```ts +export declare const empty: '' +``` + +Added in v1.0.0 + +## endsWith + +**Signature** + +```ts +export declare const endsWith: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.endsWith('abc', 'c'), true) +assert.deepStrictEqual(S.endsWith('ab', 'c'), false) +``` + +Added in v1.0.0 + +## endsWithPosition + +**Signature** + +```ts +export declare const endsWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.endsWithPosition('abc', 'b', 2), true) +assert.deepStrictEqual(S.endsWithPosition('abc', 'c', 2), false) +``` + +Added in v1.0.0 + +## includes + +Returns `true` if `searchString` appears as a substring of `self`, at one or more positions that are +greater than or equal to `0`; otherwise, returns `false`. + +**Signature** + +```ts +export declare const includes: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.includes('abc', 'b'), true) +assert.deepStrictEqual(S.includes('abc', 'd'), false) +``` + +Added in v1.0.0 + +## includesWithPosition + +Returns `true` if `searchString` appears as a substring of `self`, at one or more positions that are +greater than or equal to `position`; otherwise, returns `false`. + +**Signature** + +```ts +export declare const includesWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.includesWithPosition('abc', 'b', 1), true) +assert.deepStrictEqual(S.includesWithPosition('abc', 'a', 1), false) +``` + +Added in v1.0.0 + +## isEmpty + +Test whether a `string` is empty. + +**Signature** + +```ts +export declare const isEmpty: (self: string) => self is '' +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.isEmpty(''), true) +assert.deepStrictEqual(S.isEmpty('a'), false) +``` + +Added in v1.0.0 + +## isNonEmpty + +Test whether a `string` is non empty. + +**Signature** + +```ts +export declare const isNonEmpty: (self: string) => boolean +``` + +Added in v1.0.0 + +## length + +Calculate the number of characters in a `string`. + +**Signature** + +```ts +export declare const length: (self: string) => number +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.length('abc'), 3) +``` + +Added in v1.0.0 + +## replace + +**Signature** + +```ts +export declare const replace: { + (searchValue: string | RegExp, replaceValue: string): (self: string) => string + (self: string, searchValue: string | RegExp, replaceValue: 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): (self: string) => string + (self: string, start: number, end: number): 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): (self: string) => [string, ...string[]] + (self: string, separator: string | RegExp): [string, ...string[]] +} +``` + +**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 + +Returns `true` if the sequence of elements of `searchString` is the +same as the corresponding elements of `s` starting at +position. Otherwise returns false. + +**Signature** + +```ts +export declare const startsWith: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.startsWith('abc', 'a'), true) +assert.deepStrictEqual(S.startsWith('bc', 'a'), false) +``` + +Added in v1.0.0 + +## startsWithPosition + +**Signature** + +```ts +export declare const startsWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.startsWithPosition('abc', 'b', 1), true) +assert.deepStrictEqual(S.startsWithPosition('bc', 'a', 1), 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; (self: string, n: number): string } +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.takeLeft('Hello World', 5), 'Hello') +``` + +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): (self: string) => string; (self: string, n: number): string } +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.takeRight('Hello World', 5), 'World') +``` + +Added in v1.0.0 + +## toLowerCase + +**Signature** + +```ts +export declare const toLowerCase: (self: 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: (self: 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: (self: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.trim(' a '), 'a') +``` + +Added in v1.0.0 + +## trimEnd + +**Signature** + +```ts +export declare const trimEnd: (self: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.trimEnd(' a '), ' a') +``` + +Added in v1.0.0 + +## trimStart + +**Signature** + +```ts +export declare const trimStart: (self: string) => string +``` + +**Example** + +```ts +import * as S from '@fp-ts/core/String' + +assert.deepStrictEqual(S.trimStart(' a '), '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..f517f901a --- /dev/null +++ b/docs/modules/Struct.ts.md @@ -0,0 +1,188 @@ +--- +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

+ +- [combinators](#combinators) + - [getEquivalence](#getequivalence) + - [getMonoid](#getmonoid) + - [getOrder](#getorder) + - [getSemigroup](#getsemigroup) +- [utils](#utils) + - [omit](#omit) + - [pick](#pick) + +--- + +# combinators + +## getEquivalence + +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. + +Alias of {@link equivalence.struct}. + +**Signature** + +```ts +export declare const getEquivalence: >>( + predicates: R +) => equivalence.Equivalence<{ readonly [K in keyof R]: [R[K]] extends [equivalence.Equivalence] ? A : never }> +``` + +**Example** + +```ts +import { getEquivalence } from '@fp-ts/core/Struct' +import * as S from '@fp-ts/core/String' +import * as N from '@fp-ts/core/Number' + +const PersonEquivalence = getEquivalence({ + name: S.Equivalence, + age: N.Equivalence, +}) + +assert.deepStrictEqual(PersonEquivalence({ name: 'John', age: 25 }, { name: 'John', age: 25 }), true) +assert.deepStrictEqual(PersonEquivalence({ name: 'John', age: 25 }, { name: 'John', age: 40 }), false) +``` + +Added in v1.0.0 + +## getMonoid + +This function creates and returns a new `Monoid` for a struct of values based on the given `Monoid`s for each property in the struct. +The returned `Monoid` combines two structs of the same type by applying the corresponding `Monoid` passed as arguments to each property in the struct. + +The `empty` value of the returned `Monoid` is a struct where each property is the `empty` value of the corresponding `Monoid` in the input `monoids` object. + +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. + +See also {@link getSemigroup}. + +**Signature** + +```ts +export declare const getMonoid: }>( + fields: R +) => monoid.Monoid<{ [K in keyof R]: [R[K]] extends [monoid.Monoid] ? A : never }> +``` + +Added in v1.0.0 + +## getOrder + +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. + +Alias of {@link order.struct}. + +**Signature** + +```ts +export declare const getOrder: }>( + fields: R +) => order.Order<{ [K in keyof R]: [R[K]] extends [order.Order] ? A : never }> +``` + +Added in v1.0.0 + +## getSemigroup + +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. + +See also {@link getMonoid}. + +**Signature** + +```ts +export declare const getSemigroup: }>( + fields: R +) => semigroup.Semigroup<{ [K in keyof R]: [R[K]] extends [semigroup.Semigroup] ? A : never }> +``` + +**Example** + +```ts +import { getSemigroup } from '@fp-ts/core/Struct' +import * as Semigroup from '@fp-ts/core/typeclass/Semigroup' +import * as O from '@fp-ts/core/Option' + +const PersonSemigroup = getSemigroup({ + name: Semigroup.last(), + age: O.getOptionalMonoid(Semigroup.last()), +}) + +assert.deepStrictEqual(PersonSemigroup.combine({ name: 'John', age: O.none() }, { name: 'John', age: O.some(25) }), { + name: 'John', + age: O.some(25), +}) +assert.deepStrictEqual(PersonSemigroup.combine({ name: 'John', age: O.some(25) }, { name: 'John', age: O.none() }), { + name: 'John', + age: O.some(25), +}) +``` + +Added in v1.0.0 + +# utils + +## 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] } +``` + +**Example** + +```ts +import { omit } from '@fp-ts/core/Struct' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe({ a: 'a', b: 1, c: true }, omit('c')), { a: 'a', b: 1 }) +``` + +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] } +``` + +**Example** + +```ts +import { pick } from '@fp-ts/core/Struct' +import { pipe } from '@fp-ts/core/Function' + +assert.deepStrictEqual(pipe({ a: 'a', b: 1, c: true }, pick('a', 'b')), { a: 'a', b: 1 }) +``` + +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..848b1ef4c --- /dev/null +++ b/docs/modules/Symbol.ts.md @@ -0,0 +1,55 @@ +--- +title: Symbol.ts +nav_order: 16 +parent: Modules +--- + +## Symbol overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [guards](#guards) + - [isSymbol](#issymbol) +- [instances](#instances) + - [Equivalence](#equivalence) + +--- + +# guards + +## isSymbol + +Tests if a value is a `symbol`. + +**Signature** + +```ts +export declare const isSymbol: (u: unknown) => u is symbol +``` + +**Example** + +```ts +import { isSymbol } from '@fp-ts/core/Predicate' + +assert.deepStrictEqual(isSymbol(Symbol.for('a')), true) +assert.deepStrictEqual(isSymbol('a'), false) +``` + +Added in v1.0.0 + +# instances + +## Equivalence + +**Signature** + +```ts +export declare const Equivalence: equivalence.Equivalence +``` + +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..5f22da4be --- /dev/null +++ b/docs/modules/These.ts.md @@ -0,0 +1,1918 @@ +--- +title: These.ts +nav_order: 17 +parent: Modules +--- + +## These overview + +Added in v1.0.0 + +--- + +

Table of contents

+ +- [combinators](#combinators) + - [tap](#tap) +- [combining](#combining) + - [andThenDiscard](#andthendiscard) + - [flatMap](#flatmap) + - [flatMapEither](#flatmapeither) + - [flatMapNullable](#flatmapnullable) + - [flatMapOption](#flatmapoption) + - [flatMapThese](#flatmapthese) + - [getFirstLeftMonoid](#getfirstleftmonoid) + - [getFirstLeftSemigroup](#getfirstleftsemigroup) + - [getFirstRightOrBothSemigroup](#getfirstrightorbothsemigroup) + - [zipWith](#zipwith) +- [constructors](#constructors) + - [both](#both) + - [fail](#fail) + - [left](#left) + - [leftOrBoth](#leftorboth) + - [of](#of) + - [right](#right) + - [rightOrBoth](#rightorboth) + - [warn](#warn) +- [conversions](#conversions) + - [absolve](#absolve) + - [condemn](#condemn) + - [fromEither](#fromeither) + - [fromIterable](#fromiterable) + - [fromNullable](#fromnullable) + - [fromOption](#fromoption) + - [fromTuple](#fromtuple) + - [toEither](#toeither) + - [toValidated](#tovalidated) +- [debugging](#debugging) + - [inspectBoth](#inspectboth) + - [inspectLeft](#inspectleft) + - [inspectRight](#inspectright) + - [inspectRightOrBoth](#inspectrightorboth) +- [do notation](#do-notation) + - [Do](#do) + - [andThenBind](#andthenbind) + - [bind](#bind) + - [bindEither](#bindeither) + - [bindThese](#bindthese) + - [bindTo](#bindto) + - [let](#let) +- [equivalence](#equivalence) + - [getEquivalence](#getequivalence) +- [error handling](#error-handling) + - [firstRightOrBothOf](#firstrightorbothof) + - [mapLeft](#mapleft) + - [orElse](#orelse) + - [orElseEither](#orelseeither) + - [orElseFail](#orelsefail) +- [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) + - [getOrThrow](#getorthrow) + - [getOrThrowWith](#getorthrowwith) + - [getRightOnlyOrThrow](#getrightonlyorthrow) + - [getRightOnlyOrThrowWith](#getrightonlyorthrowwith) + - [liftThrowable](#liftthrowable) +- [lifting](#lifting) + - [lift2](#lift2) + - [liftEither](#lifteither) + - [liftNullable](#liftnullable) + - [liftOption](#liftoption) + - [liftPredicate](#liftpredicate) + - [liftThese](#liftthese) +- [mapping](#mapping) + - [as](#as) + - [asUnit](#asunit) + - [bimap](#bimap) + - [flap](#flap) + - [map](#map) + - [tupled](#tupled) +- [math](#math) + - [divide](#divide) + - [multiply](#multiply) + - [subtract](#subtract) + - [sum](#sum) +- [model](#model) + - [Both (interface)](#both-interface) + - [These (type alias)](#these-type-alias) + - [Validated (type alias)](#validated-type-alias) +- [pattern matching](#pattern-matching) + - [match](#match) +- [predicates](#predicates) + - [exists](#exists) +- [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) + - [appendElement](#appendelement) + - [composeKleisliArrow](#composekleisliarrow) + - [contains](#contains) + - [flatten](#flatten) + - [reverse](#reverse) + - [struct](#struct) + - [tuple](#tuple) + - [unit](#unit) + +--- + +# combinators + +## tap + +Returns an effect that effectfully "peeks" at the success of this effect. + +**Signature** + +```ts +export declare const tap: { + (self: These, f: (a: A) => These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + A + > + (f: (a: A) => These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +# combining + +## andThenDiscard + +Sequences the specified effect after this effect, but ignores the value +produced by the effect. + +**Signature** + +```ts +export declare const andThenDiscard: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + A + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## flatMap + +**Signature** + +```ts +export declare const flatMap: { + (f: (a: A) => These): ( + self: These + ) => These + (self: These, f: (a: A) => These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > +} +``` + +Added in v1.0.0 + +## flatMapEither + +**Signature** + +```ts +export declare const flatMapEither: { + (f: (a: A) => Either): ( + self: These + ) => These + (self: These, f: (a: A) => Either): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > +} +``` + +Added in v1.0.0 + +## flatMapNullable + +**Signature** + +```ts +export declare const flatMapNullable: { + (f: (a: A) => B | null | undefined, onNullable: (a: A) => E2): ( + self: These + ) => These> + ( + self: These, + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 + ): These> +} +``` + +Added in v1.0.0 + +## flatMapOption + +**Signature** + +```ts +export declare const flatMapOption: { + (f: (a: A) => O.Option, onNone: (a: A) => E2): ( + self: These + ) => These + (self: These, f: (a: A) => O.Option, onNone: (a: A) => E2): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > +} +``` + +Added in v1.0.0 + +## flatMapThese + +**Signature** + +```ts +export declare const flatMapThese: { + (f: (a: A) => These): ( + self: These + ) => These + (self: These, f: (a: A) => These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > +} +``` + +Added in v1.0.0 + +## getFirstLeftMonoid + +**Signature** + +```ts +export declare const getFirstLeftMonoid: (M: Monoid
) => Monoid> +``` + +Added in v1.0.0 + +## getFirstLeftSemigroup + +**Signature** + +```ts +export declare const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> +``` + +Added in v1.0.0 + +## getFirstRightOrBothSemigroup + +**Signature** + +```ts +export declare const getFirstRightOrBothSemigroup: () => Semigroup> +``` + +Added in v1.0.0 + +## zipWith + +**Signature** + +```ts +export declare const zipWith: { + ( + self: These, + that: These, + f: (a: A, b: B) => C + ): These + (that: These, f: (a: A, b: B) => C): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +# constructors + +## both + +**Signature** + +```ts +export declare const both: (left: E, right: A) => These +``` + +Added in v1.0.0 + +## fail + +**Signature** + +```ts +export declare const fail: (e: E) => These +``` + +Added in v1.0.0 + +## left + +**Signature** + +```ts +export declare const left: (left: E) => These +``` + +Added in v1.0.0 + +## leftOrBoth + +**Signature** + +```ts +export declare const leftOrBoth: { + (onSome: LazyArg): (self: O.Option) => These + (self: O.Option, onSome: LazyArg): These +} +``` + +Added in v1.0.0 + +## of + +Alias of {@link right}. + +**Signature** + +```ts +export declare const of: (right: A) => These +``` + +Added in v1.0.0 + +## right + +**Signature** + +```ts +export declare const right: (right: A) => These +``` + +Added in v1.0.0 + +## rightOrBoth + +**Signature** + +```ts +export declare const rightOrBoth: { + (onNone: LazyArg): (self: O.Option) => These + (self: O.Option, onNone: LazyArg): These +} +``` + +Added in v1.0.0 + +## warn + +**Signature** + +```ts +export declare const warn: (e: E, a: A) => These +``` + +Added in v1.0.0 + +# conversions + +## absolve + +**Signature** + +```ts +export declare const absolve: (self: These) => Either +``` + +Added in v1.0.0 + +## condemn + +**Signature** + +```ts +export declare const condemn: (self: These) => Either +``` + +Added in v1.0.0 + +## fromEither + +**Signature** + +```ts +export declare const fromEither: (self: Either) => These +``` + +Added in v1.0.0 + +## fromIterable + +**Signature** + +```ts +export declare const fromIterable: { + (onEmpty: LazyArg): (collection: Iterable) => These + (collection: Iterable, onEmpty: LazyArg): These +} +``` + +Added in v1.0.0 + +## fromNullable + +**Signature** + +```ts +export declare const fromNullable: { + (onNullable: LazyArg): (a: A) => These> + (a: A, onNullable: LazyArg): These> +} +``` + +Added in v1.0.0 + +## fromOption + +**Signature** + +```ts +export declare const fromOption: { + (onNone: LazyArg): (self: O.Option) => These + (self: O.Option, onNone: LazyArg): These +} +``` + +Added in v1.0.0 + +## fromTuple + +**Signature** + +```ts +export declare const fromTuple: (self: readonly [E, A]) => These +``` + +Added in v1.0.0 + +## toEither + +**Signature** + +```ts +export declare const toEither: { + (onBoth: (e: E, a: A) => Either): (self: These) => Either + (self: These, onBoth: (e: E, a: A) => Either): Either +} +``` + +Added in v1.0.0 + +## toValidated + +**Signature** + +```ts +export declare const toValidated: (self: These) => These +``` + +Added in v1.0.0 + +# debugging + +## inspectBoth + +**Signature** + +```ts +export declare const inspectBoth: { + (onBoth: (e: E, a: A) => void): (self: These) => These + (self: These, onBoth: (e: E, a: A) => void): These +} +``` + +Added in v1.0.0 + +## inspectLeft + +**Signature** + +```ts +export declare const inspectLeft: { + (onLeft: (e: E) => void): (self: These) => These + (self: These, onLeft: (e: E) => void): These +} +``` + +Added in v1.0.0 + +## inspectRight + +**Signature** + +```ts +export declare const inspectRight: { + (onRight: (a: A) => void): (self: These) => These + (self: These, onRight: (a: A) => void): These +} +``` + +Added in v1.0.0 + +## inspectRightOrBoth + +**Signature** + +```ts +export declare const inspectRightOrBoth: { + (onRightOrBoth: (a: A) => void): (self: These) => These + (self: These, onRightOrBoth: (a: A) => void): These +} +``` + +Added in v1.0.0 + +# do notation + +## Do + +**Signature** + +```ts +export declare const Do: These +``` + +Added in v1.0.0 + +## andThenBind + +**Signature** + +```ts +export declare const andThenBind: { + (name: Exclude, that: These): ( + self: These + ) => These + ( + self: These, + name: Exclude, + that: These + ): These +} +``` + +Added in v1.0.0 + +## bind + +**Signature** + +```ts +export declare const bind: { + ( + name: Exclude, + f: (a: A) => These + ): ( + self: These + ) => These + ( + self: These, + name: Exclude, + f: (a: A) => These + ): These +} +``` + +Added in v1.0.0 + +## bindEither + +**Signature** + +```ts +export declare const bindEither: { + (name: Exclude, f: (a: A) => Either): ( + self: These + ) => These + ( + self: These, + name: Exclude, + f: (a: A) => Either + ): These +} +``` + +Added in v1.0.0 + +## bindThese + +**Signature** + +```ts +export declare const bindThese: { + (name: Exclude, f: (a: A) => These): ( + self: These + ) => These + ( + self: These, + name: Exclude, + f: (a: A) => These + ): These +} +``` + +Added in v1.0.0 + +## bindTo + +**Signature** + +```ts +export declare const bindTo: { + (name: N): (self: These) => These + (self: These, name: N): These +} +``` + +Added in v1.0.0 + +## let + +**Signature** + +```ts +export declare const let: { + (name: Exclude, f: (a: A) => B): ( + self: These + ) => These + (self: These, name: Exclude, f: (a: A) => B): These< + E, + { [K in N | keyof A]: K extends keyof A ? A[K] : B } + > +} +``` + +Added in v1.0.0 + +# equivalence + +## getEquivalence + +**Signature** + +```ts +export declare const getEquivalence: (EE: Equivalence, EA: Equivalence) => Equivalence> +``` + +Added in v1.0.0 + +# error handling + +## firstRightOrBothOf + +**Signature** + +```ts +export declare const firstRightOrBothOf: { + (collection: Iterable>): (self: These) => These + (self: These, collection: Iterable>): These +} +``` + +Added in v1.0.0 + +## mapLeft + +Maps the `Left` side of an `These` value to a new `These` value. + +**Signature** + +```ts +export declare const mapLeft: { + (f: (e: E) => G): (self: These) => These + (self: These, f: (e: E) => G): These +} +``` + +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: (e1: E1) => These): (self: These) => These + (self: These, that: (e1: E1) => These): These +} +``` + +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: (e1: E1) => These): (self: These) => These> + (self: These, that: (e1: E1) => These): These> +} +``` + +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: LazyArg): (self: These) => These + (self: These, onLeft: LazyArg): These +} +``` + +Added in v1.0.0 + +# filtering + +## compact + +**Signature** + +```ts +export declare const compact: { + (onNone: LazyArg): (self: These>) => These + (self: These>, onNone: LazyArg): These +} +``` + +Added in v1.0.0 + +## filter + +**Signature** + +```ts +export declare const filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: These + ) => These + (predicate: Predicate, onFalse: LazyArg): (self: These) => These + ( + self: These, + refinement: Refinement, + onFalse: LazyArg + ): These + (self: These, predicate: Predicate, onFalse: LazyArg): These +} +``` + +Added in v1.0.0 + +## filterMap + +**Signature** + +```ts +export declare const filterMap: { + (f: (a: A) => O.Option, onNone: LazyArg): (self: These) => These + (self: These, f: (a: A) => O.Option, onNone: LazyArg): These +} +``` + +Added in v1.0.0 + +# getters + +## getBoth + +**Signature** + +```ts +export declare const getBoth: (self: These) => O.Option +``` + +Added in v1.0.0 + +## getBothOrElse + +**Signature** + +```ts +export declare const getBothOrElse: { + (e: LazyArg, a: LazyArg): (self: These) => [E, A] + (self: These, e: LazyArg, a: LazyArg): [E, A] +} +``` + +Added in v1.0.0 + +## getLeft + +Converts a `These` to an `Option` discarding the value (`Both` included). + +**Signature** + +```ts +export declare const getLeft: (self: These) => O.Option +``` + +Added in v1.0.0 + +## getLeftOnly + +Returns the error if and only if the value is a `Left` (i.e. `Both` is excluded). + +**Signature** + +```ts +export declare const getLeftOnly: (self: These) => O.Option +``` + +Added in v1.0.0 + +## getOrElse + +**Signature** + +```ts +export declare const getOrElse: { + (onLeft: LazyArg): (self: These) => B | A + (self: These, onLeft: LazyArg): A | B +} +``` + +Added in v1.0.0 + +## getOrNull + +**Signature** + +```ts +export declare const getOrNull: (self: These) => A | null +``` + +Added in v1.0.0 + +## getOrUndefined + +**Signature** + +```ts +export declare const getOrUndefined: (self: These) => A | undefined +``` + +Added in v1.0.0 + +## getRight + +Converts a `These` to an `Option` discarding the error (`Both` included). + +**Signature** + +```ts +export declare const getRight: (self: These) => O.Option +``` + +Added in v1.0.0 + +## getRightOnly + +Returns the value if and only if the value is a `Right` (i.e. `Both` is excluded). + +**Signature** + +```ts +export declare const getRightOnly: (self: These) => O.Option +``` + +Added in v1.0.0 + +# guards + +## isBoth + +Determine if a `These` is a `Both`. + +**Signature** + +```ts +export declare const isBoth: (self: These) => self is Both +``` + +**Example** + +```ts +import { isBoth, left, right, both } from '@fp-ts/core/These' + +assert.deepStrictEqual(isBoth(right(1)), false) +assert.deepStrictEqual(isBoth(left('error')), false) +assert.deepStrictEqual(isBoth(both('error', 1)), true) +``` + +Added in v1.0.0 + +## isLeft + +Determine if a `These` is a `Left`. + +**Signature** + +```ts +export declare const isLeft: (self: These) => self is Left +``` + +**Example** + +```ts +import { isLeft, left, right, both } from '@fp-ts/core/These' + +assert.deepStrictEqual(isLeft(right(1)), false) +assert.deepStrictEqual(isLeft(left('error')), true) +assert.deepStrictEqual(isLeft(both('error', 1)), false) +``` + +Added in v1.0.0 + +## isLeftOrBoth + +Determine if a `These` is a `Left` or a `Both`. + +**Signature** + +```ts +export declare const isLeftOrBoth: (self: These) => self is Left | Both +``` + +**Example** + +```ts +import { isLeftOrBoth, left, right, both } from '@fp-ts/core/These' + +assert.deepStrictEqual(isLeftOrBoth(right(1)), false) +assert.deepStrictEqual(isLeftOrBoth(left('error')), true) +assert.deepStrictEqual(isLeftOrBoth(both('error', 1)), true) +``` + +Added in v1.0.0 + +## isRight + +Determine if a `These` is a `Right`. + +**Signature** + +```ts +export declare const isRight: (self: These) => self is Right +``` + +**Example** + +```ts +import { isRight, left, right, both } from '@fp-ts/core/These' + +assert.deepStrictEqual(isRight(right(1)), true) +assert.deepStrictEqual(isRight(left('error')), false) +assert.deepStrictEqual(isRight(both('error', 1)), false) +``` + +Added in v1.0.0 + +## isRightOrBoth + +Determine if a `These` is a `Right` or a `Both`. + +**Signature** + +```ts +export declare const isRightOrBoth: (self: These) => self is Right | Both +``` + +**Example** + +```ts +import { isRightOrBoth, left, right, both } from '@fp-ts/core/These' + +assert.deepStrictEqual(isRightOrBoth(right(1)), true) +assert.deepStrictEqual(isRightOrBoth(left('error')), false) +assert.deepStrictEqual(isRightOrBoth(both('error', 1)), true) +``` + +Added in v1.0.0 + +## isThese + +Tests if a value is a `These`. + +**Signature** + +```ts +export declare const isThese: (input: unknown) => input is These +``` + +Added in v1.0.0 + +# instances + +## Applicative + +**Signature** + +```ts +export declare const Applicative: applicative.Applicative +``` + +Added in v1.0.0 + +## Bicovariant + +**Signature** + +```ts +export declare const Bicovariant: bicovariant.Bicovariant +``` + +Added in v1.0.0 + +## Chainable + +**Signature** + +```ts +export declare const Chainable: chainable.Chainable +``` + +Added in v1.0.0 + +## Covariant + +**Signature** + +```ts +export declare const Covariant: covariant.Covariant +``` + +Added in v1.0.0 + +## FlatMap + +**Signature** + +```ts +export declare const FlatMap: flatMap_.FlatMap +``` + +Added in v1.0.0 + +## Foldable + +**Signature** + +```ts +export declare const Foldable: foldable.Foldable +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Monad + +**Signature** + +```ts +export declare const Monad: monad.Monad +``` + +Added in v1.0.0 + +## Of + +**Signature** + +```ts +export declare const Of: of_.Of +``` + +Added in v1.0.0 + +## Pointed + +**Signature** + +```ts +export declare const Pointed: pointed.Pointed +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiAlternative + +**Signature** + +```ts +export declare const SemiAlternative: semiAlternative.SemiAlternative +``` + +Added in v1.0.0 + +## SemiApplicative + +**Signature** + +```ts +export declare const SemiApplicative: semiApplicative.SemiApplicative +``` + +Added in v1.0.0 + +## SemiCoproduct + +**Signature** + +```ts +export declare const SemiCoproduct: semiCoproduct.SemiCoproduct +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +Added in v1.0.0 + +## Traversable + +**Signature** + +```ts +export declare const Traversable: traversable.Traversable +``` + +Added in v1.0.0 + +# interop + +## getOrThrow + +Extracts the value of a `These` or throws if the `These` is `Left`. + +The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + +**Signature** + +```ts +export declare const getOrThrow: (self: These) => A +``` + +**Example** + +```ts +import * as T from '@fp-ts/core/These' + +assert.deepStrictEqual(T.getOrThrow(T.right(1)), 1) +assert.deepStrictEqual(T.getOrThrow(T.both('warning', 1)), 1) +assert.throws(() => T.getOrThrow(T.left('error'))) +``` + +Added in v1.0.0 + +## getOrThrowWith + +Extracts the value of a `These` or throws if the `These` is `Left`. + +If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + +**Signature** + +```ts +export declare const getOrThrowWith: { + (onLeft: (e: E) => unknown): (self: These) => A + (self: These, onLeft: (e: E) => unknown): A +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/These' + +assert.deepStrictEqual( + E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), + 1 +) +assert.deepStrictEqual( + E.getOrThrowWith(E.both('warning', 1), () => new Error('Unexpected Left')), + 1 +) +assert.throws(() => E.getOrThrowWith(E.left('error'), () => new Error('Unexpected Left'))) +``` + +Added in v1.0.0 + +## getRightOnlyOrThrow + +Extracts the value of a `These` or throws if the `These` is not a `Right`. + +The thrown error is a default error. To configure the error thrown, see {@link getRightOnlyOrThrowWith}. + +**Signature** + +```ts +export declare const getRightOnlyOrThrow: (self: These) => A +``` + +**Example** + +```ts +import * as T from '@fp-ts/core/These' + +assert.deepStrictEqual(T.getRightOnlyOrThrow(T.right(1)), 1) +assert.throws(() => T.getRightOnlyOrThrow(T.both('error', 1))) +assert.throws(() => T.getRightOnlyOrThrow(T.left('error'))) +``` + +Added in v1.0.0 + +## getRightOnlyOrThrowWith + +Extracts the value of a `These` or throws if the `These` is `Left`. + +If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + +**Signature** + +```ts +export declare const getRightOnlyOrThrowWith: { + (onLeftOrBoth: (e: E) => unknown): (self: These) => A + (self: These, onLeftOrBoth: (e: E) => unknown): A +} +``` + +**Example** + +```ts +import * as E from '@fp-ts/core/These' + +assert.deepStrictEqual( + E.getRightOnlyOrThrowWith(E.right(1), () => new Error('Unexpected Left or Both')), + 1 +) +assert.throws(() => E.getRightOnlyOrThrowWith(E.both('warning', 1), () => new Error('Unexpected Left or Both'))) +assert.throws(() => E.getRightOnlyOrThrowWith(E.left('error'), () => new Error('Unexpected Left or Both'))) +``` + +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) => These +``` + +Added in v1.0.0 + +# lifting + +## lift2 + +Lifts a binary function into `These`. + +**Signature** + +```ts +export declare const lift2: ( + f: (a: A, b: B) => C +) => { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + C + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## liftEither + +**Signature** + +```ts +export declare const liftEither: ( + f: (...a: A) => Either +) => (...a: A) => These +``` + +Added in v1.0.0 + +## liftNullable + +**Signature** + +```ts +export declare const liftNullable: ( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A) => These> +``` + +Added in v1.0.0 + +## liftOption + +**Signature** + +```ts +export declare const liftOption: ( + f: (...a: A) => O.Option, + onNone: (...a: A) => E +) => (...a: A) => These +``` + +Added in v1.0.0 + +## liftPredicate + +**Signature** + +```ts +export declare const liftPredicate: { + (refinement: Refinement, onFalse: (c: C) => E): (c: C) => These + (predicate: Predicate, onFalse: (b: B) => E): (b: B) => These +} +``` + +Added in v1.0.0 + +## liftThese + +**Signature** + +```ts +export declare const liftThese: ( + f: (...a: A) => These +) => (...a: A) => These +``` + +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: { + (self: These, b: B): These + (b: B): (self: These) => These +} +``` + +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: These) => These +``` + +Added in v1.0.0 + +## bimap + +**Signature** + +```ts +export declare const bimap: { + (f: (e: E1) => E2, g: (a: A) => B): (self: These) => These + (self: These, f: (e: E1) => E2, g: (a: A) => B): These +} +``` + +Added in v1.0.0 + +## flap + +**Signature** + +```ts +export declare const flap: { + (a: A, self: These B>): These + (self: These B>): (a: A) => These +} +``` + +Added in v1.0.0 + +## map + +Maps the `Right` side of an `These` value to a new `These` value. + +**Signature** + +```ts +export declare const map: { + (self: These, f: (a: A) => B): These + (f: (a: A) => B): (self: These) => These +} +``` + +Added in v1.0.0 + +## tupled + +**Signature** + +```ts +export declare const tupled: (self: These) => These +``` + +Added in v1.0.0 + +# math + +## divide + +**Signature** + +```ts +export declare const divide: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + number + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## multiply + +**Signature** + +```ts +export declare const multiply: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + number + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## subtract + +**Signature** + +```ts +export declare const subtract: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + number + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## sum + +**Signature** + +```ts +export declare const sum: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + number + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +# model + +## Both (interface) + +**Signature** + +```ts +export interface 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: These + ) => B | C | D + (self: These, onLeft: (e: E) => B, onRight: (a: A) => C, onBoth: (e: E, a: A) => D): + | B + | C + | D +} +``` + +Added in v1.0.0 + +# predicates + +## exists + +**Signature** + +```ts +export declare const exists: { + (predicate: Predicate): (self: These) => boolean + (self: These, predicate: Predicate): boolean +} +``` + +Added in v1.0.0 + +# traversing + +## sequence + +**Signature** + +```ts +export declare const sequence: ( + F: applicative.Applicative +) => (self: These>) => Kind> +``` + +Added in v1.0.0 + +## traverse + +**Signature** + +```ts +export declare const traverse: ( + F: applicative.Applicative +) => { + (f: (a: A) => Kind): (self: These) => Kind> + (self: These, f: (a: A) => Kind): Kind> +} +``` + +Added in v1.0.0 + +## traverseTap + +**Signature** + +```ts +export declare const traverseTap: ( + F: applicative.Applicative +) => { + (self: These, f: (a: A) => Kind): Kind> + (f: (a: A) => Kind): (self: These) => Kind> +} +``` + +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: { + (self: These, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## ap + +**Signature** + +```ts +export declare const ap: { + (self: These B>, that: These): These< + readonly [E1 | E2, ...(E1 | E2)[]], + B + > + (that: These): ( + self: These B> + ) => These +} +``` + +Added in v1.0.0 + +## appendElement + +Appends an element to the end of a tuple. + +**Signature** + +```ts +export declare const appendElement: { + ( + self: These, + that: These + ): These + (that: These): ( + self: These + ) => These +} +``` + +Added in v1.0.0 + +## composeKleisliArrow + +**Signature** + +```ts +export declare const composeKleisliArrow: { + (afb: (a: A) => These, bfc: (b: B) => These): ( + a: A + ) => These + (bfc: (b: B) => These): ( + afb: (a: A) => These + ) => (a: A) => These +} +``` + +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: (isEquivalent: (self: A, that: A) => boolean) => { + (a: A): (self: These) => boolean + (self: These, a: A): boolean +} +``` + +Added in v1.0.0 + +## flatten + +**Signature** + +```ts +export declare const flatten: ( + self: These> +) => These +``` + +Added in v1.0.0 + +## reverse + +**Signature** + +```ts +export declare const reverse: (self: These) => These +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: >>( + r: R +) => These< + readonly [ + [R[keyof R]] extends [These] ? E : never, + ...([R[keyof R]] extends [These] ? E : never)[] + ], + { [K in keyof R]: [R[K]] extends [These] ? A : never } +> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `These`s. + +``` +[These, These, ...] -> These +``` + +**Signature** + +```ts +export declare const tuple: []>( + ...tuple: T +) => These< + readonly [ + [T[number]] extends [These] ? E : never, + ...([T[number]] extends [These] ? E : never)[] + ], + { [I in keyof T]: [T[I]] extends [These] ? A : never } +> +``` + +Added in v1.0.0 + +## unit + +**Signature** + +```ts +export declare const unit: These +``` + +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..9b052a870 --- /dev/null +++ b/docs/modules/Tuple.ts.md @@ -0,0 +1,322 @@ +--- +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

+ +- [combinators](#combinators) + - [getEquivalence](#getequivalence) + - [getMonoid](#getmonoid) + - [getOrder](#getorder) + - [getSemigroup](#getsemigroup) +- [constructors](#constructors) + - [tuple](#tuple) +- [getters](#getters) + - [getFirst](#getfirst) + - [getSecond](#getsecond) +- [instances](#instances) + - [Bicovariant](#bicovariant) +- [mapping](#mapping) + - [bimap](#bimap) + - [mapFirst](#mapfirst) + - [mapSecond](#mapsecond) +- [type lambdas](#type-lambdas) + - [TupleTypeLambda (interface)](#tupletypelambda-interface) +- [utils](#utils) + - [appendElement](#appendelement) + - [swap](#swap) + +--- + +# combinators + +## getEquivalence + +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 getEquivalence: []>( + ...predicates: T +) => equivalence.Equivalence< + Readonly<{ [I in keyof T]: [T[I]] extends [equivalence.Equivalence] ? A : never }> +> +``` + +Added in v1.0.0 + +## getMonoid + +This function creates and returns a new `Monoid` for a tuple of values based on the given `Monoid`s for each element in the tuple. +The returned `Monoid` combines two tuples of the same type by applying the corresponding `Monoid` passed as arguments to each element in the tuple. + +The `empty` value of the returned `Monoid` is the tuple of `empty` values of the input `Monoid`s. + +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 getMonoid: []>( + ...elements: T +) => monoid.Monoid<{ readonly [I in keyof T]: [T[I]] extends [monoid.Monoid] ? A : never }> +``` + +Added in v1.0.0 + +## getOrder + +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 getOrder: []>( + ...elements: T +) => order.Order<{ [I in keyof T]: [T[I]] extends [order.Order] ? A : never }> +``` + +Added in v1.0.0 + +## getSemigroup + +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 getSemigroup: []>( + ...elements: T +) => semigroup.Semigroup<{ readonly [I in keyof T]: [T[I]] extends [semigroup.Semigroup] ? A : never }> +``` + +Added in v1.0.0 + +# constructors + +## tuple + +Constructs a new tuple from the provided values. + +**Signature** + +```ts +export declare const tuple:
(...elements: A) => A +``` + +**Example** + +```ts +import { tuple } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual(tuple(1, 'hello', true), [1, 'hello', true]) +``` + +Added in v1.0.0 + +# getters + +## getFirst + +Return the first element of a tuple. + +**Signature** + +```ts +export declare const getFirst: (self: readonly [L, R]) => L +``` + +**Example** + +```ts +import { getFirst } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual(getFirst(['hello', 42]), 'hello') +``` + +Added in v1.0.0 + +## getSecond + +Return the second element of a tuple. + +**Signature** + +```ts +export declare const getSecond: (self: readonly [L, R]) => R +``` + +**Example** + +```ts +import { getSecond } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual(getSecond(['hello', 42]), 42) +``` + +Added in v1.0.0 + +# instances + +## Bicovariant + +**Signature** + +```ts +export declare const Bicovariant: bicovariant.Bicovariant +``` + +Added in v1.0.0 + +# mapping + +## bimap + +Transforms both elements of a tuple using the given functions. + +**Signature** + +```ts +export declare const bimap: { + (f: (e: L1) => L2, g: (a: R1) => R2): (self: readonly [L1, R1]) => [L2, R2] + (self: readonly [L1, R1], f: (e: L1) => L2, g: (a: R1) => R2): [L2, R2] +} +``` + +**Example** + +```ts +import { bimap } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual( + bimap( + ['hello', 42], + (s) => s.toUpperCase(), + (n) => n.toString() + ), + ['HELLO', '42'] +) +``` + +Added in v1.0.0 + +## mapFirst + +Transforms the first component of a tuple using a given function. + +**Signature** + +```ts +export declare const mapFirst: { + (f: (left: L1) => L2): (self: readonly [L1, R]) => [L2, R] + (self: readonly [L1, R], f: (left: L1) => L2): [L2, R] +} +``` + +**Example** + +```ts +import { mapFirst } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual( + mapFirst(['hello', 42], (s) => s.toUpperCase()), + ['HELLO', 42] +) +``` + +Added in v1.0.0 + +## mapSecond + +Transforms the second component of a tuple using a given function. + +**Signature** + +```ts +export declare const mapSecond: { + (f: (right: R1) => R2): (self: readonly [L, R1]) => [L, R2] + (self: readonly [L, R1], f: (right: R1) => R2): [L, R2] +} +``` + +**Example** + +```ts +import { mapSecond } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual( + mapSecond(['hello', 42], (n) => n.toString()), + ['hello', '42'] +) +``` + +Added in v1.0.0 + +# type lambdas + +## TupleTypeLambda (interface) + +**Signature** + +```ts +export interface TupleTypeLambda extends TypeLambda { + readonly type: [this['Out1'], this['Target']] +} +``` + +Added in v1.0.0 + +# utils + +## appendElement + +Appends an element to the end of a tuple. + +**Signature** + +```ts +export declare const appendElement: { + (that: B): (self: A) => [...A, B] + (self: A, that: B): [...A, B] +} +``` + +Added in v1.0.0 + +## swap + +Swaps the two elements of a tuple. + +**Signature** + +```ts +export declare const swap: (self: readonly [L, R]) => [R, L] +``` + +**Example** + +```ts +import { swap } from '@fp-ts/core/Tuple' + +assert.deepStrictEqual(swap(['hello', 42]), [42, 'hello']) +``` + +Added in v1.0.0 diff --git a/docs/modules/data/Either.ts.md b/docs/modules/data/Either.ts.md deleted file mode 100644 index e873b2317..000000000 --- a/docs/modules/data/Either.ts.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: data/Either.ts -nav_order: 1 -parent: Modules ---- - -## Either overview - -Added in v1.0.0 - ---- - -

Table of contents

- -- [models](#models) - - [Either (type alias)](#either-type-alias) - - [Left (interface)](#left-interface) - - [Right (interface)](#right-interface) -- [type lambdas](#type-lambdas) - - [EitherTypeLambda (interface)](#eithertypelambda-interface) - ---- - -# models - -## Either (type alias) - -**Signature** - -```ts -export type Either = Left | Right
-``` - -Added in v1.0.0 - -## Left (interface) - -**Signature** - -```ts -export interface Left { - readonly _tag: 'Left' - readonly left: E -} -``` - -Added in v1.0.0 - -## Right (interface) - -**Signature** - -```ts -export interface Right { - readonly _tag: 'Right' - readonly right: A -} -``` - -Added in v1.0.0 - -# type lambdas - -## EitherTypeLambda (interface) - -**Signature** - -```ts -export interface EitherTypeLambda extends TypeLambda { - readonly type: Either -} -``` - -Added in v1.0.0 diff --git a/docs/modules/data/Option.ts.md b/docs/modules/data/Option.ts.md deleted file mode 100644 index cccca4c88..000000000 --- a/docs/modules/data/Option.ts.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: data/Option.ts -nav_order: 2 -parent: Modules ---- - -## Option overview - -Added in v1.0.0 - ---- - -

Table of contents

- -- [models](#models) - - [None (interface)](#none-interface) - - [Option (type alias)](#option-type-alias) - - [Some (interface)](#some-interface) -- [type lambdas](#type-lambdas) - - [OptionTypeLambda (interface)](#optiontypelambda-interface) - ---- - -# models - -## None (interface) - -**Signature** - -```ts -export interface 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 (interface) - -**Signature** - -```ts -export interface Some { - readonly _tag: 'Some' - readonly value: A -} -``` - -Added in v1.0.0 - -# type lambdas - -## OptionTypeLambda (interface) - -**Signature** - -```ts -export interface OptionTypeLambda extends TypeLambda { - readonly type: Option -} -``` - -Added in v1.0.0 diff --git a/docs/modules/index.ts.md b/docs/modules/index.ts.md index d377ada6d..04879ac81 100644 --- a/docs/modules/index.ts.md +++ b/docs/modules/index.ts.md @@ -1,6 +1,6 @@ --- title: index.ts -nav_order: 4 +nav_order: 7 parent: Modules --- @@ -12,26 +12,22 @@ Added in v1.0.0

Table of contents

-- [data types](#data-types) - - [either](#either) - - [option](#option) - [typeclass](#typeclass) - [alternative](#alternative) - [applicative](#applicative) - [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) @@ -44,32 +40,26 @@ Added in v1.0.0 - [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) --- -# data types - -## either - -**Signature** - -```ts -export declare const either: typeof either -``` - -Added in v1.0.0 - -## option - -**Signature** - -```ts -export declare const option: typeof option -``` - -Added in v1.0.0 - # typeclass ## alternative @@ -122,42 +112,42 @@ export declare const chainable: typeof chainable Added in v1.0.0 -## compactable +## contravariant **Signature** ```ts -export declare const compactable: typeof compactable +export declare const contravariant: typeof contravariant ``` Added in v1.0.0 -## contravariant +## coproduct **Signature** ```ts -export declare const contravariant: typeof contravariant +export declare const coproduct: typeof coproduct ``` Added in v1.0.0 -## coproduct +## covariant **Signature** ```ts -export declare const coproduct: typeof coproduct +export declare const covariant: typeof covariant ``` Added in v1.0.0 -## covariant +## equivalence **Signature** ```ts -export declare const covariant: typeof covariant +export declare const equivalence: typeof equivalence ``` Added in v1.0.0 @@ -222,16 +212,6 @@ export declare const monoid: typeof monoid Added in v1.0.0 -## nonEmptyTraversable - -**Signature** - -```ts -export declare const nonEmptyTraversable: typeof nonEmptyTraversable -``` - -Added in v1.0.0 - ## of **Signature** @@ -344,6 +324,46 @@ Added in v1.0.0 # utils +## bigint + +**Signature** + +```ts +export declare const bigint: typeof bigint +``` + +Added in v1.0.0 + +## boolean + +**Signature** + +```ts +export declare const boolean: typeof boolean +``` + +Added in v1.0.0 + +## either + +**Signature** + +```ts +export declare const either: typeof either +``` + +Added in v1.0.0 + +## function + +**Signature** + +```ts +export declare const function: typeof _function +``` + +Added in v1.0.0 + ## hkt **Signature** @@ -353,3 +373,123 @@ export declare const hkt: typeof hkt ``` Added in v1.0.0 + +## identity + +**Signature** + +```ts +export declare const identity: typeof identity +``` + +Added in v1.0.0 + +## number + +**Signature** + +```ts +export declare const number: typeof number +``` + +Added in v1.0.0 + +## option + +**Signature** + +```ts +export declare const option: typeof option +``` + +Added in v1.0.0 + +## ordering + +**Signature** + +```ts +export declare const ordering: typeof ordering +``` + +Added in v1.0.0 + +## predicate + +**Signature** + +```ts +export declare const predicate: typeof predicate +``` + +Added in v1.0.0 + +## readonlyArray + +**Signature** + +```ts +export declare const readonlyArray: typeof readonlyArray +``` + +Added in v1.0.0 + +## readonlyRecord + +**Signature** + +```ts +export declare const readonlyRecord: typeof readonlyRecord +``` + +Added in v1.0.0 + +## string + +**Signature** + +```ts +export declare const string: typeof string +``` + +Added in v1.0.0 + +## struct + +**Signature** + +```ts +export declare const struct: typeof struct +``` + +Added in v1.0.0 + +## symbol + +**Signature** + +```ts +export declare const symbol: typeof symbol +``` + +Added in v1.0.0 + +## these + +**Signature** + +```ts +export declare const these: typeof these +``` + +Added in v1.0.0 + +## tuple + +**Signature** + +```ts +export declare const tuple: typeof tuple +``` + +Added in v1.0.0 diff --git a/docs/modules/typeclass/Alternative.ts.md b/docs/modules/typeclass/Alternative.ts.md index 317683fba..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: 5 +nav_order: 19 parent: Modules --- diff --git a/docs/modules/typeclass/Applicative.ts.md b/docs/modules/typeclass/Applicative.ts.md index 4a3f40dc2..cc9f54179 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: 6 +nav_order: 20 parent: Modules --- @@ -15,7 +15,7 @@ Added in v1.0.0 - [type class](#type-class) - [Applicative (interface)](#applicative-interface) - [utils](#utils) - - [liftMonoid](#liftmonoid) + - [getMonoid](#getmonoid) --- @@ -33,14 +33,17 @@ Added in v1.0.0 # utils -## liftMonoid +## getMonoid -Lift a monoid into 'F', the inner values are combined using the provided `Monoid`. +Lift a `Monoid` into `F`, combining the inner values using the provided `Monoid`: + +- `combine` is provided by {@link semiApplicative.getSemigroup}. +- `empty` is `F.of(M.empty)` **Signature** ```ts -export declare const liftMonoid: ( +export declare const getMonoid: ( F: Applicative ) => (M: Monoid
) => Monoid> ``` diff --git a/docs/modules/typeclass/Bicovariant.ts.md b/docs/modules/typeclass/Bicovariant.ts.md index 7101ccd07..bd1d571fa 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: 7 +nav_order: 21 parent: Modules --- @@ -29,10 +29,10 @@ Added in v1.0.0 ```ts export interface Bicovariant extends TypeClass { - readonly bimap: ( - f: (e: E1) => E2, - g: (a: A) => B - ) => (self: Kind) => Kind + readonly bimap: { + (f: (e: E1) => E2, g: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (e: E1) => E2, g: (a: A) => B): Kind + } } ``` @@ -42,7 +42,7 @@ Added in v1.0.0 ## bimapComposition -Returns a default `bimap` composition. +Returns a default ternary `bimap` composition. **Signature** @@ -50,11 +50,10 @@ Returns a default `bimap` composition. export declare const bimapComposition: ( CovariantF: Covariant, BicovariantG: Bicovariant -) => ( +) => ( + self: Kind>, f: (e: E1) => E2, g: (a: A) => B -) => ( - self: Kind> ) => Kind> ``` @@ -69,19 +68,27 @@ Returns a default `map` implementation. ```ts export declare const map: ( F: Bicovariant -) => (f: (a: A) => B) => (self: Kind) => Kind +) => { + (f: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (a: A) => B): Kind +} ``` Added in v1.0.0 ## mapLeft +Returns a default `mapLeft` implementation. + **Signature** ```ts export declare const mapLeft: ( F: Bicovariant -) => (f: (e: E1) => E2) => (self: Kind) => Kind +) => { + (f: (e: E) => G): (self: Kind) => Kind + (self: Kind, f: (e: E) => G): Kind +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Bounded.ts.md b/docs/modules/typeclass/Bounded.ts.md index 32a569fea..97380847c 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: 8 +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) => Monoid +``` + +Added in v1.0.0 + +## min + +`Monoid` that returns last minimum of elements. + +**Signature** + +```ts +export declare const min: (B: Bounded) => Monoid +``` + +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 2570d40b9..6b1a4793f 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: 9 +nav_order: 23 parent: Modules --- @@ -12,17 +12,18 @@ Added in v1.0.0

Table of contents

-- [sequencing](#sequencing) +- [combining](#combining) - [andThenDiscard](#andthendiscard) +- [do notation](#do-notation) + - [bind](#bind) - [type class](#type-class) - [Chainable (interface)](#chainable-interface) - [utils](#utils) - - [bind](#bind) - [tap](#tap) --- -# sequencing +# combining ## andThenDiscard @@ -34,44 +35,63 @@ produced by the effect. ```ts export declare const andThenDiscard: ( F: Chainable -) => ( - that: Kind -) => (self: Kind) => Kind +) => { + (that: Kind): ( + self: Kind + ) => Kind + (self: Kind, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + A + > +} ``` Added in v1.0.0 -# type class +# do notation -## Chainable (interface) +## bind **Signature** ```ts -export interface Chainable extends FlatMap, Covariant {} +export declare const bind: ( + F: Chainable +) => { + (name: Exclude, f: (a: A) => Kind): < + R1, + O1, + E1 + >( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: A) => Kind + ): Kind +} ``` Added in v1.0.0 -# utils +# type class -## bind +## Chainable (interface) **Signature** ```ts -export declare const bind: ( - F: Chainable -) => ( - name: Exclude, - f: (a: A) => Kind -) => ( - self: Kind -) => Kind +export interface Chainable extends FlatMap, Covariant {} ``` Added in v1.0.0 +# utils + ## tap Returns an effect that effectfully "peeks" at the success of this effect. @@ -81,9 +101,18 @@ Returns an effect that effectfully "peeks" at the success of this effect. ```ts export declare const tap: ( F: Chainable -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind +) => { + (f: (a: A) => Kind): ( + self: Kind + ) => Kind + (self: Kind, f: (a: A) => Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + A + > +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Compactable.ts.md b/docs/modules/typeclass/Compactable.ts.md deleted file mode 100644 index b08e8fb3d..000000000 --- a/docs/modules/typeclass/Compactable.ts.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: typeclass/Compactable.ts -nav_order: 10 -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: Covariant, - G: Compactable -) => ( - self: Kind> -) => Kind> -``` - -Added in v1.0.0 - -## separate - -**Signature** - -```ts -export declare const separate: ( - F: Covariant & Compactable -) => (self: Kind) => readonly [Kind, Kind] -``` - -Added in v1.0.0 diff --git a/docs/modules/typeclass/Contravariant.ts.md b/docs/modules/typeclass/Contravariant.ts.md index 8fca0bcf2..e32cd0c6d 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: 11 +nav_order: 24 parent: Modules --- @@ -12,8 +12,6 @@ Added in v1.0.0

Table of contents

-- [constructors](#constructors) - - [make](#make) - [type class](#type-class) - [Contravariant (interface)](#contravariant-interface) - [utils](#utils) @@ -22,20 +20,6 @@ Added in v1.0.0 --- -# constructors - -## make - -**Signature** - -```ts -export declare const make: ( - contramap: (f: (b: B) => A) => (self: Kind) => Kind -) => Contravariant -``` - -Added in v1.0.0 - # type class ## Contravariant (interface) @@ -44,7 +28,10 @@ Added in v1.0.0 ```ts export interface Contravariant extends Invariant { - readonly contramap: (f: (b: B) => A) => (self: Kind) => Kind + readonly contramap: { + (f: (b: B) => A): (self: Kind) => Kind + (self: Kind, f: (b: B) => A): Kind + } } ``` @@ -56,7 +43,7 @@ Added in v1.0.0 Composing two contravariant functors yields a Covariant functor. -Returns a default `map` composition. +Returns a default binary `map` composition. **Signature** @@ -64,10 +51,9 @@ Returns a default `map` composition. export declare const contramapComposition: ( F: Contravariant, G: Contravariant -) => ( +) => ( + self: Kind>, f: (a: A) => B -) => ( - self: Kind> ) => Kind> ``` @@ -81,8 +67,11 @@ Returns a default `imap` implementation. ```ts export declare const imap: ( - contramap: (f: (b: B) => A) => (self: Kind) => Kind -) => (to: (a: A) => B, from: (b: B) => A) => (self: Kind) => Kind + contramap: (self: Kind, f: (b: B) => A) => Kind +) => { + (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind + (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Coproduct.ts.md b/docs/modules/typeclass/Coproduct.ts.md index 6d61c5902..60abbe45f 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: 12 +nav_order: 25 parent: Modules --- diff --git a/docs/modules/typeclass/Covariant.ts.md b/docs/modules/typeclass/Covariant.ts.md index dd37919a2..32581ead9 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: 13 +nav_order: 26 parent: Modules --- @@ -12,8 +12,8 @@ Added in v1.0.0

Table of contents

-- [constructors](#constructors) - - [make](#make) +- [do notation](#do-notation) + - [let](#let) - [mapping](#mapping) - [as](#as) - [asUnit](#asunit) @@ -22,21 +22,29 @@ Added in v1.0.0 - [Covariant (interface)](#covariant-interface) - [utils](#utils) - [imap](#imap) - - [let](#let) - [mapComposition](#mapcomposition) --- -# constructors +# do notation -## make +## let **Signature** ```ts -export declare const make: ( - map: (f: (a: A) => B) => (self: Kind) => Kind -) => Covariant +export declare const let: ( + F: Covariant +) => { + (name: Exclude, f: (a: A) => B): ( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: A) => B + ): Kind +} ``` Added in v1.0.0 @@ -50,7 +58,10 @@ Added in v1.0.0 ```ts export declare const as: ( F: Covariant -) => (b: B) => (self: Kind) => Kind +) => { + (b: B): (self: Kind) => Kind + (self: Kind, b: B): Kind +} ``` Added in v1.0.0 @@ -74,7 +85,10 @@ Added in v1.0.0 ```ts export declare const flap: ( F: Covariant -) =>
(a: A) => (self: Kind B>) => Kind +) => { + (self: Kind B>): (a: A) => Kind + (a: A, self: Kind B>): Kind +} ``` Added in v1.0.0 @@ -87,7 +101,10 @@ Added in v1.0.0 ```ts export interface Covariant extends Invariant { - readonly map: (f: (a: A) => B) => (self: Kind) => Kind + readonly map: { + (f: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (a: A) => B): Kind + } } ``` @@ -103,25 +120,11 @@ Returns a default `imap` implementation. ```ts export declare const imap: ( - map: (f: (a: A) => B) => (self: Kind) => Kind -) => (to: (a: A) => B, from: (b: B) => A) => (self: Kind) => Kind -``` - -Added in v1.0.0 - -## let - -**Signature** - -```ts -export declare const let: ( - F: Covariant -) => ( - name: Exclude, - f: (a: A) => B -) => ( - self: Kind -) => Kind + map: (self: Kind, f: (a: A) => B) => Kind +) => { + (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind + (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind +} ``` Added in v1.0.0 @@ -136,10 +139,9 @@ Returns a default `map` composition. export declare const mapComposition: ( F: Covariant, G: Covariant -) => ( +) => ( + self: Kind>, f: (a: A) => B -) => ( - self: Kind> ) => Kind> ``` diff --git a/docs/modules/typeclass/Equivalence.ts.md b/docs/modules/typeclass/Equivalence.ts.md new file mode 100644 index 000000000..af8b11e36 --- /dev/null +++ b/docs/modules/typeclass/Equivalence.ts.md @@ -0,0 +1,258 @@ +--- +title: typeclass/Equivalence.ts +nav_order: 27 +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) + - [struct](#struct) + - [tuple](#tuple) +- [constructors](#constructors) + - [make](#make) + - [strict](#strict) +- [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 + (self: Equivalence, f: (b: B) => A): 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: >>( + predicates: R +) => Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence] ? A : never }> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Equivalence`s. + +``` +[Equivalence, Equivalence, ...] -> Equivalence<[A, B, ...]> +``` + +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: []>( + ...predicates: T +) => Equivalence] ? A : never }>> +``` + +Added in v1.0.0 + +# constructors + +## make + +**Signature** + +```ts +export declare const make: (isEquivalent: (self: A, that: A) => boolean) => 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 + +# instances + +## Contravariant + +**Signature** + +```ts +export declare const Contravariant: contravariant.Contravariant +``` + +Added in v1.0.0 + +## Invariant + +**Signature** + +```ts +export declare const Invariant: invariant.Invariant +``` + +Added in v1.0.0 + +## Product + +**Signature** + +```ts +export declare const Product: product_.Product +``` + +Added in v1.0.0 + +## SemiProduct + +**Signature** + +```ts +export declare const SemiProduct: semiProduct.SemiProduct +``` + +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: () => Monoid> +``` + +Added in v1.0.0 + +## getSemigroup + +**Signature** + +```ts +export declare const getSemigroup: () => Semigroup> +``` + +Added in v1.0.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 { + (self: A, that: 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 index 848aa819d..03c4eab24 100644 --- a/docs/modules/typeclass/Filterable.ts.md +++ b/docs/modules/typeclass/Filterable.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/Filterable.ts -nav_order: 14 +nav_order: 28 parent: Modules --- @@ -17,10 +17,12 @@ Added in v1.0.0 - [models](#models) - [Filterable (interface)](#filterable-interface) - [utils](#utils) + - [compact](#compact) - [filter](#filter) - [filterMapComposition](#filtermapcomposition) - [partition](#partition) - - [partitionMap](#partitionmap) + - [partitionMapComposition](#partitionmapcomposition) + - [separate](#separate) --- @@ -32,7 +34,17 @@ Added in v1.0.0 ```ts export interface Filterable extends TypeClass { - readonly filterMap: (f: (a: A) => Option) => (self: Kind) => Kind + readonly partitionMap: { + (f: (a: A) => Either): ( + self: Kind + ) => [Kind, Kind] + (self: Kind, f: (a: A) => Either): [Kind, Kind] + } + + readonly filterMap: { + (f: (a: A) => Option): (self: Kind) => Kind + (self: Kind, f: (a: A) => Option): Kind + } } ``` @@ -40,6 +52,18 @@ Added in v1.0.0 # utils +## compact + +**Signature** + +```ts +export declare const compact: ( + F: Filterable +) => (self: Kind>) => Kind +``` + +Added in v1.0.0 + ## filter **Signature** @@ -52,6 +76,14 @@ export declare const filter: ( self: Kind ) => Kind (predicate: (a: A) => boolean): (self: Kind) => Kind + (self: Kind, refinement: (a: A) => a is B): Kind< + F, + R, + O, + E, + B + > + (self: Kind, predicate: (a: A) => boolean): Kind } ``` @@ -59,7 +91,7 @@ Added in v1.0.0 ## filterMapComposition -Returns a default `filterMap` composition. +Returns a default binary `filterMap` composition. **Signature** @@ -67,10 +99,9 @@ Returns a default `filterMap` composition. export declare const filterMapComposition: ( F: Covariant, G: Filterable -) => ( - f: (a: A) => any -) => ( - self: Kind> +) => ( + self: Kind>, + f: (a: A) => Option ) => Kind> ``` @@ -86,25 +117,49 @@ export declare 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] + (self: Kind, refinement: (a: A) => a is B): [ + Kind, + Kind + ] + (self: Kind, predicate: (a: A) => boolean): [ + Kind, + Kind + ] } ``` Added in v1.0.0 -## partitionMap +## partitionMapComposition + +Returns a default binary `partitionMap` composition. + +**Signature** + +```ts +export declare const partitionMapComposition: ( + F: Covariant, + G: Filterable +) => ( + self: Kind>, + f: (a: A) => Either +) => [Kind>, Kind>] +``` + +Added in v1.0.0 + +## separate **Signature** ```ts -export declare const partitionMap: ( +export declare const separate: ( F: Filterable -) => ( - f: (a: A) => any -) => (self: Kind) => readonly [Kind, Kind] +) => (self: Kind>) => [Kind, Kind] ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/FlatMap.ts.md b/docs/modules/typeclass/FlatMap.ts.md index 3fad467cb..492f0d993 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: 15 +nav_order: 29 parent: Modules --- @@ -29,9 +29,18 @@ Added in v1.0.0 ```ts export interface FlatMap extends TypeClass { - readonly flatMap: ( - f: (a: A) => Kind - ) => (self: Kind) => Kind + readonly flatMap: { + (f: (a: A) => Kind): ( + self: Kind + ) => Kind + (self: Kind, f: (a: A) => Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + B + > + } } ``` @@ -48,9 +57,18 @@ A variant of `flatMap` that ignores the value produced by this effect. ```ts export declare const andThen: ( F: FlatMap -) => ( - that: Kind -) => (self: Kind) => Kind +) => { + (that: Kind): ( + self: Kind + ) => Kind + (self: Kind, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + B + > +} ``` Added in v1.0.0 @@ -62,9 +80,14 @@ Added in v1.0.0 ```ts export declare const composeKleisliArrow: ( F: FlatMap -) => ( - bfc: (b: B) => Kind -) => (afb: (a: A) => Kind) => (a: A) => Kind +) => { + (bfc: (b: B) => Kind): ( + afb: (a: A) => Kind + ) => (a: A) => Kind + (afb: (a: A) => Kind, bfc: (b: B) => Kind): ( + a: A + ) => Kind +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Foldable.ts.md b/docs/modules/typeclass/Foldable.ts.md index 3ab20e875..c505ac843 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: 16 +nav_order: 30 parent: Modules --- @@ -15,14 +15,12 @@ Added in v1.0.0 - [type class](#type-class) - [Foldable (interface)](#foldable-interface) - [utils](#utils) - - [foldMap](#foldmap) - - [foldMapKind](#foldmapkind) + - [combineMap](#combinemap) + - [coproductMapKind](#coproductmapkind) - [reduceComposition](#reducecomposition) - [reduceKind](#reducekind) - - [reduceRight](#reduceright) - - [reduceRightKind](#reducerightkind) - - [toReadonlyArray](#toreadonlyarray) - - [toReadonlyArrayWith](#toreadonlyarraywith) + - [toArray](#toarray) + - [toArrayMap](#toarraymap) --- @@ -34,7 +32,10 @@ Added in v1.0.0 ```ts export interface Foldable extends TypeClass { - readonly reduce: (b: B, f: (b: B, a: A) => B) => (self: Kind) => B + readonly reduce: { + (b: B, f: (b: B, a: A) => B): (self: Kind) => B + (self: Kind, b: B, f: (b: B, a: A) => B): B + } } ``` @@ -42,37 +43,41 @@ Added in v1.0.0 # utils -## foldMap +## combineMap **Signature** ```ts -export declare const foldMap: ( +export declare const combineMap: ( F: Foldable -) => (M: Monoid) => (f: (a: A) => M) => (self: Kind) => M +) => (M: Monoid) => { + (f: (a: A) => M): (self: Kind) => M + (self: Kind, f: (a: A) => M): M +} ``` Added in v1.0.0 -## foldMapKind +## coproductMapKind **Signature** ```ts -export declare const foldMapKind: ( +export declare const coproductMapKind: ( F: Foldable ) => ( G: Coproduct -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind +) => { + (f: (a: A) => Kind): (self: Kind) => Kind + (self: Kind, f: (a: A) => Kind): Kind +} ``` Added in v1.0.0 ## reduceComposition -Returns a default `reduce` composition. +Returns a default ternary `reduce` composition. **Signature** @@ -80,10 +85,7 @@ Returns a default `reduce` composition. export declare const reduceComposition: ( F: Foldable, G: Foldable -) => ( - b: B, - f: (b: B, a: A) => B -) => (self: Kind>) => B +) => (self: Kind>, b: B, f: (b: B, a: A) => B) => B ``` Added in v1.0.0 @@ -97,63 +99,43 @@ export declare const reduceKind: ( F: Foldable ) => ( G: Monad -) => ( - b: B, - f: (b: B, a: A) => Kind -) => (self: Kind) => Kind -``` - -Added in v1.0.0 - -## reduceRight - -**Signature** - -```ts -export declare const reduceRight: ( - F: Foldable -) => (b: B, f: (b: B, a: A) => B) => (self: Kind) => B -``` - -Added in v1.0.0 - -## reduceRightKind - -**Signature** - -```ts -export declare const reduceRightKind: ( - F: Foldable -) => ( - G: Monad -) => ( - b: B, - f: (b: B, a: A) => Kind -) => (self: Kind) => Kind +) => { + (b: B, f: (b: B, a: A) => Kind): ( + self: Kind + ) => Kind + (self: Kind, b: B, f: (b: B, a: A) => Kind): Kind< + G, + R, + O, + E, + B + > +} ``` Added in v1.0.0 -## toReadonlyArray +## toArray **Signature** ```ts -export declare const toReadonlyArray: ( - F: Foldable -) => (self: Kind) => readonly A[] +export declare const toArray: (F: Foldable) => (self: Kind) => A[] ``` Added in v1.0.0 -## toReadonlyArrayWith +## toArrayMap **Signature** ```ts -export declare const toReadonlyArrayWith: ( +export declare const toArrayMap: ( F: Foldable -) => (f: (a: A) => B) => (self: Kind) => readonly B[] +) => { + (f: (a: A) => B): (self: Kind) => B[] + (self: Kind, f: (a: A) => B): B[] +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Invariant.ts.md b/docs/modules/typeclass/Invariant.ts.md index 23f06e68e..fa6c6d712 100644 --- a/docs/modules/typeclass/Invariant.ts.md +++ b/docs/modules/typeclass/Invariant.ts.md @@ -1,62 +1,71 @@ --- title: typeclass/Invariant.ts -nav_order: 17 +nav_order: 31 parent: Modules --- ## Invariant overview +The `Invariant` typeclass is a higher-order abstraction over types that allow mapping the contents of a type in both directions. +It is similar to the `Covariant` typeclass but provides an `imap` opration, which allows transforming a value in both directions. +This typeclass is useful when dealing with data types that can be converted to and from some other types. +The `imap` operation provides a way to convert such data types to other types that they can interact with while preserving their invariants. + Added in v1.0.0 ---

Table of contents

+- [do notation](#do-notation) + - [bindTo](#bindto) - [type class](#type-class) - [Invariant (interface)](#invariant-interface) - [utils](#utils) - - [bindTo](#bindto) - [imapComposition](#imapcomposition) - [tupled](#tupled) --- -# type class +# do notation -## Invariant (interface) +## bindTo **Signature** ```ts -export interface Invariant extends TypeClass { - readonly imap: ( - to: (a: A) => B, - from: (b: B) => A - ) => (self: Kind) => Kind +export declare const bindTo: ( + F: Invariant +) => { + (name: N): (self: Kind) => Kind + (self: Kind, name: N): Kind } ``` Added in v1.0.0 -# utils +# type class -## bindTo +## Invariant (interface) **Signature** ```ts -export declare const bindTo: ( - F: Invariant -) => ( - name: N -) => (self: Kind) => Kind +export interface Invariant extends TypeClass { + readonly imap: { + (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind + (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind + } +} ``` Added in v1.0.0 +# utils + ## imapComposition -Returns a default `imap` composition. +Returns a default ternary `imap` composition. **Signature** @@ -64,11 +73,10 @@ Returns a default `imap` composition. export declare const imapComposition: ( F: Invariant, G: Invariant -) => ( +) => ( + self: Kind>, to: (a: A) => B, from: (b: B) => A -) => ( - self: Kind> ) => Kind> ``` @@ -76,12 +84,14 @@ Added in v1.0.0 ## tupled +Convert a value in a singleton array in a given effect. + **Signature** ```ts export declare const tupled: ( F: Invariant -) => (self: Kind) => Kind +) => (self: Kind) => Kind ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Monad.ts.md b/docs/modules/typeclass/Monad.ts.md index 2ed430af1..9dea29887 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: 18 +nav_order: 32 parent: Modules --- diff --git a/docs/modules/typeclass/Monoid.ts.md b/docs/modules/typeclass/Monoid.ts.md index d57a29de2..8f62f8566 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: 19 +nav_order: 33 parent: Modules --- @@ -12,25 +12,106 @@ Added in v1.0.0

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [reverse](#reverse) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - [fromSemigroup](#fromsemigroup) - [max](#max) - [min](#min) +- [instances](#instances) + - [bigintMultiply](#bigintmultiply) + - [bigintSum](#bigintsum) + - [booleanAll](#booleanall) + - [booleanAny](#booleanany) + - [booleanEqv](#booleaneqv) + - [booleanXor](#booleanxor) + - [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 `Semigroup` for `ReadonlyArray
`. + +The `empty` value is the empty array. + +**Signature** + +```ts +export declare const array: () => 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 + +This function creates and returns a new `Monoid` for a struct of values based on the given `Monoid`s for each property in the struct. +The returned `Monoid` combines two structs of the same type by applying the corresponding `Monoid` passed as arguments to each property in the struct. + +The `empty` value of the returned `Monoid` is a struct where each property is the `empty` value of the corresponding `Monoid` in the input `monoids` object. + +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: }>( + fields: R +) => Monoid<{ readonly [K in keyof R]: [R[K]] extends [Monoid] ? A : never }> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Monoid`s. + +``` +[Monoid, Monoid, ...] -> Monoid<[A, B, ...]> +``` + +This function creates and returns a new `Monoid` for a tuple of values based on the given `Monoid`s for each element in the tuple. +The returned `Monoid` combines two tuples of the same type by applying the corresponding `Monoid` passed as arguments to each element in the tuple. + +The `empty` value of the returned `Monoid` is the tuple of `empty` values of the input `Monoid`s. + +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: []>( + ...elements: T +) => Monoid<{ readonly [I in keyof T]: [T[I]] extends [Monoid] ? A : never }> +``` + +Added in v1.0.0 + # constructors ## fromSemigroup -Optimised. - **Signature** ```ts @@ -67,57 +148,141 @@ export declare const min: (B: Bounded) => 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 + +`boolean` monoid under conjunction. -Given a struct of monoids returns a monoid for the struct. +The `empty` value is `true`. **Signature** ```ts -export declare const struct: (monoids: { [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. + +The `empty` value is `false`. + +**Signature** + +```ts +export declare const booleanAny: Monoid +``` + +Added in v1.0.0 + +## booleanEqv + +`boolean` monoid under equivalence. + +The `empty` value is `true`. + +**Signature** + +```ts +export declare const booleanEqv: Monoid +``` + +Added in v1.0.0 + +## booleanXor + +`boolean` monoid under exclusive disjunction. + +The `empty` value is `false`. + +**Signature** + +```ts +export declare const booleanXor: Monoid +``` + +Added in v1.0.0 + +## numberMultiply -Given a tuple of monoids returns a monoid for the tuple. +`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 tuple: ( - ...monoids: { [K in keyof A]: Monoid } -) => Monoid> +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 569dbb3af..000000000 --- a/docs/modules/typeclass/NonEmptyTraversable.ts.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: typeclass/NonEmptyTraversable.ts -nav_order: 20 -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 2db971ade..6ad0fd9ae 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: 21 +nav_order: 34 parent: Modules --- @@ -12,41 +12,44 @@ Added in v1.0.0

Table of contents

+- [do notation](#do-notation) + - [Do](#do) - [type class](#type-class) - [Of (interface)](#of-interface) - [utils](#utils) - - [Do](#do) - [ofComposition](#ofcomposition) - [unit](#unit) --- -# type class +# do notation -## Of (interface) +## Do **Signature** ```ts -export interface Of extends TypeClass { - readonly of:
(a: A) => Kind -} +export declare const Do: (F: Of) => Kind ``` Added in v1.0.0 -# utils +# type class -## Do +## Of (interface) **Signature** ```ts -export declare const Do: (F: Of) => Kind +export interface Of extends TypeClass { + readonly of: (a: A) => Kind +} ``` Added in v1.0.0 +# utils + ## ofComposition Returns a default `of` composition. diff --git a/docs/modules/typeclass/Order.ts.md b/docs/modules/typeclass/Order.ts.md index 3f8621165..0405c06aa 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: 22 +nav_order: 35 parent: Modules --- @@ -12,15 +12,24 @@ Added in v1.0.0

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [contramap](#contramap) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - - [fromCompare](#fromcompare) + - [make](#make) - [instances](#instances) - [Contravariant](#contravariant) - [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) @@ -28,7 +37,6 @@ Added in v1.0.0 - [utils](#utils) - [between](#between) - [clamp](#clamp) - - [contramap](#contramap) - [greaterThan](#greaterthan) - [greaterThanOrEqualTo](#greaterthanorequalto) - [lessThan](#lessthan) @@ -36,20 +44,85 @@ Added in v1.0.0 - [max](#max) - [min](#min) - [reverse](#reverse) - - [tuple](#tuple) --- -# constructors +# 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 + +## contramap + +**Signature** + +```ts +export declare const contramap: { + (f: (b: B) => A): (self: Order) => Order + (self: Order, f: (b: B) => A): 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. -## fromCompare +**Signature** + +```ts +export declare const struct: }>( + fields: R +) => Order<{ [K in keyof R]: [R[K]] extends [Order] ? A : never }> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Order`s. + +``` +[Order, Order, ...] -> Order<[A, B, ...]> +``` + +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. -Main constructor. +**Signature** + +```ts +export declare const tuple: []>( + ...elements: T +) => Order<{ [I in keyof T]: [T[I]] extends [Order] ? A : never }> +``` + +Added in v1.0.0 + +# constructors + +## make **Signature** ```ts -export declare const fromCompare: (compare: (that: A) => (self: A) => 0 | 1 | -1) => Order +export declare const make: (compare: (self: A, that: A) => 0 | 1 | -1) => Order ``` Added in v1.0.0 @@ -81,7 +154,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Product: product.Product +export declare const Product: product_.Product ``` Added in v1.0.0 @@ -96,6 +169,26 @@ export declare const SemiProduct: semiProduct.SemiProduct 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 + ## getMonoid **Signature** @@ -116,6 +209,26 @@ export declare const getSemigroup: () => Semigroup> 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 + # type class ## Order (interface) @@ -124,7 +237,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 } ``` @@ -153,7 +266,10 @@ Test whether a value is between a minimum and a maximum (inclusive). **Signature** ```ts -export declare const between: (O: Order) => (minimum: A, maximum: A) => (a: A) => boolean +export declare const between: (O: Order) => { + (minimum: A, maximum: A): (self: A) => boolean + (self: A, minimum: A, maximum: A): boolean +} ``` Added in v1.0.0 @@ -165,17 +281,10 @@ Clamp a value between a minimum and a maximum. **Signature** ```ts -export declare const clamp: (O: Order) => (minimum: A, maximum: A) => (a: A) => A -``` - -Added in v1.0.0 - -## contramap - -**Signature** - -```ts -export declare const contramap: (f: (b: B) => A) => (self: Order) => Order +export declare const clamp: (O: Order) => { + (minimum: A, maximum: A): (self: A) => A + (self: A, minimum: A, maximum: A): A +} ``` Added in v1.0.0 @@ -187,7 +296,7 @@ Test whether one value is _strictly greater than_ another. **Signature** ```ts -export declare const greaterThan: (O: Order) => (that: A) => (self: A) => boolean +export declare const greaterThan: (O: Order) => { (that: A): (self: A) => boolean; (self: A, that: A): boolean } ``` Added in v1.0.0 @@ -199,7 +308,10 @@ Test whether one value is _non-strictly greater than_ another. **Signature** ```ts -export declare const greaterThanOrEqualTo: (O: Order) => (that: A) => (self: A) => boolean +export declare const greaterThanOrEqualTo: (O: Order) => { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} ``` Added in v1.0.0 @@ -211,7 +323,7 @@ Test whether one value is _strictly less than_ another. **Signature** ```ts -export declare const lessThan: (O: Order) => (that: A) => (self: A) => boolean +export declare const lessThan: (O: Order) => { (that: A): (self: A) => boolean; (self: A, that: A): boolean } ``` Added in v1.0.0 @@ -223,7 +335,10 @@ Test whether one value is _non-strictly less than_ another. **Signature** ```ts -export declare const lessThanOrEqualTo: (O: Order) => (that: A) => (self: A) => boolean +export declare const lessThanOrEqualTo: (O: Order) => { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} ``` Added in v1.0.0 @@ -235,7 +350,7 @@ Take the maximum of two values. If they are considered equal, the first argument **Signature** ```ts -export declare const max: (O: Order) => (that: A) => (self: A) => A +export declare const max: (O: Order) => { (that: A): (self: A) => A; (self: A, that: A): A } ``` Added in v1.0.0 @@ -247,7 +362,7 @@ Take the minimum of two values. If they are considered equal, the first argument **Signature** ```ts -export declare const min: (O: Order) => (that: A) => (self: A) => A +export declare const min: (O: Order) => { (that: A): (self: A) => A; (self: A, that: A): A } ``` Added in v1.0.0 @@ -261,15 +376,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 92e80863e..553c202f7 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: 23 +nav_order: 36 parent: Modules --- diff --git a/docs/modules/typeclass/Product.ts.md b/docs/modules/typeclass/Product.ts.md index 0997d1ced..e142e70c5 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: 24 +nav_order: 37 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> } ``` @@ -43,14 +43,14 @@ Added in v1.0.0 ```ts 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 } + { [K in keyof R]: [R[K]] extends [Kind] ? A : never } > ``` @@ -64,13 +64,13 @@ Added in v1.0.0 export declare const tuple: ( F: Product ) => []>( - ...components: T + ...elements: 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 }> + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } > ``` diff --git a/docs/modules/typeclass/SemiAlternative.ts.md b/docs/modules/typeclass/SemiAlternative.ts.md index 929f0f9b2..4c7acbeff 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: 25 +nav_order: 38 parent: Modules --- diff --git a/docs/modules/typeclass/SemiApplicative.ts.md b/docs/modules/typeclass/SemiApplicative.ts.md index 9f683aa02..622a86041 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: 26 +nav_order: 39 parent: Modules --- @@ -12,123 +12,164 @@ Added in v1.0.0

Table of contents

+- [lifting](#lifting) + - [getSemigroup](#getsemigroup) + - [lift2](#lift2) - [type class](#type-class) - [SemiApplicative (interface)](#semiapplicative-interface) - [utils](#utils) - [andThen](#andthen) - [andThenDiscard](#andthendiscard) - [ap](#ap) - - [lift2](#lift2) - - [lift3](#lift3) - - [liftSemigroup](#liftsemigroup) + - [zipWith](#zipwith) --- -# type class +# lifting -## SemiApplicative (interface) +## getSemigroup + +Lift a `Semigroup` into 'F', the inner values are combined using the provided `Semigroup`. **Signature** ```ts -export interface SemiApplicative extends SemiProduct, Covariant {} +export declare const getSemigroup: ( + F: SemiApplicative +) => (S: Semigroup
) => Semigroup> ``` Added in v1.0.0 -# utils +## lift2 -## andThen +Lifts a binary function into `F`. **Signature** ```ts -export declare const andThen: ( +export declare const lift2: ( F: SemiApplicative -) => ( - that: Kind -) => (self: Kind) => Kind +) => ( + f: (a: A, b: B) => C +) => { + (that: Kind): ( + self: Kind + ) => Kind + (self: Kind, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + C + > +} ``` Added in v1.0.0 -## andThenDiscard +# type class + +## SemiApplicative (interface) **Signature** ```ts -export declare const andThenDiscard: ( - F: SemiApplicative -) => ( - that: Kind -) => (self: Kind) => Kind +export interface SemiApplicative extends SemiProduct, Covariant {} ``` Added in v1.0.0 -## ap +# utils + +## andThen **Signature** ```ts -export declare const ap: ( +export declare const andThen: ( F: SemiApplicative -) => ( - fa: Kind -) => (self: Kind B>) => Kind +) => { + (that: Kind): ( + self: Kind + ) => Kind + (self: Kind, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + B + > +} ``` Added in v1.0.0 -## lift2 - -Lifts a binary function into `F`. +## andThenDiscard **Signature** ```ts -export declare const lift2: ( +export declare const andThenDiscard: ( F: SemiApplicative -) => ( - f: (a: A, b: B) => C -) => ( - fa: Kind, - fb: Kind -) => Kind +) => { + (that: Kind): ( + self: Kind + ) => Kind + (self: Kind, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + A + > +} ``` Added in v1.0.0 -## lift3 - -Lifts a ternary function into 'F'. +## ap **Signature** ```ts -export declare const lift3: ( +export declare const ap: ( F: SemiApplicative -) => ( - f: (a: A, b: B, c: C) => D -) => ( - fa: Kind, - fb: Kind, - fc: Kind -) => Kind +) => { + (that: Kind): ( + self: Kind B> + ) => Kind + (self: Kind B>, that: Kind): Kind< + F, + R1 & R2, + O1 | O2, + E1 | E2, + B + > +} ``` Added in v1.0.0 -## liftSemigroup +## zipWith -Lift a `Semigroup` into 'F', the inner values are combined using the provided `Semigroup`. +Zips two `F` values together using a provided function, returning a new `F` of the result. **Signature** ```ts -export declare const liftSemigroup: ( +export declare const zipWith: ( F: SemiApplicative -) => (S: Semigroup) => Semigroup> +) => { + (that: Kind, f: (a: A, b: B) => C): ( + self: Kind + ) => Kind + ( + self: Kind, + that: Kind, + f: (a: A, b: B) => C + ): Kind +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/SemiCoproduct.ts.md b/docs/modules/typeclass/SemiCoproduct.ts.md index 35b65b636..cba0175d7 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: 27 +nav_order: 40 parent: Modules --- @@ -15,7 +15,6 @@ Added in v1.0.0 - [type class](#type-class) - [SemiCoproduct (interface)](#semicoproduct-interface) - [utils](#utils) - - [coproductEither](#coproducteither) - [getSemigroup](#getsemigroup) --- @@ -28,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 } ``` @@ -42,20 +43,6 @@ Added in v1.0.0 # utils -## coproductEither - -**Signature** - -```ts -export declare const coproductEither: ( - F: SemiCoproduct -) => ( - that: Kind -) => (self: Kind) => Kind -``` - -Added in v1.0.0 - ## getSemigroup **Signature** diff --git a/docs/modules/typeclass/SemiProduct.ts.md b/docs/modules/typeclass/SemiProduct.ts.md index 6ae14d7e4..c1927871c 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: 29 +nav_order: 42 parent: Modules --- @@ -14,14 +14,15 @@ Added in v1.0.0 - [constructors](#constructors) - [productMany](#productmany) +- [do notation](#do-notation) + - [andThenBind](#andthenbind) - [type class](#type-class) - [SemiProduct (interface)](#semiproduct-interface) - [utils](#utils) - - [andThenBind](#andthenbind) + - [appendElement](#appendelement) - [nonEmptyStruct](#nonemptystruct) - [nonEmptyTuple](#nonemptytuple) - [productComposition](#productcomposition) - - [productFlatten](#productflatten) - [productManyComposition](#productmanycomposition) --- @@ -30,19 +31,48 @@ Added in v1.0.0 ## productMany -Returns a default `productMany` implementation (useful for tests). +Returns a default `productMany` implementation. **Signature** ```ts export declare const productMany: ( - Covariant: Covariant, - product: ( + map: { + (f: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (a: A) => B): Kind + }, + product: ( + self: Kind, + that: Kind + ) => Kind +) => (self: Kind, collection: Iterable>) => Kind +``` + +Added in v1.0.0 + +# do notation + +## andThenBind + +**Signature** + +```ts +export declare const andThenBind: ( + F: SemiProduct +) => { + (name: Exclude, that: Kind): < + R1, + O1, + E1 + >( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, that: Kind - ) => (self: Kind) => Kind -) => ( - collection: Iterable> -) => (self: Kind) => Kind + ): Kind +} ``` Added in v1.0.0 @@ -55,13 +85,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]> } ``` @@ -69,19 +101,24 @@ Added in v1.0.0 # utils -## andThenBind +## appendElement + +Appends an element to the end of a tuple. **Signature** ```ts -export declare const andThenBind: ( +export declare const appendElement: ( F: SemiProduct -) => ( - name: Exclude, - fb: Kind -) => ( - self: Kind -) => Kind +) => { + (that: Kind): ( + self: Kind + ) => Kind + ( + self: Kind, + that: Kind + ): Kind +} ``` Added in v1.0.0 @@ -93,14 +130,14 @@ Added in v1.0.0 ```ts export declare const nonEmptyStruct: ( F: SemiProduct -) => >>( - fields: EnforceNonEmptyRecord & Record> +) => }>( + fields: EnforceNonEmptyRecord & { readonly [x: string]: Kind } ) => 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 } + { [K in keyof R]: [R[K]] extends [Kind] ? A : never } > ``` @@ -113,14 +150,14 @@ Added in v1.0.0 ```ts export declare const nonEmptyTuple: ( F: SemiProduct -) => , ...Kind[]]>( - ...components: T +) => , ...Kind[]]>( + ...elements: 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 }> + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } > ``` @@ -136,27 +173,10 @@ Returns a default `product` composition. export declare const productComposition: ( F: SemiApplicative, G: SemiProduct -) => ( +) => ( + self: Kind>, that: Kind> -) => ( - self: Kind> -) => Kind> -``` - -Added in v1.0.0 - -## productFlatten - -**Signature** - -```ts -export declare const productFlatten: ( - F: SemiProduct -) => ( - that: Kind -) => ( - self: Kind -) => Kind +) => Kind> ``` Added in v1.0.0 @@ -172,10 +192,9 @@ export declare const productManyComposition: , G: SemiProduct ) => ( + self: Kind>, collection: Iterable>> -) => ( - self: Kind> -) => Kind> +) => Kind> ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/Semigroup.ts.md b/docs/modules/typeclass/Semigroup.ts.md index 86da6ec79..a71d23454 100644 --- a/docs/modules/typeclass/Semigroup.ts.md +++ b/docs/modules/typeclass/Semigroup.ts.md @@ -1,47 +1,41 @@ --- title: typeclass/Semigroup.ts -nav_order: 28 +nav_order: 41 parent: Modules --- ## Semigroup overview -`Semigroup` describes a way of combining two values of type `A` that is associative. - -```ts -export interface Semigroup { - readonly combine: (that: A) => (self: A) => A - readonly combineMany: (collection: Iterable) => (self: A) => A -} -``` - -The combine operator must be associative, meaning that if we combine `a` with `b` and then combine the result -with `c` we must get the same value as if we combine `b` with `c` and then combine `a` with the result. - -``` -(a <> b) <> c === a <> (b <> c) -``` - -The `Semigroup` abstraction allows us to combine values of a data type to build a new value of that data type -with richer structure. - Added in v1.0.0 ---

Table of contents

+- [combinators](#combinators) + - [array](#array) + - [struct](#struct) + - [tuple](#tuple) - [constructors](#constructors) - [constant](#constant) - - [fromCombine](#fromcombine) + - [make](#make) - [max](#max) - [min](#min) - [instances](#instances) - [Invariant](#invariant) - [Product](#product) - [SemiProduct](#semiproduct) + - [bigintMultiply](#bigintmultiply) + - [bigintSum](#bigintsum) + - [booleanAll](#booleanall) + - [booleanAny](#booleanany) + - [booleanEqv](#booleaneqv) + - [booleanXor](#booleanxor) - [first](#first) - [last](#last) + - [numberMultiply](#numbermultiply) + - [numberSum](#numbersum) + - [string](#string) - [type class](#type-class) - [Semigroup (interface)](#semigroup-interface) - [type lambdas](#type-lambdas) @@ -50,11 +44,64 @@ 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 `ReadonlyArray
`. +The returned `Semigroup` combines two arrays by concatenating them. + +**Signature** + +```ts +export declare const array: () => 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: }>( + fields: R +) => Semigroup<{ readonly [K in keyof R]: [R[K]] extends [Semigroup] ? A : never }> +``` + +Added in v1.0.0 + +## tuple + +Similar to `Promise.all` but operates on `Semigroup`s. + +``` +[Semigroup, Semigroup, ...] -> Semigroup<[A, B, ...]> +``` + +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: []>( + ...elements: T +) => Semigroup<{ readonly [I in keyof T]: [T[I]] extends [Semigroup] ? A : never }> +``` + +Added in v1.0.0 + # constructors ## constant @@ -67,14 +114,15 @@ export declare const constant: (a: A) => Semigroup Added in v1.0.0 -## fromCombine - -Useful when `combineMany` can't be optimised. +## make **Signature** ```ts -export declare const fromCombine: (combine: (that: A) => (self: A) => A) => Semigroup +export declare const make: ( + combine: (self: A, that: A) => A, + combineMany?: (self: A, collection: Iterable) => A +) => Semigroup ``` Added in v1.0.0 @@ -120,7 +168,7 @@ Added in v1.0.0 **Signature** ```ts -export declare const Product: product.Product +export declare const Product: product_.Product ``` Added in v1.0.0 @@ -135,6 +183,78 @@ export declare const SemiProduct: semiProduct.SemiProduct 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 + +## booleanEqv + +`boolean` semigroup under equivalence. + +**Signature** + +```ts +export declare const booleanEqv: Semigroup +``` + +Added in v1.0.0 + +## booleanXor + +`boolean` semigroup under exclusive disjunction. + +**Signature** + +```ts +export declare const booleanXor: Semigroup +``` + +Added in v1.0.0 + ## first Always return the first argument. @@ -159,93 +279,105 @@ 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): (self: Semigroup) => Semigroup + (self: Semigroup, to: (a: A) => B, from: (b: B) => A): Semigroup +} ``` Added in v1.0.0 -## tuple +## intercalate + +**Signature** + +```ts +export declare const intercalate: { + (separator: A): (S: Semigroup) => Semigroup + (S: Semigroup, separator: A): Semigroup +} +``` -Given a tuple of associatives returns an associative for the tuple. +Added in v1.0.0 + +## reverse + +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 93c5e280c..9d32ab79e 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: 30 +nav_order: 43 parent: Modules --- @@ -16,7 +16,6 @@ Added in v1.0.0 - [Traversable (interface)](#traversable-interface) - [utils](#utils) - [sequence](#sequence) - - [sequenceComposition](#sequencecomposition) - [traverseComposition](#traversecomposition) - [traverseTap](#traversetap) @@ -32,15 +31,18 @@ Added in v1.0.0 export interface Traversable extends TypeClass { readonly traverse: ( F: Applicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> - - readonly sequence: ( - F: Applicative - ) => ( - self: Kind> - ) => Kind> + ) => { + (f: (a: A) => Kind): ( + self: Kind + ) => Kind> + (self: Kind, f: (a: A) => Kind): Kind< + F, + R, + O, + E, + Kind + > + } } ``` @@ -56,12 +58,8 @@ Returns a default `sequence` implementation. ```ts export declare const sequence: ( - traverse: ( - F: Applicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> -) => ( + T: Traversable +) => ( F: Applicative ) => ( self: Kind> @@ -70,28 +68,9 @@ export declare const sequence: ( Added in v1.0.0 -## sequenceComposition - -Returns a default `sequence` composition. - -**Signature** - -```ts -export declare const sequenceComposition: ( - T: Traversable & Covariant, - G: Traversable -) => ( - F: Applicative -) => ( - self: Kind>> -) => Kind>> -``` - -Added in v1.0.0 - ## traverseComposition -Returns a default `traverse` composition. +Returns a default binary `traverse` composition. **Signature** @@ -101,10 +80,9 @@ export declare const traverseComposition: ) => ( F: Applicative -) => ( +) => ( + self: Kind>, f: (a: A) => Kind -) => ( - self: Kind> ) => Kind>> ``` @@ -124,9 +102,18 @@ export declare const traverseTap: ( T: Traversable ) => ( F: Applicative -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind> +) => { + (f: (a: A) => Kind): ( + self: Kind + ) => Kind> + (self: Kind, f: (a: A) => Kind): Kind< + F, + R, + O, + E, + Kind + > +} ``` Added in v1.0.0 diff --git a/docs/modules/typeclass/TraversableFilterable.ts.md b/docs/modules/typeclass/TraversableFilterable.ts.md index 98f5c1e7b..4f67778ab 100644 --- a/docs/modules/typeclass/TraversableFilterable.ts.md +++ b/docs/modules/typeclass/TraversableFilterable.ts.md @@ -1,6 +1,6 @@ --- title: typeclass/TraversableFilterable.ts -nav_order: 31 +nav_order: 44 parent: Modules --- @@ -34,17 +34,33 @@ Added in v1.0.0 export interface TraversableFilterable extends TypeClass { readonly traversePartitionMap: ( F: Applicative - ) => ( - f: (a: A) => Kind> - ) => ( - self: Kind - ) => Kind, Kind]> + ) => { + (f: (a: A) => Kind>): ( + self: Kind + ) => Kind, Kind]> + (self: Kind, f: (a: A) => Kind>): Kind< + F, + R, + O, + E, + [Kind, Kind] + > + } readonly traverseFilterMap: ( F: Applicative - ) => ( - f: (a: A) => Kind> - ) => (self: Kind) => Kind> + ) => { + (f: (a: A) => Kind>): ( + self: Kind + ) => Kind> + (self: Kind, f: (a: A) => Kind>): Kind< + F, + R, + O, + E, + Kind + > + } } ``` @@ -61,27 +77,34 @@ export declare const traverseFilter: ( T: TraversableFilterable ) => ( F: Applicative -) => ( - predicate: (a: A) => Kind -) => (self: Kind) => Kind> +) => { + (predicate: (a: A) => Kind): ( + self: Kind + ) => Kind> + ( + self: Kind, + predicate: (a: A) => Kind + ): Kind> +} ``` Added in v1.0.0 ## traverseFilterMap -Returns a default `traverseFilterMap` implementation. +Returns a default binary `traverseFilterMap` implementation. **Signature** ```ts export declare const traverseFilterMap: ( - T: Traversable & compactable.Compactable -) => ( + T: Traversable & filterable.Filterable +) => ( F: Applicative -) => ( - f: (a: A) => Kind -) => (self: Kind) => Kind> +) => ( + self: Kind, + f: (a: A) => Kind> +) => Kind> ``` Added in v1.0.0 @@ -95,31 +118,34 @@ export declare const traversePartition: ( T: TraversableFilterable ) => ( F: Applicative -) => ( - predicate: (a: A) => Kind -) => ( - self: Kind -) => Kind, Kind]> +) => { + (predicate: (a: A) => Kind): ( + self: Kind + ) => Kind, Kind]> + ( + self: Kind, + predicate: (a: A) => Kind + ): Kind, Kind]> +} ``` Added in v1.0.0 ## traversePartitionMap -Returns a default `traversePartitionMap` implementation. +Returns a default binary `traversePartitionMap` implementation. **Signature** ```ts export declare const traversePartitionMap: ( - T: Traversable & Covariant & compactable.Compactable -) => ( + T: Traversable & Covariant & filterable.Filterable +) => ( F: Applicative -) => ( - f: (a: A) => Kind -) => ( - self: Kind -) => Kind, Kind]> +) => ( + self: Kind, + f: (a: A) => Kind> +) => Kind, Kind]> ``` Added in v1.0.0 diff --git a/dtslint/index.d.ts b/dtslint/index.d.ts index 611f70e40..22568c17c 100644 --- a/dtslint/index.d.ts +++ b/dtslint/index.d.ts @@ -1 +1 @@ -// TypeScript Version: 4.7 +// TypeScript Version: 4.8 diff --git a/dtslint/ts4.7/SemiApplicative.ts b/dtslint/ts4.7/SemiApplicative.ts deleted file mode 100644 index 6a261798c..000000000 --- a/dtslint/ts4.7/SemiApplicative.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as _ from "@fp-ts/core/typeclass/SemiApplicative" -import type { TypeLambda } from "@fp-ts/core/HKT" - -interface RAW { - (r: R): () => Promise -} - -interface RAWTypeLambda extends TypeLambda { - readonly type: RAW -} - -declare const fa: RAW<{ a: string }, "a", string> -declare const fb: RAW<{ b: number }, "b", number> -declare const fc: RAW<{ c: boolean }, "c", boolean> - -declare const SemiApplicative: _.SemiApplicative - -// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", readonly [string, number, boolean]> -_.lift3(SemiApplicative)((a: string, b: number, c: boolean) => [a, b, c] as const)(fa, fb, fc) - -// $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", readonly [unknown, unknown, unknown]> -_.lift3(SemiApplicative)((a: A, b: B, c: C): readonly [A, B, C] => [a, b, c])(fa, fb, fc) diff --git a/dtslint/ts4.7/FlatMap.ts b/dtslint/ts4.8/FlatMap.ts similarity index 90% rename from dtslint/ts4.7/FlatMap.ts rename to dtslint/ts4.8/FlatMap.ts index b3890d50b..fe241dd32 100644 --- a/dtslint/ts4.7/FlatMap.ts +++ b/dtslint/ts4.8/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.8/Monoid.ts b/dtslint/ts4.8/Monoid.ts new file mode 100644 index 000000000..05fd68a52 --- /dev/null +++ b/dtslint/ts4.8/Monoid.ts @@ -0,0 +1,23 @@ +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 +_.tuple( + String.Monoid, + Number.MonoidSum +) + +// +// struct +// + +// $ExpectType Monoid<{ readonly a: string; readonly b: number; }> +_.struct({ + a: String.Monoid, + b: Number.MonoidSum +}) diff --git a/dtslint/ts4.8/Option.ts b/dtslint/ts4.8/Option.ts new file mode 100644 index 000000000..7caa49ebc --- /dev/null +++ b/dtslint/ts4.8/Option.ts @@ -0,0 +1,92 @@ +import { pipe } from '@fp-ts/core/Function' +import * as _ from '@fp-ts/core/Option' + +declare const n: number +declare const sn: string | number +declare const isString: (u: unknown) => u is string +declare const predicate: (sn: string | number) => boolean +declare const on: _.Option +declare const osn: _.Option + +// ------------------------------------------------------------------------------------- +// liftPredicate +// ------------------------------------------------------------------------------------- + +// $ExpectType Option +pipe(sn, _.liftPredicate(isString)) +pipe( + sn, + _.liftPredicate( + ( + n // $ExpectType string | number + ): n is number => typeof n === 'number' + ) +) + +// $ExpectType Option +pipe(sn, _.liftPredicate(predicate)) +// $ExpectType Option +pipe(n, _.liftPredicate(predicate)) +// $ExpectType Option +pipe( + n, + _.liftPredicate( + ( + _n // $ExpectType number + ) => true + ) +) + +// ------------------------------------------------------------------------------------- +// getOrElse +// ------------------------------------------------------------------------------------- + +// $ExpectType string | null +pipe(_.some('a'), _.getOrElse(() => null)) + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +// $ExpectType Option<{ a1: number; a2: string; }> +pipe( + _.Do, + _.bind('a1', () => _.some(1)), + _.bind('a2', () => _.some('b')) +) + +// ------------------------------------------------------------------------------------- +// filter +// ------------------------------------------------------------------------------------- + +// $ExpectType Option +pipe(on, _.filter(predicate)) + +// $ExpectType Option +_.filter(on, predicate) + +// $ExpectType Option +pipe(osn, _.filter(isString)) + +// $ExpectType Option +_.filter(osn, isString) + +// $ExpectType Option +pipe( + on, + _.filter( + ( + x // $ExpectType number + ): x is number => true + ) +) + +// $ExpectType Option +pipe( + on, + _.filter( + ( + _x // $ExpectType number + ) => true + ) +) diff --git a/dtslint/ts4.8/Predicate.ts b/dtslint/ts4.8/Predicate.ts new file mode 100644 index 000000000..abcdae77f --- /dev/null +++ b/dtslint/ts4.8/Predicate.ts @@ -0,0 +1,162 @@ +import * as _ from '@fp-ts/core/Predicate' + +declare const u: unknown +declare const anys: Array +declare const unknowns: Array +declare const numberOrNull: Array +declare const numberOrUndefined: Array +declare const numberOrNullOrUndefined: Array + +// ------------------------------------------------------------------------------------- +// isString +// ------------------------------------------------------------------------------------- + +// $ExpectType string[] +unknowns.filter(_.isString) + +// ------------------------------------------------------------------------------------- +// isNumber +// ------------------------------------------------------------------------------------- + +// $ExpectType number[] +unknowns.filter(_.isNumber) + +// ------------------------------------------------------------------------------------- +// isBoolean +// ------------------------------------------------------------------------------------- + +// $ExpectType boolean[] +unknowns.filter(_.isBoolean) + +// ------------------------------------------------------------------------------------- +// isBigint +// ------------------------------------------------------------------------------------- + +// $ExpectType bigint[] +unknowns.filter(_.isBigint) + +// ------------------------------------------------------------------------------------- +// isSymbol +// ------------------------------------------------------------------------------------- + +// $ExpectType symbol[] +unknowns.filter(_.isSymbol) + +// ------------------------------------------------------------------------------------- +// isUndefined +// ------------------------------------------------------------------------------------- + +// $ExpectType undefined[] +unknowns.filter(_.isUndefined) + +// ------------------------------------------------------------------------------------- +// isNotUndefined +// ------------------------------------------------------------------------------------- + +// $ExpectType number[] +numberOrUndefined.filter(_.isNotUndefined) + +// $ExpectType (number | null)[] +numberOrNullOrUndefined.filter(_.isNotUndefined) + +// ------------------------------------------------------------------------------------- +// isUndefined +// ------------------------------------------------------------------------------------- + +// $ExpectType null[] +unknowns.filter(_.isNull) + +// ------------------------------------------------------------------------------------- +// isNotUndefined +// ------------------------------------------------------------------------------------- + +// $ExpectType number[] +numberOrNull.filter(_.isNotNull) + +// $ExpectType (number | undefined)[] +numberOrNullOrUndefined.filter(_.isNotNull) + +// ------------------------------------------------------------------------------------- +// isNever +// ------------------------------------------------------------------------------------- + +// $ExpectType never[] +unknowns.filter(_.isNever) + +// ------------------------------------------------------------------------------------- +// isUnknown +// ------------------------------------------------------------------------------------- + +// $ExpectType unknown[] +anys.filter(_.isUnknown) + +// ------------------------------------------------------------------------------------- +// isObject +// ------------------------------------------------------------------------------------- + +// $ExpectType object[] +anys.filter(_.isObject) + +// ------------------------------------------------------------------------------------- +// isNullable +// ------------------------------------------------------------------------------------- + +// $ExpectType null[] +numberOrNull.filter(_.isNullable) + +// $ExpectType undefined[] +numberOrUndefined.filter(_.isNullable) + +// $ExpectType (null | undefined)[] +numberOrNullOrUndefined.filter(_.isNullable) + +if (_.isNullable(u)) { + // $ExpectType never + u +} + +// ------------------------------------------------------------------------------------- +// isNotNullable +// ------------------------------------------------------------------------------------- + +// $ExpectType number[] +numberOrNull.filter(_.isNotNullable) + +// $ExpectType number[] +numberOrUndefined.filter(_.isNotNullable) + +// $ExpectType number[] +numberOrNullOrUndefined.filter(_.isNotNullable) + +if (_.isNotNullable(u)) { + // $ExpectType {} + u +} + +// ------------------------------------------------------------------------------------- +// isError +// ------------------------------------------------------------------------------------- + +// $ExpectType Error[] +unknowns.filter(_.isError) + +// ------------------------------------------------------------------------------------- +// isDate +// ------------------------------------------------------------------------------------- + +// $ExpectType Date[] +unknowns.filter(_.isDate) + +// ------------------------------------------------------------------------------------- +// isRecord +// ------------------------------------------------------------------------------------- + +// $ExpectType { [x: string]: unknown; [x: symbol]: unknown; }[] +unknowns.filter(_.isRecord) + +// ------------------------------------------------------------------------------------- +// isReadonlyRecord +// ------------------------------------------------------------------------------------- + +// $ExpectType { readonly [x: string]: unknown; readonly [x: symbol]: unknown; }[] +unknowns.filter(_.isReadonlyRecord) diff --git a/dtslint/ts4.7/Product.ts b/dtslint/ts4.8/Product.ts similarity index 83% rename from dtslint/ts4.7/Product.ts rename to dtslint/ts4.8/Product.ts index 8287e5f69..68ee95b7f 100644 --- a/dtslint/ts4.7/Product.ts +++ b/dtslint/ts4.8/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.8/ReadonlyArray.ts b/dtslint/ts4.8/ReadonlyArray.ts new file mode 100644 index 000000000..fe48330a2 --- /dev/null +++ b/dtslint/ts4.8/ReadonlyArray.ts @@ -0,0 +1,88 @@ +import { pipe } from '@fp-ts/core/Function' +import * as RA from '@fp-ts/core/ReadonlyArray' +import * as O from '@fp-ts/core/Option' + +declare const neras: RA.NonEmptyReadonlyArray +declare const neas: RA.NonEmptyArray +declare const ras: ReadonlyArray +declare const as: Array + +// ------------------------------------------------------------------------------------- +// isEmpty +// ------------------------------------------------------------------------------------- + +if (RA.isEmpty(ras)) { + // $ExpectType readonly [] + ras +} + +// $ExpectType (c: readonly A[]) => Option +O.liftPredicate(RA.isEmpty) + +// ------------------------------------------------------------------------------------- +// isEmptyArray +// ------------------------------------------------------------------------------------- + +if (RA.isEmptyArray(as)) { + // $ExpectType [] + as +} + +// $ExpectType (c: A[]) => Option<[]> +O.liftPredicate(RA.isEmptyArray) + +// ------------------------------------------------------------------------------------- +// isNonEmpty +// ------------------------------------------------------------------------------------- + +if (RA.isNonEmpty(ras)) { + // $ExpectType readonly [number, ...number[]] + ras +} + +// $ExpectType (c: readonly A[]) => Option +O.liftPredicate(RA.isNonEmpty) + +// ------------------------------------------------------------------------------------- +// isNonEmptyArray +// ------------------------------------------------------------------------------------- + +if (RA.isNonEmptyArray(as)) { + // $ExpectType [number, ...number[]] + as +} + +// $ExpectType (c: A[]) => Option<[A, ...A[]]> +O.liftPredicate(RA.isNonEmptyArray) + +// ------------------------------------------------------------------------------------- +// map +// ------------------------------------------------------------------------------------- + +// $ExpectType number[] +RA.map(ras, n => n + 1) + +// $ExpectType number[] +pipe(ras, RA.map(n => n + 1)) + +// $ExpectType number[] +RA.map(as, n => n + 1) + +// $ExpectType number[] +pipe(as, RA.map(n => n + 1)) + +// ------------------------------------------------------------------------------------- +// mapNonEmpty +// ------------------------------------------------------------------------------------- + +// $ExpectType [number, ...number[]] +RA.mapNonEmpty(neras, n => n + 1) + +// $ExpectType [number, ...number[]] +pipe(neras, RA.mapNonEmpty(n => n + 1)) + +// $ExpectType [number, ...number[]] +RA.mapNonEmpty(neas, n => n + 1) + +// $ExpectType [number, ...number[]] +pipe(neas, RA.mapNonEmpty(n => n + 1)) diff --git a/dtslint/ts4.8/ReadonlyRecord.ts b/dtslint/ts4.8/ReadonlyRecord.ts new file mode 100644 index 000000000..a9d0e1314 --- /dev/null +++ b/dtslint/ts4.8/ReadonlyRecord.ts @@ -0,0 +1,69 @@ +import { pipe } from '@fp-ts/core/Function' +import * as RR from '@fp-ts/core/ReadonlyRecord' + +declare const r: Record +declare const struct: Record<'a' | 'b', number> + +// +// map +// + +// $ExpectType Record +RR.map(r, ( + value, // $ExpectType number + _key, // $ExpectType string +) => value > 0) + +// $ExpectType Record +pipe(r, RR.map(( + value, // $ExpectType number + _key, // $ExpectType string +) => value > 0)) + +// $ExpectType Record<"a" | "b", boolean> +RR.map(struct, ( + value, // $ExpectType number + _key, // $ExpectType "a" | "b" +) => value > 0) + +// $ExpectType Record<"a" | "b", boolean> +pipe(struct, RR.map(( + value, // $ExpectType number + _key, // $ExpectType "a" | "b" +) => value > 0)) + +const constStruct = { a: 1, b: 2 } as const; + +function mapToBoolean(): { [K in keyof typeof constStruct]: boolean } { + return RR.map(constStruct, () => true); +} + +// $ExpectType { readonly a: boolean; readonly b: boolean; } +mapToBoolean() + +// +// get +// + +// $ExpectType Option +pipe(r, RR.get('a')) + +// +// replaceOption +// + +// $ExpectType Option> +pipe(r, RR.replaceOption('a', 2)) + +// $ExpectType Option> +pipe(r, RR.replaceOption('a', true)) + +// +// modifyOption +// + +// $ExpectType Option> +pipe(r, RR.modifyOption('a', () => 2)) + +// $ExpectType Option> +pipe(r, RR.modifyOption('a', () => true)) diff --git a/dtslint/ts4.7/SemiAlternative.ts b/dtslint/ts4.8/SemiAlternative.ts similarity index 85% rename from dtslint/ts4.7/SemiAlternative.ts rename to dtslint/ts4.8/SemiAlternative.ts index 8ea619f71..6d6024319 100644 --- a/dtslint/ts4.7/SemiAlternative.ts +++ b/dtslint/ts4.8/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 @@ -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/SemiProduct.ts b/dtslint/ts4.8/SemiProduct.ts similarity index 81% rename from dtslint/ts4.7/SemiProduct.ts rename to dtslint/ts4.8/SemiProduct.ts index e4024d816..ea422d2d1 100644 --- a/dtslint/ts4.7/SemiProduct.ts +++ b/dtslint/ts4.8/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.8/Semigroup.ts b/dtslint/ts4.8/Semigroup.ts new file mode 100644 index 000000000..a94b8c717 --- /dev/null +++ b/dtslint/ts4.8/Semigroup.ts @@ -0,0 +1,23 @@ +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 +_.tuple( + String.Semigroup, + Number.SemigroupSum +) + +// +// struct +// + +// $ExpectType Semigroup<{ readonly a: string; readonly b: number; }> +_.struct({ + a: String.Semigroup, + b: Number.SemigroupSum +}) diff --git a/dtslint/ts4.8/Tuple.ts b/dtslint/ts4.8/Tuple.ts new file mode 100644 index 000000000..653b581f1 --- /dev/null +++ b/dtslint/ts4.8/Tuple.ts @@ -0,0 +1,16 @@ +import * as T from '@fp-ts/core/Tuple' +import { pipe } from '@fp-ts/core/Function' + +// +// tuple +// + +// $ExpectType [string, number, boolean] +T.tuple('a', 1, true) + +// +// appendElement +// + +// $ExpectType [string, number, boolean] +pipe(T.tuple('a', 1), T.appendElement(true)) diff --git a/dtslint/ts4.7/index.d.ts b/dtslint/ts4.8/index.d.ts similarity index 100% rename from dtslint/ts4.7/index.d.ts rename to dtslint/ts4.8/index.d.ts diff --git a/dtslint/ts4.7/index.ts b/dtslint/ts4.8/index.ts similarity index 100% rename from dtslint/ts4.7/index.ts rename to dtslint/ts4.8/index.ts diff --git a/dtslint/ts4.7/tsconfig.json b/dtslint/ts4.8/tsconfig.json similarity index 87% rename from dtslint/ts4.7/tsconfig.json rename to dtslint/ts4.8/tsconfig.json index 109760a8d..cc38df3bd 100644 --- a/dtslint/ts4.7/tsconfig.json +++ b/dtslint/ts4.8/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "noEmit": true, "strict": true, - "noImplicitAny": true, + "noImplicitAny": false, "noImplicitThis": true, "strictNullChecks": true, "strictFunctionTypes": true, @@ -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/*"], diff --git a/dtslint/ts4.7/tslint.json b/dtslint/ts4.8/tslint.json similarity index 91% rename from dtslint/ts4.7/tslint.json rename to dtslint/ts4.8/tslint.json index 222a6c998..1457aa8de 100644 --- a/dtslint/ts4.7/tslint.json +++ b/dtslint/ts4.8/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/guides/Either.md b/guides/Either.md new file mode 100644 index 000000000..5f6bb7808 --- /dev/null +++ b/guides/Either.md @@ -0,0 +1,725 @@ +# 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 + +By convention in `@fp-ts/core`, this single field which uses literal types is named "\_tag" (but you can use any name when defining your unions). + +Furthermore, `Either` is a "polymorphic" data type, that is, it makes use of a feature of TypeScript named ["Generics"](https://www.typescriptlang.org/docs/handbook/2/generics.html), meaning that the `Either` data type is a container that can hold any type. + +Here's the complete definition of the `Either` type: + +```ts +// Holds the information for a failure case +export type Left = { + // Discriminating field used to identify the variant + readonly _tag: "Left"; + // The actual error + readonly left: E; +}; + +// Holds the result of a successful computation +export type Right = { + // Discriminating field used to identify the variant + readonly _tag: "Right"; + // The actual value + readonly right: A; +}; + +export type Either = Left | Right; +``` + +The `Either` data type is defined as a union of two other types, `Left` and `Right`, that represent the two possible outcomes of a computation: a failure or a success. + +The type parameters `E` and `A` are used to specify the type of the failure value and the success value that the `Either` holds respectively. + +The `_tag` field is used to distinguish between the two variants, `Left` and `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"); +``` + +Let's summarize the two cases in a table: + +**Cheat sheet** (constructors) + +| Name | Given | To | +| ------- | ----- | ------------------ | +| `right` | `A` | `Either` | +| `left` | `E` | `Either` | + +# Conversions + +You can also use the `fromOption` function to convert an `Option` to an `Either`. + +```ts +import { Either, fromOption } from "@fp-ts/core/Either"; +import { none, some } from "@fp-ts/core/Option"; + +const success: Either = fromOption( + some(1), + () => "error message" +); +const failure: Either = fromOption( + none(), + () => "error message" +); +``` + +The `fromOption` function requires a second 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. + +**Cheat sheet** (conversions) + +| Name | Given | To | Note | +| -------------- | ------------------------------------ | ------------------ | ------------------- | +| `fromOption` | `Option`, `onNone: LazyArg` | `Either` | | +| `toOption` | `Either` | `Option` | | +| `getRight` | `Either` | `Option` | alias of `toOption` | +| `getLeft` | `Either` | `Option` | | +| `toRefinement` | `A => Either` | `Refinement` | | +| `fromIterable` | `Iterable`, `onEmpty: LazyArg` | `Either` | | +| `toArray` | `Either` | `Array` | | + +# 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` function can be used to transform the `Right` values: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, right, map } from "@fp-ts/core/Either"; + +const success: Either = pipe( + right(1), + map((x) => x + 1) +); // right(2) +``` + +As you can see you can transform the result of your computation without unwrapping and wrapping the underlying value of `Either`. + +What is very convenient about `Either` is how the absence of value (i.e. a `Left`) is handled. See the example below: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, left, map } from "@fp-ts/core/Either"; + +const failure: Either = pipe( + left("error"), + // tries to map the value inside the `Right`, but it does not exist, resulting in `Left` + map((x) => x + 1) +); +``` + +As you can see, even though we started with a `Left` value, we can still operate on our `Either`. No errors are thrown or shown to the user, unless we do it intentionally. What happens is that when the `Either` is `Left`, the mapping doesn't even happen and the `Left` value representing the failed computation is returned unchanged. + +In case you want to map the value contained in the `Left`, for example to change the type of error you want to express, you can use the `mapLeft` API which acts like `map` but this time on the `Left` part of an `Either`: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, left, mapLeft } from "@fp-ts/core/Either"; + +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") +``` + +What happens if we add a call to the `parseNumber` function to a pipeline that already involves an `Either`? + +```ts +const result = pipe( + right("2"), + map((s) => parseNumber(s)), + map((n) => n2) // type-checker error! +); +``` + +There's something wrong, we received an error from the type checker, what happened? + +The problem is that in the second `map` the parameter `n` is of type `Either` and not `number`. + +```ts +const result = pipe( + right("2"), + map((s) => parseNumber(s)), + map((x: Either) => ...) +); +``` + +Fortunately, the fix is simple, when adding a computation that returns an `Either` to our pipeline we should use the `flatMap` function instead of the `map` function: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { right, flatMap, map } from "@fp-ts/core/Either"; + +const result = pipe( + right("2"), + flatMap((s) => parseNumber(s)), + map((n) => n2) // ok! now `n` has type `number` +); +``` + +Let's summarize the two cases in a table: + +**Cheat sheet** (sequencing) + +| Name | Given | To | +| --------- | -------------------------------------- | --------------------- | +| `map` | `Either`, `A => B` | `Either` | +| `flatMap` | `Either`, `E1 => Either` | `Either` | + +The `flatMap` function offers the same convenience as the `map` function, which only continues with the computations contained in the pipeline if a `Left` value is **not** encountered: + +**Happy path, starting with a valid input** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, right, flatMap, map } from "@fp-ts/core/Either"; + +const success: Either = pipe( + right("2"), + flatMap((s) => parseNumber(s)), // parse the input to number + map((x) => x2), // double the parsed number + map((x) => x - 3) // subtract 3 +); // right(1) +``` + +**Error path, starting with an invalid input** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, right, flatMap, map } from "@fp-ts/core/Either"; + +const failure: Either = pipe( + right("Not a number"), + flatMap((s) => parseNumber(s)), // parse the input to number + map((x) => x2), // This will not be executed because parseNumber will return Left + map((x) => x - 3) // This will not be executed +); // left("Cannot parse 'Not a number' as a number") +``` + +**Error path, starting with None** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, left, flatMap, map } from "@fp-ts/core/Either"; + +const leftStart: Either = pipe( + left("error message"), + flatMap((s) => parseNumber(s)), // This will not be executed because it starts with Left + map((x) => x2), // This will not be executed + map((x) => x - 3) // This will not be executed +); // left("error message") +``` + +When using this approach, the **desired outcome** is always in clear view while defining your pipeline. This allows you to focus on the expected result, while leaving it to `Either` to handle any potential errors that may arise seamlessly and transparently. + +You can focus on the successful scenario and let `Either` handle the tedious task of managing potential errors at every step of the pipeline, without the need for explicit handling. + +# Debugging + +At any time, it is possible to inspect what is happening in your pipeline using two utility functions: + +**Cheat sheet** (debugging) + +| Name | Given | To | Note | +| -------------- | --------------------------- | -------------- | ------------------------------------- | +| `inspectRight` | `Either`, `A => void` | `Either` | callback called if it is a `Right` | +| `inspectLeft` | `Either`, `E => void` | `Either` | callback called if it is a `Left` | + +Let's see an example where both are in action: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { + Either, + right, + inspectRight, + flatMap, + inspectLeft, + map, +} from "@fp-ts/core/Option"; + +const failure: Either = pipe( + right("Not a number"), + inspectRight(console.log), + flatMap((s) => parseNumber(s)), + inspectLeft(console.error), + map((x) => x2), + map((x) => x - 3) +); +// "Not a number" +// "Cannot parse 'Not a number' as a number" +``` + +Please note that these two functions should only be used for debugging purposes and it is not recommended to use them for performing side effects or encoding business logic. + +# Pattern matching and error handling + +We have seen how easy and convenient it is to build pipelines involving the `Either` data type, leaving it to handle any errors that may occur at any step. However, at some point, you will be interested in manually handling the error to understand the overall result obtained from the pipeline and decide what to do accordingly. + +The fastest way to get the value wrapped in an `Either` is to call the `getOrThrow` function, but be aware that, as the name suggests, an exception will be thrown in case the `Either` you are querying is a `Left`: + +```ts +import { getOrThrow } from "@fp-ts/core/Either"; + +console.log(getOrThrow(right(10)); // 10 +console.log(getOrThrow(left("error message")); // throws new Error("getOrThrow called on a Left") +``` + +A more safe alternative is using the `isRight` and `isLeft` guards: + +```ts +import { right, left, isRight, isLeft } from "@fp-ts/core/Either"; + +const success = some(1); + +// Use the `isRight` function to check if the `success` is an instance of `Right` +if (isRight(success)) { + console.log(`Either has a value: ${success.right}`); +} else { + console.log(`Either is a Left.`); +} +// Either has a value: 1 + +const failure = left("error message"); + +// Use the `isLeft` function to check if the `failure` is an instance of `Left` +if (isLeft(failure)) { + console.log(`Either has error: ${failure.left}`); +} else { + console.log(`Either is a Right.`); +} +// Either has error: error message +``` + +Another alternative is [pattern matching](https://github.com/gvergnaud/ts-pattern#what-is-pattern-matching) on the `Either`. + +The `match` function allows us to match on the `Left` and `Right` cases of an `Either` value and provide different actions for each. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { right, match } from "@fp-ts/core/Either"; + +const either = right(1); + +/** + * Use the `match` function to conditionally return a string based on whether the `Either` is `Left` or `Right`. + * If the `Either` is `Left`, the `left` will be passed to the first function. + * If the `Either` is `Right`, the `right` will be passed to the second function. + */ +const output = match( + option, + (left) => `Either has error. ${left}`, + (right) => `Either has a value: ${right}` +); + +console.log(output); // Either has a value: 1 +``` + +One reason to use `match` instead of `isRight` or `isLeft` is that `match` is more expressive and provides a clear way to handle both cases of an `Either`. With `match`, you can directly provide two functions to handle the case of the `Either` being `Left` or `Right`, respectively. On the other hand, with `isRight` or `isLeft`, you would need to manually check the value and take separate actions based on whether it's `Right` or `Left`. With `match`, the code can be more concise and easy to understand. Additionally, if you have complex logic to handle both cases, using `match` can make the code easier to read and maintain. + +There are specializations of `match` to make working with code that does not use `Either` more convenient and faster, particularly `getOrNull` and `getOrUndefined`. + +```ts +import { getOrNull, getOrUndefined, right, left } from "@fp-ts/core/Either"; + +getOrNull(right(5)); // 5 +getOrNull(left("error")); // null + +getOrUndefined(right(5)); // 5 +getOrUndefined(left("error")); // undefined +``` + +For greater flexibility, there is also the `getOrElse` function which allows you to set what value corresponds to the `Left` case: + +```ts +import { getOrElse, right, left } from "@fp-ts/core/Either"; + +getOrElse(right(5), () => 0); // 5 +getOrElse(left("error"), () => 0); // 0 +``` + +It often happens that the action you want to take when a computation returns `None` is to continue with another computation that returns an `Option`, in this case you can use the `orElse` API: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Either, some, none, orElse } from "@fp-ts/core/Either"; + +const fetchData = (): Either => { + // Imagine we have a function that returns an `Either` of data + return Math.random() < 0.5 + ? right("Data fetched successfully") + : left("Data fetched unsuccessfully"); +}; + +const retryFetchData = (): Either => + pipe( + fetchData(), // Call the function for the first time + orElse(() => fetchData()) // If it fails, call it again + ); + +const result = retryFetchData(); +``` + +**Cheat sheet** (error handling) + +| Name | Given | To | +| ---------------- | --------------------------------------------------- | -------------------- | +| `match` | `Either`, `onLeft: E => B`, `onRight: A => C` | `B \| C` | +| `getOrThrow` | `Either` | `A` (may throw) | +| `getOrNull` | `Either` | `A \| null` | +| `getOrUndefined` | `Either` | `A \| undefined` | +| `getOrElse` | `Either`, `onLeft: E => B` | `A \| B` | +| `orElse` | `Either`, `LazyArg>` | `Either` | +| `firstRightOf` | `Either`, `Iterable>` | `Either` | + +# Interop + +A need that arises quickly when using the `Either` data type is the ability to interoperate with code that does not share the same style, in particular code that for example uses `undefined` or `null` to indicate that a value is optional, or code that throws exceptions. + +The `Either` data type offers a series of APIs to make this task easier, let's start with the first of the two cases, that is when the need is to interoperate with code that use a nullable type to indicate that a value is optional. + +It is possible to create an `Eitehr` from a nullable value using the `fromNullable` API, let's see an example: + +```ts +import { fromNullable, right, left } from "@fp-ts/core/Either"; + +console.log(fromNullable(null, () => "error")); // left("erro") +console.log(fromNullable(undefined, () => "error")); // left("erro") +console.log(fromNullable(1, () => "error")); // right(1) +``` + +Instead of a single value, we can also modify the definition of a function that returns a nullable value to a function that returns an `Either` (a process that goes by the name of "lifting"): + +```ts +import { liftNullable, left, right } from "@fp-ts/core/Either"; + +const parse = (s: string): number | undefined => { + const n = parseFloat(s); + return isNaN(n) ? undefined : n; +}; + +// const parseEither: (s: string) => Either +const parseEither = liftNullable( + parse, + (s) => `Cannot parse '${s}' as a number` +); + +console.log(parseEither("1")); // right(1) +console.log(parseEither("not a number")); // left("Cannot parse 'not a number' as a number") +``` + +On the other hand, if we have a value of type `Either` and we want to convert it into a nullable value we have two possibilities: + +- convert `Left` to `null` +- convert `Left` to `undefined` + +The two APIs `getOrNull` and `getOrUndefined` respectively achieve these two tasks: + +```ts +import { getOrNull, getOrUndefined, right, left } from "@fp-ts/core/Either"; + +console.log(getOrNull(right(1))); // 1 +console.log(getOrNull(left("error message"))); // null + +console.log(getOrUndefined(right(1))); // 1 +console.log(getOrUndefined(left("error message"))); // undefined +``` + +**Cheat sheet** (interop - nullable) + +| Name | Given | To | +| ----------------- | ----------------------------------------------------------------- | --------------------------------------- | +| `fromNullable` | `A`, `A => E` | `Either>` | +| `liftNullable` | `(...a: A) => B \| null \| undefined`, `(...a: A) => E` | `(...a: A) => Either` | +| `flatMapNullable` | `Either`, `(...a: A) => B \| null \| undefined`, `A => E2` | `Either>` | +| `getOrNull` | `Either` | `A \| null` | +| `getOrUndefined` | `Either` | `A \| undefined` | +| `merge` | `Either` | `E \| A` | + +Now let's see the other case, that is when we need to interoperate with code that throws exceptions. + +In a previous section, we saw how to convert the following function that can throw exceptions: + +```ts +function parseNumber(s: string): number { + const n = parseFloat(s); + if (isNaN(n)) { + throw new Error(`Cannot parse '${s}' as a number`); + } + return n; +} +``` + +into a function that returns a `Option`: + +```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); +} +``` + +However, this involves tedious, error-prone, and boilerplate-heavy work. It would be much more convenient not to have to rewrite the `parseNumber` function from scratch but only to transform it into the desired result in one step, and that's exactly what the `fromThrowable` API takes care of doing: + +```ts +import { liftThrowable } from "@fp-ts/core/Either"; + +const parse = liftThrowable(JSON.parse, () => "parse error"); + +console.log(parse("1")); // right(1) +console.log(parse("")); // left("parse error") +``` + +On the other hand, if we have a value of type `Option` and want to get the wrapped value, accepting the fact that if the `Option` is a `None` we will get an exception, we can use the `getOrThrow` API: + +```ts +import { getOrThrow, right, left } from "@fp-ts/core/Either"; + +console.log(getOrThrow(right(10)); // 10 +console.log(getOrThrow(left("error message")); // throws new Error("getOrThrow called on a Left") +``` + +**Cheat sheet** (interop - throwing) + +| Name | Given | To | +| --------------- | -------------------------------------------- | --------------------------- | +| `liftThrowable` | `(...a: A) => B` (may throw), `unknown => E` | `(...a: A) => Either` | +| `getOrThrow` | `Either` | `A` (may throw) | + +# Combining two or more `Either`s + +The `zipWith` function allows you to combine two `Either`s using a provided function. The resulting value is a new `Either` that holds the combined value of both original `Either`s. + +Let's consider the following example where we have two `Either`s that hold values of two different types, `string` and `number`: + +```ts +import { Either, right } from "@fp-ts/core/Either"; + +const name: Either = right("John"); +const age: Either = right(25); +``` + +If we want to combine these two `Either`s into a single `Either` that holds an object with properties `name` and `age`, we can use the `zipWith` function: + +```ts +import { zipWith } from "@fp-ts/core/Either"; + +const combine = zipWith(name, age, (n, a) => ({ name: n, age: a })); +console.log(combine); // right({ name: 'John', age: 25 }) +``` + +The `zipWith` function takes three arguments: the two `Either`s that you want to combine, and a function that takes two arguments - the values held by the two `Either`s - and returns the combined value. + +If either of the two `Either`s is `Left`, the resulting `Either` will be `Left` as well: + +```ts +const name: Either = left("missing name"); +const age: Either = right(25); +const combine = zipWith(name, age, (n, a) => ({ name: n, age: a })); +console.log(combine); // left("missing name") +``` + +This is because the `zipWith` function only combines the values if both `Either`s are `Right`. + +**Cheat sheet** (combining) + +| Name | Given | To | +| --------------- | ----------------------------------------------- | ---------------------------------------------- | +| `zipWith` | `Either`, `Either`, `(A, B) => C` | `Either` | +| `tuple` | `[Either, Either, ...]` | `Either` | +| `struct` | `{ a: Either, b: Either, ... }` | `Either` | +| `all` | `Iterable>` | `Either` | +| `appendElement` | `Either`, `Either` | `Either` | +| `ap` | `Either B>`, `Either` | `Either` | + +For convenience, a series of algebraic operations such as sums and products are exported. + +```ts +import { right, left, sum } from "@fp-ts/core/Either"; + +const num1 = right(3); +const num2 = right(4); +const num3 = left("not a number"); + +// Summing two `Right` values will result in a `Right` with the sum of the values +const sumOfRight = sum(num1, num2); +console.log(sumOfRight); // right(7) + +// Summing a `Right` and a `Left` will result in a `Left` +const sumOfRightAndLeft = sum(num1, num3); +console.log(sumOfRightAndLeft); // left("not a number") +``` + +**Cheat sheet** (algebraic operations) + +| Name | Given | To | +| ---------- | ------------------------------------------ | -------------------------- | +| `sum` | `Either`, `Either` | `Either` | +| `multiply` | `Either`, `Either` | `Either` | +| `subtract` | `Either`, `Either` | `Either` | +| `divide` | `Either`, `Either` | `Either` | + +# Validations + +Say you must implement a web form to signup for an account. The form contains two field: `username` and `password` and the following validation rules must hold: + +- `username` must not be empty +- `username` can't have dashes in it +- `password` needs to have at least 6 characters +- `password` needs to have at least one capital letter +- `password` needs to have at least one number + +The `Either` type represents a computation that might fail with an error of type `E` or succeed with a value of type `A`, so is a good candidate for implementing our validation rules. + +For example let's encode each `password` rule: + +```ts +import * as E from "@fp-ts/core/Either"; + +const minLength = (s: string): E.Either => + s.length >= 6 ? E.right(s) : E.left("at least 6 characters"); + +const oneCapital = (s: string): E.Either => + /[A-Z]/g.test(s) ? E.right(s) : E.left("at least one capital letter"); + +const oneNumber = (s: string): E.Either => + /[0-9]/g.test(s) ? E.right(s) : E.left("at least one number"); +``` + +We can chain all the rules using `flatMap`: + +```ts +import { pipe } from "@fp-ts/core/Function"; + +const validatePassword = (s: string): E.Either => + pipe(minLength(s), E.flatMap(oneCapital), E.flatMap(oneNumber)); +``` + +Because we are using `Either` the checks are **fail-fast**. That is, any failed check shortcircuits subsequent checks so we will only ever get one error. + +```ts +assert.deepStrictEqual(validatePassword("ab"), E.left("at least 6 characters")); + +assert.deepStrictEqual( + validatePassword("abcdef"), + E.left("at least one capital letter") +); + +assert.deepStrictEqual( + validatePassword("Abcdef"), + E.left("at least one number") +); +``` + +However this could lead to a bad UX, it would be nice to have all of these errors be reported simultaneously. + +The `Validated` abstraction may help here. + +## Validated + +Validations are similar to `Either`, where they represent a computation that may fail with an error of type `E` or succeed with a value of type `A`. Unlike typical computations involving `Either`, however, validations are capable of **accumulating multiple failures**. + +For this to be possible, the `Validated` data type must have the ability to combine two or more values of type `E` and the simplest way is to wrap them in a (non-empty) `ReadonlyArray`. + +This is the definition of the `Validated` data type: + +```ts +/** + * Represents a computation that may fail with one or more errors of type `E` + * or succeed with a value of type `A`. + */ +export type Validated = Either, A>; +``` + +To proceed, we must first modify all the rules so that they return a `Validated` value. + +Instead of having to rewrite all previous functions, which can be cumbersome, we can use the `liftEither` helper. This helper converts a check that outputs an `Either` into a check that outputs a `Validate`. + +```ts +const minLengthValidated = E.liftEither(minLength); +// ^? const minLengthValidated: (s: string) => E.Validated +const oneCapitalValidated = E.liftEither(oneCapital); +// ^? const oneCapitalValidated: (s: string) => E.Validated +const oneNumberValidated = E.liftEither(oneNumber); +// ^? const oneNumberValidated: (s: string) => E.Validated +``` + +Let's bring it all together. The `validatePassword` function takes a string `s` as input, and uses the `tupleValidated` helper to perform all three validation checks, returning a `Validated` value that collects all the validation error messages. If all the checks pass, the function returns the original string s as a successful `Validated` value: + +```ts +const validatePassword = (s: string): E.Validated => + pipe( + E.tupleValidated( + minLengthValidated(s), + oneCapitalValidated(s), + oneNumberValidated(s) + ), + E.map(() => s) + ); + +assert.deepStrictEqual( + validatePassword("ab"), + E.left([ + "at least 6 characters", + "at least one capital letter", + "at least one number", + ]) +); + +assert.deepStrictEqual(validatePassword("Abcde6"), E.right("Abcde6")); +``` diff --git a/FAQ.md b/guides/FAQ.md similarity index 100% rename from FAQ.md rename to guides/FAQ.md diff --git a/guides/Option.md b/guides/Option.md new file mode 100644 index 000000000..6e7dd8ae9 --- /dev/null +++ b/guides/Option.md @@ -0,0 +1,757 @@ +# The `Option` data type + +The `Option` data type represents an optional value: every `Option` is either `Some` and contains a value, or `None`, and does not. `Option` types are very common in functional programming, as they have a number of uses: + +- Initial values +- Return values for functions that are not defined over their entire input range (partial functions) +- Return value for otherwise reporting simple errors, where `None` is returned on error +- Optional struct fields +- Optional function arguments + +# Definition + +The `Option` data type is a union of two members: `None` and `Some`. The `@fp-ts/core` library models this union in TypeScript using a feature called [Discriminating Unions](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminatory-unions). + +A common approach for working with unions is to have a single field that uses literal types, which helps TypeScript narrow down the possible current type. In `@fp-ts/core`, this single field is named "\_tag" (but any name can be used when defining your own unions). + +The `Option` data type is a "polymorphic" data type, which makes use of a feature of TypeScript named ["Generics"](https://www.typescriptlang.org/docs/handbook/2/generics.html). This means that the `Option` data type is a container that can hold any type. + +Here is the complete definition of the `Option` data type: + +```ts +// Represents the absence of a value +export type None = { + // Discriminating field used to identify the variant + readonly _tag: "None"; +}; + +// Represents the presence of a value +export type Some = { + // Discriminatory field used to identify the variant + readonly _tag: "Some"; + // The actual value + readonly value: A; +}; + +// Define the `Option` data type as the union of `None` and `Some` +export type Option = None | Some; +``` + +The type parameter `A` is used to specify the type of the `value` that the `Option` holds. +The `_tag` field is used to distinguish between the two variants, `None` and `Some`. + +# Using `Option` + +The `Option` data type can be used to handle the presence or absence of a value in a safe and predictable manner. The `Option` data type has two constructors `some` and `none` that can be used to create a new instance of `Option` holding either a `Some` value or a `None` value, respectively. + +## Constructing a `Some` value + +The `some` constructor takes a value of type `A` and returns an instance of `Option` that holds that value: + +```ts +import { some } from "@fp-ts/core/Option"; + +const value: Option = some(1); // an Option holding the number 1 +``` + +## Constructing a `None` value + +The `none` constructor returns an instance of `Option representing the absence of a value: + +```ts +import { none } from "@fp-ts/core/Option"; + +const empty: Option = none(); // an Option holding no value +``` + +By default, `none` returns an instance of `Option`, which can be assigned to any `Option` regardless of the type `A`: + +```ts +const optionNumber: Option = none(); +const optionString: Option = none(); +const optionBoolean: Option = none(); +``` + +However, if you prefer, you can specify the desired type at the call site by explicitly indicating the type `A` you're interested in. In this case, you won't need to provide type annotations: + +```ts +const optionNumber = none(); +const optionString = none(); +const optionBoolean = none(); +``` + +Here's a quick reference guide for the two constructors: + +**Cheat sheet** (constructors) + +| **Function** | **Given input** | **Resulting Output** | +| ------------ | --------------- | -------------------- | +| `some` | `A` | `Option` | +| `none` | | `Option` | + +With these two constructors, you can construct an `Option` holding either a `Some` value or a `None` value, depending on your needs. + +# Conversions + +The following table provides a quick reference for the various conversion functions available in this module: + +**Cheat sheet** (conversions) + +| **Function** | **Given input** | **Resulting Output** | +| -------------- | --------------------------------- | -------------------- | --------------------- | +| `fromEither` | `Either` | `Option` | | +| `toEither` | `Option`, `onNone: LazyArg` | `Either` | | +| `getRight` | `Either` | `Option` | alias of `fromEither` | +| `getLeft` | `Either` | `Option` | | +| `toRefinement` | `A => Option` | `Refinement` | | +| `fromIterable` | `Iterable` | `Option` | | +| `toArray` | `Option` | `Array` | | + +## fromEither + +The `fromEither` function takes in an `Either` value and returns an `Option`. This is useful when you have a value that can either be of type `E` (an error) or `A` (the correct value), and you want to convert it to an `Option` discarding the error. + +Example: + +```ts +import * as O from "@fp-ts/core/Option"; +import * as E from "@fp-ts/core/Either"; + +console.log(O.fromEither(E.right(1))); // some(1) +console.log(O.fromEither(E.left("error message"))); // none() +``` + +In this example, `fromEither` is used to convert the `Either` value `E.right(1)` to an `Option`. The result is `some(1)`, which indicates that the input `Either` was of type `Right` and contained the value `1`. + +If the input `Either` was of type `Left` (`E.left("error message")` in this case), the result would be `none()`, which indicates that the input contained an error and no valid value was present. + +## toEither + +The `toEither` function takes in an `Option` value and a `LazyArg` value and returns an `Either`. + +The `LazyArg` value is a lazy (or "deferred") argument that is only executed if the input `Option` is `None`. If the input `Option` is `None`, the `toEither` function returns an `Either` with the `Left` value being the result of the lazy argument. If the input `Option` is `Some`, the `toEither` function returns an `Either` with the `Right` value being the value contained in the `Some` case of the `Option`. + +Here's an example of how to use toEither: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import * as O from "@fp-ts/core/Option"; +import * as E from "@fp-ts/core/Either"; + +const onNone = () => "error"; +console.log(pipe(O.some(1), O.toEither(onNone))); // right(1) +console.log(pipe(O.none(), O.toEither(onNone))); // left("error") +``` + +## getRight + +The `getRight` function is an alias for `fromEither`, and is used to convert an `Either` value to an `Option`. See the explanation and example for `fromEither` for more information. + +## getLeft + +The `getLeft` function is a utility function that is used to extract the `Left` value from an `Either` value. The function takes in a single argument - an `Either` value and returns an `Option` value. + +```ts +import * as O from "@fp-ts/core/Option"; +import * as E from "@fp-ts/core/Either"; + +console.log(O.getLeft(E.right("ok"))); // none() +console.log(O.getLeft(E.left("error"))); // some("error") +``` + +Note that the `Option` value returned by the `getLeft` function will be `Some(value)` if the input `Either` value is a `Left` value, and `None` if the input `Either` value is a `Right` value. + +## toRefinement + +This function allows to convert a function `A => Option` into a `(a: A) => a is B`, which can be used as a predefined type guard. +A type guard function is used to check if a value is of a certain type. + +The `toRefinement` ensures that a type guard definition is type-safe. + +Here is an example of using `toRefinement` to create a type guard for positive numbers: + +```ts +import * as O from "@fp-ts/core/Option"; + +// This function checks if a given number is positive +const parsePositive = (n: number): O.Option => + n > 0 ? O.some(n) : O.none(); + +// convert the `parsePositive` function into a type guard +const isPositive = O.toRefinement(parsePositive); + +console.log(isPositive(1)); // true +console.log(isPositive(-1)); // false +``` + +In this example, `parsePositive` is a function that takes in a number and returns an `Option`. If the number is positive, it returns `some(n)`, where `n` is the positive number. If the number is not positive, it returns `none()`. + +`toRefinement` takes in the `parsePositive` function and returns a type guard function `isPositive`. The `isPositive` function can be used to check if a value is a positive number and can be used in type refinement statements to provide type-safety for your code. + +## fromIterable + +The `fromIterable` function takes an iterable (something you can loop over, for example arrays, sets, maps, etc.) and returns an `Option` value. + +If the iterable is not empty (i.e., it has at least one item), `fromIterable` returns the first value of the iterable wrapped in a `Some` value. If the iterable is empty, `fromIterable` returns `None`. + +Here are two examples to demonstrate the usage of `fromIterable`: + +```ts +import { fromIterable, some, none } from "@fp-ts/core/Option"; + +console.log(fromIterable([1, 2, 3])); // some(1) +``` + +In this example, `fromIterable` is passed an array with three values. Since the array is not empty, `fromIterable` returns the first value, `1`, wrapped in a `Some` value. + +```ts +console.log(fromIterable([])); // none() +``` + +In this example, `fromIterable` is passed an empty array. Since the array is empty, `fromIterable` returns `None`. + +## toArray + +The `toArray` function takes in an `Option` value and returns an array. + +If the input is a `Some` value, the value inside the `Some` is wrapped in an array and returned. + +If the input is a `None` value, an empty array is returned. + +Here are two examples of how `toArray` can be used: + +```ts +import * as O from "@fp-ts/core/Option"; + +console.log(O.toArray(O.some(1))); // [1] +``` + +In this example, `some(1)` is passed as the argument to `toArray`, which returns an array with the value `1`. + +```ts +console.log(O.toArray(O.none())); // [] +``` + +In this example, `none()` is passed as the argument to `toArray`, which returns an empty array. + +# Modeling optional properties with `Option` + +Here is an example of a `User` model where the `email` field is of type `Option`. This means that the value of the `email` field may or may not be present and will be of type `string` when it is present. + +```ts +interface User { + id: number; + username: string; + email: Option; +} + +import { some, none } from "@fp-ts/core/Option"; + +// case with email +const user1: User = { + id: 1, + username: "john_doe", + email: some("john.doe@example.com"), +}; + +// case without email +const user2: User = { + id: 2, + username: "jane_doe", + email: none(), +}; +``` + +It's important to note that the optionality only concerns the **value** of the `email` field, while the key `"email"` will always be present in the object. + +# 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 { Opiton, some, map } from "@fp-ts/core/Option"; + +const success: Option = pipe( + some(1), + // maps the value inside the Option, adding 1, resulting in some(2) + map((x) => x + 1) +); +``` + +As you can see you can transform the result of your computation without unwrapping and wrapping the underlying value of `Option`. +This allows for a safe and convenient way of transforming optional values. + +What is also convenient about `Option` is how the absence of value (i.e. a `None`) is handled. See the example below: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Option, none, map } from "@fp-ts/core/Option"; + +const failure: Option = pipe( + none(), + // tries to map the value inside the `Some`, but it does not exist, resulting in `None` + map((x) => x + 1) +); +``` + +As you can see, even though we started with a `None` value, we can still operate on our `Option`. No errors are thrown or shown to the user, unless we do it intentionally. When the `Option` is `None, the mapping doesn't even occur, and the `None` value representing the absence of value is returned unchanged. + +# Handling failing computations + +In software development, there are times when a function can "fail" to produce a result, either because of invalid inputs, lack of data, or other reasons. The `Option` data type helps us to handle these cases in a clean and functional way. + +Here's an example of a function `parseNumber` that takes a `string` as input and returns either a `number` or `null` depending on the input: + +```ts +function parseNumber(s: string): number | null { + const n = parseFloat(s); + if (isNaN(n)) { + return null; + } + return n; +} +``` + +A better way to handle these types of computations is to use the `Option` data type. This data type offers a cleaner way to model the "success" or "failure" of a computation. With `Option`, we can eliminate the need to return a `null` value. Instead, we will always return a value, but this value will be of type `Option`. + +- if `parseNumber` returns a `None` value, it means that the computation "failed" +- if the result is a `Some` value, it means that the computation "succeeded" and the computed value is wrapped inside the `Some` + +Here's how the `parseNumber` function would look using the `Option` data type: + +```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() +``` + +Now, let's say we have a pipeline of computations that already involves the `Option` data type and we want to add a call to the `parseNumber` function. We might run into an issue with the following code: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { some, map } from "@fp-ts/core/Option"; + +const result = pipe( + some("2"), + map((s) => parseNumber(s)), + map((n) => n2) // type-checker error! +); +``` + +The code above generates a type-checker error. This happens because the second `map` function expects the input `n` to be of type `number`, but `n` is of type `Option`. + +```ts +const result = pipe( + some("2"), + map((s) => parseNumber(s)), + map((x: Option) => ...) +); +``` + +To solve this issue, we need to use the `flatMap` function instead of the `map` function when adding a computation that returns an `Option` to our pipeline: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { some, flatMap, map } from "@fp-ts/core/Option"; + +const result = pipe( + some("2"), + flatMap((s) => parseNumber(s)), + map((n) => n2) // ok! now `n` has type `number` +); +``` + +Let's summarize the two cases in a table: + +**Cheat sheet** (sequencing) + +| **Function** | **Given input** | **Resulting Output** | +| ------------ | ----------------------------- | -------------------- | +| `map` | `Option`, `A => B` | `Option` | +| `flatMap` | `Option`, `A => Option` | `Option` | + +The `flatMap` function works similarly to the `map` function, but with the added feature of only continuing with the computations if a `None` value is not encountered. Let's look at some code examples to understand how these functions work in practice. + +**Example 1: Successful Path with Valid Input** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Option, some, flatMap, map } from "@fp-ts/core/Option"; + +const success: Option = pipe( + some("2"), + flatMap((s) => parseNumber(s)), // parse the input to number + map((x) => x2), // double the parsed number + map((x) => x - 3) // subtract 3 +); // some(1) +``` + +In this example, the `pipe` function is used to chain together a series of computations, starting with a string value of `"2"`. This value is first passed to the `flatMap` function which applies the `parseNumber` function to parse the input string to a number. If the parsing is successful, the resulting number is then passed to the `map` function which doubles it. Finally, the resulting value is passed to another `map` function which subtracts `3` from it. The final output of the pipeline is the `Option` value of `some(1)`. + +**Example 2: Error Path with Invalid Input** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Option, some, flatMap, map } from "@fp-ts/core/Option"; + +const failure: Option = pipe( + some("Not a number"), + flatMap((s) => parseNumber(s)), // parse the input to number + map((x) => x2), // This will not be executed because parseNumber will return None + map((x) => x - 3) // This will not be executed +); // none() +``` + +In this example, the input to the pipeline is the string value of `"Not a number"`. When this value is passed to the `flatMap` function which applies the `parseNumber` function, it will return `None` as the string cannot be parsed to a number. This means that the following `map `functions will not be executed and the final output of the pipeline will be `None`. + +**Example 3: Error Path Starting with None** + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { Option, none, flatMap, map } from "@fp-ts/core/Option"; + +const noneStart: Option = pipe( + none, + flatMap((s) => parseNumber(s)), // This will not be executed because it starts with None + map((x) => x2), // This will not be executed + map((x) => x - 3) // This will not be executed +); // none() +``` + +In this example, we start the pipeline with the `None` value, which represents an absent or empty value. This means that the `flatMap` step will not be executed and any subsequent steps in the pipeline will not be executed either. + +The advantage of using this approach is that the desired outcome is always in clear view while defining your pipeline. This allows you to focus on the expected result, while leaving it to the `Option` type to handle any potential errors that may arise seamlessly and transparently. + +You can concentrate on the successful scenario and let `Option` handle the management of potential errors at every step of the pipeline, without the need for explicit error handling. + +# Debugging + +Debugging your code can be difficult, especially when you have multiple transformations happening in a pipeline. The `Option` module provides two utility functions, `inspectSome` and `inspectNone`, that can help you inspect what is happening in your code and diagnose issues. + +The `inspectSome` function returns the original `Option` value, but if it is a `Some`, the provided callback function is called with the value wrapped inside the `Some`. + +The `inspectNone` function returns the original `Option` value, but if it is a `None`, the provided callback function is called without any arguments. + +Here is an example of how you can use `inspectSome` and `inspectNone` to debug a pipeline: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import * as O from "@fp-ts/core/Option"; + +const failure: O.Option = pipe( + O.some("Not a number"), // start with a Some containing the string "Not a number" + O.inspectSome(console.log), // log the value if it is a Some + O.flatMap((s) => parseNumber(s)), // attempt to parse the string as a number + O.inspectNone(() => console.error("none")), // log an error if the parseNumber function returns None + O.map((x) => x * 2), // double the number if it is a Some + O.map((x) => x - 3) // subtract 3 from the number if it is a Some +); +// logs "Not a number" to the console +// logs "none" to the console (because the parseNumber function returns None) +``` + +It is important to note that `inspectSome` and `inspectNone` should only be used for debugging purposes, and it is not recommended to use them for performing side effects or encoding business logic. + +**Cheat sheet** (debugging) + +| **Function** | **Given input** | **Resulting Output** | **Note** | +| ------------- | ------------------------- | -------------------- | ------------------------------------ | +| `inspectSome` | `Option`, `A => void` | `Option` | callback called if it is a `Some` | +| `inspectNone` | `Option`, `() => void` | `Option` | callback called if it is a `None` | + +# Pattern matching + +We have seen how easy and convenient it is to build pipelines involving the `Option` data type, leaving it to handle any errors that may occur at any step. However, at some point, you will be interested in manually handling the error to understand the overall result obtained from the pipeline and decide what to do accordingly. + +## Getting the value from an `Option` + +To extract the value from an `Option`, you can use the `getOrThrow` function, which retrieves the value wrapped in an `Option`, or throws an error if the `Option` you are querying is a `None`. + +Here's an example of how you can use `getOrThrow`: + +```ts +import { getOrThrow, some, none } from "@fp-ts/core/Option"; + +console.log(getOrThrow(some(10)); // 10 +console.log(getOrThrow(none()); // throws new Error("getOrThrow called on a None") +``` + +However, using `getOrThrow` can lead to exceptions being thrown in your code, which can lead to unexpected behavior and crashes. To avoid this, you can use the `isSome` and `isNone` guards: + +```ts +import { some, isSome } from "@fp-ts/core/Option"; + +const option = some(1); + +// Use the `isSome` function to check if the `option` is an instance of `Some` +if (isSome(option)) { + console.log(`Option has a value: ${option.value}`); +} else { + console.log(`Option is empty.`); +} +// Output: Option has a value: 1 +``` + +## Pattern matching with `Option` + +An alternative way to handle the cases of an `Option` being `None` or `Some` is by using the `match` function. The `match` function allows you to provide different actions for each case of the `Option` value. + +```ts +import { pipe } from "@fp-ts/core/Function"; +import { some, match } from "@fp-ts/core/Option"; + +const option = some(1); + +/** + * Use the `match` function to conditionally return a string based on whether the `Option` is `None` or `Some`. + * If the `Option` is `None`, the first function will be called with no arguments. + * If the `Option` is `Some`, the `value` will be passed to the second function. + */ +const output = match( + option, + () => `Option is empty.`, + (value) => `Option has a value: ${value}` +); + +console.log(output); // Output: Option has a value: 1 +``` + +Using `match` instead of `isSome` or `isNone` can be more expressive and provide a clear way to handle both cases of an `Option`. Additionally, if you have complex logic to handle both cases, using `match` can make your code easier to read and maintain. + +## Other functions for extracting values from an `Option` + +To make working with code that does not use `Option` more convenient, there are specializations of `match` called `getOrNull` and `getOrUndefined`, which allow you to retrieve the value of an `Option` or `null` or `undefined`, respectively. + +Here's an example of how you can use `getOrNull` and `getOrUndefined`: + +```ts +import * as O from "@fp-ts/core/Option"; + +O.getOrNull(O.some(5)); // 5 +O.getOrNull(O.none()); // null + +O.getOrUndefined(O.some(5)); // 5 +O.getOrUndefined(O.none()); // undefined +``` + +`getOrElse` allows you to specify a default value that should be returned if the `Option` is `None`. Here's an example of how you can use `getOrElse`: + +```ts +import * as O from "@fp-ts/core/Option"; + +O.getOrElse(O.some(5), () => 0); // 5 +O.getOrElse(O.none(), () => 0); // 0 +``` + +Sometimes, when a computation returns `None`, you may want to continue with another computation that returns an `Option`. In this case, you can use the `orElse` function. This is useful for implementing retry logic, for example, where you want to attempt a computation multiple times until you either succeed or exhaust all possible attempts. + +Here's an example: + +```ts +import { pipe } from "@fp-ts/core/Function"; +import * as O from "@fp-ts/core/Option"; + +const tryToConnect = (): O.Option => { + // Imagine we have a function that returns an `Option` of connection status + return Math.random() < 0.5 ? O.some("Connected successfully") : O.none(); +}; + +const retryConnect = (attemptsLeft: number): O.Option => + pipe( + tryToConnect(), // Try to connect for the first time + O.orElse(() => { + // If it fails, check if we still have attempts left + if (attemptsLeft > 0) { + return retryConnect(attemptsLeft - 1); // If we do, try again with one less attempt + } + return O.none(); // If we don't, return none + }) + ); + +const result = retryConnect(3); // Try to connect three times +``` + +In this example, the function `tryToConnect` returns an `Option` representing the connection status. We use `orElse` to implement retry logic by attempting the connection again if the first attempt fails (returns `None`) and we still have attempts left. If all attempts fail, `retryConnect` returns `None`. + +The `firstSomeOf` function is used to retrieve the first value that is present within an `Iterable` of `Option` values. The function takes an `Iterable` of `Option` values and returns the first `Option` value that is `Some`, or `None` if there are no `Some` values in the `Iterable`. + +Here is an example of how you can use `firstSomeOf`: + +```ts +import * as O from "@fp-ts/core/Option"; + +const arr = [O.none(), O.some(2), O.none(), O.some(3)]; + +const first = O.firstSomeOf(arr); // some(2) +``` + +**Cheat sheet** (pattern matching) + +| **Function** | **Given input** | **Resulting Output** | +| ---------------- | --------------------------------------------------- | -------------------- | +| `match` | `Option`, `onNone: LazyArg`, `onSome: A => C` | `B \| C` | +| `getOrThrow` | `Option` | `A` (may throw) | +| `getOrThrowWith` | `Option`, `onNone: () => unknown` | `A` (may throw) | +| `getOrNull` | `Option` | `A \| null` | +| `getOrUndefined` | `Option` | `A \| undefined` | +| `getOrElse` | `Option`, `onNone: LazyArg` | `A \| B` | +| `orElse` | `Option`, `LazyArg>` | `Option` | +| `firstSomeOf` | `Iterable>` | `Option` | + +# Interop with Code Using Nullable Types + +When using the `Option` data type, you may need to interact with code that uses `undefined` or `null` to indicate optional values. The `Option` data type provides several APIs to make this task easier. + +## Converting a Nullable Value to an Option + +You can create an `Option` from a nullable value using the `fromNullable` API. + +```ts +import * as O from "@fp-ts/core/Option"; + +console.log(O.fromNullable(null)); // none() +console.log(O.fromNullable(undefined)); // none() +console.log(O.fromNullable(1)); // some(1) +``` + +You can also modify a function that returns a nullable value to a function that returns an `Option` using the `liftNullable` API. This process is known as "lifting." + +```ts +import * as O from "@fp-ts/core/Option"; + +const parse = (s: string): number | undefined => { + const n = parseFloat(s); + return isNaN(n) ? undefined : n; +}; + +// const parseOption: (s: string) => Option +const parseOption = O.liftNullable(parse); + +console.log(parseOption("1")); // some(1) +console.log(parseOption("not a number")); // none() +``` + +## Converting an Option to a Nullable Value + +If you have a value of type `Option` and want to convert it to a nullable value, you have two options: + +- Convert `None` to `null` using the `getOrNull` API +- Convert `None` to `undefined` using the `getOrUndefined` API + +```ts +import * as O from "@fp-ts/core/Option"; + +console.log(O.getOrNull(O.some(1))); // 1 +console.log(O.getOrNull(O.none())); // null + +console.log(O.getOrUndefined(O.some(1))); // 1 +console.log(O.getOrUndefined(O.none())); // undefined +``` + +**Cheat sheet** (interop - nullable) + +| **Function** | **Given input** | **Resulting Output** | +| ----------------- | -------------------------------------------------- | ------------------------------------ | +| `fromNullable` | `A` | `Option>` | +| `liftNullable` | `(...a: A) => B \| null \| undefined` | `(...a: A) => Option` | +| `flatMapNullable` | `Option`, `(...a: A) => B \| null \| undefined` | `Option>` | +| `getOrNull` | `Option` | `A \| null` | +| `getOrUndefined` | `Option` | `A \| undefined` | + +# Combining two or more `Option`s + +The `zipWith` function allows you to combine two `Option`s using a provided function. The resulting value is a new `Option` that holds the combined value of both original `Option`s. + +Let's consider the following example where we have two `Option`s that hold values of two different types, `string` and `number`: + +```ts +import { Option, some } from "@fp-ts/core/Option"; + +const name: Option = some("John"); +const age: Option = some(25); +``` + +If we want to combine these two `Option`s into a single `Option` that holds an object with properties `name` and `age`, we can use the `zipWith` function: + +```ts +import { zipWith } from "@fp-ts/core/Option"; + +const combine = zipWith(name, age, (n, a) => ({ name: n, age: a })); +console.log(combine); // some({ name: 'John', age: 25 }) +``` + +The `zipWith` function takes three arguments: + +- The first `Option` you want to combine +- The second `Option` you want to combine +- A function that takes two arguments, which are the values held by the two `Options`, and returns the combined value + +It's important to note that if either of the two `Option`s is `None`, the resulting `Option` will also be `None`. This is because the `zipWith` function only combines the values if both `Option`s are `Some`. + +For example: + +```ts +const name: Option = none(); +const age: Option = some(25); +const combine = zipWith(name, age, (n, a) => ({ name: n, age: a })); +console.log(combine); // none() +``` + +**Cheat sheet** (combining) + +| **Function** | **Given input** | **Resulting Output** | +| --------------- | --------------------------------------- | ----------------------------- | +| `zipWith` | `Option`, `Option`, `(A, B) => C` | `Option` | +| `tuple` | `[Option, Option, ...]` | `Option<[A, B, ...]>` | +| `struct` | `{ a: Option, b: Option, ... }` | `Option<{ a: A, b: B, ... }>` | +| `all` | `Iterable>` | `Option` | +| `appendElement` | `Option<[A, B, ...]>`, `Option` | `Option<[A, B, ..., C]>` | +| `ap` | `Option<(a: A) => B>`, `Option` | `Option` | + +## Algebraic operations with `Option`s + +In addition to `zipWith`, a series of algebraic operations such as sums, products, subtractions, and divisions are exported to make it easier to work with `Option`s. + +For example, consider the following `Option`s holding numbers: + +```ts +import * as O from "@fp-ts/core/Option"; + +const num1 = O.some(3); +const num2 = O.some(4); +const num3 = O.none(); +``` + +Summing two `Some` values will result in a `Some` with the sum of the values: + +```ts +// Summing two `Some` values will result in a `Some` with the sum of the values +const sumOfSome = sum(num1, num2); +console.log(sumOfSome); // some(7) +``` + +Summing a `Some` and a `None` will result in a `None`: + +```ts +// Summing a `Some` and a `None` will result in a `None` +const sumOfSomeAndNone = sum(num1, num3); +console.log(sumOfSomeAndNone); // none() +``` + +**Cheat sheet** (algebraic operations) + +| **Function** | **Given input** | **Resulting Output** | +| ------------ | ---------------------------------- | -------------------- | +| `sum` | `Option`, `Option` | `Option` | +| `multiply` | `Option`, `Option` | `Option` | +| `subtract` | `Option`, `Option` | `Option` | +| `divide` | `Option`, `Option` | `Option` | diff --git a/guides/These.md b/guides/These.md new file mode 100644 index 000000000..7e6cab46d --- /dev/null +++ b/guides/These.md @@ -0,0 +1,15 @@ +# The `These` data type + +## 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. diff --git a/guides/ts-types.md b/guides/ts-types.md new file mode 100644 index 000000000..54ac931f6 --- /dev/null +++ b/guides/ts-types.md @@ -0,0 +1,158 @@ +# Standard TypeScript types + +## 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` | + +## 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 | SemigroupMax | | `Semigroup` | +| Number | SemigroupMin | | `Semigroup` | +| Number | MonoidSum | | `Monoid` | +| Number | MonoidMultiply | | `Monoid` | +| Number | MonoidMax | | `Monoid` | +| Number | MonoidMin | | `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` | + +## 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` | + +## symbols + +| Module | Name | Given | To | +| ----------- | -------- | ----- | ----------------------------- | +| Equivalence | symbol | | `Equivalence` | +| Predicate | isSymbol | | `Refinement` | +| Symbol | isSymbol | | `Refinement` | + +## 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, ...]` (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 + +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>` | +| 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 | 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 + +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>` | diff --git a/Overview.md b/guides/typeclass.md similarity index 86% rename from Overview.md rename to guides/typeclass.md index 0ced453dc..f6d82343d 100644 --- a/Overview.md +++ b/guides/typeclass.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))` @@ -177,15 +194,6 @@ Extends: | andThenDiscard | `F`, `F` | `F` | | bind | `F`, `name: string`, `A => F` | `F` | -### Compactable - -`Compactable` represents data structures which can be compacted / separated. - -| Name | Given | To | -| ----------- | ----------------- | -------------- | -| **compact** | `F>` | `F` | -| separate | `F>` | `[F, F]` | - ### Contravariant Contravariant functors. @@ -252,13 +260,16 @@ Extends: `Filterable` allows you to `map` and filter out elements simultaneously. -| Name | Given | To | -| -------------------- | --------------------------- | -------------- | -| **filterMap** | `F`, `A => Option` | `F` | -| filterMapComposition | `F>`, `A => Option` | `F>` | -| filter | `F`, `A => boolean` | `F` | -| partitionMap | `F`, `A => Either` | `[F, F]` | -| partition | `F`, `A => boolean` | `[F, F]` | +| Name | Given | To | +| ----------------------- | ------------------------------ | -------------------- | +| **partitionMap** | `F`, `A => Either` | `[F, F]` | +| **filterMap** | `F`, `A => Option` | `F` | +| compact | `F>` | `F` | +| separate | `F>` | `[F, F]` | +| filter | `F`, `A => boolean` | `F` | +| partition | `F`, `A => boolean` | `[F, F]` | +| partitionMapComposition | `F>`, `A => Either` | `[F>, F>]` | +| filterMapComposition | `F>`, `A => Option` | `F>` | ### FlatMap @@ -311,21 +322,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 | @@ -432,9 +428,8 @@ Traversal over a structure with an effect. | Name | Given | To | | ------------------- | ---------------------------------------- | ------------ | | **traverse** | `Applicative`, `T`, `A => F` | `F>` | -| **sequence** | `Applicative`, `T>` | `F>` | | traverseComposition | `Applicative`, `T>`, `A => F` | `F>>` | -| sequenceComposition | `Applicative`, `T>>` | `F>>` | +| sequence | `Applicative`, `T>` | `F>` | | traverseTap | `Applicative`, `T`, `A => F` | `F>` | ### TraversableFilterable diff --git a/package.json b/package.json index 686783ae5..a0ab494fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fp-ts/core", - "version": "0.0.9", + "version": "0.2.1", "publishConfig": { "access": "public", "directory": "dist" @@ -63,46 +63,49 @@ }, "devDependencies": { "@babel/cli": "^7.18.6", - "@babel/core": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/core": "^7.20.5", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", "@changesets/changelog-github": "^0.4.7", - "@changesets/cli": "^2.25.0", + "@changesets/cli": "^2.25.2", "@effect-ts/build-utils": "0.40.3", "@effect-ts/core": "^0.60.2", + "@effect/language-service": "^0.0.17", "@repo-tooling/eslint-plugin-dprint": "^0.0.4", - "@types/benchmark": "^1.0.31", - "@types/glob": "^7.1.3", - "@types/jest": "^29.0.3", - "@types/node": "^18.0.0", - "@types/prettier": "1.10.0", + "@types/benchmark": "^2.1.2", + "@types/glob": "^8.0.0", + "@types/jest": "^29.2.3", + "@types/node": "^18.11.9", + "@types/prettier": "2.7.1", "@types/rimraf": "^3.0.2", - "@typescript-eslint/eslint-plugin": "^5.36.2", - "@typescript-eslint/parser": "^5.36.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", + "benchmark": "^2.1.4", "c8": "^7.11.3", - "concurrently": "^7.2.2", + "concurrently": "^7.6.0", "cpx": "^1.5.0", - "docs-ts": "^0.7.0", + "docs-ts": "0.6.10", "dtslint": "github:gcanti/dtslint", - "eslint": "^8.23.0", - "eslint-import-resolver-typescript": "^3.5.1", + "eslint": "^8.28.0", + "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-codegen": "0.16.1", - "eslint-plugin-deprecation": "^1.3.2", + "eslint-plugin-deprecation": "^1.3.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-sort-destructure-keys": "^1.4.0", - "fast-check": "^3.1.4", + "fast-check": "^3.3.0", "glob": "^8.0.3", "madge": "^5.0.1", "picocolors": "^1.0.0", - "prettier": "^2.7.1", + "prettier": "^2.8.0", "rimraf": "^3.0.2", "ts-node": "^10.9.1", "tslint": "^6.1.3", - "typescript": "^4.8.2", + "typescript": "^4.9.3", "ultra-runner": "^3.10.5", - "vite": "^2.9.13", - "vitest": "0.16.0" + "vite": "^3.2.4", + "vitest": "0.25.3" }, "pnpm": { "patchedDependencies": { diff --git a/patches/docs-ts@0.6.10.patch b/patches/docs-ts@0.6.10.patch index 73d4b8fa3..dd3c9ed12 100644 --- a/patches/docs-ts@0.6.10.patch +++ b/patches/docs-ts@0.6.10.patch @@ -1,5 +1,5 @@ diff --git a/lib/Core.js b/lib/Core.js -index c0f282ca9e3ddd93e44a62e1d05c1cc45b9f5c6a..a75fde491fec9f0d6e14c3adf2cc9b000f1c625e 100644 +index c0f282ca9e3ddd93e44a62e1d05c1cc45b9f5c6a..616f7f6223459e0621522279ea7fc1cf1868abe8 100644 --- a/lib/Core.js +++ b/lib/Core.js @@ -167,7 +167,30 @@ var typeCheckExamples = function (modules) { @@ -20,8 +20,8 @@ index c0f282ca9e3ddd93e44a62e1d05c1cc45b9f5c6a..a75fde491fec9f0d6e14c3adf2cc9b00 + "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/*"], @@ -33,4 +33,23 @@ index c0f282ca9e3ddd93e44a62e1d05c1cc45b9f5c6a..a75fde491fec9f0d6e14c3adf2cc9b00 + )}); }), RTE.chain(function () { return spawnTsNode; }), RTE.chain(function () { return cleanExamples; })); })); }; - // ------------------------------------------------------------------------------------- \ No newline at end of file + // ------------------------------------------------------------------------------------- +diff --git a/lib/index.js b/lib/index.js +index 51bdb346c4c33e8835bbda0c16aea71bc0c3115e..7d084d363efd3f8aad76051f4253b0b59cd0c5cd 100644 +--- a/lib/index.js ++++ b/lib/index.js +@@ -33,7 +33,13 @@ exports.exit = TE.fold(onLeft, function () { return onRight; }); + * @internal + */ + exports.compilerOptions = { +- strict: true ++ strict: true, ++ paths: { ++ "@fp-ts/core": ["./src/index.ts"], ++ "@fp-ts/core/test/*": ["./test/*"], ++ "@fp-ts/core/examples/*": ["./examples/*"], ++ "@fp-ts/core/*": ["./src/*"] ++ } + }; + var capabilities = { + example: Example_1.Example, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4be52330..924247087 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ patchedDependencies: hash: wzvflwv62ngf22ppz3frldq37m path: patches/@effect-ts__build-utils@0.40.3.patch docs-ts@0.6.10: - hash: pulkjstupuiumlrt7ngjhrpf2e + hash: nbukyc6gzxb3taliljzd2tnn6m path: patches/docs-ts@0.6.10.patch importers: @@ -13,88 +13,94 @@ importers: .: specifiers: '@babel/cli': ^7.18.6 - '@babel/core': ^7.18.6 - '@babel/plugin-transform-modules-commonjs': ^7.18.6 + '@babel/core': ^7.20.5 + '@babel/plugin-transform-modules-commonjs': ^7.19.6 '@changesets/changelog-github': ^0.4.7 - '@changesets/cli': ^2.25.0 + '@changesets/cli': ^2.25.2 '@effect-ts/build-utils': 0.40.3 '@effect-ts/core': ^0.60.2 + '@effect/language-service': ^0.0.17 '@repo-tooling/eslint-plugin-dprint': ^0.0.4 - '@types/benchmark': ^1.0.31 - '@types/glob': ^7.1.3 - '@types/jest': ^29.0.3 - '@types/node': ^18.0.0 - '@types/prettier': 1.10.0 + '@types/benchmark': ^2.1.2 + '@types/glob': ^8.0.0 + '@types/jest': ^29.2.3 + '@types/node': ^18.11.9 + '@types/prettier': 2.7.1 '@types/rimraf': ^3.0.2 - '@typescript-eslint/eslint-plugin': ^5.36.2 - '@typescript-eslint/parser': ^5.36.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 + benchmark: ^2.1.4 c8: ^7.11.3 - concurrently: ^7.2.2 + concurrently: ^7.6.0 cpx: ^1.5.0 - docs-ts: ^0.7.0 + docs-ts: 0.6.10 dtslint: github:gcanti/dtslint - eslint: ^8.23.0 - eslint-import-resolver-typescript: ^3.5.1 + eslint: ^8.28.0 + eslint-import-resolver-typescript: ^3.5.2 eslint-plugin-codegen: 0.16.1 - eslint-plugin-deprecation: ^1.3.2 + eslint-plugin-deprecation: ^1.3.3 eslint-plugin-import: ^2.26.0 eslint-plugin-simple-import-sort: ^8.0.0 eslint-plugin-sort-destructure-keys: ^1.4.0 - fast-check: ^3.1.4 + fast-check: ^3.3.0 glob: ^8.0.3 madge: ^5.0.1 picocolors: ^1.0.0 - prettier: ^2.7.1 + prettier: ^2.8.0 rimraf: ^3.0.2 ts-node: ^10.9.1 tslint: ^6.1.3 - typescript: ^4.8.2 + typescript: ^4.9.3 ultra-runner: ^3.10.5 - vite: ^2.9.13 - vitest: 0.16.0 + vite: ^3.2.4 + vitest: 0.25.3 devDependencies: - '@babel/cli': 7.19.3_@babel+core@7.19.3 - '@babel/core': 7.19.3 - '@babel/plugin-transform-modules-commonjs': 7.18.6_@babel+core@7.19.3 + '@babel/cli': 7.19.3_@babel+core@7.20.5 + '@babel/core': 7.20.5 + '@babel/plugin-transform-modules-commonjs': 7.19.6_@babel+core@7.20.5 '@changesets/changelog-github': 0.4.7 - '@changesets/cli': 2.25.0 + '@changesets/cli': 2.25.2 '@effect-ts/build-utils': 0.40.3_wzvflwv62ngf22ppz3frldq37m_smskbqsf3q6kbozdrpluio47kq '@effect-ts/core': 0.60.4 - '@repo-tooling/eslint-plugin-dprint': 0.0.4_typescript@4.8.4 - '@types/benchmark': 1.0.33 - '@types/glob': 7.2.0 - '@types/jest': 29.1.2 - '@types/node': 18.8.0 - '@types/prettier': 1.10.0 + '@effect/language-service': 0.0.17 + '@repo-tooling/eslint-plugin-dprint': 0.0.4_typescript@4.9.3 + '@types/benchmark': 2.1.2 + '@types/glob': 8.0.0 + '@types/jest': 29.2.3 + '@types/node': 18.11.9 + '@types/prettier': 2.7.1 '@types/rimraf': 3.0.2 - '@typescript-eslint/eslint-plugin': 5.38.1_c7qepppml3d4ahu5cnfwqe6ltq - '@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa - babel-plugin-annotate-pure-calls: 0.4.0_@babel+core@7.19.3 + '@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 + benchmark: 2.1.4 c8: 7.12.0 - concurrently: 7.4.0 + concurrently: 7.6.0 cpx: 1.5.0 - docs-ts: 0.7.0_dmjzcauo3cshxeipj7bv64ktgm - dtslint: github.com/gcanti/dtslint/f361dc93d6a195f530df28779082548e01cecd5e - eslint: 8.24.0 - eslint-import-resolver-typescript: 3.5.1_dg2pe6kqkrddxbf2funb723kue + docs-ts: 0.6.10_nbukyc6gzxb3taliljzd2tnn6m_brpckf4sz23pco3jyty2eys3iq + dtslint: github.com/gcanti/dtslint/4552d162099399c4e14f8486ced673411e5b3659 + eslint: 8.28.0 + eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee eslint-plugin-codegen: 0.16.1 - eslint-plugin-deprecation: 1.3.2_ypn2ylkkyfa5i233caldtndbqa - eslint-plugin-import: 2.26.0_3vub7fqunrjizhghsam4lcr2pi - eslint-plugin-simple-import-sort: 8.0.0_eslint@8.24.0 - eslint-plugin-sort-destructure-keys: 1.4.0_eslint@8.24.0 - fast-check: 3.1.4 + eslint-plugin-deprecation: 1.3.3_hsf322ms6xhhd4b5ne6lb74y4a + eslint-plugin-import: 2.26.0_vc54pluhgv7booofyyjouvuf74 + eslint-plugin-simple-import-sort: 8.0.0_eslint@8.28.0 + eslint-plugin-sort-destructure-keys: 1.4.0_eslint@8.28.0 + fast-check: 3.3.0 glob: 8.0.3 madge: 5.0.1 picocolors: 1.0.0 - prettier: 2.7.1 + prettier: 2.8.0 rimraf: 3.0.2 - ts-node: 10.9.1_5ra3kupzwcghzakkfdrtyjk72u - tslint: 6.1.3_typescript@4.8.4 - typescript: 4.8.4 + ts-node: 10.9.1_wup25etrarvlqkprac7h35hj7u + tslint: 6.1.3_typescript@4.9.3 + typescript: 4.9.3 ultra-runner: 3.10.5 - vite: 2.9.15 - vitest: 0.16.0_c8@7.12.0 + vite: 3.2.4_@types+node@18.11.9 + vitest: 0.25.3 publishDirectory: dist packages/babel-plugin: @@ -132,17 +138,17 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.15 + '@jridgewell/trace-mapping': 0.3.17 dev: true - /@babel/cli/7.19.3_@babel+core@7.19.3: + /@babel/cli/7.19.3_@babel+core@7.20.5: resolution: {integrity: sha512-643/TybmaCAe101m2tSVHi9UKpETXP9c/Ff4mD2tAwkdP6esKIfaauZFc67vGEM6r9fekbEGid+sZhbEnSe3dg==} engines: {node: '>=6.9.0'} hasBin: true peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.3 + '@babel/core': 7.20.5 '@jridgewell/trace-mapping': 0.3.15 commander: 4.1.1 convert-source-map: 1.8.0 @@ -162,26 +168,26 @@ packages: '@babel/highlight': 7.18.6 dev: true - /@babel/compat-data/7.19.3: - resolution: {integrity: sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==} + /@babel/compat-data/7.20.5: + resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} engines: {node: '>=6.9.0'} dev: true - /@babel/core/7.19.3: - resolution: {integrity: sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==} + /@babel/core/7.20.5: + resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.0 '@babel/code-frame': 7.18.6 - '@babel/generator': 7.19.3 - '@babel/helper-compilation-targets': 7.19.3_@babel+core@7.19.3 - '@babel/helper-module-transforms': 7.19.0 - '@babel/helpers': 7.19.0 - '@babel/parser': 7.19.3 + '@babel/generator': 7.20.5 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helpers': 7.20.6 + '@babel/parser': 7.20.5 '@babel/template': 7.18.10 - '@babel/traverse': 7.19.3 - '@babel/types': 7.19.3 - convert-source-map: 1.8.0 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 + convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.1 @@ -193,28 +199,28 @@ packages: /@babel/generator/7.12.17: resolution: {integrity: sha512-DSA7ruZrY4WI8VxuS1jWSRezFnghEoYEFrZcw9BizQRmOZiUsiHl59+qEARGPqPikwA/GPTyRCi7isuCK/oyqg==} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 jsesc: 2.5.2 source-map: 0.5.7 dev: true - /@babel/generator/7.19.3: - resolution: {integrity: sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==} + /@babel/generator/7.20.5: + resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 dev: true - /@babel/helper-compilation-targets/7.19.3_@babel+core@7.19.3: - resolution: {integrity: sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==} + /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.5: + resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.19.3 - '@babel/core': 7.19.3 + '@babel/compat-data': 7.20.5 + '@babel/core': 7.20.5 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 semver: 6.3.0 @@ -230,60 +236,60 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 dev: true /@babel/helper-hoist-variables/7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 dev: true /@babel/helper-module-imports/7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 dev: true - /@babel/helper-module-transforms/7.19.0: - resolution: {integrity: sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==} + /@babel/helper-module-transforms/7.20.2: + resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.18.6 + '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.18.10 - '@babel/traverse': 7.19.3 - '@babel/types': 7.19.3 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 transitivePeerDependencies: - supports-color dev: true - /@babel/helper-plugin-utils/7.19.0: - resolution: {integrity: sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==} + /@babel/helper-plugin-utils/7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-simple-access/7.18.6: - resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} + /@babel/helper-simple-access/7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 dev: true /@babel/helper-split-export-declaration/7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 dev: true - /@babel/helper-string-parser/7.18.10: - resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} + /@babel/helper-string-parser/7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} dev: true @@ -297,13 +303,13 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helpers/7.19.0: - resolution: {integrity: sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==} + /@babel/helpers/7.20.6: + resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.10 - '@babel/traverse': 7.19.3 - '@babel/types': 7.19.3 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 transitivePeerDependencies: - supports-color dev: true @@ -322,29 +328,36 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 + dev: true + + /@babel/parser/7.20.5: + resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.20.5 dev: true - /@babel/plugin-transform-modules-commonjs/7.18.6_@babel+core@7.19.3: - resolution: {integrity: sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==} + /@babel/plugin-transform-modules-commonjs/7.19.6_@babel+core@7.20.5: + resolution: {integrity: sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.19.3 - '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.19.0 - '@babel/helper-simple-access': 7.18.6 - babel-plugin-dynamic-import-node: 2.3.3 + '@babel/core': 7.20.5 + '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/runtime/7.19.0: - resolution: {integrity: sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==} + /@babel/runtime/7.20.6: + resolution: {integrity: sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==} engines: {node: '>=6.9.0'} dependencies: - regenerator-runtime: 0.13.9 + regenerator-runtime: 0.13.11 dev: true /@babel/template/7.18.10: @@ -352,8 +365,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.19.3 - '@babel/types': 7.19.3 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 dev: true /@babel/traverse/7.19.3: @@ -361,24 +374,42 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/generator': 7.19.3 + '@babel/generator': 7.20.5 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.19.3 - '@babel/types': 7.19.3 + '@babel/types': 7.20.5 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types/7.19.3: - resolution: {integrity: sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==} + /@babel/traverse/7.20.5: + resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-string-parser': 7.18.10 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.20.5 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.19.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.20.5 + '@babel/types': 7.20.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.20.5: + resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 dev: true @@ -387,10 +418,10 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@changesets/apply-release-plan/6.1.1: - resolution: {integrity: sha512-LaQiP/Wf0zMVR0HNrLQAjz3rsNsr0d/RlnP6Ef4oi8VafOwnY1EoWdK4kssuUJGgNgDyHpomS50dm8CU3D7k7g==} + /@changesets/apply-release-plan/6.1.2: + resolution: {integrity: sha512-H8TV9E/WtJsDfoDVbrDGPXmkZFSv7W2KLqp4xX4MKZXshb0hsQZUNowUa8pnus9qb/5OZrFFRVsUsDCVHNW/AQ==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/config': 2.2.0 '@changesets/get-version-range-type': 0.3.2 '@changesets/git': 1.5.0 @@ -400,7 +431,7 @@ packages: fs-extra: 7.0.1 lodash.startcase: 4.4.0 outdent: 0.5.0 - prettier: 2.7.1 + prettier: 2.8.0 resolve-from: 5.0.0 semver: 5.7.1 dev: true @@ -408,7 +439,7 @@ packages: /@changesets/assemble-release-plan/5.2.2: resolution: {integrity: sha512-B1qxErQd85AeZgZFZw2bDKyOfdXHhG+X5S+W3Da2yCem8l/pRy4G/S7iOpEcMwg6lH8q2ZhgbZZwZ817D+aLuQ==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/errors': 0.1.4 '@changesets/get-dependents-graph': 1.3.4 '@changesets/types': 5.2.0 @@ -432,12 +463,12 @@ packages: - encoding dev: true - /@changesets/cli/2.25.0: - resolution: {integrity: sha512-Svu5KD2enurVHGEEzCRlaojrHjVYgF9srmMP9VQSy9c1TspX6C9lDPpulsSNIjYY9BuU/oiWpjBgR7RI9eQiAA==} + /@changesets/cli/2.25.2: + resolution: {integrity: sha512-ACScBJXI3kRyMd2R8n8SzfttDHi4tmKSwVwXBazJOylQItSRSF4cGmej2E4FVf/eNfGy6THkL9GzAahU9ErZrA==} hasBin: true dependencies: - '@babel/runtime': 7.19.0 - '@changesets/apply-release-plan': 6.1.1 + '@babel/runtime': 7.20.6 + '@changesets/apply-release-plan': 6.1.2 '@changesets/assemble-release-plan': 5.2.2 '@changesets/changelog-git': 0.1.13 '@changesets/config': 2.2.0 @@ -449,7 +480,7 @@ packages: '@changesets/pre': 1.0.13 '@changesets/read': 0.5.8 '@changesets/types': 5.2.0 - '@changesets/write': 0.2.1 + '@changesets/write': 0.2.2 '@manypkg/get-packages': 1.1.3 '@types/is-ci': 3.0.0 '@types/semver': 6.2.3 @@ -511,7 +542,7 @@ packages: /@changesets/get-release-plan/3.0.15: resolution: {integrity: sha512-W1tFwxE178/en+zSj/Nqbc3mvz88mcdqUMJhRzN1jDYqN3QI4ifVaRF9mcWUU+KI0gyYEtYR65tour690PqTcA==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/assemble-release-plan': 5.2.2 '@changesets/config': 2.2.0 '@changesets/pre': 1.0.13 @@ -527,7 +558,7 @@ packages: /@changesets/git/1.5.0: resolution: {integrity: sha512-Xo8AT2G7rQJSwV87c8PwMm6BAc98BnufRMsML7m7Iw8Or18WFvFmxqG5aOL5PBvhgq9KrKvaeIBNIymracSuHg==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.0 '@manypkg/get-packages': 1.1.3 @@ -551,7 +582,7 @@ packages: /@changesets/pre/1.0.13: resolution: {integrity: sha512-jrZc766+kGZHDukjKhpBXhBJjVQMied4Fu076y9guY1D3H622NOw8AQaLV3oQsDtKBTrT2AUFjt9Z2Y9Qx+GfA==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.0 '@manypkg/get-packages': 1.1.3 @@ -561,7 +592,7 @@ packages: /@changesets/read/0.5.8: resolution: {integrity: sha512-eYaNfxemgX7f7ELC58e7yqQICW5FB7V+bd1lKt7g57mxUrTveYME+JPaBPpYx02nP53XI6CQp6YxnR9NfmFPKw==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/git': 1.5.0 '@changesets/logger': 0.0.5 '@changesets/parse': 0.3.15 @@ -579,14 +610,14 @@ packages: resolution: {integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==} dev: true - /@changesets/write/0.2.1: - resolution: {integrity: sha512-KUd49nt2fnYdGixIqTi1yVE1nAoZYUMdtB3jBfp77IMqjZ65hrmZE5HdccDlTeClZN0420ffpnfET3zzeY8pdw==} + /@changesets/write/0.2.2: + resolution: {integrity: sha512-kCYNHyF3xaId1Q/QE+DF3UTrHTyg3Cj/f++T8S8/EkC+jh1uK2LFnM9h+EzV+fsmnZDrs7r0J4LLpeI/VWC5Hg==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/types': 5.2.0 fs-extra: 7.0.1 human-id: 1.0.2 - prettier: 2.7.1 + prettier: 2.8.0 dev: true /@cspotcode/source-map-support/0.8.1: @@ -636,6 +667,22 @@ packages: resolution: {integrity: sha512-gGnOCvDv6829EeeC+NgTnwIt4XxclR0KqrG7BRemmNX5cm75cF0CKiuEB/o902wjAlt+MdrxTtFC+XsYdLnxzA==} dev: true + /@effect/language-service/0.0.17: + resolution: {integrity: sha512-98c6VtueGDvCGDfkQijsQ5uP/g4J2VhSbI83f/Rv84Gg0uJ+BbA9jno1dQubvaPRGBMIVE+1egZUduSWY6tfEg==} + dependencies: + '@fp-ts/core': 0.0.11 + '@fp-ts/data': 0.0.41 + dev: true + + /@esbuild/android-arm/0.15.16: + resolution: {integrity: sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.14.54: resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} engines: {node: '>=12'} @@ -645,15 +692,24 @@ packages: dev: true optional: true - /@eslint/eslintrc/1.3.2: - resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==} + /@esbuild/linux-loong64/0.15.16: + resolution: {integrity: sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc/1.3.3: + resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.4.0 - globals: 13.17.0 - ignore: 5.2.0 + espree: 9.4.1 + globals: 13.18.0 + ignore: 5.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -662,8 +718,18 @@ packages: - supports-color dev: true - /@humanwhocodes/config-array/0.10.7: - resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} + /@fp-ts/core/0.0.11: + resolution: {integrity: sha512-BCAJBYzghwoJpcUOARJ1tui50HoYJFlV2pJlVMlsEkDFhD8MTtq8xQVpZCRF66RmtkxtGBYINCQ+5H1lRaL35Q==} + dev: true + + /@fp-ts/data/0.0.41: + resolution: {integrity: sha512-0S93kOQ91D7wp60q/PSVWvMsJjSWJutjt4qX/BvVNV7+fymuC2hTfW2HlE2OcMK11xCNiI6Rq2AkvyzoMhOlpg==} + dependencies: + '@fp-ts/core': 0.0.11 + dev: true + + /@humanwhocodes/config-array/0.11.7: + resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 @@ -673,10 +739,6 @@ packages: - supports-color dev: true - /@humanwhocodes/gitignore-to-minimatch/1.0.2: - resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} - dev: true - /@humanwhocodes/module-importer/1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} @@ -691,18 +753,18 @@ packages: engines: {node: '>=8'} dev: true - /@jest/expect-utils/29.1.2: - resolution: {integrity: sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg==} + /@jest/expect-utils/29.3.1: + resolution: {integrity: sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.0.0 + jest-get-type: 29.2.0 dev: true /@jest/schemas/29.0.0: resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@sinclair/typebox': 0.24.44 + '@sinclair/typebox': 0.24.51 dev: true /@jest/types/26.6.2: @@ -711,20 +773,20 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.8.0 + '@types/node': 18.11.9 '@types/yargs': 15.0.14 chalk: 4.1.2 dev: true - /@jest/types/29.1.2: - resolution: {integrity: sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg==} + /@jest/types/29.3.1: + resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.8.0 - '@types/yargs': 17.0.13 + '@types/node': 18.11.9 + '@types/yargs': 17.0.14 chalk: 4.1.2 dev: true @@ -742,7 +804,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.15 + '@jridgewell/trace-mapping': 0.3.17 dev: true /@jridgewell/resolve-uri/3.1.0: @@ -766,6 +828,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping/0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /@jridgewell/trace-mapping/0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: @@ -776,7 +845,7 @@ packages: /@manypkg/find-root/1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -785,7 +854,7 @@ packages: /@manypkg/get-packages/1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.20.6 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -829,24 +898,24 @@ packages: open: 8.4.0 picocolors: 1.0.0 tiny-glob: 0.2.9 - tslib: 2.4.0 + tslib: 2.4.1 dev: true - /@repo-tooling/eslint-plugin-dprint/0.0.4_typescript@4.8.4: + /@repo-tooling/eslint-plugin-dprint/0.0.4_typescript@4.9.3: resolution: {integrity: sha512-1gQXioqAlyYoRLAQ/4GQ3id8VI9nNO8KKGbbSl5xZf+lo/7Q2zDFhFiuG6owHFYh+2nslQLHy4wJJjOPm2yWEw==} dependencies: '@dprint/formatter': 0.2.0 '@dprint/typescript': 0.68.5 - '@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa + '@typescript-eslint/utils': 5.39.0_hsf322ms6xhhd4b5ne6lb74y4a diff: 5.1.0 - eslint: 8.24.0 + eslint: 8.28.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@sinclair/typebox/0.24.44: - resolution: {integrity: sha512-ka0W0KN5i6LfrSocduwliMMpqVgohtPFidKdMEOUjoOFCHcOOYkKsPRxfs5f15oPNHTm6ERAm0GV/+/LTKeiWg==} + /@sinclair/typebox/0.24.51: + resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true /@ts-morph/common/0.7.5: @@ -876,38 +945,35 @@ packages: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/benchmark/1.0.33: - resolution: {integrity: sha512-rG7Ieasa9UfZJnL72qiFvY9ivhEIYjCGgfcLLb5tJ/EL9+Mcxernj6W3HVCv/cOfJYuwNUwvVVhnrKl8iT8aqA==} + /@types/benchmark/2.1.2: + resolution: {integrity: sha512-EDKtLYNMKrig22jEvhXq8TBFyFgVNSPmDF2b9UzJ7+eylPqdZVo17PCUMkn1jP6/1A/0u78VqYC6VrX6b8pDWA==} dev: true /@types/chai-subset/1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@types/chai': 4.3.3 + '@types/chai': 4.3.4 dev: true /@types/chai/4.3.3: resolution: {integrity: sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==} dev: true - /@types/glob/7.2.0: - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - dependencies: - '@types/minimatch': 5.1.2 - '@types/node': 18.8.0 + /@types/chai/4.3.4: + resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.8.0 + '@types/node': 18.11.9 dev: true /@types/is-ci/3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: - ci-info: 3.4.0 + ci-info: 3.7.0 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -926,11 +992,11 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/jest/29.1.2: - resolution: {integrity: sha512-y+nlX0h87U0R+wsGn6EBuoRWYyv3KFtwRNP3QWp9+k2tJ2/bqcGS3UxD7jgT+tiwJWWq3UsyV4Y+T6rsMT4XMg==} + /@types/jest/29.2.3: + resolution: {integrity: sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w==} dependencies: - expect: 29.1.2 - pretty-format: 29.1.2 + expect: 29.3.1 + pretty-format: 29.3.1 dev: true /@types/json-schema/7.0.11: @@ -957,6 +1023,10 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true + /@types/node/18.11.9: + resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==} + dev: true + /@types/node/18.8.0: resolution: {integrity: sha512-u+h43R6U8xXDt2vzUaVP3VwjjLyOJk6uEciZS8OSyziUQGOwmk+l+4drxcsDboHXwyTaqS1INebghmWMRxq3LA==} dev: true @@ -965,21 +1035,25 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true - /@types/prettier/1.10.0: - resolution: {integrity: sha512-mjEM5uxPvMYg8LLcnmHzMy2G7HFRv7aQtcKslpmHg8SIIQutnLpLfps+1soV2YBlVBFrskR+F6I+nBI9NTh49w==} + /@types/prettier/2.7.1: + resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} dev: true /@types/rimraf/3.0.2: resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==} dependencies: '@types/glob': 8.0.0 - '@types/node': 18.8.0 + '@types/node': 18.11.9 dev: true /@types/semver/6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true + /@types/semver/7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true @@ -994,14 +1068,14 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@types/yargs/17.0.13: - resolution: {integrity: sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==} + /@types/yargs/17.0.14: + resolution: {integrity: sha512-9Pj7abXoW1RSTcZaL2Hk6G2XyLMlp5ECdVC/Zf2p/KBjC3srijLGgRAXOBjtFrJoIrvxdTKyKDA14bEcbxBaWw==} dependencies: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/5.38.1_c7qepppml3d4ahu5cnfwqe6ltq: - resolution: {integrity: sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==} + /@typescript-eslint/eslint-plugin/5.44.0_fnsv2sbzcckq65bwfk7a5xwslu: + resolution: {integrity: sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -1011,36 +1085,37 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa - '@typescript-eslint/scope-manager': 5.38.1 - '@typescript-eslint/type-utils': 5.38.1_ypn2ylkkyfa5i233caldtndbqa - '@typescript-eslint/utils': 5.38.1_ypn2ylkkyfa5i233caldtndbqa + '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/type-utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + '@typescript-eslint/utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a debug: 4.3.4 - eslint: 8.24.0 - ignore: 5.2.0 + eslint: 8.28.0 + ignore: 5.2.1 + natural-compare-lite: 1.4.0 regexpp: 3.2.0 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/experimental-utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-n5N9kG/oGu2xXhHzsWzn94s6CWoiUj59FPU2dF2IQZxPftw+q6Jm5sV2vj5qTgAElRooHhrgtl2gxBQDCPt6WA==} + /@typescript-eslint/experimental-utils/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-j8GLemAySe8oUCgILdUaT66pemdWSYcwUYG2Pb71O119hCdvkU+4q8sUTbnDg8NhlZEzSWG2N1v4IxT1kEZrGg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa - eslint: 8.24.0 + '@typescript-eslint/utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + eslint: 8.28.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/parser/5.38.1_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==} + /@typescript-eslint/parser/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1049,24 +1124,16 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.38.1 - '@typescript-eslint/types': 5.38.1 - '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 debug: 4.3.4 - eslint: 8.24.0 - typescript: 4.8.4 + eslint: 8.28.0 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.38.1: - resolution: {integrity: sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.38.1 - '@typescript-eslint/visitor-keys': 5.38.1 - dev: true - /@typescript-eslint/scope-manager/5.39.0: resolution: {integrity: sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1075,8 +1142,16 @@ packages: '@typescript-eslint/visitor-keys': 5.39.0 dev: true - /@typescript-eslint/type-utils/5.38.1_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==} + /@typescript-eslint/scope-manager/5.44.0: + resolution: {integrity: sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/visitor-keys': 5.44.0 + dev: true + + /@typescript-eslint/type-utils/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1085,12 +1160,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 - '@typescript-eslint/utils': 5.38.1_ypn2ylkkyfa5i233caldtndbqa + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 + '@typescript-eslint/utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a debug: 4.3.4 - eslint: 8.24.0 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + eslint: 8.28.0 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true @@ -1100,13 +1175,13 @@ packages: engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} dev: true - /@typescript-eslint/types/5.38.1: - resolution: {integrity: sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==} + /@typescript-eslint/types/5.39.0: + resolution: {integrity: sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types/5.39.0: - resolution: {integrity: sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==} + /@typescript-eslint/types/5.44.0: + resolution: {integrity: sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -1124,15 +1199,15 @@ packages: debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 + semver: 7.3.8 tsutils: 3.21.0_typescript@3.9.10 typescript: 3.9.10 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.38.1_typescript@4.8.4: - resolution: {integrity: sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==} + /@typescript-eslint/typescript-estree/5.39.0_typescript@4.9.3: + resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1140,20 +1215,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.38.1 - '@typescript-eslint/visitor-keys': 5.38.1 + '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/visitor-keys': 5.39.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree/5.39.0_typescript@4.8.4: - resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==} + /@typescript-eslint/typescript-estree/5.44.0_typescript@4.9.3: + resolution: {integrity: sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1161,49 +1236,51 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.39.0 - '@typescript-eslint/visitor-keys': 5.39.0 + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/visitor-keys': 5.44.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.7 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.38.1_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==} + /@typescript-eslint/utils/5.39.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.38.1 - '@typescript-eslint/types': 5.38.1 - '@typescript-eslint/typescript-estree': 5.38.1_typescript@4.8.4 - eslint: 8.24.0 + '@typescript-eslint/scope-manager': 5.39.0 + '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/typescript-estree': 5.39.0_typescript@4.9.3 + eslint: 8.28.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.24.0 + eslint-utils: 3.0.0_eslint@8.28.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==} + /@typescript-eslint/utils/5.44.0_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.39.0 - '@typescript-eslint/types': 5.39.0 - '@typescript-eslint/typescript-estree': 5.39.0_typescript@4.8.4 - eslint: 8.24.0 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.44.0 + '@typescript-eslint/types': 5.44.0 + '@typescript-eslint/typescript-estree': 5.44.0_typescript@4.9.3 + eslint: 8.28.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.24.0 + eslint-utils: 3.0.0_eslint@8.28.0 + semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript @@ -1217,28 +1294,47 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /@typescript-eslint/visitor-keys/5.38.1: - resolution: {integrity: sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==} + /@typescript-eslint/visitor-keys/5.39.0: + resolution: {integrity: sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.38.1 + '@typescript-eslint/types': 5.39.0 eslint-visitor-keys: 3.3.0 dev: true - /@typescript-eslint/visitor-keys/5.39.0: - resolution: {integrity: sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==} + /@typescript-eslint/visitor-keys/5.44.0: + resolution: {integrity: sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.39.0 + '@typescript-eslint/types': 5.44.0 eslint-visitor-keys: 3.3.0 dev: true - /acorn-jsx/5.3.2_acorn@8.8.0: + /@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: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.8.0 + acorn: 8.8.1 dev: true /acorn-walk/8.2.0: @@ -1252,6 +1348,12 @@ packages: hasBin: true dev: true + /acorn/8.8.1: + resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -1410,6 +1512,16 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /array.prototype.flat/1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + dev: true + /arrify/1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -1454,18 +1566,12 @@ packages: gulp-header: 1.8.12 dev: true - /babel-plugin-annotate-pure-calls/0.4.0_@babel+core@7.19.3: + /babel-plugin-annotate-pure-calls/0.4.0_@babel+core@7.20.5: resolution: {integrity: sha512-oi4M/PWUJOU9ZyRGoPTfPMqdyMp06jbJAomd3RcyYuzUtBOddv98BqLm96Lucpi2QFoQHkdGQt0ACvw7VzVEQA==} peerDependencies: '@babel/core': ^6.0.0-0 || 7.x dependencies: - '@babel/core': 7.19.3 - dev: true - - /babel-plugin-dynamic-import-node/2.3.3: - resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} - dependencies: - object.assign: 4.1.4 + '@babel/core': 7.20.5 dev: true /babel-runtime/6.26.0: @@ -1496,6 +1602,13 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /benchmark/2.1.4: + resolution: {integrity: sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==} + dependencies: + lodash: 4.17.21 + platform: 1.3.6 + dev: true + /better-path-resolve/1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1588,10 +1701,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001414 - electron-to-chromium: 1.4.270 + caniuse-lite: 1.0.30001434 + electron-to-chromium: 1.4.284 node-releases: 2.0.6 - update-browserslist-db: 1.0.9_browserslist@4.21.4 + update-browserslist-db: 1.0.10_browserslist@4.21.4 dev: true /buffer-from/1.1.2: @@ -1629,6 +1742,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'} @@ -1670,8 +1788,8 @@ packages: engines: {node: '>=6'} dev: true - /caniuse-lite/1.0.30001414: - resolution: {integrity: sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg==} + /caniuse-lite/1.0.30001434: + resolution: {integrity: sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==} dev: true /chai/4.3.6: @@ -1687,6 +1805,19 @@ packages: type-detect: 4.0.8 dev: true + /chai/4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.2 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1747,8 +1878,9 @@ packages: dev: true optional: true - /ci-info/3.4.0: - resolution: {integrity: sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==} + /ci-info/3.7.0: + resolution: {integrity: sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==} + engines: {node: '>=8'} dev: true /class-utils/0.3.6: @@ -1885,8 +2017,8 @@ packages: source-map: 0.6.1 dev: true - /concurrently/7.4.0: - resolution: {integrity: sha512-M6AfrueDt/GEna/Vg9BqQ+93yuvzkSKmoTixnwEJkH0LlcGrRC2eCmjeG1tLLHIYfpYJABokqSGyMcXjm96AFA==} + /concurrently/7.6.0: + resolution: {integrity: sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==} engines: {node: ^12.20.0 || ^14.13.0 || >=16.0.0} hasBin: true dependencies: @@ -1894,11 +2026,11 @@ packages: date-fns: 2.29.3 lodash: 4.17.21 rxjs: 7.5.7 - shell-quote: 1.7.3 + shell-quote: 1.7.4 spawn-command: 0.0.2-1 supports-color: 8.1.1 tree-kill: 1.2.2 - yargs: 17.6.0 + yargs: 17.6.2 dev: true /convert-source-map/1.8.0: @@ -1907,6 +2039,10 @@ packages: safe-buffer: 5.1.2 dev: true + /convert-source-map/1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + /copy-descriptor/0.1.1: resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} @@ -2027,8 +2163,8 @@ packages: ms: 2.1.2 dev: true - /decamelize-keys/1.1.0: - resolution: {integrity: sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==} + /decamelize-keys/1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} dependencies: decamelize: 1.2.0 @@ -2052,6 +2188,13 @@ packages: type-detect: 4.0.8 dev: true + /deep-eql/4.1.2: + resolution: {integrity: sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-extend/0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -2061,8 +2204,8 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /defaults/1.0.3: - resolution: {integrity: sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==} + /defaults/1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 dev: true @@ -2164,7 +2307,7 @@ packages: dependencies: debug: 4.3.4 is-url: 1.2.4 - postcss: 8.4.17 + postcss: 8.4.19 postcss-values-parser: 2.0.1 transitivePeerDependencies: - supports-color @@ -2175,8 +2318,8 @@ packages: engines: {node: 12.x || 14.x || 16.x} dependencies: is-url: 1.2.4 - postcss: 8.4.17 - postcss-values-parser: 5.0.0_postcss@8.4.17 + postcss: 8.4.19 + postcss-values-parser: 5.0.0_postcss@8.4.19 dev: true /detective-sass/3.0.2: @@ -2221,8 +2364,8 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /diff-sequences/29.0.0: - resolution: {integrity: sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==} + /diff-sequences/29.3.1: + resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -2266,8 +2409,8 @@ packages: dev: true patched: true - /docs-ts/0.7.0_dmjzcauo3cshxeipj7bv64ktgm: - resolution: {integrity: sha512-S9UM7Ddh0l3qwftSJiko+3+aGJrnGTnigKq4wpsOvQBmMJz7kMmdAeiTwNCRgzQI70oT3T2uyuzR9hy5JT2xPQ==} + /docs-ts/0.6.10_nbukyc6gzxb3taliljzd2tnn6m_brpckf4sz23pco3jyty2eys3iq: + resolution: {integrity: sha512-DTX9c5AJ92ojMOKqqvwF8t77C8Gdgs9FPB8seymHs+F+Wl6aapc3ZkHUM+p8o+jwcBmPoihxssdK903dfwQ1JQ==} hasBin: true peerDependencies: prettier: ^2.0.0 @@ -2281,12 +2424,13 @@ packages: io-ts: 2.2.19_fp-ts@2.12.3 logging-ts: 0.3.4_fp-ts@2.12.3 markdown-toc: 1.2.0 - prettier: 2.7.1 + prettier: 2.8.0 rimraf: 2.7.1 ts-morph: 9.1.0 - ts-node: 8.10.2_typescript@4.8.4 - typescript: 4.8.4 + 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==} @@ -2311,16 +2455,16 @@ packages: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: true - /electron-to-chromium/1.4.270: - resolution: {integrity: sha512-KNhIzgLiJmDDC444dj9vEOpZEgsV96ult9Iff98Vanumn+ShJHd5se8aX6KeVxdc0YQeqdrezBZv89rleDbvSg==} + /electron-to-chromium/1.4.284: + resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true - /enhanced-resolve/5.10.0: - resolution: {integrity: sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==} + /enhanced-resolve/5.12.0: + resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==} engines: {node: '>=10.13.0'} dependencies: graceful-fs: 4.2.10 @@ -2370,6 +2514,36 @@ packages: unbox-primitive: 1.0.2 dev: true + /es-abstract/1.20.4: + resolution: {integrity: sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.3 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + unbox-primitive: 1.0.2 + dev: true + /es-shim-unscopables/1.0.0: resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} dependencies: @@ -2394,6 +2568,15 @@ packages: dev: true optional: true + /esbuild-android-64/0.15.16: + resolution: {integrity: sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-android-arm64/0.14.54: resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} engines: {node: '>=12'} @@ -2403,6 +2586,15 @@ packages: dev: true optional: true + /esbuild-android-arm64/0.15.16: + resolution: {integrity: sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-64/0.14.54: resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} engines: {node: '>=12'} @@ -2412,6 +2604,15 @@ packages: dev: true optional: true + /esbuild-darwin-64/0.15.16: + resolution: {integrity: sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-arm64/0.14.54: resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} engines: {node: '>=12'} @@ -2421,6 +2622,15 @@ packages: dev: true optional: true + /esbuild-darwin-arm64/0.15.16: + resolution: {integrity: sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-64/0.14.54: resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} engines: {node: '>=12'} @@ -2430,6 +2640,15 @@ packages: dev: true optional: true + /esbuild-freebsd-64/0.15.16: + resolution: {integrity: sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-arm64/0.14.54: resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} engines: {node: '>=12'} @@ -2439,6 +2658,15 @@ packages: dev: true optional: true + /esbuild-freebsd-arm64/0.15.16: + resolution: {integrity: sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-32/0.14.54: resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} engines: {node: '>=12'} @@ -2448,6 +2676,15 @@ packages: dev: true optional: true + /esbuild-linux-32/0.15.16: + resolution: {integrity: sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-64/0.14.54: resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} engines: {node: '>=12'} @@ -2457,6 +2694,15 @@ packages: dev: true optional: true + /esbuild-linux-64/0.15.16: + resolution: {integrity: sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm/0.14.54: resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} engines: {node: '>=12'} @@ -2466,6 +2712,15 @@ packages: dev: true optional: true + /esbuild-linux-arm/0.15.16: + resolution: {integrity: sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm64/0.14.54: resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} engines: {node: '>=12'} @@ -2475,6 +2730,15 @@ packages: dev: true optional: true + /esbuild-linux-arm64/0.15.16: + resolution: {integrity: sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-mips64le/0.14.54: resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} engines: {node: '>=12'} @@ -2484,6 +2748,15 @@ packages: dev: true optional: true + /esbuild-linux-mips64le/0.15.16: + resolution: {integrity: sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-ppc64le/0.14.54: resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} engines: {node: '>=12'} @@ -2493,6 +2766,15 @@ packages: dev: true optional: true + /esbuild-linux-ppc64le/0.15.16: + resolution: {integrity: sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-riscv64/0.14.54: resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} engines: {node: '>=12'} @@ -2502,6 +2784,15 @@ packages: dev: true optional: true + /esbuild-linux-riscv64/0.15.16: + resolution: {integrity: sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-s390x/0.14.54: resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} engines: {node: '>=12'} @@ -2511,6 +2802,15 @@ packages: dev: true optional: true + /esbuild-linux-s390x/0.15.16: + resolution: {integrity: sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-netbsd-64/0.14.54: resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} engines: {node: '>=12'} @@ -2520,6 +2820,15 @@ packages: dev: true optional: true + /esbuild-netbsd-64/0.15.16: + resolution: {integrity: sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-openbsd-64/0.14.54: resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} engines: {node: '>=12'} @@ -2529,6 +2838,15 @@ packages: dev: true optional: true + /esbuild-openbsd-64/0.15.16: + resolution: {integrity: sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-sunos-64/0.14.54: resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} engines: {node: '>=12'} @@ -2538,8 +2856,26 @@ packages: dev: true optional: true - /esbuild-windows-32/0.14.54: - resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + /esbuild-sunos-64/0.15.16: + resolution: {integrity: sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.15.16: + resolution: {integrity: sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -2556,6 +2892,15 @@ packages: dev: true optional: true + /esbuild-windows-64/0.15.16: + resolution: {integrity: sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-arm64/0.14.54: resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} engines: {node: '>=12'} @@ -2565,6 +2910,15 @@ packages: dev: true optional: true + /esbuild-windows-arm64/0.15.16: + resolution: {integrity: sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild/0.14.54: resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} engines: {node: '>=12'} @@ -2594,6 +2948,36 @@ packages: esbuild-windows-arm64: 0.14.54 dev: true + /esbuild/0.15.16: + resolution: {integrity: sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.16 + '@esbuild/linux-loong64': 0.15.16 + esbuild-android-64: 0.15.16 + esbuild-android-arm64: 0.15.16 + esbuild-darwin-64: 0.15.16 + esbuild-darwin-arm64: 0.15.16 + esbuild-freebsd-64: 0.15.16 + esbuild-freebsd-arm64: 0.15.16 + esbuild-linux-32: 0.15.16 + esbuild-linux-64: 0.15.16 + esbuild-linux-arm: 0.15.16 + esbuild-linux-arm64: 0.15.16 + esbuild-linux-mips64le: 0.15.16 + esbuild-linux-ppc64le: 0.15.16 + esbuild-linux-riscv64: 0.15.16 + esbuild-linux-s390x: 0.15.16 + esbuild-netbsd-64: 0.15.16 + esbuild-openbsd-64: 0.15.16 + esbuild-sunos-64: 0.15.16 + esbuild-windows-32: 0.15.16 + esbuild-windows-64: 0.15.16 + esbuild-windows-arm64: 0.15.16 + dev: true + /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -2636,27 +3020,27 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript/3.5.1_dg2pe6kqkrddxbf2funb723kue: - resolution: {integrity: sha512-U7LUjNJPYjNsHvAUAkt/RU3fcTSpbllA0//35B4eLYTX74frmOepbt7F7J3D1IGtj9k21buOpaqtDd4ZlS/BYQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + /eslint-import-resolver-typescript/3.5.2_ktrec6dplf4now6nlbc6d67jee: + resolution: {integrity: sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' dependencies: debug: 4.3.4 - enhanced-resolve: 5.10.0 - eslint: 8.24.0 - eslint-plugin-import: 2.26.0_3vub7fqunrjizhghsam4lcr2pi + enhanced-resolve: 5.12.0 + eslint: 8.28.0 + eslint-plugin-import: 2.26.0_vc54pluhgv7booofyyjouvuf74 get-tsconfig: 4.2.0 globby: 13.1.2 - is-core-module: 2.10.0 + is-core-module: 2.11.0 is-glob: 4.0.3 synckit: 0.8.4 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils/2.7.4_36jginl7aok4giyuaq3k6zvh2u: + /eslint-module-utils/2.7.4_d2eo2jksnn7c2x6eoou4blnbne: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -2677,11 +3061,11 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa + '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a debug: 3.2.7 - eslint: 8.24.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-import-resolver-typescript: 3.5.1_dg2pe6kqkrddxbf2funb723kue + eslint-import-resolver-typescript: 3.5.2_ktrec6dplf4now6nlbc6d67jee transitivePeerDependencies: - supports-color dev: true @@ -2689,7 +3073,7 @@ packages: /eslint-plugin-codegen/0.16.1: resolution: {integrity: sha512-+mKDJezeoziyQOuPicRj6im1GxzxDvv+EgAZAOTIxBwaWTadppgwZGFMeUrWucNM2duJXmvLDcbsmVT69ORgQA==} dependencies: - '@babel/core': 7.19.3 + '@babel/core': 7.20.5 '@babel/generator': 7.12.17 '@babel/parser': 7.19.3 '@babel/traverse': 7.19.3 @@ -2706,22 +3090,22 @@ packages: - supports-color dev: true - /eslint-plugin-deprecation/1.3.2_ypn2ylkkyfa5i233caldtndbqa: - resolution: {integrity: sha512-z93wbx9w7H/E3ogPw6AZMkkNJ6m51fTZRNZPNQqxQLmx+KKt7aLkMU9wN67s71i+VVHN4tLOZ3zT3QLbnlC0Mg==} + /eslint-plugin-deprecation/1.3.3_hsf322ms6xhhd4b5ne6lb74y4a: + resolution: {integrity: sha512-Bbkv6ZN2cCthVXz/oZKPwsSY5S/CbgTLRG4Q2s2gpPpgNsT0uJ0dB5oLNiWzFYY8AgKX4ULxXFG1l/rDav9QFA==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: ^3.7.5 || ^4.0.0 dependencies: - '@typescript-eslint/experimental-utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa - eslint: 8.24.0 - tslib: 2.4.0 - tsutils: 3.21.0_typescript@4.8.4 - typescript: 4.8.4 + '@typescript-eslint/experimental-utils': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a + eslint: 8.28.0 + tslib: 2.4.1 + tsutils: 3.21.0_typescript@4.9.3 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import/2.26.0_3vub7fqunrjizhghsam4lcr2pi: + /eslint-plugin-import/2.26.0_vc54pluhgv7booofyyjouvuf74: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -2731,14 +3115,14 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.38.1_ypn2ylkkyfa5i233caldtndbqa + '@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 - eslint: 8.24.0 + eslint: 8.28.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_36jginl7aok4giyuaq3k6zvh2u + eslint-module-utils: 2.7.4_d2eo2jksnn7c2x6eoou4blnbne has: 1.0.3 is-core-module: 2.10.0 is-glob: 4.0.3 @@ -2752,21 +3136,21 @@ packages: - supports-color dev: true - /eslint-plugin-simple-import-sort/8.0.0_eslint@8.24.0: + /eslint-plugin-simple-import-sort/8.0.0_eslint@8.28.0: resolution: {integrity: sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==} peerDependencies: eslint: '>=5.0.0' dependencies: - eslint: 8.24.0 + eslint: 8.28.0 dev: true - /eslint-plugin-sort-destructure-keys/1.4.0_eslint@8.24.0: + /eslint-plugin-sort-destructure-keys/1.4.0_eslint@8.28.0: resolution: {integrity: sha512-txU9l22mblz7YpyjJNYFy4wb5PVXiRMbc9lqFPPhvY4wKyBBYQvb31TIcduf7iRb4Bv01aiXcJiuCkOOrVY48Q==} engines: {node: '>=6.0.0'} peerDependencies: eslint: 3 - 8 dependencies: - eslint: 8.24.0 + eslint: 8.28.0 natural-compare-lite: 1.4.0 dev: true @@ -2786,13 +3170,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.24.0: + /eslint-utils/3.0.0_eslint@8.28.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.24.0 + eslint: 8.28.0 eslint-visitor-keys: 2.1.0 dev: true @@ -2806,15 +3190,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.24.0: - resolution: {integrity: sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==} + /eslint/8.28.0: + resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint/eslintrc': 1.3.2 - '@humanwhocodes/config-array': 0.10.7 - '@humanwhocodes/gitignore-to-minimatch': 1.0.2 + '@eslint/eslintrc': 1.3.3 + '@humanwhocodes/config-array': 0.11.7 '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 @@ -2822,23 +3206,23 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.24.0 + eslint-utils: 3.0.0_eslint@8.28.0 eslint-visitor-keys: 3.3.0 - espree: 9.4.0 + espree: 9.4.1 esquery: 1.4.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.17.0 - globby: 11.1.0 + globals: 13.18.0 grapheme-splitter: 1.0.4 - ignore: 5.2.0 + ignore: 5.2.1 import-fresh: 3.3.0 imurmurhash: 0.1.4 is-glob: 4.0.3 - js-sdsl: 4.1.5 + is-path-inside: 3.0.3 + js-sdsl: 4.2.0 js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 @@ -2854,12 +3238,12 @@ packages: - supports-color dev: true - /espree/9.4.0: - resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} + /espree/9.4.1: + resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.8.0 - acorn-jsx: 5.3.2_acorn@8.8.0 + acorn: 8.8.1 + acorn-jsx: 5.3.2_acorn@8.8.1 eslint-visitor-keys: 3.3.0 dev: true @@ -2939,15 +3323,15 @@ packages: jest-regex-util: 26.0.0 dev: true - /expect/29.1.2: - resolution: {integrity: sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw==} + /expect/29.3.1: + resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/expect-utils': 29.1.2 - jest-get-type: 29.0.0 - jest-matcher-utils: 29.1.2 - jest-message-util: 29.1.2 - jest-util: 29.1.2 + '@jest/expect-utils': 29.3.1 + jest-get-type: 29.2.0 + jest-matcher-utils: 29.3.1 + jest-message-util: 29.3.1 + jest-util: 29.3.1 dev: true /extend-shallow/2.0.1: @@ -3008,6 +3392,13 @@ packages: pure-rand: 5.0.3 dev: true + /fast-check/3.3.0: + resolution: {integrity: sha512-Zu6tZ4g0T4H9Tiz3tdNPEHrSbuICj7yhdOM9RCZKNMkpjZ9avDV3ORklXaEmh4zvkX24/bGZ9DxKKqWfXttUqw==} + engines: {node: '>=8.0.0'} + dependencies: + pure-rand: 5.0.5 + dev: true + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -3063,7 +3454,7 @@ packages: app-module-path: 2.2.0 commander: 2.20.3 debug: 4.3.4 - enhanced-resolve: 5.10.0 + enhanced-resolve: 5.12.0 is-relative-path: 1.0.2 module-definition: 3.4.0 module-lookup-amd: 7.0.1 @@ -3222,7 +3613,7 @@ packages: requiresBuild: true dependencies: bindings: 1.5.0 - nan: 2.16.0 + nan: 2.17.0 dev: true optional: true @@ -3244,7 +3635,7 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.1.4 - es-abstract: 1.20.3 + es-abstract: 1.20.4 functions-have-names: 1.2.3 dev: true @@ -3365,8 +3756,8 @@ packages: engines: {node: '>=4'} dev: true - /globals/13.17.0: - resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==} + /globals/13.18.0: + resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -3383,7 +3774,7 @@ packages: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.2.12 - ignore: 5.2.0 + ignore: 5.2.1 merge2: 1.4.1 slash: 3.0.0 dev: true @@ -3394,7 +3785,7 @@ packages: dependencies: dir-glob: 3.0.1 fast-glob: 3.2.12 - ignore: 5.2.0 + ignore: 5.2.1 merge2: 1.4.1 slash: 4.0.0 dev: true @@ -3549,6 +3940,11 @@ packages: engines: {node: '>= 4'} dev: true + /ignore/5.2.1: + resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} + engines: {node: '>= 4'} + dev: true + /import-fresh/3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -3678,7 +4074,7 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true dependencies: - ci-info: 3.4.0 + ci-info: 3.7.0 dev: true /is-core-module/2.10.0: @@ -3687,6 +4083,12 @@ packages: has: 1.0.3 dev: true + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + /is-data-descriptor/0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} @@ -3836,6 +4238,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + /is-plain-obj/1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -4000,14 +4407,14 @@ packages: pretty-format: 26.6.2 dev: true - /jest-diff/29.1.2: - resolution: {integrity: sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ==} + /jest-diff/29.3.1: + resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - diff-sequences: 29.0.0 - jest-get-type: 29.0.0 - pretty-format: 29.1.2 + diff-sequences: 29.3.1 + jest-get-type: 29.2.0 + pretty-format: 29.3.1 dev: true /jest-get-type/26.3.0: @@ -4015,8 +4422,8 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /jest-get-type/29.0.0: - resolution: {integrity: sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==} + /jest-get-type/29.2.0: + resolution: {integrity: sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -4030,14 +4437,14 @@ packages: pretty-format: 26.6.2 dev: true - /jest-matcher-utils/29.1.2: - resolution: {integrity: sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw==} + /jest-matcher-utils/29.3.1: + resolution: {integrity: sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - jest-diff: 29.1.2 - jest-get-type: 29.0.0 - pretty-format: 29.1.2 + jest-diff: 29.3.1 + jest-get-type: 29.2.0 + pretty-format: 29.3.1 dev: true /jest-message-util/26.6.2: @@ -4052,22 +4459,22 @@ packages: micromatch: 4.0.5 pretty-format: 26.6.2 slash: 3.0.0 - stack-utils: 2.0.5 + stack-utils: 2.0.6 dev: true - /jest-message-util/29.1.2: - resolution: {integrity: sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ==} + /jest-message-util/29.3.1: + resolution: {integrity: sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.1.2 + '@jest/types': 29.3.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.1.2 + pretty-format: 29.3.1 slash: 3.0.0 - stack-utils: 2.0.5 + stack-utils: 2.0.6 dev: true /jest-regex-util/26.0.0: @@ -4075,20 +4482,20 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /jest-util/29.1.2: - resolution: {integrity: sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ==} + /jest-util/29.3.1: + resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.1.2 - '@types/node': 18.8.0 + '@jest/types': 29.3.1 + '@types/node': 18.11.9 chalk: 4.1.2 - ci-info: 3.4.0 + ci-info: 3.7.0 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true - /js-sdsl/4.1.5: - resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} + /js-sdsl/4.2.0: + resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} dev: true /js-tokens/4.0.0: @@ -4141,6 +4548,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: @@ -4293,6 +4704,12 @@ packages: get-func-name: 2.0.0 dev: true + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -4413,7 +4830,7 @@ packages: dependencies: '@types/minimist': 1.2.2 camelcase-keys: 6.2.2 - decamelize-keys: 1.1.0 + decamelize-keys: 1.1.1 hard-rejection: 2.1.0 minimist-options: 4.1.0 normalize-package-data: 2.5.0 @@ -4543,6 +4960,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'} @@ -4585,8 +5011,8 @@ packages: minimatch: 3.1.2 dev: true - /nan/2.16.0: - resolution: {integrity: sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==} + /nan/2.17.0: + resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} requiresBuild: true dev: true optional: true @@ -4644,7 +5070,7 @@ packages: resolution: {integrity: sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==} engines: {node: '>=6.0'} dependencies: - '@babel/parser': 7.19.3 + '@babel/parser': 7.20.5 dev: true /normalize-package-data/2.5.0: @@ -4915,6 +5341,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 @@ -4944,6 +5378,18 @@ 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 + + /platform/1.3.6: + resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + dev: true + /pluralize/8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -4963,7 +5409,7 @@ packages: uniq: 1.0.1 dev: true - /postcss-values-parser/5.0.0_postcss@8.4.17: + /postcss-values-parser/5.0.0_postcss@8.4.19: resolution: {integrity: sha512-2viDDjMMrt21W2izbeiJxl3kFuD/+asgB0CBwPEgSyhCmBnDIa/y+pLaoyX+q3I3DHH0oPPL3cgjVTQvlS1Maw==} engines: {node: '>=10'} peerDependencies: @@ -4971,7 +5417,7 @@ packages: dependencies: color-name: 1.1.4 is-url-superb: 4.0.0 - postcss: 8.4.17 + postcss: 8.4.19 quote-unquote: 1.0.0 dev: true @@ -4984,6 +5430,15 @@ packages: source-map-js: 1.0.2 dev: true + /postcss/8.4.19: + resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /precinct/8.3.1: resolution: {integrity: sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==} engines: {node: ^10.13 || ^12 || >=14} @@ -5037,6 +5492,12 @@ packages: hasBin: true dev: true + /prettier/2.8.0: + resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-format/26.6.2: resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} engines: {node: '>= 10'} @@ -5047,8 +5508,8 @@ packages: react-is: 17.0.2 dev: true - /pretty-format/29.1.2: - resolution: {integrity: sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg==} + /pretty-format/29.3.1: + resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.0.0 @@ -5085,6 +5546,10 @@ packages: resolution: {integrity: sha512-9N8x1h8dptBQpHyC7aZMS+iNOAm97WMGY0AFrguU1cpfW3I5jINkWe5BIY5md0ofy+1TCIELsVcm/GJXZSaPbw==} dev: true + /pure-rand/5.0.5: + resolution: {integrity: sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==} + dev: true + /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -5206,8 +5671,8 @@ packages: resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} dev: true - /regenerator-runtime/0.13.9: - resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} + /regenerator-runtime/0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} dev: true /regex-cache/0.4.4: @@ -5354,6 +5819,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -5363,7 +5836,7 @@ packages: /rxjs/7.5.7: resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: true /safe-buffer/5.1.2: @@ -5410,8 +5883,8 @@ packages: hasBin: true dev: true - /semver/7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} hasBin: true dependencies: @@ -5467,6 +5940,10 @@ packages: resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} dev: true + /shell-quote/1.7.4: + resolution: {integrity: sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==} + dev: true + /shellwords-ts/3.0.0: resolution: {integrity: sha512-4uZTHR2P7zKRZmSoOiUCFK1K+5LlDxay/RVNWDDImnGG1/4r/dZ2Y3rzpo871Iche913yOgYeKrrxl+3vengFw==} dev: true @@ -5479,6 +5956,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 @@ -5503,7 +5984,7 @@ packages: engines: {node: '>=6'} hasBin: true dependencies: - array.prototype.flat: 1.3.0 + array.prototype.flat: 1.3.1 breakword: 1.0.5 grapheme-splitter: 1.0.4 strip-ansi: 6.0.1 @@ -5626,13 +6107,17 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stack-utils/2.0.5: - resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} + /stack-utils/2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} dependencies: 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'} @@ -5677,6 +6162,14 @@ packages: es-abstract: 1.20.3 dev: true + /string.prototype.trimend/1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + /string.prototype.trimstart/1.0.5: resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} dependencies: @@ -5685,6 +6178,14 @@ packages: es-abstract: 1.20.3 dev: true + /string.prototype.trimstart/1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + /string_decoder/1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -5740,6 +6241,18 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal/0.4.2: + resolution: {integrity: sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==} + dependencies: + 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'} @@ -5788,7 +6301,7 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/utils': 2.3.1 - tslib: 2.4.0 + tslib: 2.4.1 dev: true /tapable/2.2.1: @@ -5833,16 +6346,30 @@ packages: globrex: 0.1.2 dev: true + /tinybench/2.3.1: + resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} + dev: true + /tinypool/0.2.4: resolution: {integrity: sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==} engines: {node: '>=14.0.0'} dev: true + /tinypool/0.3.0: + resolution: {integrity: sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==} + engines: {node: '>=14.0.0'} + dev: true + /tinyspy/0.3.3: resolution: {integrity: sha512-gRiUR8fuhUf0W9lzojPf1N1euJYA30ISebSfgca8z76FOvXtVXqd5ojEIaKLWbDQhAaC3ibxZIjqbyi4ybjcTw==} engines: {node: '>=14.0.0'} dev: true + /tinyspy/1.0.2: + resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} + engines: {node: '>=14.0.0'} + dev: true + /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -5944,6 +6471,37 @@ packages: yn: 3.1.1 dev: true + /ts-node/10.9.1_wup25etrarvlqkprac7h35hj7u: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.11.9 + acorn: 8.8.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /ts-node/8.10.2_typescript@4.8.4: resolution: {integrity: sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==} engines: {node: '>=6.0.0'} @@ -5959,6 +6517,21 @@ packages: yn: 3.1.1 dev: true + /ts-node/8.10.2_typescript@4.9.3: + resolution: {integrity: sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==} + engines: {node: '>=6.0.0'} + hasBin: true + peerDependencies: + typescript: '>=2.7' + dependencies: + arg: 4.1.3 + diff: 4.0.2 + make-error: 1.3.6 + source-map-support: 0.5.21 + typescript: 4.9.3 + yn: 3.1.1 + dev: true + /tsconfig-paths/3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} dependencies: @@ -5976,11 +6549,11 @@ packages: resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} dev: true - /tslib/2.4.0: - resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: true - /tslint/5.20.1_typescript@4.8.4: + /tslint/5.20.1_typescript@4.9.3: resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} engines: {node: '>=4.8.0'} hasBin: true @@ -5999,11 +6572,11 @@ packages: resolve: 1.22.1 semver: 5.7.1 tslib: 1.14.1 - tsutils: 2.29.0_typescript@4.8.4 - typescript: 4.8.4 + tsutils: 2.29.0_typescript@4.9.3 + typescript: 4.9.3 dev: true - /tslint/6.1.3_typescript@4.8.4: + /tslint/6.1.3_typescript@4.9.3: resolution: {integrity: sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==} engines: {node: '>=4.8.0'} deprecated: TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information. @@ -6023,17 +6596,17 @@ packages: resolve: 1.22.1 semver: 5.7.1 tslib: 1.14.1 - tsutils: 2.29.0_typescript@4.8.4 - typescript: 4.8.4 + tsutils: 2.29.0_typescript@4.9.3 + typescript: 4.9.3 dev: true - /tsutils/2.29.0_typescript@4.8.4: + /tsutils/2.29.0_typescript@4.9.3: resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} peerDependencies: typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' dependencies: tslib: 1.14.1 - typescript: 4.8.4 + typescript: 4.9.3 dev: true /tsutils/3.21.0_typescript@3.9.10: @@ -6046,14 +6619,14 @@ packages: typescript: 3.9.10 dev: true - /tsutils/3.21.0_typescript@4.8.4: + /tsutils/3.21.0_typescript@4.9.3: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.8.4 + typescript: 4.9.3 dev: true /tty-table/4.1.6: @@ -6067,7 +6640,7 @@ packages: smartwrap: 2.0.2 strip-ansi: 6.0.1 wcwidth: 1.0.1 - yargs: 17.6.0 + yargs: 17.6.2 dev: true /type-check/0.3.2: @@ -6136,6 +6709,16 @@ packages: hasBin: true dev: true + /typescript/4.9.3: + resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} + engines: {node: '>=4.2.0'} + 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'} @@ -6202,8 +6785,8 @@ packages: isobject: 3.0.1 dev: true - /update-browserslist-db/1.0.9_browserslist@4.21.4: - resolution: {integrity: sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==} + /update-browserslist-db/1.0.10_browserslist@4.21.4: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -6243,7 +6826,7 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.15 '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.8.0 + convert-source-map: 1.9.0 dev: true /validate-npm-package-license/3.0.4: @@ -6253,6 +6836,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'} @@ -6277,6 +6883,40 @@ packages: fsevents: 2.3.2 dev: true + /vite/3.2.4_@types+node@18.11.9: + resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.9 + esbuild: 0.15.16 + postcss: 8.4.19 + resolve: 1.22.1 + rollup: 2.79.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /vitest/0.16.0: resolution: {integrity: sha512-Ntp6jrM8wf2NMtamMBLkRBBdeqHkgAH/WMh5Xryts1j2ft2D8QZQbiSVFkSl4WmEQzcPP0YM069g/Ga1vtnEtg==} engines: {node: '>=v14.16.0'} @@ -6312,40 +6952,98 @@ packages: - supports-color dev: true - /vitest/0.16.0_c8@7.12.0: - resolution: {integrity: sha512-Ntp6jrM8wf2NMtamMBLkRBBdeqHkgAH/WMh5Xryts1j2ft2D8QZQbiSVFkSl4WmEQzcPP0YM069g/Ga1vtnEtg==} + /vitest/0.25.3: + resolution: {integrity: sha512-/UzHfXIKsELZhL7OaM2xFlRF8HRZgAHtPctacvNK8H4vOcbJJAMEgbWNGSAK7Y9b1NBe5SeM7VTuz2RsTHFJJA==} engines: {node: '>=v14.16.0'} hasBin: true peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' '@vitest/ui': '*' - c8: '*' happy-dom: '*' jsdom: '*' peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true '@vitest/ui': optional: true - c8: + 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 + chai: 4.3.7 + debug: 4.3.4 + local-pkg: 0.4.2 + source-map: 0.6.1 + strip-literal: 0.4.2 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 3.2.4_@types+node@18.11.9 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - 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.3 + '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 - '@types/node': 18.8.0 - c8: 7.12.0 - chai: 4.3.6 + '@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 - tinypool: 0.2.4 - tinyspy: 0.3.3 - vite: 2.9.15 + 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: @@ -6356,7 +7054,7 @@ packages: /wcwidth/1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: - defaults: 1.0.3 + defaults: 1.0.4 dev: true /webidl-conversions/3.0.1: @@ -6407,6 +7105,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'} @@ -6512,8 +7219,8 @@ packages: yargs-parser: 20.2.9 dev: true - /yargs/17.6.0: - resolution: {integrity: sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==} + /yargs/17.6.2: + resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==} engines: {node: '>=12'} dependencies: cliui: 8.0.1 @@ -6535,8 +7242,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'} @@ -6545,6 +7252,6 @@ packages: fs-extra: 6.0.1 parsimmon: 1.18.1 strip-json-comments: 2.0.1 - tslint: 5.20.1_typescript@4.8.4 - typescript: 4.8.4 + tslint: 5.20.1_typescript@4.9.3 + typescript: 4.9.3 dev: true diff --git a/src/Bigint.ts b/src/Bigint.ts new file mode 100644 index 000000000..8cc82b156 --- /dev/null +++ b/src/Bigint.ts @@ -0,0 +1,447 @@ +/** + * 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 + */ + +import { dual } from "@fp-ts/core/Function" +import type { Ordering } from "@fp-ts/core/Ordering" +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" + +/** + * Tests if a value is a `bigint`. + * + * @param input - The value to test. + * + * @example + * import { isBigint } from "@fp-ts/core/Bigint" + * + * assert.deepStrictEqual(isBigint(1n), true) + * assert.deepStrictEqual(isBigint(1), false) + * + * @category guards + * @since 1.0.0 + */ +export const isBigint: (u: unknown) => u is bigint = predicate.isBigint + +/** + * Provides an addition operation on `bigint`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { sum } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(sum(2n, 3n), 5n) + * + * @category math + * @since 1.0.0 + */ +export const sum: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = dual(2, semigroup.bigintSum.combine) + +/** + * Provides a multiplication operation on `bigint`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { multiply } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(multiply(2n, 3n), 6n) + * + * @category math + * @since 1.0.0 + */ +export const multiply: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = dual(2, semigroup.bigintMultiply.combine) + +/** + * Provides a subtraction operation on `bigint`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { subtract } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(subtract(2n, 3n), -1n) + * + * @category math + * @since 1.0.0 + */ +export const subtract: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self - that) + +/** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * @param self - The dividend operand. + * @param that - The divisor operand. + * + * @example + * import { divide } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(divide(6n, 3n), 2n) + * assert.deepStrictEqual(divide(6n, 4n), 1n) + * + * @category math + * @since 1.0.0 + * @since 1.0.0 + */ +export const divide: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self / that) + +/** + * Returns the result of adding `1n` to a given number. + * + * @param n - A `bigint` to be incremented. + * + * @example + * import { increment } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(increment(2n), 3n) + * + * @category math + * @since 1.0.0 + */ +export const increment = (n: bigint): bigint => n + 1n + +/** + * Decrements a number by `1n`. + * + * @param n - A `bigint` to be decremented. + * + * @example + * import { decrement } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(decrement(3n), 2n) + * + * @category math + * @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 + +/** + * Returns `true` if the first argument is less than the second, otherwise `false`. + * + * @param self - The first argument. + * @param that - The second argument. + * + * @example + * import { lessThan } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(lessThan(2n, 3n), true) + * assert.deepStrictEqual(lessThan(3n, 3n), false) + * assert.deepStrictEqual(lessThan(4n, 3n), false) + * + * @category predicates + * @since 1.0.0 + */ +export const lessThan: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} = order.lessThan(Order) + +/** + * Returns a function that checks if a given `bigint` is less than or equal to the provided one. + * + * @param self - The first `bigint` to compare with. + * @param that - The second `bigint` to compare with. + * + * @example + * import { lessThanOrEqualTo } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(lessThanOrEqualTo(2n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(4n, 3n), false) + * + * @category predicates + * @since 1.0.0 + */ +export const lessThanOrEqualTo: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} = order.lessThanOrEqualTo(Order) + +/** + * Returns `true` if the first argument is greater than the second, otherwise `false`. + * + * @param self - The first argument. + * @param that - The second argument. + * + * @example + * import { greaterThan } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(greaterThan(2n, 3n), false) + * assert.deepStrictEqual(greaterThan(3n, 3n), false) + * assert.deepStrictEqual(greaterThan(4n, 3n), true) + * + * @category predicates + * @since 1.0.0 + */ +export const greaterThan: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} = order.greaterThan(Order) + +/** + * Returns a function that checks if a given `bigint` is greater than or equal to the provided one. + * + * @param self - The first `bigint` to compare with. + * @param that - The second `bigint` to compare with. + * + * @example + * import { greaterThanOrEqualTo } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(greaterThanOrEqualTo(2n, 3n), false) + * assert.deepStrictEqual(greaterThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(greaterThanOrEqualTo(4n, 3n), true) + * + * @category predicates + * @since 1.0.0 + */ +export const greaterThanOrEqualTo: { + (that: bigint): (self: bigint) => boolean + (self: bigint, that: bigint): boolean +} = order.greaterThanOrEqualTo(Order) + +/** + * Checks if a `bigint` is between a `minimum` and `maximum` value (inclusive). + * + * @param self - The `number` to check. + * @param minimum - The `minimum` value to check. + * @param maximum - The `maximum` value to check. + * + * @example + * import { between } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(between(0n, 5n)(3n), true) + * assert.deepStrictEqual(between(0n, 5n)(-1n), false) + * assert.deepStrictEqual(between(0n, 5n)(6n), false) + * + * @category predicates + * @since 1.0.0 + */ +export const between: { + (minimum: bigint, maximum: bigint): (self: bigint) => boolean + (self: bigint, minimum: bigint, maximum: bigint): boolean +} = order.between(Order) + +/** + * Restricts the given `bigint` to be within the range specified by the `minimum` and `maximum` values. + * + * - If the `bigint` is less than the `minimum` value, the function returns the `minimum` value. + * - If the `bigint` is greater than the `maximum` value, the function returns the `maximum` value. + * - Otherwise, it returns the original `bigint`. + * + * @param self - The `bigint` to be clamped. + * @param minimum - The lower end of the range. + * @param maximum - The upper end of the range. + * + * @example + * import { clamp } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(clamp(0n, 5n)(3n), 3n) + * assert.deepStrictEqual(clamp(0n, 5n)(-1n), 0n) + * assert.deepStrictEqual(clamp(0n, 5n)(6n), 5n) + * + * @since 1.0.0 + */ +export const clamp: { + (minimum: bigint, maximum: bigint): (self: bigint) => bigint + (self: bigint, minimum: bigint, maximum: bigint): bigint +} = order.clamp(Order) + +/** + * Returns the minimum between two `bigint`s. + * + * @param self - The first `bigint`. + * @param that - The second `bigint`. + * + * @example + * import { min } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(min(2n, 3n), 2n) + * + * @since 1.0.0 + */ +export const min: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = order.min(Order) + +/** + * Returns the maximum between two `bigint`s. + * + * @param self - The first `bigint`. + * @param that - The second `bigint`. + * + * @example + * import { max } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(max(2n, 3n), 3n) + * + * @since 1.0.0 + */ +export const max: { + (that: bigint): (self: bigint) => bigint + (self: bigint, that: bigint): bigint +} = order.max(Order) + +/** + * `bigint` semigroup under addition. + * + * @example + * import { SemigroupSum } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(SemigroupSum.combine(2n, 3n), 5n) + * + * @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 + +/** + * A `Semigroup` that uses the minimum between two values. + * + * @example + * import { SemigroupMin } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(SemigroupMin.combine(2n, 3n), 2n) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMin: semigroup.Semigroup = semigroup.min(Order) + +/** + * A `Semigroup` that uses the maximum between two values. + * + * @example + * import { SemigroupMax } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(SemigroupMax.combine(2n, 3n), 3n) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMax: semigroup.Semigroup = semigroup.max(Order) + +/** + * `bigint` monoid under addition. + * + * The `empty` value is `0n`. + * + * @example + * import { MonoidSum } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(MonoidSum.combine(2n, 3n), 5n) + * assert.deepStrictEqual(MonoidSum.combine(2n, MonoidSum.empty), 2n) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidSum: monoid.Monoid = monoid.bigintSum + +/** + * `bigint` monoid under multiplication. + * + * The `empty` value is `1n`. + * + * @example + * import { MonoidMultiply } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(MonoidMultiply.combine(2n, 3n), 6n) + * assert.deepStrictEqual(MonoidMultiply.combine(2n, MonoidMultiply.empty), 2n) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMultiply: monoid.Monoid = monoid.bigintMultiply + +/** + * Determines the sign of a given `bigint`. + * + * @param n - The `bigint` to determine the sign of. + * + * @example + * import { sign } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(sign(-5n), -1) + * assert.deepStrictEqual(sign(0n), 0) + * assert.deepStrictEqual(sign(5n), 1) + * + * @category math + * @since 1.0.0 + */ +export const sign = (n: bigint): Ordering => Order.compare(n, 0n) + +/** + * Takes an `Iterable` of `bigint`s and returns their sum as a single `bigint + * + * @param collection - The collection of `bigint`s to sum. + * + * @example + * import { sumAll } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(sumAll([2n, 3n, 4n]), 9n) + * + * @category math + * @since 1.0.0 + */ +export const sumAll: (collection: Iterable) => bigint = MonoidSum.combineAll + +/** + * Takes an `Iterable` of `bigint`s and returns their multiplication as a single `number`. + * + * @param collection - The collection of `bigint`s to multiply. + * + * @example + * import { multiplyAll } from '@fp-ts/core/Bigint' + * + * assert.deepStrictEqual(multiplyAll([2n, 3n, 4n]), 24n) + * + * @category math + * @since 1.0.0 + */ +export const multiplyAll: (collection: Iterable) => bigint = MonoidMultiply.combineAll diff --git a/src/Boolean.ts b/src/Boolean.ts new file mode 100644 index 000000000..2b432317d --- /dev/null +++ b/src/Boolean.ts @@ -0,0 +1,351 @@ +/** + * 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" +import { dual, flow } from "@fp-ts/core/Function" +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" + +/** + * Tests if a value is a `boolean`. + * + * @param input - The value to test. + * + * @example + * import { isBoolean } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(isBoolean(true), true) + * assert.deepStrictEqual(isBoolean("true"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isBoolean: (input: unknown) => input is boolean = predicate.isBoolean + +/** + * This function returns the result of either of the given functions depending on the value of the boolean parameter. + * It is useful when you have to run one of two functions depending on the boolean value. + * + * @param value - the boolean value that decides which function will be executed. + * @param onFalse - a lazy evaluation function that will be executed when the `value` is `false`. + * @param onTrue - a lazy evaluation function that will be executed when the `value` is `true`. + * + * @example + * import * as B from "@fp-ts/core/Boolean" + * + * assert.deepStrictEqual( + * B.match(true, () => "It's false!", () => "It's true!"), + * "It's true!" + * ) + * + * @category pattern matching + * @since 1.0.0 + */ +export const match: { + (onFalse: LazyArg, onTrue: LazyArg): (value: boolean) => A | B + (value: boolean, onFalse: LazyArg, onTrue: LazyArg): A | B +} = dual( + 3, + (value: boolean, onFalse: LazyArg, onTrue: LazyArg): 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. + * + * @example + * import { SemigroupAll } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(SemigroupAll.combine(true, true), true) + * assert.deepStrictEqual(SemigroupAll.combine(true, false), false) + * assert.deepStrictEqual(SemigroupAll.combine(false, true), false) + * assert.deepStrictEqual(SemigroupAll.combine(false, false), false) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupAll: semigroup.Semigroup = semigroup.booleanAll + +/** + * `boolean` semigroup under disjunction. + * + * @example + * import { SemigroupAny } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(SemigroupAny.combine(true, true), true) + * assert.deepStrictEqual(SemigroupAny.combine(true, false), true) + * assert.deepStrictEqual(SemigroupAny.combine(false, true), true) + * assert.deepStrictEqual(SemigroupAny.combine(false, false), false) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupAny: semigroup.Semigroup = semigroup.booleanAny + +/** + * `boolean` semigroup under exclusive disjunction. + * + * @example + * import { SemigroupXor } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(SemigroupXor.combine(true, true), false) + * assert.deepStrictEqual(SemigroupXor.combine(true, false), true) + * assert.deepStrictEqual(SemigroupXor.combine(false, true), true) + * assert.deepStrictEqual(SemigroupXor.combine(false, false), false) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupXor: semigroup.Semigroup = semigroup.booleanXor + +/** + * `boolean` semigroup under equivalence. + * + * @example + * import { SemigroupEqv } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(SemigroupEqv.combine(true, true), true) + * assert.deepStrictEqual(SemigroupEqv.combine(true, false), false) + * assert.deepStrictEqual(SemigroupEqv.combine(false, true), false) + * assert.deepStrictEqual(SemigroupEqv.combine(false, false), true) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupEqv: semigroup.Semigroup = semigroup.booleanEqv + +/** + * `boolean` monoid under conjunction, see also {@link SemigroupAll}. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidAll: monoid.Monoid = monoid.booleanAll + +/** + * `boolean` monoid under disjunction, see also {@link SemigroupAny}. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidAny: monoid.Monoid = monoid.booleanAny + +/** + * `boolean` monoid under exclusive disjunction, see also {@link SemigroupXor}. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidXor: monoid.Monoid = monoid.booleanXor + +/** + * `boolean` monoid under equivalence. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const MonoidEqv: monoid.Monoid = monoid.booleanEqv + +/** + * Negates the given boolean: `!self` + * + * @example + * import { not } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(not(true), false) + * assert.deepStrictEqual(not(false), true) + * + * @category combinators + * @since 1.0.0 + */ +export const not = (self: boolean): boolean => !self + +/** + * Combines two boolean using AND: `self && that`. + * + * @example + * import { and } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(and(true, true), true) + * assert.deepStrictEqual(and(true, false), false) + * assert.deepStrictEqual(and(false, true), false) + * assert.deepStrictEqual(and(false, false), false) + * + * @category combinators + * @since 1.0.0 + */ +export const and: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, semigroup.booleanAll.combine) + +/** + * Combines two boolean using NAND: `!(self && that)`. + * + * @example + * import { nand } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(nand(true, true), false) + * assert.deepStrictEqual(nand(true, false), true) + * assert.deepStrictEqual(nand(false, true), true) + * assert.deepStrictEqual(nand(false, false), true) + * + * @category combinators + * @since 1.0.0 + */ +export const nand: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, flow(semigroup.booleanAll.combine, not)) + +/** + * Combines two boolean using OR: `self || that`. + * + * @example + * import { or } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(or(true, true), true) + * assert.deepStrictEqual(or(true, false), true) + * assert.deepStrictEqual(or(false, true), true) + * assert.deepStrictEqual(or(false, false), false) + * + * @category combinators + * @since 1.0.0 + */ +export const or: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, semigroup.booleanAny.combine) + +/** + * Combines two booleans using NOR: `!(self || that)`. + * + * @example + * import { nor } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(nor(true, true), false) + * assert.deepStrictEqual(nor(true, false), false) + * assert.deepStrictEqual(nor(false, true), false) + * assert.deepStrictEqual(nor(false, false), true) + * + * @category combinators + * @since 1.0.0 + */ +export const nor: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, flow(semigroup.booleanAny.combine, not)) + +/** + * Combines two booleans using XOR: `(!self && that) || (self && !that)`. + * + * @example + * import { xor } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(xor(true, true), false) + * assert.deepStrictEqual(xor(true, false), true) + * assert.deepStrictEqual(xor(false, true), true) + * assert.deepStrictEqual(xor(false, false), false) + * + * @category combinators + * @since 1.0.0 + */ +export const xor: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, semigroup.booleanXor.combine) + +/** + * Combines two booleans using EQV (aka XNOR): `!xor(self, that)`. + * + * @example + * import { eqv } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(eqv(true, true), true) + * assert.deepStrictEqual(eqv(true, false), false) + * assert.deepStrictEqual(eqv(false, true), false) + * assert.deepStrictEqual(eqv(false, false), true) + * + * @category combinators + * @since 1.0.0 + */ +export const eqv: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, semigroup.booleanEqv.combine) + +/** + * Combines two booleans using an implication: `(!self || that)`. + * + * @example + * import { implies } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(implies(true, true), true) + * assert.deepStrictEqual(implies(true, false), false) + * assert.deepStrictEqual(implies(false, true), true) + * assert.deepStrictEqual(implies(false, false), true) + * + * @category combinators + * @since 1.0.0 + */ +export const implies: { + (that: boolean): (self: boolean) => boolean + (self: boolean, that: boolean): boolean +} = dual(2, (self, that) => self ? that : true) + +/** + * This utility function is used to check if all the elements in a collection of boolean values are `true`. + * + * @param collection - An iterable collection of booleans. + * + * @example + * import { all } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(all([true, true, true]), true) + * assert.deepStrictEqual(all([true, false, true]), false) + * + * @since 1.0.0 + */ +export const all: (collection: Iterable) => boolean = MonoidAll.combineAll + +/** + * This utility function is used to check if at least one of the elements in a collection of boolean values is `true`. + * + * @param collection - An iterable collection of booleans. + * + * @example + * import { any } from '@fp-ts/core/Boolean' + * + * assert.deepStrictEqual(any([true, false, true]), true) + * assert.deepStrictEqual(any([false, false, false]), false) + * + * @since 1.0.0 + */ +export const any: (collection: Iterable) => boolean = MonoidAny.combineAll diff --git a/src/Either.ts b/src/Either.ts new file mode 100644 index 000000000..a64a487ea --- /dev/null +++ b/src/Either.ts @@ -0,0 +1,1492 @@ +/** + * @since 1.0.0 + */ + +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, dual, identity } 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 * as N from "@fp-ts/core/Number" +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 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" +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 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 Either = Left | Right + +/** + * @category models + * @since 1.0.0 + */ +export interface Left { + readonly _tag: "Left" + readonly left: E +} + +/** + * @category models + * @since 1.0.0 + */ +export interface Right { + readonly _tag: "Right" + readonly right: A +} + +/** + * @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. + * + * @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 + +/** + * Alias of {@link right}. + * + * @category constructors + * @since 1.0.0 + */ +export const of: (a: A) => Either = right + +/** + * Tests if a value is a `Either`. + * + * @param input - The value to test. + * + * @example + * import { isEither, left, right } from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(isEither(right(1)), true) + * assert.deepStrictEqual(isEither(left("error")), true) + * assert.deepStrictEqual(isEither({ right: 1 }), false) + * + * @category guards + * @since 1.0.0 + */ +export const isEither = (input: unknown): input is Either => + typeof input === "object" && input != null && structural in input && "_tag" in input && + (input["_tag"] === "Left" || input["_tag"] === "Right") + +/** + * Determine if a `Either` is a `Left`. + * + * @param self - The `Either` to check. + * + * @example + * import { isLeft, left, right } from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(isLeft(right(1)), false) + * assert.deepStrictEqual(isLeft(left("error")), true) + * + * @category guards + * @since 1.0.0 + */ +export const isLeft: (self: Either) => self is Left = either.isLeft + +/** + * Determine if a `Either` is a `Right`. + * + * @param self - The `Either` to check. + * + * @example + * import { isRight, left, right } from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(isRight(right(1)), true) + * assert.deepStrictEqual(isRight(left("error")), false) + * + * @category guards + * @since 1.0.0 + */ +export const isRight: (self: Either) => self is Right = either.isRight + +/** + * 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)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable: { + (onEmpty: LazyArg): (collection: Iterable) => Either + (collection: Iterable, onEmpty: LazyArg): Either +} = dual(2, (collection: Iterable, onEmpty: LazyArg): Either => { + for (const a of collection) { + return right(a) + } + return left(onEmpty()) +}) + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * @param self - The `Either` to convert to an `Option`. + * + * @example + * import * as O from '@fp-ts/core/Option' + * import * as E from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(E.toOption(E.right(1)), O.some(1)) + * assert.deepStrictEqual(E.toOption(E.left('a')), O.none()) + * + * @category conversions + * @since 1.0.0 + */ +export const toOption: (self: Either) => Option = either.getRight + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * Alias of {@link toOption}. + * + * @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 conversions + * @since 1.0.0 + */ +export const getRight: (self: Either) => Option = toOption + +/** + * Converts a `Either` to an `Option` discarding the value. + * + * @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 conversions + * @since 1.0.0 + */ +export const getLeft: (self: Either) => Option = either.getLeft + +/** + * @example + * import * as E from '@fp-ts/core/Either' + * import * as O from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(E.fromOption(O.some(1), () => 'error'), E.right(1)) + * assert.deepStrictEqual(E.fromOption(O.none(), () => 'error'), E.left('error')) + * + * @category conversions + * @since 1.0.0 + */ +export const fromOption: { + (fa: Option, onNone: () => E): Either + (onNone: () => E): (fa: Option) => Either +} = either.fromOption + +/** + * @category equivalence + * @since 1.0.0 + */ +export const getEquivalence = ( + EE: Equivalence, + EA: Equivalence +): Equivalence> => + equivalence.make((x, y) => + x === y || + (isLeft(x) ? + isLeft(y) && EE(x.left, y.left) : + isRight(y) && EA(x.right, y.right)) + ) + +/** + * @category mapping + * @since 1.0.0 + */ +export const bimap: { + (f: (e: E1) => E2, g: (a: A) => B): (self: Either) => Either + (self: Either, f: (e: E1) => E2, g: (a: A) => B): Either +} = dual( + 3, + (self: Either, f: (e: E1) => E2, g: (a: A) => B): Either => + isLeft(self) ? left(f(self.left)) : right(g(self.right)) +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bicovariant: bicovariant.Bicovariant = { + bimap +} + +/** + * Maps the `Left` side of an `Either` value to a new `Either` value. + * + * @param self - The input `Either` value to map. + * @param f - A transformation function to apply to the `Left` value of the input `Either`. + * + * @category error handling + * @since 1.0.0 + */ +export const mapLeft: { + (f: (e: E) => G): (self: Either) => Either + (self: Either, f: (e: E) => G): Either +} = bicovariant.mapLeft(Bicovariant) + +/** + * Maps the `Right` side of an `Either` value to a new `Either` value. + * + * @param self - An `Either` to map + * @param f - The function to map over the value of the `Either` + * + * @category mapping + * @since 1.0.0 + */ +export const map: { + (f: (a: A) => B): (self: Either) => Either + (self: Either, f: (a: A) => B): Either +} = dual( + 2, + (self: Either, f: (a: A) => B): Either => + isRight(self) ? right(f(self.right)) : self +) + +const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + imap, + map +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: { + (a: A, self: Either B>): Either + (self: Either B>): (a: A) => 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: { + (self: Either, b: B): Either + (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 +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: Either = of_.unit(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + of, + imap, + map +} + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMap: { + (f: (a: A) => Either): (self: Either) => Either + (self: Either, f: (a: A) => Either): Either +} = dual( + 2, + (self: Either, f: (a: A) => 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: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = flatMap_.andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: { + ( + afb: (a: A) => Either, + bfc: (b: B) => Either + ): (a: A) => Either + ( + 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 = { + imap, + map, + flatMap +} + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category combining + * @since 1.0.0 + */ +export const andThenDiscard: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = chainable.andThenDiscard(Chainable) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + of, + map, + flatMap +} + +const product = (self: Either, that: Either): Either => + isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self + +const productMany = ( + self: Either, + collection: Iterable> +): 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 = { + imap, + product, + productMany +} + +/** + * Similar to `Promise.all` but operates on `Either`s. + * + * ``` + * Iterable> -> Either + * ``` + * + * Flattens a collection of `Either`s into a single `Either` that contains a list of all the `Right` values. + * If there is a `Left` value in the collection, it returns the first `Left` found as the result. + * + * @param collection - An iterable collection of `Either`s to flatten. + * + * @example + * import * as E from "@fp-ts/core/Either" + * + * assert.deepStrictEqual(E.all([E.right(1), E.right(2), E.right(3)]), E.right([1, 2, 3])) + * assert.deepStrictEqual(E.all([E.right(1), E.left("error"), E.right(3)]), E.left("error")) + * + * @category combining + * @since 1.0.0 + */ +export const all = ( + 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, + imap, + product, + productMany, + productAll: all +} + +/** + * Similar to `Promise.all` but operates on `Either`s. + * + * ``` + * [Either, Either, ...] -> Either + * ``` + * + * @since 1.0.0 + */ +export const tuple: >>( + ...elements: T +) => Either< + [T[number]] extends [Either] ? E : never, + { [I in keyof T]: [T[I]] extends [Either] ? A : never } +> = product_.tuple(Product) + +/** + * @since 1.0.0 + */ +export const struct: >>( + fields: R +) => Either< + [R[keyof R]] extends [Either] ? E : never, + { [K in keyof R]: [R[K]] extends [Either] ? A : never } +> = product_.struct(Product) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + imap, + map, + product, + productMany +} + +/** + * Lifts a binary function into `Either`. + * + * @param f - The function to lift. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: (f: (a: A, b: B) => C) => { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = semiApplicative.lift2(SemiApplicative) + +/** + * @category combining + * @since 1.0.0 + */ +export const zipWith: { + ( + self: Either, + that: Either, + f: (a: A, b: B) => C + ): Either + ( + that: Either, + f: (a: A, b: B) => C + ): (self: Either) => Either +} = semiApplicative.zipWith(SemiApplicative) + +/** + * @since 1.0.0 + */ +export const ap: { + (self: Either B>, that: Either): Either + (that: Either): (self: Either B>) => Either +} = semiApplicative.ap(SemiApplicative) + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + imap, + of, + map, + product, + productMany, + productAll: all +} + +/** + * `Semigroup` returning the left-most `Left` value. If both operands are `Right`s then the inner values + * are combined using the provided `Semigroup`. + * + * ``` + * | self | that | combine(self, that) | + * | ---------- | ---------- | ----------------------- | + * | left(e1) | left(e2) | left(e1) | + * | left(e1) | right(a2) | left(e1) | + * | right(a1) | left(e2) | left(e2) | + * | right(a1) | right(a2) | right(combine(a1, a2)) | + * ``` + * + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> = + semiApplicative.getSemigroup(SemiApplicative) + +/** + * `Monoid` returning the left-most `Left` value. If both operands are `Right`s then the inner values + * are combined using the provided `Monoid`. + * + * - `combine` is provided by {@link getFirstLeftSemigroup}. + * - `empty` is `right(M.empty)` + * + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative + .getMonoid(Applicative) + +const coproduct = ( + self: Either, + that: Either +): Either => isRight(self) ? self : that + +const coproductMany = ( + self: Either, + collection: Iterable> +): 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 = { + imap, + coproduct, + coproductMany +} + +/** + * @category error handling + * @since 1.0.0 + */ +export const firstRightOf: { + (collection: Iterable>): (self: Either) => Either + (self: Either, collection: Iterable>): Either +} = dual(2, coproductMany) + +/** + * Semigroup returning the left-most `Right` value. + * + * ``` + * | self | that | combine(self, that) | + * | ---------- | ---------- | ------------------- | + * | left(e1) | left(e2) | left(e2) | + * | left(e1) | right(a2) | right(a2) | + * | right(a1) | left(e2) | right(a1) | + * | right(a1) | right(a2) | right(a1) | + * ``` + * + * @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( + * E.getOrElse(E.right(1), () => 0), + * 1 + * ) + * assert.deepStrictEqual( + * E.getOrElse(E.left('error'), () => 0), + * 0 + * ) + * + * @category getters + * @since 1.0.0 + */ +export const getOrElse: { + (onLeft: (e: E) => B): (self: Either) => B | A + (self: Either, onLeft: (e: E) => B): A | B +} = dual( + 2, + (self: Either, onLeft: (e: E) => B): A | B => + isLeft(self) ? onLeft(self.left) : self.right +) + +/** + * 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: (e1: E1) => Either): (self: Either) => Either + (self: Either, that: (e1: E1) => Either): Either +} = dual( + 2, + (self: Either, that: (e1: E1) => Either): Either => + isLeft(self) ? that(self.left) : 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: (e1: E1) => Either): (self: Either) => Either> + (self: Either, that: (e1: E1) => Either): Either> +} = dual( + 2, + (self: Either, that: (e1: E1) => Either): Either> => + isLeft(self) ? + map(that(self.left), right) : + map(self, 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 + (self: Either, onLeft: LazyArg): Either +} = dual( + 2, + (self: Either, onLeft: LazyArg): Either => + orElse(self, () => left(onLeft())) +) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + map, + imap, + coproduct, + coproductMany: firstRightOf +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: dual( + 3, + (self: Either, b: B, f: (b: B, a: A) => B): B => + isLeft(self) ? b : f(b, self.right) + ) +} + +/** + * Transforms an `Either` into an `Array`. + * If the input is `Left`, an empty array is returned. + * If the input is `Right`, the value is wrapped in an array. + * + * @param self - The `Either` to convert to an array. + * + * @example + * import { right, left, toArray } from '@fp-ts/core/Either' + * + * assert.deepStrictEqual(toArray(right(1)), [1]) + * assert.deepStrictEqual(toArray(left("error")), []) + * + * @category conversions + * @since 1.0.0 + */ +export const toArray: (self: Either) => Array = foldable.toArray(Foldable) + +/** + * 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.deepStrictEqual( + * pipe( + * E.right(1), + * E.match(onLeft , onRight) + * ), + * 'Ok: 1' + * ) + * assert.deepStrictEqual( + * 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 + (self: Either, onLeft: (e: E) => B, onRight: (a: A) => C): B | C +} = dual( + 3, + (self: Either, onLeft: (e: E) => B, onRight: (a: A) => C): 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 interop + * @since 1.0.0 + */ +export const fromNullable: { + (onNullable: (a: A) => E): (a: A) => Either> + (a: A, onNullable: (a: A) => E): Either> +} = dual( + 2, + (a: A, onNullable: (a: A) => E): Either> => + a == null ? left(onNullable(a)) : right(a as NonNullable) +) + +/** + * @category interop + * @since 1.0.0 + */ +export const liftNullable = , B, E>( + f: (...a: A) => B | null | undefined, + onNullable: (...a: A) => E +) => (...a: A): Either> => fromNullable(f(...a), () => onNullable(...a)) + +/** + * @category interop + * @since 1.0.0 + */ +export const merge: (self: Either) => E | A = match(identity, identity) + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMapNullable: { + ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 + ): (self: Either) => Either> + ( + self: Either, + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 + ): Either> +} = dual(3, ( + self: Either, + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +): Either> => flatMap(self, liftNullable(f, onNullable))) + +/** + * Extracts the value of an `Either` or throws if the `Either` is `Left`. + * + * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + * + * @param self - The `Either` to extract the value from. + * @param onLeft - A function that will be called if the `Either` is `Left`. It returns the error to be thrown. + * + * @example + * import * as E from "@fp-ts/core/Either" + * + * assert.deepStrictEqual( + * E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), + * 1 + * ) + * assert.throws(() => E.getOrThrowWith(E.left("error"), () => new Error('Unexpected Left'))) + * + * @category interop + * @since 1.0.0 + */ +export const getOrThrowWith: { + (onLeft: (e: E) => unknown): (self: Either) => A + (self: Either, onLeft: (e: E) => unknown): A +} = dual(2, (self: Either, onLeft: (e: E) => unknown): A => { + if (isRight(self)) { + return self.right + } + throw onLeft(self.left) +}) + +/** + * Extracts the value of an `Either` or throws if the `Either` is `Left`. + * + * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + * + * @param self - The `Either` to extract the value from. + * @throws `Error("getOrThrow called on a Left")` + * + * @example + * import * as E from "@fp-ts/core/Either" + * + * assert.deepStrictEqual(E.getOrThrow(E.right(1)), 1) + * assert.throws(() => E.getOrThrow(E.left("error"))) + * + * @category interop + * @since 1.0.0 + */ +export const getOrThrow: (self: Either) => A = getOrThrowWith(() => + new Error("getOrThrow called on a 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) => { + try { + return right(f(...a)) + } catch (e) { + return left(onThrow(e)) + } + } + +/** + * @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 filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: Either + ) => Either + ( + predicate: Predicate, + onFalse: LazyArg + ): (self: Either) => Either + ( + self: Either, + refinement: Refinement, + onFalse: LazyArg + ): Either + ( + self: Either, + predicate: Predicate, + onFalse: LazyArg + ): Either +} = dual(3, ( + self: Either, + predicate: Predicate, + onFalse: LazyArg +): 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 + ( + self: Either, + f: (a: A) => Option, + onNone: LazyArg + ): Either +} = dual(3, ( + self: Either, + f: (a: A) => Option, + onNone: LazyArg +): Either => + flatMap(self, (a) => { + const ob = f(a) + return option.isNone(ob) ? left(onNone()) : right(ob.value) + })) + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact: { + (onNone: LazyArg): (self: Either>) => Either + (self: Either>, onNone: LazyArg): Either +} = dual( + 2, + (self: Either>, onNone: LazyArg): Either => + filterMap(self, identity, onNone) +) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind + ): (self: Either) => Kind> + ( + self: Either, + f: (a: A) => Kind + ): Kind> +} => + dual(2, ( + self: Either, + f: (a: A) => Kind + ): Kind> => + isLeft(self) ? + F.of>(self) : + F.map>(f(self.right), right)) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => ( + self: Either> +) => Kind> = traversable.sequence(Traversable) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => { + ( + self: Either, + f: (a: A) => Kind + ): Kind> + ( + f: (a: A) => Kind + ): (self: Either) => Kind> +} = traversable.traverseTap(Traversable) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @category combinators + * @since 1.0.0 + */ +export const tap: { + (self: Either, f: (a: A) => Either): Either + (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 + (self: Either, onRight: (a: A) => void): Either +} = dual(2, (self: Either, onRight: (a: A) => void): Either => { + if (isRight(self)) { + onRight(self.right) + } + return self +}) + +/** + * @category debugging + * @since 1.0.0 + */ +export const inspectLeft: { + (onLeft: (e: E) => void): (self: Either) => Either + (self: Either, onLeft: (e: E) => void): Either +} = dual(2, (self: Either, onLeft: (e: E) => void): Either => { + if (isLeft(self)) { + onLeft(self.left) + } + 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 + (self: Either, onLeft: (e: E1) => Either): Either +} = dual( + 2, + (self: Either, onLeft: (e: E1) => Either): Either => { + if (isRight(self)) { + return self + } + const out = onLeft(self.left) + return isLeft(out) ? out : self + } +) + +/** + * @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: (c: C) => E + ): (c: C) => Either + (predicate: Predicate, onFalse: (b: B) => E): (b: B) => Either +} = (predicate: Predicate, onFalse: (b: B) => E) => + (b: B) => predicate(b) ? right(b) : left(onFalse(b)) + +/** + * @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 combining + * @since 1.0.0 + */ +export const flatMapOption: { + ( + f: (a: A) => Option, + onNone: (a: A) => E2 + ): (self: Either) => Either + ( + self: Either, + f: (a: A) => Option, + onNone: (a: A) => E2 + ): Either +} = dual(3, ( + self: Either, + f: (a: A) => Option, + onNone: (a: A) => E2 +): Either => flatMap(self, 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 = (isEquivalent: (self: A, that: A) => boolean): { + (a: A): (self: Either) => boolean + (self: Either, a: A): boolean +} => + dual( + 2, + (self: Either, a: A): boolean => isLeft(self) ? false : isEquivalent(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.deepStrictEqual(f(E.left('a')), false) + * assert.deepStrictEqual(f(E.right(1)), false) + * assert.deepStrictEqual(f(E.right(3)), true) + * + * @since 1.0.0 + */ +export const exists: { + (predicate: Predicate): (self: Either) => boolean + (self: Either, predicate: Predicate): boolean +} = dual( + 2, + (self: Either, predicate: Predicate): boolean => + isLeft(self) ? false : predicate(self.right) +) + +/** + * Semigroup that models the combination of values that may be absent, elements that are `Left` are ignored + * while elements that are `Right` are combined using the provided `Semigroup`. + * + * @category instances + * @since 1.0.0 + */ +export const getOptionalSemigroup = (S: Semigroup): Semigroup> => + semigroup.make(( + x, + y + ) => (isLeft(y) ? x : isLeft(x) ? y : right(S.combine(x.right, y.right)))) + +/** + * @category math + * @since 1.0.0 + */ +export const sum: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = lift2(N.sum) + +/** + * @category math + * @since 1.0.0 + */ +export const multiply: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = lift2(N.multiply) + +/** + * @category math + * @since 1.0.0 + */ +export const subtract: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = lift2(N.subtract) + +/** + * @category math + * @since 1.0.0 + */ +export const divide: { + (self: Either, that: Either): Either + (that: Either): (self: Either) => Either +} = lift2(N.divide) + +/** + * 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 (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 (isLeft(a)) { + out.push(a.left) + } + } + return out +} + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const tupled: (self: Either) => Either = invariant.tupled( + Invariant +) + +/** + * Appends an element to the end of a tuple. + * + * @category do notation + * @since 1.0.0 + */ +export const appendElement: { + , E2, B>( + self: Either, + that: Either + ): Either + ( + that: Either + ): >(self: Either) => Either +} = semiProduct.appendElement(SemiProduct) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + ( + name: N + ): (self: Either) => Either + (self: Either, name: N): Either +} = invariant.bindTo(Invariant) + +const let_: { + ( + name: Exclude, + f: (a: A) => B + ): ( + self: Either + ) => Either + ( + self: Either, + name: Exclude, + f: (a: A) => B + ): Either +} = covariant.let(Covariant) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Either = of_.Do(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: { + ( + name: Exclude, + f: (a: A) => Either + ): ( + self: Either + ) => Either + ( + self: Either, + name: Exclude, + f: (a: A) => Either + ): Either +} = chainable.bind(Chainable) + +/** + * Extends the `Either` value with the value of another `Either` type. + * + * If both `Either` instances are `Left`, then the result will be the first `Left`. + * + * @param self - The original `Either` value. + * @param name - The name of the property that will be added to the original `Either` type. + * @param that - The `Either` value that will be added to the original `Either` type. + * + * @example + * import * as E from '@fp-ts/core/Either' + * import { pipe } from '@fp-ts/core/Function' + * + * const result = pipe( + * E.Do, + * E.bind("a", () => E.left("e1")), + * E.andThenBind("b", E.left("e2")) + * ) + * + * assert.deepStrictEqual(result, E.left("e1")) + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: { + ( + name: Exclude, + that: Either + ): ( + self: Either + ) => Either + ( + self: Either, + name: Exclude, + that: Either + ): Either +} = semiProduct.andThenBind(SemiProduct) diff --git a/src/Function.ts b/src/Function.ts new file mode 100644 index 000000000..a64e6e02d --- /dev/null +++ b/src/Function.ts @@ -0,0 +1,752 @@ +/** + * @since 1.0.0 + */ +import type { TypeLambda } from "@fp-ts/core/HKT" + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface FunctionTypeLambda extends TypeLambda { + readonly type: (a: this["In"]) => this["Target"] +} + +/** + * Tests if a value is a `function`. + * + * @param input - The value to test. + * + * @example + * import { isFunction } from '@fp-ts/core/Predicate' + * + * assert.deepStrictEqual(isFunction(isFunction), true) + * assert.deepStrictEqual(isFunction("function"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isFunction = (input: unknown): input is Function => typeof input === "function" + +/** + * Creates a function that can be used in a data-last (aka `pipe`able) or data-first style. + * + * @param arity - The arity of the uncurried function. + * @param body - The definition of the uncurried function. + * + * @example + * import { dual, pipe } from "@fp-ts/core/Function" + * + * export const sum: { + * (that: number): (self: number) => number + * (self: number, that: number): number + * } = dual(2, (self: number, that: number): number => self + that) + * + * assert.deepStrictEqual(sum(2, 3), 5) + * assert.deepStrictEqual(pipe(2, sum(3)), 5) + * + * @since 1.0.0 + */ +export const dual = < + DataLast extends (...args: Array) => any, + DataFirst extends (...args: Array) => any +>( + arity: Parameters["length"], + body: DataFirst +): DataLast & DataFirst => { + // @ts-expect-error + return function() { + if (arguments.length >= arity) { + // @ts-expect-error + return body.apply(this, arguments) + } + return ((self: any) => body(self, ...arguments)) as any + } +} + +/** + * Apply a function to a given value. + * + * @param a - The value that the function will be applied to. + * @param self - The function to be applied to a value. + * + * @example + * import { pipe, apply } from "@fp-ts/core/Function" + * import { length } from '@fp-ts/core/String' + * + * assert.deepStrictEqual(pipe(length, apply("hello")), 5) + * + * @since 1.0.0 + */ +export const apply = (a: A) => (self: (a: A) => B): B => self(a) + +/** + * A lazy argument. + * + * @example + * import { LazyArg, constant } from "@fp-ts/core/Function" + * + * export const constNull: LazyArg = constant(null) + * + * @since 1.0.0 + */ +export interface LazyArg { + (): A +} + +/** + * @example + * import { FunctionN } from "@fp-ts/core/Function" + * + * export const sum: FunctionN<[number, number], number> = (a, b) => a + b + * + * @since 1.0.0 + */ +export interface FunctionN, B> { + (...args: A): B +} + +/** + * The identity function, i.e. A function that returns its input argument. + * + * @param a - The input argument. + * + * @example + * import { identity } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(identity(5), 5) + * + * @since 1.0.0 + */ +export const identity = (a: A): A => a + +/** + * Casts the result to the specified type. + * + * @param a - The value to be casted to the target type. + * + * @example + * import { unsafeCoerce, identity } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(unsafeCoerce, identity) + * + * @since 1.0.0 + */ +export const unsafeCoerce: (a: A) => B = identity as any + +/** + * Creates a constant value that never changes. + * + * This is useful when you want to pass a value to a higher-order function (a function that takes another function as its argument) + * and want that inner function to always use the same value, no matter how many times it is called. + * + * @param value - The constant value to be returned. + * + * @example + * import { constant } from "@fp-ts/core/Function" + * + * const constNull = constant(null) + * + * assert.deepStrictEqual(constNull(), null) + * assert.deepStrictEqual(constNull(), null) + * + * @since 1.0.0 + */ +export const constant = (value: A): LazyArg => () => value + +/** + * A thunk that returns always `true`. + * + * @example + * import { constTrue } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(constTrue(), true) + * + * @since 1.0.0 + */ +export const constTrue: LazyArg = constant(true) + +/** + * A thunk that returns always `false`. + * + * @example + * import { constFalse } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(constFalse(), false) + * + * @since 1.0.0 + */ +export const constFalse: LazyArg = constant(false) + +/** + * A thunk that returns always `null`. + * + * @example + * import { constNull } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(constNull(), null) + * + * @since 1.0.0 + */ +export const constNull: LazyArg = constant(null) + +/** + * A thunk that returns always `undefined`. + * + * @example + * import { constUndefined } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(constUndefined(), undefined) + * + * @since 1.0.0 + */ +export const constUndefined: LazyArg = constant(undefined) + +/** + * A thunk that returns always `void`. + * + * @example + * import { constVoid } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(constVoid(), undefined) + * + * @since 1.0.0 + */ +export const constVoid: LazyArg = constUndefined + +/** + * Reverses the order of arguments for a curried function. + * + * @param f - A curried function that takes multiple arguments. + * + * @example + * import { flip } from "@fp-ts/core/Function" + * + * const f = (a: number) => (b: string) => a - b.length + * + * assert.deepStrictEqual(flip(f)('aaa')(2), -1) + * + * @since 1.0.0 + */ +export const flip = , B extends Array, C>( + 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. + * + * See also {@link pipe}. + * + * @example + * 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.deepStrictEqual(f('aaa'), 6) + * + * @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 +} + +/** + * Composes two functions, `ab` and `bc` into a single function that takes in an argument `a` of type `A` and returns a result of type `C`. + * The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`. + * + * @param ab - A function that maps from `A` to `B`. + * @param bc - A function that maps from `B` to `C`. + * + * @example + * import { compose } from "@fp-ts/core/Function" + * + * const increment = (n: number) => n + 1; + * const square = (n: number) => n * n; + * + * assert.strictEqual(compose(increment, square)(2), 9); + * + * @since 1.0.0 + */ +export const compose: { + (bc: (b: B) => C): (self: (a: A) => B) => (a: A) => C + (self: (a: A) => B, bc: (b: B) => C): (a: A) => C +} = dual(2, (ab: (a: A) => B, bc: (b: B) => C): (a: A) => C => flow(ab, bc)) + +/** + * The `absurd` function is a stub for cases where a value of type `never` is encountered in your code, + * meaning that it should be impossible for this code to be executed. + * + * This function is particularly when it's necessary to specify that certain cases are impossible. + * + * @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/core/Function" + * + * const sumTupled = tupled((x: number, y: number): number => x + y) + * + * assert.deepStrictEqual(sumTupled([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` + * + * @example + * import { untupled } from "@fp-ts/core/Function" + * + * const getFirst = untupled((tuple: [A, B]): A => tuple[0]) + * + * assert.deepStrictEqual(getFirst(1, 2), 1) + * + * @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. + * + * This is useful in combination with data-last functions as a simulation of methods: + * + * ``` + * as.map(f).filter(g) -> pipe(as, map(f), filter(g)) + * ``` + * + * See also {@link flow}. + * + * @example + * import { pipe } from "@fp-ts/core/Function" + * + * const length = (s: string): number => s.length + * const double = (n: number): number => n * 2 + * const decrement = (n: number): number => n - 1 + * + * assert.deepStrictEqual(pipe(length("hello"), double, decrement), 9) + * + * @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 = unsafeCoerce(absurd) + +/** + * The SK combinator, also known as the "S-K combinator" or "S-combinator", is a fundamental combinator in the + * lambda calculus and the SKI combinator calculus. + * + * This function is useful for discarding the first argument passed to it and returning the second argument. + * + * @param _ - The first argument to be discarded. + * @param b - The second argument to be returned. + * + * @example + * import { SK } from "@fp-ts/core/Function"; + * + * assert.deepStrictEqual(SK(0, "hello"), "hello") + * + * @since 1.0.0 + */ +export const SK = (_: A, b: B): B => b diff --git a/src/Identity.ts b/src/Identity.ts new file mode 100644 index 000000000..71fec1041 --- /dev/null +++ b/src/Identity.ts @@ -0,0 +1,299 @@ +/** + * @since 1.0.0 + */ +import { dual, identity } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" +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 type * 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 * as of_ from "@fp-ts/core/typeclass/Of" +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 type * 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 type * 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 +} + +const map: { + (f: (a: A) => B): (self: Identity) => Identity + (self: Identity, f: (a: A) => B): Identity +} = dual(2, (self: Identity, f: (a: A) => B): Identity => f(self)) + +const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + imap, + map +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +const of: (a: A) => Identity = identity + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + of, + imap, + map +} + +const flatMap: { + (f: (a: A) => B): (self: Identity) => Identity + (self: Identity, f: (a: A) => B): Identity +} = dual(2, (self: Identity, f: (a: A) => B): Identity => f(self)) + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + imap, + map, + flatMap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + of, + map, + flatMap +} + +const product = (self: Identity, that: Identity): Identity<[A, B]> => [self, that] + +const productMany = ( + self: Identity, + collection: Iterable +): [A, ...Array] => [self, ...collection] + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll: readonlyArray.fromIterable +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + imap, + map, + product, + productMany +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + imap, + of, + map, + product, + productMany, + productAll: readonlyArray.fromIterable +} + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemiCoproduct = ( + S: Semigroup +): semiCoproduct.SemiCoproduct> => ({ + imap, + coproduct: dual(2, S.combine), + coproductMany: dual(2, S.combineMany) +}) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemiAlternative = ( + S: Semigroup +): semiAlternative.SemiAlternative> => ({ + ...getSemiCoproduct(S), + map +}) + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: dual(3, (self: Identity, b: B, f: (b: B, a: A) => B): B => f(b, self)) +} + +const traverse = ( + F: applicative.Applicative +): { + (f: (a: A) => Kind): (self: Identity) => Kind + (self: Identity, f: (a: A) => Kind): Kind +} => + dual( + 2, + (self: Identity, f: (a: A) => Kind): Kind => + f(self) + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse +} + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + (name: N): (self: Identity) => Identity<{ [K in N]: A }> + (self: Identity, name: N): Identity<{ [K in N]: A }> +} = invariant.bindTo(Invariant) + +const let_: { + ( + name: Exclude, + f: (a: A) => B + ): (self: Identity) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Identity, + name: Exclude, + f: (a: A) => B + ): Identity<{ [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 do notation + * @since 1.0.0 + */ +export const Do: Identity<{}> = of_.Do(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: { + ( + name: Exclude, + f: (a: A) => Identity + ): (self: Identity) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Identity, + name: Exclude, + f: (a: A) => Identity + ): Identity<{ [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, + that: Identity + ): (self: Identity) => Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Identity, + name: Exclude, + that: Identity + ): Identity<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = semiProduct.andThenBind(SemiProduct) diff --git a/src/Number.ts b/src/Number.ts new file mode 100644 index 000000000..dbc36825e --- /dev/null +++ b/src/Number.ts @@ -0,0 +1,516 @@ +/** + * 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 { dual } from "@fp-ts/core/Function" +import type { Ordering } from "@fp-ts/core/Ordering" +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 * 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" + +/** + * Tests if a value is a `number`. + * + * @param input - The value to test. + * + * @example + * import { isNumber } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(isNumber(2), true) + * assert.deepStrictEqual(isNumber("2"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNumber: (input: unknown) => input is number = predicate.isNumber + +/** + * Provides an addition operation on `number`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { sum } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(sum(2, 3), 5) + * + * @category math + * @since 1.0.0 + */ +export const sum: { + (that: number): (self: number) => number + (self: number, that: number): number +} = dual(2, semigroup.numberSum.combine) + +/** + * Provides a multiplication operation on `number`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { multiply } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(multiply(2, 3), 6) + * + * @category math + * @since 1.0.0 + */ +export const multiply: { + (that: number): (self: number) => number + (self: number, that: number): number +} = dual(2, semigroup.numberMultiply.combine) + +/** + * Provides a subtraction operation on `number`s. + * + * @param self - The first operand. + * @param that - The second operand. + * + * @example + * import { subtract } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(subtract(2, 3), -1) + * + * @category math + * @since 1.0.0 + */ +export const subtract: { + (that: number): (self: number) => number + (self: number, that: number): number +} = dual(2, (self: number, that: number): number => self - that) + +/** + * Provides a division operation on `number`s. + * + * @param self - The dividend operand. + * @param that - The divisor operand. + * + * @example + * import { divide } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(divide(6, 3), 2) + * + * @category math + * @since 1.0.0 + */ +export const divide: { + (that: number): (self: number) => number + (self: number, that: number): number +} = dual(2, (self: number, that: number): number => self / that) + +/** + * Returns the result of adding `1` to a given number. + * + * @param n - A `number` to be incremented. + * + * @example + * import { increment } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(increment(2), 3) + * + * @category math + * @since 1.0.0 + */ +export const increment = (n: number): number => n + 1 + +/** + * Decrements a number by `1`. + * + * @param n - A `number` to be decremented. + * + * @example + * import { decrement } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(decrement(3), 2) + * + * @category math + * @since 1.0.0 + */ +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 + */ +export const Order: order.Order = order.number + +/** + * Returns `true` if the first argument is less than the second, otherwise `false`. + * + * @param self - The first argument. + * @param that - The second argument. + * + * @example + * import { lessThan } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(lessThan(2, 3), true) + * assert.deepStrictEqual(lessThan(3, 3), false) + * assert.deepStrictEqual(lessThan(4, 3), false) + * + * @category predicates + * @since 1.0.0 + */ +export const lessThan: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} = order.lessThan(Order) + +/** + * Returns a function that checks if a given `number` is less than or equal to the provided one. + * + * @param self - The first `number` to compare with. + * @param that - The second `number` to compare with. + * + * @example + * import { lessThanOrEqualTo } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(lessThanOrEqualTo(2, 3), true) + * assert.deepStrictEqual(lessThanOrEqualTo(3, 3), true) + * assert.deepStrictEqual(lessThanOrEqualTo(4, 3), false) + * + * @category predicates + * @since 1.0.0 + */ +export const lessThanOrEqualTo: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} = order.lessThanOrEqualTo(Order) + +/** + * Returns `true` if the first argument is greater than the second, otherwise `false`. + * + * @param self - The first argument. + * @param that - The second argument. + * + * @example + * import { greaterThan } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(greaterThan(2, 3), false) + * assert.deepStrictEqual(greaterThan(3, 3), false) + * assert.deepStrictEqual(greaterThan(4, 3), true) + * + * @category predicates + * @since 1.0.0 + */ +export const greaterThan: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} = order.greaterThan(Order) + +/** + * Returns a function that checks if a given `number` is greater than or equal to the provided one. + * + * @param self - The first `number` to compare with. + * @param that - The second `number` to compare with. + * + * @example + * import { greaterThanOrEqualTo } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(greaterThanOrEqualTo(2, 3), false) + * assert.deepStrictEqual(greaterThanOrEqualTo(3, 3), true) + * assert.deepStrictEqual(greaterThanOrEqualTo(4, 3), true) + * + * @category predicates + * @since 1.0.0 + */ +export const greaterThanOrEqualTo: { + (that: number): (self: number) => boolean + (self: number, that: number): boolean +} = order.greaterThanOrEqualTo(Order) + +/** + * Checks if a `number` is between a `minimum` and `maximum` value (inclusive). + * + * @param self - The `number` to check. + * @param minimum - The `minimum` value to check. + * @param maximum - The `maximum` value to check. + * + * @example + * import { between } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(between(0, 5)(3), true) + * assert.deepStrictEqual(between(0, 5)(-1), false) + * assert.deepStrictEqual(between(0, 5)(6), false) + * + * @category predicates + * @since 1.0.0 + */ +export const between: { + (minimum: number, maximum: number): (self: number) => boolean + (self: number, minimum: number, maximum: number): boolean +} = order.between(Order) + +/** + * Restricts the given `number` to be within the range specified by the `minimum` and `maximum` values. + * + * - If the `number` is less than the `minimum` value, the function returns the `minimum` value. + * - If the `number` is greater than the `maximum` value, the function returns the `maximum` value. + * - Otherwise, it returns the original `number`. + * + * @param self - The `number` to be clamped. + * @param minimum - The lower end of the range. + * @param maximum - The upper end of the range. + * + * @example + * import { clamp } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(clamp(0, 5)(3), 3) + * assert.deepStrictEqual(clamp(0, 5)(-1), 0) + * assert.deepStrictEqual(clamp(0, 5)(6), 5) + * + * @since 1.0.0 + */ +export const clamp: { + (minimum: number, maximum: number): (self: number) => number + (self: number, minimum: number, maximum: number): number +} = order.clamp(Order) + +/** + * Returns the minimum between two `number`s. + * + * @param self - The first `number`. + * @param that - The second `number`. + * + * @example + * import { min } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(min(2, 3), 2) + * + * @since 1.0.0 + */ +export const min: { + (that: number): (self: number) => number + (self: number, that: number): number +} = order.min(Order) + +/** + * Returns the maximum between two `number`s. + * + * @param self - The first `number`. + * @param that - The second `number`. + * + * @example + * import { max } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(max(2, 3), 3) + * + * @since 1.0.0 + */ +export const max: { + (that: number): (self: number) => number + (self: number, that: number): number +} = order.max(Order) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bounded: bounded.Bounded = bounded.number + +/** + * `number` semigroup under addition. + * + * @example + * import { SemigroupSum } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(SemigroupSum.combine(2, 3), 5) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupSum: semigroup.Semigroup = semigroup.numberSum + +/** + * `number` semigroup under multiplication. + * + * @example + * import { SemigroupMultiply } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(SemigroupMultiply.combine(2, 3), 6) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMultiply: semigroup.Semigroup = semigroup.numberMultiply + +/** + * A `Semigroup` that uses the minimum between two values. + * + * @example + * import { SemigroupMin } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(SemigroupMin.combine(2, 3), 2) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMin: semigroup.Semigroup = semigroup.min(Order) + +/** + * A `Semigroup` that uses the maximum between two values. + * + * @example + * import { SemigroupMax } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(SemigroupMax.combine(2, 3), 3) + * + * @category instances + * @since 1.0.0 + */ +export const SemigroupMax: semigroup.Semigroup = semigroup.max(Order) + +/** + * `number` monoid under addition. + * + * The `empty` value is `0`. + * + * @example + * import { MonoidSum } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(MonoidSum.combine(2, 3), 5) + * assert.deepStrictEqual(MonoidSum.combine(2, MonoidSum.empty), 2) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidSum: monoid.Monoid = monoid.numberSum + +/** + * `number` monoid under multiplication. + * + * The `empty` value is `1`. + * + * @example + * import { MonoidMultiply } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(MonoidMultiply.combine(2, 3), 6) + * assert.deepStrictEqual(MonoidMultiply.combine(2, MonoidMultiply.empty), 2) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMultiply: monoid.Monoid = monoid.numberMultiply + +/** + * A `Monoid` that uses the minimum between two values. + * + * The `empty` value is `-Infinity`. + * + * @example + * import { MonoidMin } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(MonoidMin.combine(2, 3), 2) + * assert.deepStrictEqual(MonoidMin.combine(2, MonoidMin.empty), 2) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMin: monoid.Monoid = bounded.min(Bounded) + +/** + * A `Monoid` that uses the maximum between two values. + * + * The `empty` value is `Infinity`. + * + * @example + * import { MonoidMax } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(MonoidMax.combine(2, 3), 3) + * assert.deepStrictEqual(MonoidMax.combine(2, MonoidMax.empty), 2) + * + * @category instances + * @since 1.0.0 + */ +export const MonoidMax: monoid.Monoid = bounded.max(Bounded) + +/** + * Determines the sign of a given `number`. + * + * @param n - The `number` to determine the sign of. + * + * @example + * import { sign } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(sign(-5), -1) + * assert.deepStrictEqual(sign(0), 0) + * assert.deepStrictEqual(sign(5), 1) + * + * @category math + * @since 1.0.0 + */ +export const sign = (n: number): Ordering => Order.compare(n, 0) + +/** + * Takes an `Iterable` of `number`s and returns their sum as a single `number`. + * + * @param collection - The collection of `number`s to sum. + * + * @example + * import { sumAll } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(sumAll([2, 3, 4]), 9) + * + * @category math + * @since 1.0.0 + */ +export const sumAll: (collection: Iterable) => number = MonoidSum.combineAll + +/** + * Takes an `Iterable` of `number`s and returns their multiplication as a single `number`. + * + * @param collection - The collection of `number`s to multiply. + * + * @example + * import { multiplyAll } from '@fp-ts/core/Number' + * + * assert.deepStrictEqual(multiplyAll([2, 3, 4]), 24) + * + * @category math + * @since 1.0.0 + */ +export const multiplyAll: (collection: Iterable) => number = MonoidMultiply.combineAll + +/** + * Returns the remainder left over when one operand is divided by a second operand. + * + * It always takes the sign of the dividend. + * + * @param self - The dividend. + * @param divisor - The divisor. + * + * @example + * import { remainder } from "@fp-ts/core/Number" + * + * assert.deepStrictEqual(remainder(2, 2), 0) + * assert.deepStrictEqual(remainder(3, 2), 1) + * assert.deepStrictEqual(remainder(-4, 2), -0) + * + * @category math + * @since 1.0.0 + */ +export const remainder: { + (divisor: number): (self: number) => number + (self: number, divisor: number): number +} = dual(2, (self: number, divisor: number): number => { + // https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034 + const selfDecCount = (self.toString().split(".")[1] || "").length + const divisorDecCount = (divisor.toString().split(".")[1] || "").length + const decCount = selfDecCount > divisorDecCount ? selfDecCount : divisorDecCount + const selfInt = parseInt(self.toFixed(decCount).replace(".", "")) + const divisorInt = parseInt(divisor.toFixed(decCount).replace(".", "")) + return (selfInt % divisorInt) / Math.pow(10, decCount) +}) diff --git a/src/Option.ts b/src/Option.ts new file mode 100644 index 000000000..fee13ef07 --- /dev/null +++ b/src/Option.ts @@ -0,0 +1,1712 @@ +/** + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, dual } 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 * as N from "@fp-ts/core/Number" +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 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 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 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 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 Option = None | Some + +/** + * @category models + * @since 1.0.0 + */ +export interface None { + readonly _tag: "None" +} + +/** + * @category models + * @since 1.0.0 + */ +export interface Some { + readonly _tag: "Some" + readonly value: A +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface OptionTypeLambda extends TypeLambda { + readonly type: Option +} + +/** + * Creates a new `Option` that represents the absence of a value. + * + * @category constructors + * @since 1.0.0 + */ +export const none = (): Option => option.none + +/** + * Creates a new `Option` that wraps the given value. + * + * @param value - The value to wrap. + * + * @category constructors + * @since 1.0.0 + */ +export const some: (value: A) => Option = option.some + +/** + * Alias of {@link some}. + * + * @category constructors + * @since 1.0.0 + */ +export const of: (value: A) => Option = some + +/** + * Tests if a value is a `Option`. + * + * @param input - The value to check. + * + * @example + * import { some, none, isOption } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(isOption(some(1)), true) + * assert.deepStrictEqual(isOption(none()), true) + * assert.deepStrictEqual(isOption({}), false) + * + * @category guards + * @since 1.0.0 + */ +export const isOption = (input: unknown): input is Option => + typeof input === "object" && input != null && structural in input && "_tag" in input && + (input["_tag"] === "None" || input["_tag"] === "Some") + +/** + * Determine if a `Option` is a `None`. + * + * @param self - The `Option` to check. + * + * @example + * import { some, none, isNone } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(isNone(some(1)), false) + * assert.deepStrictEqual(isNone(none()), true) + * + * @category guards + * @since 1.0.0 + */ +export const isNone: (self: Option) => self is None = option.isNone + +/** + * Determine if a `Option` is a `Some`. + * + * @param self - The `Option` to check. + * + * @example + * import { some, none, isSome } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(isSome(some(1)), true) + * assert.deepStrictEqual(isSome(none()), false) + * + * @category guards + * @since 1.0.0 + */ +export const isSome: (self: Option) => self is Some = option.isSome + +/** + * Matches the given `Option` and returns either the provided `onNone` value or the result of the provided `onSome` + * function when passed the `Option`'s value. + * + * @param self - The `Option` to match + * @param onNone - The value to be returned if the `Option` is `None` + * @param onSome - The function to be called if the `Option` is `Some`, it will be passed the `Option`'s value and its result will be returned + * + * @example + * import { some, none, match } from '@fp-ts/core/Option' + * import { pipe } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual( + * pipe( + * some(1), + * match(() => 'a none', a => `a some containing ${a}`) + * ), + * 'a some containing 1' + * ) + * + * assert.deepStrictEqual( + * 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 + (self: Option, onNone: LazyArg, onSome: (a: A) => C): B | C +} = dual( + 3, + (self: Option, onNone: LazyArg, onSome: (a: A) => C): B | C => + isNone(self) ? onNone() : onSome(self.value) +) + +/** + * Returns a type guard from a `Option` returning function. + * This function ensures that a type guard definition is type-safe. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * const parsePositive = (n: number): O.Option => + * n > 0 ? O.some(n) : O.none() + * + * const isPositive = O.toRefinement(parsePositive) + * + * assert.deepStrictEqual(isPositive(1), true) + * assert.deepStrictEqual(isPositive(-1), false) + * + * @category conversions + * @since 1.0.0 + */ +export const toRefinement = (f: (a: A) => Option): (a: A) => a is B => + (a: A): a is B => isSome(f(a)) + +/** + * Converts an `Iterable` of values into an `Option`. Returns the first value of the `Iterable` wrapped in a `Some` + * if the `Iterable` is not empty, otherwise returns `None`. + * + * @param collection - The `Iterable` to be converted to an `Option`. + * + * @example + * import { fromIterable, some, none } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(fromIterable([1, 2, 3]), some(1)) + * assert.deepStrictEqual(fromIterable([]), none()) + * + * @category conversions + * @since 1.0.0 + */ +export const fromIterable = (collection: Iterable): Option => { + for (const a of collection) { + return some(a) + } + return none() +} + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * @param self - The `Either` to convert to an `Option`. + * + * @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('error message')), O.none()) + * + * @category conversions + * @since 1.0.0 + */ +export const fromEither: (self: Either) => Option = either.getRight + +/** + * Converts a `Either` to an `Option` discarding the error. + * + * Alias of {@link fromEither}. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * assert.deepStrictEqual(O.getRight(E.right('ok')), O.some('ok')) + * assert.deepStrictEqual(O.getRight(E.left('err')), O.none()) + * + * @category conversions + * @since 1.0.0 + */ +export const getRight: (self: Either) => Option = fromEither + +/** + * Converts a `Either` to an `Option` discarding the value. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * assert.deepStrictEqual(O.getLeft(E.right("ok")), O.none()) + * assert.deepStrictEqual(O.getLeft(E.left("error")), O.some("error")) + * + * @category conversions + * @since 1.0.0 + */ +export const getLeft: (self: Either) => Option = either.getLeft + +/** + * Converts an `Option` to an `Either`, allowing you to provide a value to be used in the case of a `None`. + * + * @param self - the `Option` to convert. + * @param onNone - a function that produces an error value when the `Option` is `None`. + * + * @example + * import { pipe } from "@fp-ts/core/Function" + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * const onNone = () => 'error' + * assert.deepStrictEqual(pipe(O.some(1), O.toEither(onNone)), E.right(1)) + * assert.deepStrictEqual(pipe(O.none(), O.toEither(onNone)), E.left('error')) + * + * @category conversions + * @since 1.0.0 + */ +export const toEither: { + (self: Option, onNone: () => E): Either + (onNone: () => E): (self: Option) => Either +} = either.fromOption + +/** + * Returns the value of the `Option` if it is `Some`, otherwise returns `onNone` + * + * @param self - The `Option` to get the value of. + * @param onNone - Function that returns the default value to return if the `Option` is `None`. + * + * @example + * import { some, none, getOrElse } from '@fp-ts/core/Option' + * import { pipe } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(pipe(some(1), getOrElse(() => 0)), 1) + * assert.deepStrictEqual(pipe(none(), getOrElse(() => 0)), 0) + * + * @category getters + * @since 1.0.0 + */ +export const getOrElse: { + (onNone: LazyArg): (self: Option) => B | A + (self: Option, onNone: LazyArg): A | B +} = dual( + 2, + (self: Option, onNone: LazyArg): A | B => isNone(self) ? onNone() : self.value +) + +/** + * Returns the provided `Option` `that` if `self` is `None`, otherwise returns `self`. + * + * @param self - The first `Option` to be checked. + * @param that - The `Option` to return if `self` is `None`. + * + * @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: LazyArg>): (self: Option) => Option + (self: Option, that: LazyArg>): Option +} = dual( + 2, + (self: Option, that: LazyArg>): Option => isNone(self) ? that() : self +) + +/** + * Similar to `orElse`, but instead of returning a simple union, it returns an `Either` object, + * which contains information about which of the two `Option`s has been chosen. + * + * This is useful when it's important to know whether the value was retrieved from the first `Option` or the second option. + * + * @param self - The first `Option` to be checked. + * @param that - The second `Option` to be considered if the first `Option` is `None`. + * + * @category error handling + * @since 1.0.0 + */ +export const orElseEither: { + (that: LazyArg>): (self: Option) => Option> + (self: Option, that: LazyArg>): Option> +} = dual( + 2, + (self: Option, that: LazyArg>): Option> => + isNone(self) ? map(that(), either.right) : map(self, either.left) +) + +/** + * Given an `Iterable` collection of `Option`s, returns the first `Some` found in the collection. + * + * @param collection - An iterable collection of `Option` to be searched. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.firstSomeOf([O.none(), O.some(1), O.some(2)]), O.some(1)) + * + * @category error handling + * @since 1.0.0 + */ +export const firstSomeOf = (collection: Iterable>): Option => { + let out: Option = none() + for (out of collection) { + if (isSome(out)) { + return out + } + } + return out +} + +/** + * Similar to `Promise.all` but operates on `Option`s. + * + * ``` + * Iterable> -> Option + * ``` + * + * Flattens a collection of `Option`s into a single `Option` that contains a list of all the `Some` values. + * If there is a `None` value in the collection, it returns `None` as the result. + * + * @param collection - An iterable collection of `Option`s to flatten. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.all([O.some(1), O.some(2), O.some(3)]), O.some([1, 2, 3])) + * assert.deepStrictEqual(O.all([O.some(1), O.none(), O.some(3)]), O.none()) + * + * @category combining + * @since 1.0.0 + */ +export const all = (collection: Iterable>): Option> => { + const out: Array = [] + for (const o of collection) { + if (isNone(o)) { + return none() + } + out.push(o.value) + } + return some(out) +} + +/** + * 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`. + * + * @param nullableValue - The nullable value to be converted to an `Option`. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.fromNullable(undefined), O.none()) + * assert.deepStrictEqual(O.fromNullable(null), O.none()) + * assert.deepStrictEqual(O.fromNullable(1), O.some(1)) + * + * @category conversions + * @since 1.0.0 + */ +export const fromNullable = ( + nullableValue: A +): Option< + NonNullable +> => (nullableValue == null ? none() : some(nullableValue as NonNullable)) + +/** + * This API is useful for lifting a function that returns `null` or `undefined` into the `Option` context. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * const parse = (s: string): number | undefined => { + * const n = parseFloat(s) + * return isNaN(n) ? undefined : n + * } + * + * const parseOption = O.liftNullable(parse) + * + * assert.deepStrictEqual(parseOption('1'), O.some(1)) + * assert.deepStrictEqual(parseOption('not a number'), O.none()) + * + * @category conversions + * @since 1.0.0 + */ +export const liftNullable = , B>( + f: (...a: A) => B | null | undefined +): ((...a: A) => Option>) => (...a) => fromNullable(f(...a)) + +/** + * Returns the value of the `Option` if it is a `Some`, otherwise returns `null`. + * + * @param self - The `Option` to extract the value from. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.getOrNull(O.some(1)), 1) + * assert.deepStrictEqual(O.getOrNull(O.none()), null) + * + * @category getters + * @since 1.0.0 + */ +export const getOrNull: (self: Option) => A | null = getOrElse(constNull) + +/** + * Returns the value of the `Option` if it is a `Some`, otherwise returns `undefined`. + * + * @param self - The `Option` to extract the value from. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.getOrUndefined(O.some(1)), 1) + * assert.deepStrictEqual(O.getOrUndefined(O.none()), undefined) + * + * @category getters + * @since 1.0.0 + */ +export const getOrUndefined: (self: Option) => A | undefined = getOrElse(constUndefined) + +/** + * A utility function that lifts a function that throws exceptions into a function that returns an `Option`. + * + * This function is useful for any function that might throw an exception, allowing the developer to handle + * the exception in a more functional way. + * + * @param f - the function that can throw exceptions. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * const parse = O.liftThrowable(JSON.parse) + * + * assert.deepStrictEqual(parse("1"), O.some(1)) + * assert.deepStrictEqual(parse(""), O.none()) + * + * @category conversions + * @since 1.0.0 + */ +export const liftThrowable = , B>( + f: (...a: A) => B +): ((...a: A) => Option) => + (...a) => { + try { + return some(f(...a)) + } catch (e) { + return none() + } + } + +/** + * Extracts the value of an `Option` or throws if the `Option` is `None`. + * + * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + * + * @param self - The `Option` to extract the value from. + * @param onNone - A function that will be called if the `Option` is `None`. It returns the error to be thrown. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual( + * O.getOrThrowWith(O.some(1), () => new Error('Unexpected None')), + * 1 + * ) + * assert.throws(() => O.getOrThrowWith(O.none(), () => new Error('Unexpected None'))) + * + * @category conversions + * @since 1.0.0 + */ +export const getOrThrowWith: { + (onNone: () => unknown): (self: Option) => A + (self: Option, onNone: () => unknown): A +} = dual(2, (self: Option, onNone: () => unknown): A => { + if (isSome(self)) { + return self.value + } + throw onNone() +}) + +/** + * Extracts the value of an `Option` or throws if the `Option` is `None`. + * + * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + * + * @param self - The `Option` to extract the value from. + * @throws `Error("getOrThrow called on a None")` + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.getOrThrow(O.some(1)), 1) + * assert.throws(() => O.getOrThrow(O.none())) + * + * @category conversions + * @since 1.0.0 + */ +export const getOrThrow: (self: Option) => A = getOrThrowWith(() => + new Error("getOrThrow called on a None") +) + +/** + * Maps the `Some` side of an `Option` value to a new `Option` value. + * + * @param self - An `Option` to map + * @param f - The function to map over the value of the `Option` + * + * @category transforming + * @since 1.0.0 + */ +export const map: { + (f: (a: A) => B): (self: Option) => Option + (self: Option, f: (a: A) => B): Option +} = dual( + 2, + (self: Option, f: (a: A) => B): Option => isNone(self) ? none() : some(f(self.value)) +) + +const imap = covariant.imap(map) + +/** + * @since 1.0.0 + */ +export const Covariant: covariant.Covariant = { + imap, + map +} + +/** + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +/** + * @category transforming + * @since 1.0.0 + */ +export const flap: { + (a: A, self: Option<(a: A) => B>): Option + (self: Option<(a: A) => B>): (a: A) => Option +} = covariant.flap(Covariant) + +/** + * Maps the `Some` value of this `Option` to the specified constant value. + * + * @category transforming + * @since 1.0.0 + */ +export const as: { + <_, B>(self: Option<_>, b: B): Option + (b: B): <_>(self: Option<_>) => Option +} = covariant.as(Covariant) + +/** + * Returns the `Option` resulting from mapping the `Some` value to `void`. + * + * This is useful when the value of the `Option` is not needed, but the presence or absence of the value is important. + * + * @category transforming + * @since 1.0.0 + */ +export const asUnit: <_>(self: Option<_>) => Option = covariant.asUnit(Covariant) + +/** + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: Option = of_.unit(Of) + +/** + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + of, + imap, + map +} + +/** + * Applies a function to the value of an `Option` and flattens the result, if the input is `Some`. + * + * @category transforming + * @since 1.0.0 + */ +export const flatMap: { + (f: (a: A) => Option): (self: Option) => Option + (self: Option, f: (a: A) => Option): Option +} = dual( + 2, + (self: Option, f: (a: A) => Option): Option => + isNone(self) ? none() : f(self.value) +) + +/** + * Applies a provided function that returns an `Either` to the contents of an `Option`, flattening the result into another `Option`. + * + * @param self - The `Option` to apply the function to. + * @param f - The function to be applied to the contents of the `Option`. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * import { pipe } from "@fp-ts/core/Function" + * + * const f = (n: number) => (n > 2 ? E.left('Too big') : E.right(n + 1)) + * + * assert.deepStrictEqual(pipe(O.some(1), O.flatMapEither(f)), O.some(2)) + * assert.deepStrictEqual(pipe(O.some(3), O.flatMapEither(f)), O.none()) + * + * @category transforming + * @since 1.0.0 + */ +export const flatMapEither: { + (f: (a: A) => Either): (self: Option) => Option + (self: Option, f: (a: A) => Either): Option +} = dual( + 2, + (self: Option, f: (a: A) => Either): Option => flatMap(self, liftEither(f)) +) + +/** + * This is `flatMap` + `fromNullable`, useful when working with optional values. + * + * @example + * import { some, none, 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( + * some(employee1), + * flatMapNullable(employee => employee.company?.address?.street?.name), + * ), + * some('high street') + * ) + * + * const employee2: Employee = { company: { address: { street: {} } } } + * + * assert.deepStrictEqual( + * pipe( + * some(employee2), + * flatMapNullable(employee => employee.company?.address?.street?.name), + * ), + * none() + * ) + * + * @category transforming + * @since 1.0.0 + */ +export const flatMapNullable: { + (f: (a: A) => B | null | undefined): (self: Option) => Option> + (self: Option, f: (a: A) => B | null | undefined): Option> +} = dual( + 2, + (self: Option, f: (a: A) => B | null | undefined): Option> => + isNone(self) ? none() : fromNullable(f(self.value)) +) + +/** + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @category transforming + * @since 1.0.0 + */ +export const flatten: (self: Option>) => Option = flatMap_.flatten(FlatMap) + +/** + * @category transforming + * @since 1.0.0 + */ +export const andThen: { + <_, B>(self: Option<_>, that: Option): Option + (that: Option): <_>(self: Option<_>) => Option +} = flatMap_.andThen(FlatMap) + +/** + * @category transforming + * @since 1.0.0 + */ +export const composeKleisliArrow: { + (afb: (a: A) => Option, bfc: (b: B) => Option): (a: A) => Option + (bfc: (b: B) => Option): (afb: (a: A) => Option) => (a: A) => Option +} = flatMap_.composeKleisliArrow(FlatMap) + +/** + * @since 1.0.0 + */ +export const Chainable: chainable.Chainable = { + imap, + map, + flatMap +} + +/** + * Sequences the specified `that` `Option` but ignores its value. + * + * It is useful when we want to chain multiple operations, but only care about the result of `self`. + * + * @param that - The `Option` that will be ignored in the chain and discarded + * @param self - The `Option` we care about + * + * @category transforming + * @since 1.0.0 + */ +export const andThenDiscard: { + (self: Option, that: Option<_>): Option + <_>(that: Option<_>): (self: Option) => Option +} = chainable.andThenDiscard(Chainable) + +/** + * Applies the provided function `f` to the value of the `Option` if it is `Some` and returns the original `Option` + * unless `f` returns `None`, in which case it returns `None`. + * + * This function is useful for performing additional computations on the value of the input `Option` without affecting its value. + * + * @param f - Function to apply to the value of the `Option` if it is `Some` + * @param self - The `Option` to apply the function to + * + * @category transforming + * @since 1.0.0 + */ +export const tap: { + (self: Option, f: (a: A) => Option<_>): Option + (f: (a: A) => Option<_>): (self: Option) => Option +} = chainable.tap(Chainable) + +/** + * Useful for debugging purposes, the `onSome` callback is called with the value of `self` if it is a `Some`. + * + * @param self - the `Option` to inspect + * @param onSome - callback function that is called with the value of `self` if it is a `Some` + * + * @category debugging + * @since 1.0.0 + */ +export const inspectSome: { + (onSome: (a: A) => void): (self: Option) => Option + (self: Option, onSome: (a: A) => void): Option +} = dual(2, (self: Option, onSome: (a: A) => void): Option => { + if (isSome(self)) { + onSome(self.value) + } + return self +}) + +/** + * Useful for debugging purposes, the `onNone` callback is is called if `self` is a `None`. + * + * @param self - the `Option` to inspect + * @param onNone - callback function that is is called if `self` is a `None` + * + * @category debugging + * @since 1.0.0 + */ +export const inspectNone: { + (onNone: () => void): (self: Option) => Option + (self: Option, onNone: () => void): Option +} = dual(2, (self: Option, onNone: () => void): Option => { + if (isNone(self)) { + onNone() + } + return self +}) + +/** + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + of, + map, + flatMap +} + +const product = (self: Option, that: Option): Option<[A, B]> => + isSome(self) && isSome(that) ? some([self.value, that.value]) : none() + +const productMany = ( + self: Option, + collection: Iterable> +): Option<[A, ...Array]> => { + if (isNone(self)) { + return none() + } + const out: [A, ...Array] = [self.value] + for (const o of collection) { + if (isNone(o)) { + return none() + } + out.push(o.value) + } + return some(out) +} + +/** + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +/** + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll: all +} + +/** + * Similar to `Promise.all` but operates on `Option`s. + * + * ``` + * [Option, Option, ...] -> Option<[A, B, ...]> + * ``` + * + * Takes a tuple of `Option`s and returns an `Option` of a tuple of values. + * + * @param elements - the tuple of `Option`s to be sequenced. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.tuple(O.some(1), O.some("hello")), O.some([1, "hello"])) + * assert.deepStrictEqual(O.tuple(O.some(1), O.none()), O.none()) + * + * @category combining + * @since 1.0.0 + */ +export const tuple: >>( + ...elements: T +) => Option<{ [I in keyof T]: [T[I]] extends [Option] ? A : never }> = product_.tuple( + Product +) + +/** + * Takes a struct of `Option`s and returns an `Option` of a struct of values. + * + * @param fields - the struct of `Option`s to be sequenced. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.struct({ a: O.some(1), b: O.some("hello") }), O.some({ a: 1, b: "hello" })) + * assert.deepStrictEqual(O.struct({ a: O.some(1), b: O.none() }), O.none()) + * + * @category combining + * @since 1.0.0 + */ +export const struct: >>( + fields: R +) => Option<{ [K in keyof R]: [R[K]] extends [Option] ? A : never }> = product_ + .struct(Product) + +/** + * @since 1.0.0 + */ +export const SemiApplicative: semiApplicative.SemiApplicative = { + imap, + map, + product, + productMany +} + +/** + * Monoid that models the combination of values that may be absent, elements that are `None` are ignored + * while elements that are `Some` are combined using the provided `Semigroup`. + * + * The `empty` value is `none()`. + * + * @param Semigroup - The `Semigroup` used to combine two values of type `A`. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as N from '@fp-ts/core/Number' + * import { pipe } from "@fp-ts/core/Function" + * + * const M = O.getOptionalMonoid(N.SemigroupSum) + * + * assert.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) + * assert.deepStrictEqual(M.combine(O.some(1), O.none()), O.some(1)) + * assert.deepStrictEqual(M.combine(O.none(), O.some(1)), O.some(1)) + * assert.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(3)) + * + * @since 1.0.0 + */ +export const getOptionalMonoid = ( + Semigroup: Semigroup +): Monoid> => + monoid.fromSemigroup( + semigroup.make((self, that) => + isNone(self) ? that : isNone(that) ? self : some(Semigroup.combine(self.value, that.value)) + ), + none() + ) + +/** + * Zips two `Option` values together using a provided function, returning a new `Option` of the result. + * + * @param self - The left-hand side of the zip operation + * @param that - The right-hand side of the zip operation + * @param f - The function used to combine the values of the two `Option`s + * + * @category combining + * @since 1.0.0 + */ +export const zipWith: { + (self: Option, that: Option, f: (a: A, b: B) => C): Option + (that: Option, f: (a: A, b: B) => C): (self: Option) => Option +} = semiApplicative.zipWith(SemiApplicative) + +/** + * @category combining + * @since 1.0.0 + */ +export const ap: { + (self: Option<(a: A) => B>, that: Option): Option + (that: Option): (self: Option<(a: A) => B>) => Option +} = semiApplicative.ap(SemiApplicative) + +/** + * Semigroup that models the combination of computations that can fail, if at least one element is `None` + * then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination + * is the combination of the wrapped elements using the provided `Semigroup`. + * + * See also `getFailureMonoid` if you need a `Monoid` instead of a `Semigroup`. + * + * @category combining + * @since 1.0.0 + */ +export const getFailureSemigroup: (S: Semigroup) => Semigroup> = semiApplicative + .getSemigroup(SemiApplicative) + +/** + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + imap, + of, + map, + product, + productMany, + productAll: all +} + +/** + * Monoid that models the combination of computations that can fail, if at least one element is `None` + * then the resulting combination is `None`, otherwise if all elements are `Some` then the resulting combination + * is the combination of the wrapped elements using the provided `Monoid`. + * + * The `empty` value is `some(M.empty)`. + * + * See also `getFailureSemigroup` if you need a `Semigroup` instead of a `Monoid`. + * + * @category combining + * @since 1.0.0 + */ +export const getFailureMonoid: (M: Monoid) => Monoid> = applicative.getMonoid( + Applicative +) + +const coproduct = (self: Option, that: Option): Option => + isSome(self) ? self : that + +const coproductMany = (self: Option, collection: Iterable>): Option => + isSome(self) ? self : firstSomeOf(collection) + +/** + * @since 1.0.0 + */ +export const SemiCoproduct: semiCoproduct.SemiCoproduct = { + imap, + coproduct, + coproductMany +} + +/** + * Semigroup returning the first `Some` value encountered. + * + * @category combining + * @since 1.0.0 + */ +export const getFirstSomeSemigroup: () => Semigroup> = semiCoproduct.getSemigroup( + SemiCoproduct +) + +/** + * @since 1.0.0 + */ +export const Coproduct: coproduct_.Coproduct = { + imap, + coproduct, + coproductMany, + zero: none, + coproductAll: firstSomeOf +} + +/** + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + map, + imap, + coproduct, + coproductMany +} + +/** + * @since 1.0.0 + */ +export const Alternative: alternative.Alternative = { + map, + imap, + coproduct, + coproductMany, + coproductAll: firstSomeOf, + zero: none +} + +/** + * Reduces an `Iterable` of `Option` to a single value of type `B`, elements that are `None` are ignored. + * + * @param self - The Iterable of `Option` to be reduced. + * @param b - The initial value of the accumulator. + * @param f - The reducing function that takes the current accumulator value and the unwrapped value of an `Option`. + * + * @example + * import { some, none, reduceCompact } from '@fp-ts/core/Option' + * import { pipe } from "@fp-ts/core/Function" + * + * const iterable = [some(1), none(), some(2), none()] + * assert.deepStrictEqual(pipe(iterable, reduceCompact(0, (b, a) => b + a)), 3) + * + * @category folding + * @since 1.0.0 + */ +export const reduceCompact: { + (b: B, f: (b: B, a: A) => B): (self: Iterable>) => B + (self: Iterable>, b: B, f: (b: B, a: A) => B): B +} = dual( + 3, + (self: Iterable>, b: B, f: (b: B, a: A) => B): B => { + let out: B = b + for (const oa of self) { + if (isSome(oa)) { + out = f(out, oa.value) + } + } + return out + } +) + +/** + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: dual( + 3, + (self: Option, b: B, f: (b: B, a: A) => B): B => isNone(self) ? b : f(b, self.value) + ) +} + +/** + * Transforms an `Option` into an `Array`. + * If the input is `None`, an empty array is returned. + * If the input is `Some`, the value is wrapped in an array. + * + * @param self - The `Option` to convert to an array. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.toArray(O.some(1)), [1]) + * assert.deepStrictEqual(O.toArray(O.none()), []) + * + * @category conversions + * @since 1.0.0 + */ +export const toArray: (self: Option) => Array = foldable.toArray(Foldable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partitionMap: { + (f: (a: A) => Either): (self: Option) => [Option, Option] + (self: Option, f: (a: A) => Either): [Option, Option] +} = dual(2, ( + self: Option, + f: (a: A) => Either +): [Option, Option] => { + if (isNone(self)) { + return [none(), none()] + } + const e = f(self.value) + return either.isLeft(e) ? [some(e.left), none()] : [none(), some(e.right)] +}) + +/** + * Maps over the value of an `Option` and filters out `None`s. + * + * Useful when in addition to filtering you also want to change the type of the `Option`. + * + * @param self - The `Option` to map over. + * @param f - A function to apply to the value of the `Option`. + * + * @category filtering + * @since 1.0.0 + */ +export const filterMap: { + (f: (a: A) => Option): (self: Option) => Option + (self: Option, f: (a: A) => Option): Option +} = dual( + 2, + (self: Option, f: (a: A) => Option): Option => + isNone(self) ? none() : f(self.value) +) + +/** + * @since 1.0.0 + */ +export const Filterable: filterable.Filterable = { + partitionMap, + filterMap +} + +/** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @param predicate - A predicate function to apply to the `Option` value. + * @param fb - The `Option` to filter. + * + * @category filtering + * @since 1.0.0 + */ +export const filter: { + (self: Option, refinement: (a: A) => a is B): Option + (self: Option, predicate: (a: A) => boolean): Option + (refinement: (a: A) => a is B): (self: Option) => Option + (predicate: (a: A) => boolean): (self: Option) => Option +} = filterable.filter(Filterable) + +/** + * Applies an `Option` value to an effectful function that returns an `F` value. + * + * @param F - {@link applicative.Applicative} instance + * @param self - The `Option` value. + * @param f - An effectful function that returns an `F` value. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * const traverse = O.traverse(E.Applicative) + * const f = (n: number) => n >= 0 ? E.right(1) : E.left("negative") + * + * assert.deepStrictEqual(traverse(O.some(1), f), E.right(O.some(1))) + * assert.deepStrictEqual(traverse(O.some(-1), f), E.left("negative")) + * assert.deepStrictEqual(traverse(O.none(), f), E.right(O.none())) + * + * @category combining + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind + ): (self: Option) => Kind> + (self: Option, f: (a: A) => Kind): Kind> +} => + dual( + 2, + ( + self: Option, + f: (a: A) => Kind + ): Kind> => isNone(self) ? F.of(none()) : F.map(f(self.value), some) + ) + +/** + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse +} + +/** + * Combines an `Option` of an `F`-structure to an `F`-structure of an `Option` with the same inner type. + * + * @param F - {@link applicative.Applicative} instance + * @param self - `Option` of Kind `F` + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * const sequence = O.sequence(E.Applicative) + * + * assert.deepStrictEqual(sequence(O.some(E.right(1))), E.right(O.some(1))) + * assert.deepStrictEqual(sequence(O.some(E.left("error"))), E.left("error")) + * assert.deepStrictEqual(sequence(O.none()), E.right(O.none())) + * + * @category combining + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => (self: Option>) => Kind> = traversable + .sequence(Traversable) + +/** + * @category combining + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => { + ( + self: Option, + f: (a: A) => Kind + ): Kind> + ( + f: (a: A) => Kind + ): (self: Option) => Kind> +} = traversable.traverseTap(Traversable) + +/** + * @example + * import { none, some, getEquivalence } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/Number' + * + * const isEquivalent = getEquivalence(N.Equivalence) + * assert.deepStrictEqual(isEquivalent(none(), none()), true) + * assert.deepStrictEqual(isEquivalent(none(), some(1)), false) + * assert.deepStrictEqual(isEquivalent(some(1), none()), false) + * assert.deepStrictEqual(isEquivalent(some(1), some(2)), false) + * assert.deepStrictEqual(isEquivalent(some(1), some(1)), true) + * + * @category equivalence + * @since 1.0.0 + */ +export const getEquivalence = (E: Equivalence): Equivalence> => + equivalence.make((x, y) => + x === y || (isNone(x) ? isNone(y) : isNone(y) ? false : E(x.value, y.value)) + ) + +/** + * 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, getOrder } from '@fp-ts/core/Option' + * import * as N from '@fp-ts/core/Number' + * import { pipe } from "@fp-ts/core/Function" + * + * const O = getOrder(N.Order) + * assert.deepStrictEqual(O.compare(none(), none()), 0) + * assert.deepStrictEqual(O.compare(none(), some(1)), -1) + * assert.deepStrictEqual(O.compare(some(1), none()), 1) + * assert.deepStrictEqual(O.compare(some(1), some(2)), -1) + * assert.deepStrictEqual(O.compare(some(1), some(1)), 0) + * + * @category sorting + * @since 1.0.0 + */ +export const getOrder = (O: Order): Order> => + order.make((self, that) => + isSome(self) ? (isSome(that) ? O.compare(self.value, that.value) : 1) : -1 + ) + +/** + * Lifts a binary function into `Option`. + * + * @param f - The function to lift. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: (f: (a: A, b: B) => C) => { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} = semiApplicative.lift2(SemiApplicative) + +/** + * Transforms a `Predicate` function into a `Some` of the input value if the predicate returns `true` or `None` + * if the predicate returns `false`. + * + * @param predicate - A `Predicate` function that takes in a value of type `A` and returns a boolean. + * + * @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) : none() + +/** + * Lifts an `Either` function to an `Option` function. + * + * @param f - Any variadic function that returns an `Either`. + * + * @example + * import * as O from "@fp-ts/core/Option" + * import * as E from "@fp-ts/core/Either" + * + * const parse = (s: string) => + * isNaN(+s) ? E.left(`Error: ${s} is not a number`) : E.right(+s) + * + * const parseNumber = O.liftEither(parse) + * + * assert.deepEqual(parseNumber('12'), O.some(12)) + * assert.deepEqual(parseNumber('not a number'), O.none()) + * + * @category lifting + * @since 1.0.0 + */ +export const liftEither = , E, B>( + f: (...a: A) => Either +) => (...a: A): Option => fromEither(f(...a)) + +/** + * Returns a function that checks if an `Option` contains a given value using a provided `Equivalence` instance. + * + * @param equivalent - An `Equivalence` instance to compare values of the `Option`. + * @param self - The `Option` to apply the comparison to. + * @param a - The value to compare against the `Option`. + * + * @example + * import { some, none, contains } from '@fp-ts/core/Option' + * import { Equivalence } from '@fp-ts/core/Number' + * import { pipe } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(pipe(some(2), contains(Equivalence)(2)), true) + * assert.deepStrictEqual(pipe(some(1), contains(Equivalence)(2)), false) + * assert.deepStrictEqual(pipe(none(), contains(Equivalence)(2)), false) + * + * @since 1.0.0 + */ +export const contains = (isEquivalent: (self: A, that: A) => boolean): { + (a: A): (self: Option) => boolean + (self: Option, a: A): boolean +} => dual(2, (self: Option, a: A): boolean => isNone(self) ? false : isEquivalent(self.value, a)) + +/** + * Check if a value in an `Option` type meets a certain predicate. + * + * @param self - The `Option` to check. + * @param predicate - The condition to check. + * + * @example + * import { some, none, exists } from '@fp-ts/core/Option' + * import { pipe } from "@fp-ts/core/Function" + * + * const isEven = (n: number) => n % 2 === 0 + * + * assert.deepStrictEqual(pipe(some(2), exists(isEven)), true) + * assert.deepStrictEqual(pipe(some(1), exists(isEven)), false) + * assert.deepStrictEqual(pipe(none(), exists(isEven)), false) + * + * @since 1.0.0 + */ +export const exists: { + (predicate: Predicate): (self: Option) => boolean + (self: Option, predicate: Predicate): boolean +} = dual( + 2, + (self: Option, predicate: Predicate): boolean => + isNone(self) ? false : predicate(self.value) +) + +// ------------------------------------------------------------------------------------- +// math +// ------------------------------------------------------------------------------------- + +/** + * @category math + * @since 1.0.0 + */ +export const sum: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} = lift2(N.sum) + +/** + * @category math + * @since 1.0.0 + */ +export const multiply: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} = lift2(N.multiply) + +/** + * @category math + * @since 1.0.0 + */ +export const subtract: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} = lift2(N.subtract) + +/** + * @category math + * @since 1.0.0 + */ +export const divide: { + (self: Option, that: Option): Option + (that: Option): (self: Option) => Option +} = lift2(N.divide) + +/** + * Sum all numbers in an iterable of `Option` ignoring the `None` values. + * + * @param self - The iterable of `Option` to be summed. + * + * @example + * import { sumCompact, some, none } from '@fp-ts/core/Option' + * + * const iterable = [some(2), none(), some(3), none()] + * assert.deepStrictEqual(sumCompact(iterable), 5) + * + * @category math + * @since 1.0.0 + */ +export const sumCompact = (self: Iterable>): number => { + let out = 0 + for (const oa of self) { + if (isSome(oa)) { + out += oa.value + } + } + return out +} + +/** + * Multiply all numbers in an iterable of `Option` ignoring the `None` values. + * + * @param self - The iterable of `Option` to be multiplied. + * + * @example + * import { multiplyCompact, some, none } from '@fp-ts/core/Option' + * + * const iterable = [some(2), none(), some(3), none()] + * assert.deepStrictEqual(multiplyCompact(iterable), 6) + * + * @category math + * @since 1.0.0 + */ +export const multiplyCompact = (self: Iterable>): number => { + let out = 1 + for (const oa of self) { + if (isSome(oa)) { + const a: number = oa.value + if (a === 0) { + return 0 + } + out *= a + } + } + return out +} + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const tupled: (self: Option) => Option<[A]> = invariant.tupled(Invariant) + +/** + * Appends an element to the end of a tuple wrapped in an `Option` type. + * + * @param self - The option of a tuple to which an element needs to be added. + * @param that - The element which needs to be added to the tuple. + * + * @example + * import * as O from "@fp-ts/core/Option" + * + * assert.deepStrictEqual(O.appendElement(O.some([1, 2]), O.some(3)), O.some([1, 2, 3])) + * assert.deepStrictEqual(O.appendElement(O.some([1, 2]), O.none()), O.none()) + * + * @category do notation + * @since 1.0.0 + */ +export const appendElement: { + , B>(self: Option, that: Option): Option<[...A, B]> + (that: Option): >(self: Option) => Option<[...A, B]> +} = semiProduct.appendElement(SemiProduct) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + (name: N): (self: Option) => Option<{ [K in N]: A }> + (self: Option, name: N): Option<{ [K in N]: A }> +} = invariant.bindTo(Invariant) + +const let_: { + ( + name: Exclude, + f: (a: A) => B + ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Option, + name: Exclude, + f: (a: A) => B + ): Option<{ [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 do notation + * @since 1.0.0 + */ +export const bind: { + ( + name: Exclude, + f: (a: A) => Option + ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Option, + name: Exclude, + f: (a: A) => Option + ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = chainable.bind(Chainable) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Option<{}> = of_.Do(Of) + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: { + ( + name: Exclude, + that: Option + ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Option, + name: Exclude, + that: Option + ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = semiProduct.andThenBind(SemiProduct) diff --git a/src/Ordering.ts b/src/Ordering.ts new file mode 100644 index 000000000..60d443af5 --- /dev/null +++ b/src/Ordering.ts @@ -0,0 +1,119 @@ +/** + * @since 1.0.0 + */ +import type { LazyArg } from "@fp-ts/core/Function" +import { dual } from "@fp-ts/core/Function" +import * as monoid from "@fp-ts/core/typeclass/Monoid" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * @category model + * @since 1.0.0 + */ +export type Ordering = -1 | 0 | 1 + +/** + * Inverts the ordering of the input `Ordering`. + * + * @param o - The input `Ordering`. + * + * @example + * import { reverse } from "@fp-ts/core/Ordering" + * + * assert.deepStrictEqual(reverse(1), -1) + * assert.deepStrictEqual(reverse(-1), 1) + * assert.deepStrictEqual(reverse(0), 0) + * + * @since 1.0.0 + */ +export const reverse = (o: Ordering): Ordering => (o === -1 ? 1 : o === 1 ? -1 : 0) + +/** + * Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. + * + * @param self - The `Ordering` parameter to match against. + * @param onLessThan - A function that will be called if the `Ordering` parameter is `-1`. + * @param onEqual - A function that will be called if the `Ordering` parameter is `0`. + * @param onGreaterThan - A function that will be called if the `Ordering` parameter is `1`. + * + * @example + * import { match } from "@fp-ts/core/Ordering" + * import { constant } from "@fp-ts/core/Function" + * + * const toMessage = match( + * constant('less than'), + * constant('equal'), + * constant('greater than') + * ) + * + * assert.deepStrictEqual(toMessage(-1), "less than") + * assert.deepStrictEqual(toMessage(0), "equal") + * assert.deepStrictEqual(toMessage(1), "greater than") + * + * @category pattern matching + * @since 1.0.0 + */ +export const match: { + ( + onLessThan: LazyArg, + onEqual: LazyArg, + onGreaterThan: LazyArg + ): (self: Ordering) => A | B | C + ( + o: Ordering, + onLessThan: LazyArg, + onEqual: LazyArg, + onGreaterThan: LazyArg + ): A | B | C +} = dual(4, ( + self: Ordering, + onLessThan: LazyArg, + onEqual: LazyArg, + onGreaterThan: LazyArg +): A | B | C => self === -1 ? onLessThan() : self === 0 ? onEqual() : onGreaterThan()) + +/** + * `Semigroup` instance for `Ordering`, returns the left-most non-zero `Ordering`. + * + * @example + * import { Semigroup } from "@fp-ts/core/Ordering" + * + * assert.deepStrictEqual(Semigroup.combine(0, -1), -1) + * assert.deepStrictEqual(Semigroup.combine(0, 1), 1) + * assert.deepStrictEqual(Semigroup.combine(1, -1), 1) + * + * @category instances + * @since 1.0.0 + */ +export const Semigroup: semigroup.Semigroup = semigroup.make( + (self, that) => self !== 0 ? self : that, + (self, collection) => { + let ordering = self + if (ordering !== 0) { + return ordering + } + for (ordering of collection) { + if (ordering !== 0) { + return ordering + } + } + return ordering + } +) + +/** + * `Monoid` instance for `Ordering`, returns the left-most non-zero `Ordering`. + * + * The `empty` value is `0`. + * + * @example + * import { Monoid } from "@fp-ts/core/Ordering" + * + * assert.deepStrictEqual(Monoid.combine(Monoid.empty, -1), -1) + * assert.deepStrictEqual(Monoid.combine(Monoid.empty, 1), 1) + * assert.deepStrictEqual(Monoid.combine(1, -1), 1) + * + * @category instances + * @since 1.0.0 + */ +export const Monoid: monoid.Monoid = monoid.fromSemigroup(Semigroup, 0) diff --git a/src/Predicate.ts b/src/Predicate.ts new file mode 100644 index 000000000..0bc999687 --- /dev/null +++ b/src/Predicate.ts @@ -0,0 +1,737 @@ +/** + * @since 1.0.0 + */ +import { constFalse, constTrue, dual, isFunction as isFunction_ } 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" +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 { 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 +} + +/** + * @category models + * @since 1.0.0 + */ +export interface Refinement { + (a: A): a is B +} + +/** + * Given a `Predicate` returns a `Predicate` + * + * @param self - the `Predicate` to be transformed to `Predicate`. + * @param f - a function to transform `B` to `A`. + * + * @example + * import * as P from "@fp-ts/core/Predicate" + * import * as N from "@fp-ts/core/Number" + * + * const minLength3 = P.contramap(N.greaterThan(2), (s: string) => s.length) + * + * assert.deepStrictEqual(minLength3("a"), false) + * assert.deepStrictEqual(minLength3("aa"), false) + * assert.deepStrictEqual(minLength3("aaa"), true) + * assert.deepStrictEqual(minLength3("aaaa"), true) + * + * @category constructors + * @since 1.0.0 + */ +export const contramap: { + (f: (b: B) => A): (self: Predicate) => Predicate + (self: Predicate, f: (b: B) => A): Predicate +} = dual(2, (self: Predicate, f: (b: B) => A): Predicate => (b) => self(f(b))) + +/** + * Tests if a value is a `string`. + * + * @param input - The value to test. + * + * @example + * import { isString } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isString("a"), true) + * + * assert.deepStrictEqual(isString(1), false) + * + * @category guards + * @since 1.0.0 + */ +export const isString = (input: unknown): input is string => typeof input === "string" + +/** + * Tests if a value is a `number`. + * + * @param input - The value to test. + * + * @example + * import { isNumber } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNumber(2), true) + * + * assert.deepStrictEqual(isNumber("2"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNumber = (input: unknown): input is number => typeof input === "number" + +/** + * Tests if a value is a `boolean`. + * + * @param input - The value to test. + * + * @example + * import { isBoolean } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isBoolean(true), true) + * + * assert.deepStrictEqual(isBoolean("true"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isBoolean = (input: unknown): input is boolean => typeof input === "boolean" + +/** + * Tests if a value is a `bigint`. + * + * @param input - The value to test. + * + * @example + * import { isBigint } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isBigint(1n), true) + * + * assert.deepStrictEqual(isBigint(1), false) + * + * @category guards + * @since 1.0.0 + */ +export const isBigint = (input: unknown): input is bigint => typeof input === "bigint" + +/** + * Tests if a value is a `symbol`. + * + * @param input - The value to test. + * + * @example + * import { isSymbol } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true) + * + * assert.deepStrictEqual(isSymbol("a"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isSymbol = (input: unknown): input is symbol => typeof input === "symbol" + +/** + * Tests if a value is a `function`. + * + * @param input - The value to test. + * + * @example + * import { isFunction } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isFunction(isFunction), true) + * + * assert.deepStrictEqual(isFunction("function"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isFunction: (input: unknown) => input is Function = isFunction_ + +/** + * Tests if a value is `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isUndefined } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isUndefined(undefined), true) + * + * assert.deepStrictEqual(isUndefined(null), false) + * assert.deepStrictEqual(isUndefined("undefined"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isUndefined = (input: unknown): input is undefined => input === undefined + +/** + * Tests if a value is not `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isNotUndefined } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNotUndefined(null), true) + * assert.deepStrictEqual(isNotUndefined("undefined"), true) + * + * assert.deepStrictEqual(isNotUndefined(undefined), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNotUndefined = (input: A): input is Exclude => input !== undefined + +/** + * Tests if a value is `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isNull } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNull(null), true) + * + * assert.deepStrictEqual(isNull(undefined), false) + * assert.deepStrictEqual(isNull("null"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNull = (input: unknown): input is null => input === null + +/** + * Tests if a value is not `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isNotNull } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNotNull(undefined), true) + * assert.deepStrictEqual(isNotNull("null"), true) + * + * assert.deepStrictEqual(isNotNull(null), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNotNull = (input: A): input is Exclude => input !== null + +/** + * A guard that always fails. + * + * @param _ - The value to test. + * + * @example + * import { isNever } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNever(null), false) + * assert.deepStrictEqual(isNever(undefined), false) + * assert.deepStrictEqual(isNever({}), false) + * assert.deepStrictEqual(isNever([]), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNever: (input: unknown) => input is never = (_: unknown): _ is never => false + +/** + * A guard that always succeeds. + * + * @param _ - The value to test. + * + * @example + * import { isUnknown } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isUnknown(null), true) + * assert.deepStrictEqual(isUnknown(undefined), true) + * + * assert.deepStrictEqual(isUnknown({}), true) + * assert.deepStrictEqual(isUnknown([]), true) + * + * @category guards + * @since 1.0.0 + */ +export const isUnknown: (input: unknown) => input is unknown = (_): _ is unknown => true + +/** + * Tests if a value is an `object`. + * + * @param input - The value to test. + * + * @example + * import { isObject } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isObject({}), true) + * assert.deepStrictEqual(isObject([]), true) + * + * assert.deepStrictEqual(isObject(null), false) + * assert.deepStrictEqual(isObject(undefined), false) + * + * @category guards + * @since 1.0.0 + */ +export const isObject = (input: unknown): input is object => + typeof input === "object" && input != null + +/** + * A guard that succeeds when the input is `null` or `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isNullable } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNullable(null), true) + * assert.deepStrictEqual(isNullable(undefined), true) + * + * assert.deepStrictEqual(isNullable({}), false) + * assert.deepStrictEqual(isNullable([]), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNullable = (input: A): input is Extract => + input === null || input === undefined + +/** + * A guard that succeeds when the input is not `null` or `undefined`. + * + * @param input - The value to test. + * + * @example + * import { isNotNullable } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isNotNullable({}), true) + * assert.deepStrictEqual(isNotNullable([]), true) + * + * assert.deepStrictEqual(isNotNullable(null), false) + * assert.deepStrictEqual(isNotNullable(undefined), false) + * + * @category guards + * @since 1.0.0 + */ +export const isNotNullable = (input: A): input is NonNullable => + input !== null && input !== undefined + +/** + * A guard that succeeds when the input is an `Error`. + * + * @param input - The value to test. + * + * @example + * import { isError } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isError(new Error()), true) + * + * assert.deepStrictEqual(isError(null), false) + * assert.deepStrictEqual(isError({}), false) + * + * @category guards + * @since 1.0.0 + */ +export const isError = (input: unknown): input is Error => input instanceof Error + +/** + * A guard that succeeds when the input is a `Date`. + * + * @param input - The value to test. + * + * @example + * import { isDate } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isDate(new Date()), true) + * + * assert.deepStrictEqual(isDate(null), false) + * assert.deepStrictEqual(isDate({}), false) + * + * @category guards + * @since 1.0.0 + */ +export const isDate = (input: unknown): input is Date => input instanceof Date + +/** + * A guard that succeeds when the input is a record. + * + * @param input - The value to test. + * + * @example + * import { isRecord } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isRecord({}), true) + * assert.deepStrictEqual(isRecord({ a: 1 }), true) + * + * assert.deepStrictEqual(isRecord([]), false) + * assert.deepStrictEqual(isRecord([1, 2, 3]), false) + * assert.deepStrictEqual(isRecord(null), false) + * assert.deepStrictEqual(isRecord(undefined), false) + * + * @category guards + * @since 1.0.0 + */ +export const isRecord = (input: unknown): input is { [x: string | symbol]: unknown } => + isObject(input) && !Array.isArray(input) + +/** + * A guard that succeeds when the input is a readonly record. + * + * @param input - The value to test. + * + * @example + * import { isReadonlyRecord } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isReadonlyRecord({}), true) + * assert.deepStrictEqual(isReadonlyRecord({ a: 1 }), true) + * + * assert.deepStrictEqual(isReadonlyRecord([]), false) + * assert.deepStrictEqual(isReadonlyRecord([1, 2, 3]), false) + * assert.deepStrictEqual(isReadonlyRecord(null), false) + * assert.deepStrictEqual(isReadonlyRecord(undefined), false) + * + * @category guards + * @since 1.0.0 + */ +export const isReadonlyRecord: ( + input: unknown +) => input is { readonly [x: string | symbol]: unknown } = isRecord + +/** + * @since 1.0.0 + */ +export const compose: { + (bc: Refinement): (ab: Refinement) => Refinement + (ab: Refinement, bc: Refinement): Refinement +} = dual( + 2, + (ab: Refinement, bc: Refinement): Refinement => + (a): a is C => ab(a) && bc(a) +) + +const imap = contravariant.imap(contramap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Contravariant: contravariant.Contravariant = { + imap, + contramap +} + +/** + * @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 +) as any + +/** + * @since 1.0.0 + */ +export const of = (_: A): Predicate => isUnknown + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: Predicate = of_.unit(Of) + +const product = (self: Predicate, that: Predicate): Predicate => + ([a, b]) => self(a) && that(b) + +const productAll = ( + collection: Iterable> +): Predicate> => { + const predicates = readonlyArray.fromIterable(collection) + return (as) => { + const len = Math.min(as.length, predicates.length) + for (let i = 0; i < len; i++) { + if (predicates[i](as[i]) === false) { + return false + } + } + return true + } +} + +const productMany = ( + self: Predicate, + collection: Iterable> +): Predicate]> => { + const rest = productAll(collection) + return ([head, ...tail]) => self(head) === false ? false : rest(tail) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll +} + +/** + * This function appends a predicate to a tuple-like predicate, allowing you to create a new predicate that includes + * the original elements and the new one. + * + * @param self - The tuple-like predicate to append to. + * @param that - The predicate to append. + * + * @since 1.0.0 + */ +export const appendElement: { + , B>( + self: Predicate, + that: Predicate + ): Predicate + ( + that: Predicate + ): >(self: Predicate) => Predicate +} = semiProduct.appendElement(SemiProduct) as any + +/** + * Similar to `Promise.all` but operates on `Predicate`s. + * + * ``` + * [Predicate, Predicate, ...] -> Predicate<[A, B, ...]> + * ``` + * + * @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) + +/** + * Negates the result of a given predicate. + * + * @param self - A predicate. + * + * @example + * import * as P from "@fp-ts/core/Predicate" + * import * as N from "@fp-ts/core/Number" + * + * const isPositive = P.not(N.lessThan(0)) + * + * assert.deepStrictEqual(isPositive(-1), false) + * assert.deepStrictEqual(isPositive(0), true) + * assert.deepStrictEqual(isPositive(1), true) + * + * @category combinators + * @since 1.0.0 + */ +export const not = (self: Predicate): Predicate => (a) => !self(a) + +/** + * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`. + * + * @param self - A predicate. + * @param that - A predicate. + * + * @example + * import * as P from "@fp-ts/core/Predicate" + * import * as N from "@fp-ts/core/Number" + * + * const nonZero = P.or(N.lessThan(0), N.greaterThan(0)) + * + * assert.deepStrictEqual(nonZero(-1), true) + * assert.deepStrictEqual(nonZero(0), false) + * assert.deepStrictEqual(nonZero(1), true) + * + * @category combinators + * @since 1.0.0 + */ +export const or: { + (that: Predicate): (self: Predicate) => Predicate + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) || that(a)) + +/** + * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`. + * + * @param self - A predicate. + * @param that - A predicate. + * + * @example + * import * as P from "@fp-ts/core/Predicate" + * + * const minLength = (n: number) => (s: string) => s.length >= n + * const maxLength = (n: number) => (s: string) => s.length <= n + * + * const length = (n: number) => P.and(minLength(n), maxLength(n)) + * + * assert.deepStrictEqual(length(2)("aa"), true) + * assert.deepStrictEqual(length(2)("a"), false) + * assert.deepStrictEqual(length(2)("aaa"), false) + * + * @category combinators + * @since 1.0.0 + */ +export const and: { + (that: Predicate): (self: Predicate) => Predicate + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) && that(a)) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroupAny = (): Semigroup> => + semigroup.make>( + or, + (self, collection) => + a => { + if (self(a)) { + return true + } + for (const p of collection) { + if (p(a)) { + return true + } + } + return false + } + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const getMonoidAny = (): monoid.Monoid> => + monoid.fromSemigroup(getSemigroupAny(), constFalse) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroupAll = (): Semigroup> => + semigroup.make>( + and, + (self, collection) => + a => { + if (!self(a)) { + return false + } + for (const p of collection) { + if (!p(a)) { + return false + } + } + return true + } + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const getMonoidAll = (): monoid.Monoid> => + monoid.fromSemigroup(getSemigroupAll(), 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) + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + (name: N): (self: Predicate) => Predicate<{ readonly [K in N]: A }> + (self: Predicate, name: N): Predicate<{ readonly [K in N]: A }> +} = invariant.bindTo(Invariant) + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: Predicate<{}> = of_.Do(Of) + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: { + ( + name: Exclude, + that: Predicate + ): ( + self: Predicate + ) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Predicate, + name: Exclude, + that: Predicate + ): Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = semiProduct.andThenBind(SemiProduct) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts new file mode 100644 index 000000000..2bd5f6aed --- /dev/null +++ b/src/ReadonlyArray.ts @@ -0,0 +1,2399 @@ +/** + * This module provides utility functions for working with arrays in TypeScript. + * + * @since 1.0.0 + */ + +import type { Either } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" +import { dual, identity } from "@fp-ts/core/Function" +import type { LazyArg } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" +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 { Coproduct } from "@fp-ts/core/typeclass/Coproduct" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type * 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 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 * 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 * 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 + * @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] + +/** + * Builds a `NonEmptyArray` from an non-empty collection of elements. + * + * @category constructors + * @since 1.0.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. + * + * @example + * import { makeBy } from '@fp-ts/core/ReadonlyArray' + * + * assert.deepStrictEqual(makeBy(5, n => n * 2), [0, 2, 4, 6, 8]) + * + * @category constructors + * @since 1.0.0 + */ +export const makeBy = (n: number, f: (i: number) => A): 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 +} + +/** + * Return a `NonEmptyArray` containing a range of integers, including both endpoints. + * + * @example + * import { range } from '@fp-ts/core/ReadonlyArray' + * + * assert.deepStrictEqual(range(1, 3), [1, 2, 3]) + * + * @category constructors + * @since 1.0.0 + */ +export const range = (start: number, end: number): NonEmptyArray => + start <= end ? makeBy(end - start + 1, (i) => start + i) : [start] + +/** + * Return a `NonEmptyArray` containing a value repeated the specified number of times. + * + * **Note**. `n` is normalized to an integer >= 1. + * + * @example + * import { replicate } from '@fp-ts/core/ReadonlyArray' + * + * assert.deepStrictEqual(replicate("a", 3), ["a", "a", "a"]) + * + * @category constructors + * @since 1.0.0 + */ +export const replicate: { + (n: number): (a: A) => NonEmptyArray + (a: A, n: number): NonEmptyArray +} = dual(2, (a: A, n: number): NonEmptyArray => makeBy(n, () => a)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable: (collection: Iterable) => Array = readonlyArray.fromIterable + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromOption: (self: Option) => Array = O.toArray + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromEither: (self: Either) => Array = E.toArray + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const match: { + ( + onEmpty: LazyArg, + onNonEmpty: (self: NonEmptyReadonlyArray) => C + ): (self: ReadonlyArray) => B | C + ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (self: NonEmptyReadonlyArray) => C + ): B | C +} = dual(3, ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (self: NonEmptyReadonlyArray) => C +): B | C => isNonEmpty(self) ? onNonEmpty(self) : onEmpty()) + +/** + * @category pattern matching + * @since 1.0.0 + */ +export const matchLeft: { + ( + onEmpty: LazyArg, + onNonEmpty: (head: A, tail: Array) => C + ): (self: ReadonlyArray) => B | C + ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (head: A, tail: Array) => C + ): B | C +} = dual(3, ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (head: A, tail: Array) => C +): 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 + ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (init: Array, last: A) => C + ): B | C +} = dual(3, ( + self: ReadonlyArray, + onEmpty: LazyArg, + onNonEmpty: (init: Array, last: A) => C +): B | C => + isNonEmpty(self) ? + onNonEmpty(initNonEmpty(self), lastNonEmpty(self)) : + onEmpty()) + +/** + * Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`. + * + * @since 1.0.0 + */ +export const prepend: { + (head: B): (self: Iterable) => NonEmptyArray + (self: Iterable, head: B): NonEmptyArray +} = dual(2, (self: Iterable, head: B): NonEmptyArray => [head, ...self]) + +/** + * @since 1.0.0 + */ +export const prependAll: { + (that: Iterable): (self: Iterable) => Array + (self: Iterable, that: Iterable): Array +} = dual( + 2, + (self: Iterable, that: Iterable): Array => + fromIterable(that).concat(fromIterable(self)) +) + +/** + * @since 1.0.0 + */ +export const prependAllNonEmpty: { + (that: NonEmptyReadonlyArray): (self: Iterable) => NonEmptyArray + (that: Iterable): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: Iterable, that: NonEmptyReadonlyArray): NonEmptyArray + (self: NonEmptyReadonlyArray, that: Iterable): NonEmptyArray +} = dual( + 2, + (self: Iterable, that: Iterable): Array => prependAll(self, that) +) + +/** + * Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`. + * + * @since 1.0.0 + */ +export const append: { + (last: B): (self: Iterable) => NonEmptyArray + (self: Iterable, last: B): NonEmptyArray +} = dual(2, (self: Iterable, last: B): Array => [...self, last]) + +/** + * @since 1.0.0 + */ +export const appendAll: { + (that: Iterable): (self: Iterable) => Array + (self: Iterable, that: Iterable): Array +} = dual( + 2, + (self: Iterable, that: Iterable): Array => + fromIterable(self).concat(fromIterable(that)) +) + +/** + * @since 1.0.0 + */ +export const appendAllNonEmpty: { + (that: NonEmptyReadonlyArray): (self: Iterable) => NonEmptyArray + (that: Iterable): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: Iterable, that: NonEmptyReadonlyArray): NonEmptyArray + (self: NonEmptyReadonlyArray, that: Iterable): NonEmptyArray +} = dual( + 2, + (self: Iterable, that: Iterable): Array => appendAll(self, that) +) + +/** + * Reduce 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 + (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray +} = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray => { + const out: NonEmptyArray = [b] + let i = 0 + for (const a of self) { + out[i + 1] = f(out[i], a) + i++ + } + return out +}) + +/** + * Reduce 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 + (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray +} = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): 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 +}) + +/** + * Determine if an `Array` is empty narrowing down the type to `[]`. + * + * @param self - The `Array` to check. + * + * @example + * import { isEmptyArray } from "@fp-ts/core/ReadonlyArray" + * + * assert.deepStrictEqual(isEmptyArray([]), true); + * assert.deepStrictEqual(isEmptyArray([1, 2, 3]), false); + * + * @category guards + * @since 1.0.0 + */ +export const isEmptyArray = (self: Array): self is [] => self.length === 0 + +/** + * Determine if a `ReadonlyArray` is empty narrowing down the type to `readonly []`. + * + * @param self - The `ReadonlyArray` to check. + * + * @example + * import { isEmpty } from "@fp-ts/core/ReadonlyArray" + * + * assert.deepStrictEqual(isEmpty([]), true); + * assert.deepStrictEqual(isEmpty([1, 2, 3]), false); + * + * @category guards + * @since 1.0.0 + */ +// TODO: rename to isEmptyReadonlyArray +export const isEmpty: (self: ReadonlyArray) => self is readonly [] = isEmptyArray as any + +/** + * Determine if an `Array` is non empty narrowing down the type to `NonEmptyArray`. + * + * An `Array` is considered to be a `NonEmptyArray` if it contains at least one element. + * + * @param self - The `Array` to check. + * + * @example + * import { isNonEmptyArray } from "@fp-ts/core/ReadonlyArray" + * + * assert.deepStrictEqual(isNonEmptyArray([]), false); + * assert.deepStrictEqual(isNonEmptyArray([1, 2, 3]), true); + * + * @category guards + * @since 1.0.0 + */ +export const isNonEmptyArray: (self: Array) => self is NonEmptyArray = + readonlyArray.isNonEmptyArray + +/** + * Determine if a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`. + * + * A `ReadonlyArray` is considered to be a `NonEmptyReadonlyArray` if it contains at least one element. + * + * @param self - The `ReadonlyArray` to check. + * + * @example + * import { isNonEmpty } from "@fp-ts/core/ReadonlyArray" + * + * assert.deepStrictEqual(isNonEmpty([]), false); + * assert.deepStrictEqual(isNonEmpty([1, 2, 3]), true); + * + * @category guards + * @since 1.0.0 + */ +// TODO: rename to isNonEmptyReadonlyArray +export const isNonEmpty: (self: ReadonlyArray) => self is NonEmptyReadonlyArray = + readonlyArray.isNonEmptyArray + +/** + * Return the number of elements in a `ReadonlyArray`. + * + * @category getters + * @since 1.0.0 + */ +export const length = (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 get: { + (index: number): (self: ReadonlyArray) => Option + (self: ReadonlyArray, index: number): Option +} = dual(2, (self: ReadonlyArray, index: number): Option => { + const i = Math.floor(index) + return isOutOfBound(i, self) ? O.none() : O.some(self[i]) +}) + +/** + * Gets an element unsafely, will throw on out of bounds. + * + * @since 1.0.0 + * @category unsafe + */ +export const unsafeGet: { + (index: number): (self: ReadonlyArray) => A + (self: ReadonlyArray, index: number): A +} = dual(2, (self: ReadonlyArray, index: number): A => { + const i = Math.floor(index) + if (isOutOfBound(i, self)) { + throw new Error(`Index ${i} out of bounds`) + } + return 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 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) + +/** + * @category getters + * @since 1.0.0 + */ +export const headNonEmpty: (self: NonEmptyReadonlyArray) => A = unsafeGet(0) + +/** + * Get the last element in a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. + * + * @category getters + * @since 1.0.0 + */ +export const last = (self: ReadonlyArray): Option => + isNonEmpty(self) ? O.some(lastNonEmpty(self)) : O.none() + +/** + * @category getters + * @since 1.0.0 + */ +export const lastNonEmpty = (self: NonEmptyReadonlyArray): A => self[self.length - 1] + +/** + * 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 tail = (self: Iterable): Option> => { + const input = fromIterable(self) + return isNonEmpty(input) ? O.some(tailNonEmpty(input)) : O.none() +} + +/** + * @category getters + * @since 1.0.0 + */ +export const tailNonEmpty = (self: NonEmptyReadonlyArray): Array => self.slice(1) + +/** + * 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 init = (self: Iterable): Option> => { + const input = fromIterable(self) + return isNonEmpty(input) ? O.some(initNonEmpty(input)) : O.none() +} + +/** + * Get all but the last element of a non empty array, creating a new array. + * + * @category getters + * @since 1.0.0 + */ +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 + (self: Iterable, n: number): Array +} = dual(2, (self: Iterable, n: number): 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 + (self: Iterable, n: number): Array +} = dual(2, (self: Iterable, n: number): 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 const takeWhile: { + (refinement: Refinement): (self: Iterable) => Array + (predicate: Predicate): (self: Iterable) => Array + (self: Iterable, refinement: Refinement): Array + (self: Iterable, predicate: Predicate): Array +} = dual(2, (self: Iterable, predicate: Predicate): Array => { + 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 +} + +/** + * 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 span: { + ( + refinement: Refinement + ): (self: Iterable) => [init: Array, rest: Array] + (predicate: Predicate): (self: Iterable) => [init: Array, rest: Array] + ( + self: Iterable, + refinement: Refinement + ): [init: Array, rest: Array] + (self: Iterable, predicate: Predicate): [init: Array, rest: Array] +} = dual( + 2, + (self: Iterable, predicate: Predicate): [init: Array, rest: Array] => + splitAt(self, spanIndex(self, predicate)) +) + +/** + * 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 drop: { + (n: number): (self: Iterable) => Array + (self: Iterable, n: number): Array +} = dual(2, (self: Iterable, n: number): Array => { + const input = fromIterable(self) + return input.slice(clamp(n, input), input.length) +}) + +/** + * 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 dropRight: { + (n: number): (self: Iterable) => Array + (self: Iterable, n: number): Array +} = dual(2, (self: Iterable, n: number): Array => { + const input = fromIterable(self) + return input.slice(0, input.length - clamp(n, input)) +}) + +/** + * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. + * + * @category getters + * @since 1.0.0 + */ +export const dropWhile: { + (refinement: Refinement): (self: Iterable) => Array + (predicate: Predicate): (self: Iterable) => Array + (self: Iterable, refinement: Refinement): Array + (self: Iterable, predicate: Predicate): Array +} = dual( + 2, + (self: Iterable, predicate: Predicate): Array => + 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 + (self: Iterable, predicate: Predicate): Option +} = dual(2, (self: Iterable, predicate: Predicate): Option => { + let i = 0 + for (const a of self) { + if (predicate(a)) { + return O.some(i) + } + i++ + } + return O.none() +}) + +/** + * Return the last index for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export const findLastIndex: { + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, predicate: Predicate): Option +} = dual(2, (self: Iterable, predicate: Predicate): Option => { + const input = fromIterable(self) + for (let i = input.length - 1; i >= 0; i--) { + if (predicate(input[i])) { + return O.some(i) + } + } + return O.none() +}) + +/** + * Find the first element for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export const findFirst: { + (refinement: Refinement): (self: Iterable) => Option + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, refinement: Refinement): Option + (self: Iterable, predicate: Predicate): Option +} = dual(2, (self: Iterable, predicate: Predicate): Option => { + const input = fromIterable(self) + for (let i = 0; i < input.length; i++) { + if (predicate(input[i])) { + return O.some(input[i]) + } + } + return O.none() +}) + +/** + * Find the last element for which a predicate holds. + * + * @category getters + * @since 1.0.0 + */ +export const findLast: { + (refinement: Refinement): (self: Iterable) => Option + (predicate: Predicate): (self: Iterable) => Option + (self: Iterable, refinement: Refinement): Option + (self: Iterable, predicate: Predicate): Option +} = dual(2, (self: Iterable, predicate: Predicate): Option => { + const input = fromIterable(self) + for (let i = input.length - 1; i >= 0; i--) { + if (predicate(input[i])) { + return O.some(input[i]) + } + } + return O.none() +}) + +/** + * Insert an element at the specified index, creating a new `NonEmptyArray`, + * or return `None` if the index is out of bounds. + * + * @since 1.0.0 + */ +export const insertAt: { + (i: number, b: B): (self: Iterable) => Option> + (self: Iterable, i: number, b: B): Option> +} = dual(3, (self: Iterable, i: number, b: B): Option> => { + const out: Array = Array.from(self) + // v--- `= self.length` is ok, it means inserting in last position + if (i < 0 || i > out.length) { + return O.none() + } + out.splice(i, 0, b) + return O.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. + * + * @since 1.0.0 + */ +export const replace: { + (i: number, b: B): (self: Iterable) => Array + (self: Iterable, i: number, b: B): Array +} = dual(3, (self: Iterable, i: number, b: B): Array => modify(self, i, () => b)) + +/** + * @since 1.0.0 + */ +export const replaceOption: { + (i: number, b: B): (self: Iterable) => Option> + (self: Iterable, i: number, b: B): Option> +} = dual( + 3, + (self: Iterable, i: number, b: B): Option> => modifyOption(self, 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. + * + * @since 1.0.0 + */ +export const modify: { + (i: number, f: (a: A) => B): (self: Iterable) => Array + (self: Iterable, i: number, f: (a: A) => B): Array +} = dual( + 3, + (self: Iterable, i: number, f: (a: A) => B): Array => + O.getOrElse(modifyOption(self, i, f), () => 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. + * + * @since 1.0.0 + */ +export const modifyOption: { + (i: number, f: (a: A) => B): (self: Iterable) => Option> + (self: Iterable, i: number, f: (a: A) => B): Option> +} = dual(3, (self: Iterable, i: number, f: (a: A) => B): 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. + * + * @since 1.0.0 + */ +export const remove: { + (i: number): (self: Iterable) => Array + (self: Iterable, i: number): Array +} = dual(2, (self: Iterable, i: number): 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`. + * + * @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 = E.rights + +/** + * Return all the `Left` elements from an `Interable` of `Either`s. + * + * @category getters + * @since 1.0.0 + */ +export const lefts: (self: Iterable>) => Array = E.lefts + +/** + * 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(O.compare) + 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. + * + * @since 1.0.0 + */ +export const zip: { + (that: Iterable): (self: Iterable) => Array<[A, B]> + (self: Iterable, that: Iterable): Array<[A, B]> +} = dual( + 2, + (self: Iterable, that: Iterable): Array<[A, B]> => + zipWith(self, 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. + * + * @since 1.0.0 + */ +export const zipWith: { + (that: Iterable, f: (a: A, b: B) => C): (self: Iterable) => Array + (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Array +} = dual(3, (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Array => { + const as = fromIterable(self) + const bs = fromIterable(that) + return isNonEmpty(as) && isNonEmpty(bs) ? zipNonEmptyWith(bs, f)(as) : [] +}) + +/** + * @since 1.0.0 + */ +export const zipNonEmpty: { + (that: NonEmptyReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray<[A, B]> + (self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> +} = dual( + 2, + (self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> => + zipNonEmptyWith(self, that, (a, b) => [a, b]) +) + +/** + * @since 1.0.0 + */ +export const zipNonEmptyWith: { + ( + that: NonEmptyReadonlyArray, + f: (a: A, b: B) => C + ): (self: NonEmptyReadonlyArray) => NonEmptyArray + ( + self: NonEmptyReadonlyArray, + that: NonEmptyReadonlyArray, + f: (a: A, b: B) => C + ): NonEmptyArray +} = dual(3, ( + self: NonEmptyReadonlyArray, + that: NonEmptyReadonlyArray, + f: (a: A, b: B) => C +): 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. + * + * @since 1.0.0 + */ +export const unzip = (self: Iterable<[A, B]>): [Array, Array] => { + const input = fromIterable(self) + return isNonEmpty(input) ? unzipNonEmpty(input) : [[], []] +} + +/** + * @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` + * + * @since 1.0.0 + */ +export const intersperse: { + (middle: B): (self: Iterable) => Array + (self: Iterable, middle: B): Array +} = dual(2, (self: Iterable, middle: B): Array => { + const input = fromIterable(self) + return (isNonEmpty(input) ? intersperseNonEmpty(input, middle) : []) +}) + +/** + * Places an element in between members of a `NonEmptyReadonlyArray` + * + * @since 1.0.0 + */ +export const intersperseNonEmpty: { + (middle: B): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, middle: B): NonEmptyArray +} = dual(2, (self: NonEmptyReadonlyArray, middle: B): 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`. + * + * @since 1.0.0 + */ +export const modifyNonEmptyHead: { + (f: (a: A) => B): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray +} = dual( + 2, + ( + self: NonEmptyReadonlyArray, + f: (a: A) => B + ): NonEmptyArray => [f(headNonEmpty(self)), ...tailNonEmpty(self)] +) + +/** + * Change the head, creating a new `NonEmptyReadonlyArray`. + * + * @since 1.0.0 + */ +export const setNonEmptyHead: { + (b: B): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, b: B): NonEmptyArray +} = dual( + 2, + (self: NonEmptyReadonlyArray, b: B): NonEmptyArray => + modifyNonEmptyHead(self, () => b) +) + +/** + * Apply a function to the last element, creating a new `NonEmptyReadonlyArray`. + * + * @since 1.0.0 + */ +export const modifyNonEmptyLast: { + (f: (a: A) => B): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray +} = dual( + 2, + (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray => + append(initNonEmpty(self), f(lastNonEmpty(self))) +) + +/** + * Change the last element, creating a new `NonEmptyReadonlyArray`. + * + * @since 1.0.0 + */ +export const setNonEmptyLast: { + (b: B): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, b: B): NonEmptyArray +} = dual( + 2, + (self: NonEmptyReadonlyArray, b: B): NonEmptyArray => + modifyNonEmptyLast(self, () => b) +) + +/** + * Rotate an `Iterable` by `n` steps. + * + * @since 1.0.0 + */ +export const rotate: { + (n: number): (self: Iterable) => Array + (self: Iterable, n: number): Array +} = dual(2, (self: Iterable, n: number): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? rotateNonEmpty(input, n) : [] +}) + +/** + * Rotate a `NonEmptyReadonlyArray` by `n` steps. + * + * @since 1.0.0 + */ +export const rotateNonEmpty: { + (n: number): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: NonEmptyReadonlyArray, n: number): NonEmptyArray +} = dual(2, (self: NonEmptyReadonlyArray, n: number): 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(self, -m) + return appendAllNonEmpty(s, f) + } else { + return rotateNonEmpty(self, m - len) + } +}) + +/** + * 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 = (isEquivalent: (self: A, that: A) => boolean): { + (a: A): (self: Iterable) => boolean + (self: Iterable, a: A): boolean +} => + dual(2, (self: Iterable, a: A): boolean => { + for (const i of self) { + if (isEquivalent(a, i)) { + return true + } + } + return false + }) + +/** + * Remove duplicates from am `Iterable`, keeping the first occurrence of an element. + * + * @since 1.0.0 + */ +export const uniq = (isEquivalent: (self: A, that: A) => boolean) => + (self: Iterable): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? uniqNonEmpty(isEquivalent)(input) : [] + } + +/** + * Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element. + * + * @since 1.0.0 + */ +export const uniqNonEmpty = (isEquivalent: (self: A, that: A) => boolean) => + (self: NonEmptyReadonlyArray): NonEmptyArray => { + const out: NonEmptyArray = [headNonEmpty(self)] + const rest = tailNonEmpty(self) + for (const a of rest) { + if (out.every((o) => !isEquivalent(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 + ( + self: Iterable, + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] + ): Array +} = dual(2, ( + self: Iterable, + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] +): Array => { + const input = fromIterable(self) + return isNonEmpty(input) ? chopNonEmpty(input, f) : [] +}) + +/** + * 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 + ( + self: NonEmptyReadonlyArray, + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] + ): NonEmptyArray +} = dual(2, ( + self: NonEmptyReadonlyArray, + f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] +): NonEmptyArray => { + const [b, rest] = f(self) + const out: NonEmptyArray = [b] + let next: ReadonlyArray = rest + while (readonlyArray.isNonEmptyArray(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] + (self: Iterable, n: number): [Array, Array] +} = dual(2, (self: Iterable, n: number): [Array, Array] => { + const input = Array.from(self) + return n >= 1 && isNonEmpty(input) ? + splitNonEmptyAt(input, n) : + isEmpty(input) ? + [input, []] : + [[], input] +}) + +/** + * @since 1.0.0 + */ +export const copy: { + (self: NonEmptyReadonlyArray): NonEmptyArray + (self: ReadonlyArray): Array +} = ((self: ReadonlyArray): Array => self.slice()) as any + +/** + * 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] + (self: NonEmptyReadonlyArray, n: number): [NonEmptyArray, Array] +} = dual(2, (self: NonEmptyReadonlyArray, n: number): [NonEmptyArray, Array] => { + const m = Math.max(1, n) + return m >= self.length ? + [copy(self), []] : + [prepend(self.slice(1, m), 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> + (self: Iterable, n: number): Array> +} = dual(2, (self: Iterable, n: number): Array> => { + const input = fromIterable(self) + return isNonEmpty(input) ? chunksOfNonEmpty(input, n) : [] +}) + +/** + * 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> + (self: NonEmptyReadonlyArray, n: number): NonEmptyArray> +} = dual( + 2, + (self: NonEmptyReadonlyArray, n: number): NonEmptyArray> => + chopNonEmpty(self, splitNonEmptyAt(n)) +) + +/** + * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s. + * + * @category grouping + * @since 1.0.0 + */ +export const group = (isEquivalent: (self: A, that: A) => boolean) => + (self: NonEmptyReadonlyArray): NonEmptyArray> => + chopNonEmpty(self, (as) => { + const h = headNonEmpty(as) + const out: NonEmptyArray = [h] + let i = 1 + for (; i < as.length; i++) { + const a = as[i] + if (isEquivalent(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> + (self: Iterable, f: (a: A) => string): Record> +} = dual(2, (self: Iterable, f: (a: A) => string): 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 +}) + +/** + * @since 1.0.0 + */ +export const union = (isEquivalent: (self: A, that: A) => boolean): { + (that: ReadonlyArray): (self: ReadonlyArray) => Array + (self: ReadonlyArray, that: ReadonlyArray): Array +} => + dual(2, (self: ReadonlyArray, that: ReadonlyArray): Array => { + const a = Array.from(self) + const b = Array.from(that) + return isNonEmpty(a) && isNonEmpty(b) ? + unionNonEmpty(isEquivalent)(a, b) : + isNonEmpty(a) ? + a : + b + }) + +/** + * @since 1.0.0 + */ +export const unionNonEmpty = (isEquivalent: (self: A, that: A) => boolean): { + (that: NonEmptyReadonlyArray): (self: ReadonlyArray) => NonEmptyArray + (that: ReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray + (self: ReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray + (self: NonEmptyReadonlyArray, that: ReadonlyArray): NonEmptyArray +} => + dual( + 2, + (self: NonEmptyReadonlyArray, that: ReadonlyArray): NonEmptyArray => + uniqNonEmpty(isEquivalent)(appendAllNonEmpty(self, that)) + ) + +/** + * 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`. + * + * @since 1.0.0 + */ +export const intersection = (isEquivalent: (self: A, that: A) => boolean): { + (that: Iterable): (self: Iterable) => Array + (self: Iterable, that: Iterable): Array +} => { + const has = contains(isEquivalent) + return dual( + 2, + (self: Iterable, that: Iterable): Array => + fromIterable(self).filter((a) => has(that, a)) + ) +} + +/** + * 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`. + * + * @since 1.0.0 + */ +export const difference = (isEquivalent: (self: A, that: A) => boolean): { + (that: Iterable): (self: Iterable) => Array + (self: Iterable, that: Iterable): Array +} => { + const has = contains(isEquivalent) + return dual( + 2, + (self: Iterable, that: Iterable): Array => + fromIterable(self).filter((a) => !has(that, a)) + ) +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const of = (a: A): NonEmptyArray => [a] + +/** + * @category constructors + * @since 1.0.0 + */ +export const empty: () => Array = () => [] + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @category mapping + * @since 1.0.0 + */ +export const map: { + (f: (a: A, i: number) => B): (self: ReadonlyArray) => Array + (self: ReadonlyArray, f: (a: A, i: number) => B): Array +} = dual(2, (self: ReadonlyArray, f: (a: A, i: number) => B): Array => self.map(f)) + +/** + * @category mapping + * @since 1.0.0 + */ +export const mapNonEmpty: { + (f: (a: A, i: number) => B): (self: readonly [A, ...Array]) => [B, ...Array] + (self: readonly [A, ...Array], f: (a: A, i: number) => B): [B, ...Array] +} = map as any + +const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.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: ReadonlyArray) => Array<[A]> = invariant + .tupled(Invariant) as any + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: { + (a: A, self: ReadonlyArray<(a: A) => B>): Array + (self: ReadonlyArray<(a: A) => B>): (a: A) => Array +} = covariant.flap(Covariant) as any + +/** + * Maps the success value of this effect to the specified constant value. + * + * @category mapping + * @since 1.0.0 + */ +export const as: { + <_, B>(self: ReadonlyArray<_>, b: B): Array + (b: B): <_>(self: ReadonlyArray<_>) => Array +} = covariant.as(Covariant) as any + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + of, + imap, + map +} + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMap: { + (f: (a: A, i: number) => ReadonlyArray): (self: ReadonlyArray) => Array + (self: ReadonlyArray, f: (a: A, i: number) => ReadonlyArray): Array +} = dual( + 2, + (self: ReadonlyArray, f: (a: A, i: number) => 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 combining + * @since 1.0.0 + */ +export const flatMapNonEmpty: { + ( + f: (a: A, i: number) => NonEmptyReadonlyArray + ): (self: NonEmptyReadonlyArray) => NonEmptyArray + ( + self: NonEmptyReadonlyArray, + f: (a: A, i: number) => NonEmptyReadonlyArray + ): NonEmptyArray +} = flatMap as any + +/** + * @category instances + * @since 1.0.0 + */ +export const FlatMap: flatMap_.FlatMap = { + flatMap +} + +/** + * @category combining + * @since 1.0.0 + */ +export const flatten: (self: ReadonlyArray>) => Array = flatMap_ + .flatten(FlatMap) as any + +/** + * @category combining + * @since 1.0.0 + */ +export const flattenNonEmpty: ( + self: NonEmptyReadonlyArray> +) => NonEmptyArray = flatMapNonEmpty(identity) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: { + ( + afb: (a: A) => ReadonlyArray, + bfc: (b: B) => ReadonlyArray + ): (a: A) => ReadonlyArray + ( + 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 = { + imap, + map, + flatMap +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const filterMap: { + (f: (a: A, i: number) => Option): (self: Iterable) => Array + (self: Iterable, f: (a: A, i: number) => Option): Array +} = dual( + 2, + (self: Iterable, f: (a: A, i: number) => Option): Array => { + const as = fromIterable(self) + const out: Array = [] + for (let i = 0; i < as.length; i++) { + const o = f(as[i], i) + if (O.isSome(o)) { + out.push(o.value) + } + } + return out + } +) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partitionMap: { + (f: (a: A, i: number) => Either): (self: Iterable) => [Array, Array] + (self: Iterable, f: (a: A, i: number) => Either): [Array, Array] +} = dual( + 2, + (self: Iterable, f: (a: A, i: number) => Either): [Array, Array] => { + const left: Array = [] + const right: Array = [] + const as = fromIterable(self) + for (let i = 0; i < as.length; i++) { + const e = f(as[i], i) + if (E.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 = { + partitionMap, + filterMap +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact: (self: Iterable>) => Array = filterMap(identity) + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + ( + refinement: (a: A, i: number) => a is B + ): (self: Iterable) => Array + (predicate: (a: A, i: number) => boolean): (self: Iterable) => Array + ( + self: Iterable, + refinement: (a: A, i: number) => a is B + ): Array + (self: Iterable, predicate: (a: A, i: number) => boolean): Array +} = dual( + 2, + (self: Iterable, predicate: (a: A, i: number) => boolean): Array => { + const as = fromIterable(self) + const out: Array = [] + for (let i = 0; i < as.length; i++) { + if (predicate(as[i], i)) { + out.push(as[i]) + } + } + return out + } +) + +/** + * @category filtering + * @since 1.0.0 + */ +export const partition: { + (refinement: (a: A, i: number) => a is B): ( + self: Iterable + ) => [Array, Array] + ( + predicate: (a: A, i: number) => boolean + ): (self: Iterable) => [Array, Array] + ( + self: Iterable, + refinement: (a: A, i: number) => a is B + ): [Array, Array] + ( + self: Iterable, + predicate: (a: A, i: number) => boolean + ): [Array, Array] +} = dual( + 2, + ( + self: Iterable, + predicate: (a: A, i: number) => boolean + ): [Array, Array] => { + const left: Array = [] + const right: Array = [] + const as = fromIterable(self) + for (let i = 0; i < as.length; i++) { + if (predicate(as[i], i)) { + right.push(as[i]) + } else { + left.push(as[i]) + } + } + return [left, right] + } +) + +/** + * @category filtering + * @since 1.0.0 + */ +export const separate: (self: Iterable>) => [Array, Array] = partitionMap( + identity +) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseNonEmpty = ( + F: semiApplicative.SemiApplicative +): { + ( + f: (a: A, i: number) => Kind + ): (self: NonEmptyReadonlyArray) => Kind> + ( + self: NonEmptyReadonlyArray, + f: (a: A, i: number) => Kind + ): Kind> +} => + dual(2, ( + self: NonEmptyReadonlyArray, + f: (a: A, i: number) => Kind + ): Kind> => { + const [head, ...tail] = mapNonEmpty(self, f) + return F.productMany(head, tail) + }) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = (F: applicative.Applicative): { + ( + f: (a: A, i: number) => Kind + ): (self: Iterable) => Kind> + ( + self: Iterable, + f: (a: A, i: number) => Kind + ): Kind> +} => + dual(2, ( + self: Iterable, + f: (a: A, i: number) => Kind + ): Kind> => F.productAll(fromIterable(self).map(f))) + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence = ( + F: applicative.Applicative +): ( + self: ReadonlyArray> +) => Kind> => traverse(F)(identity) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse: traverse as any +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => { + ( + self: ReadonlyArray, + f: (a: A) => Kind + ): Kind> + ( + f: (a: A) => Kind + ): (self: ReadonlyArray) => Kind> +} = traversable.traverseTap(Traversable) as any + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequenceNonEmpty = ( + F: semiApplicative.SemiApplicative +): (( + self: NonEmptyReadonlyArray> +) => Kind>) => traverseNonEmpty(F)(identity) + +const product = (self: ReadonlyArray, that: ReadonlyArray): ReadonlyArray<[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 +} + +const productMany = semiProduct.productMany(map, product) + +/** + * @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 = { + imap, + map, + product, + productMany +} + +/** + * @since 1.0.0 + */ +export const ap: { + (self: ReadonlyArray<(a: A) => B>, that: ReadonlyArray): Array + (that: ReadonlyArray): (self: ReadonlyArray<(a: A) => B>) => Array +} = semiApplicative.ap(SemiApplicative) as any + +/** + * Lifts a binary function into `ReadonlyArray`. + * + * @param f - The function to lift. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: (f: (a: A, b: B) => C) => { + (self: ReadonlyArray, that: ReadonlyArray): Array + (that: ReadonlyArray): (self: ReadonlyArray) => Array +} = semiApplicative.lift2(SemiApplicative) as any + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll: (collection) => { + const arrays = fromIterable(collection) + return isEmpty(arrays) ? empty() : SemiProduct.productMany(arrays[0], arrays.slice(1)) + } +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Applicative: applicative.Applicative = { + imap, + of, + map, + product, + productMany, + productAll: Product.productAll +} + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftMonoid: (M: Monoid) => Monoid> = applicative + .getMonoid( + Applicative + ) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + of, + map, + flatMap +} + +/** + * @category folding + * @since 1.0.0 + */ +export const reduce: { + (b: B, f: (b: B, a: A, i: number) => B): (self: Iterable) => B + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B +} = dual( + 3, + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B => + fromIterable(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, i: number) => B): (self: Iterable) => B + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B +} = dual( + 3, + (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B => + fromIterable(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 combineMap = (Monoid: Monoid): { + (f: (a: A, i: number) => M): (self: Iterable) => M + (self: Iterable, f: (a: A, i: number) => M): M +} => + dual( + 2, + (self: Iterable, f: (a: A, i: number) => M): M => + fromIterable(self).reduce((m, a, i) => Monoid.combine(m, f(a, i)), Monoid.empty) + ) + +/** + * @category folding + * @since 1.0.0 + */ +export const combineMapNonEmpty = (S: Semigroup): { + (f: (a: A, i: number) => S): (self: NonEmptyReadonlyArray) => S + (self: NonEmptyReadonlyArray, f: (a: A, i: number) => S): S +} => + dual( + 2, + (self: NonEmptyReadonlyArray, f: (a: A, i: number) => S): S => + tailNonEmpty(self).reduce((s, a, i) => S.combine(s, f(a, i + 1)), f(headNonEmpty(self), 0)) + ) + +/** + * @category folding + * @since 1.0.0 + */ +export const reduceKind: ( + G: monad.Monad +) => { + ( + b: B, + f: (b: B, a: A) => Kind + ): (self: ReadonlyArray) => Kind + ( + self: ReadonlyArray, + b: B, + f: (b: B, a: A) => Kind + ): Kind +} = foldable.reduceKind(Foldable) + +/** + * @category folding + * @since 1.0.0 + */ +export const coproductMapKind: ( + G: Coproduct +) => { + ( + f: (a: A) => Kind + ): (self: ReadonlyArray) => Kind + ( + self: ReadonlyArray, + f: (a: A) => Kind + ): Kind +} = foldable.coproductMapKind(Foldable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traversePartitionMap = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind> + ): (self: ReadonlyArray) => Kind, Array]> + ( + self: ReadonlyArray, + f: (a: A) => Kind> + ): Kind, Array]> +} => + dual(2, ( + self: ReadonlyArray, + f: (a: A) => Kind> + ): Kind, Array]> => { + return F.map(traverse(F)(self, f), separate) + }) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traverseFilterMap = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind> + ): (self: ReadonlyArray) => Kind> + ( + self: ReadonlyArray, + f: (a: A) => Kind> + ): Kind> +} => + dual(2, ( + self: ReadonlyArray, + f: (a: A) => Kind> + ): Kind> => { + return F.map(traverse(F)(self, f), compact) + }) + +/** + * @category instances + * @since 1.0.0 + */ +export const TraversableFilterable: traversableFilterable.TraversableFilterable< + ReadonlyArrayTypeLambda +> = { + traversePartitionMap: traversePartitionMap as any, + traverseFilterMap: traverseFilterMap as any +} + +/** + * Filter values inside a context. + * + * @since 1.0.0 + */ +export const traverseFilter: ( + F: applicative.Applicative +) => { + ( + predicate: (a: A) => Kind + ): (self: ReadonlyArray) => Kind> + ( + self: ReadonlyArray, + predicate: (a: A) => Kind + ): Kind> +} = traversableFilterable.traverseFilter(TraversableFilterable) as any + +/** + * @since 1.0.0 + */ +export const traversePartition: ( + F: applicative.Applicative +) => { + ( + predicate: (a: A) => Kind + ): (self: ReadonlyArray) => Kind, Array]> + ( + self: ReadonlyArray, + predicate: (a: A) => Kind + ): Kind, Array]> +} = traversableFilterable.traversePartition(TraversableFilterable) as any + +/** + * @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 combining + * @since 1.0.0 + */ +export const flatMapNullable: { + (f: (a: A) => B | null | undefined): (self: ReadonlyArray) => Array> + (self: ReadonlyArray, f: (a: A) => B | null | undefined): Array> +} = dual( + 2, + (self: ReadonlyArray, f: (a: A) => B | null | undefined): 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 E.isLeft(e) ? [] : [e.right] + } + +/** + * Check if a predicate holds true for every `ReadonlyArray` member. + * + * @category predicates + * @since 1.0.0 + */ +export function every( + refinement: Refinement +): Refinement, ReadonlyArray> +export function every(predicate: Predicate): Predicate> +export function every(predicate: Predicate): Predicate> { + return (self) => self.every(predicate) +} + +/** + * Check if a predicate holds true for some `ReadonlyArray` member. + * + * @category predicates + * @since 1.0.0 + */ +export const some = (predicate: Predicate) => + (self: ReadonlyArray): self is NonEmptyReadonlyArray => self.some(predicate) + +/** + * Fold an `Iterable`, accumulating values in some `Monoid`, combining adjacent elements + * using the specified separator. + * + * @since 1.0.0 + */ +export const intercalate = (M: Monoid): { + (middle: A): (self: Iterable) => A + (self: Iterable, middle: A): A +} => + dual( + 2, + (self: Iterable, middle: A): A => { + const as = fromIterable(self) + return isNonEmpty(as) ? intercalateNonEmpty(M)(as, middle) : 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 + (self: NonEmptyReadonlyArray, middle: A): A +} => + dual( + 2, + (self: NonEmptyReadonlyArray, middle: A): A => + semigroup.intercalate(S, middle).combineMany(headNonEmpty(self), tailNonEmpty(self)) + ) + +/** + * @since 1.0.0 + */ +export const join: { + (middle: string): (self: ReadonlyArray) => string + (self: ReadonlyArray, middle: string): string +} = intercalate(string.Monoid) + +/** + * @since 1.0.0 + */ +export const extend: { + (f: (as: ReadonlyArray) => B): (self: ReadonlyArray) => Array + (self: ReadonlyArray, f: (as: ReadonlyArray) => B): Array +} = dual( + 2, + (self: ReadonlyArray, f: (as: ReadonlyArray) => B): 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(S.combine) +} + +/** + * @since 1.0.0 + */ +export const max = (O: Order): ((self: NonEmptyReadonlyArray) => A) => { + const S = semigroup.max(O) + return (self) => self.reduce(S.combine) +} + +/** + * @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 (O.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 = ( + isEquivalent: (self: A, that: A) => boolean +): Semigroup> => semigroup.make(union(isEquivalent)) as any + +/** + * @category instances + * @since 1.0.0 + */ +export const getUnionMonoid = ( + isEquivalent: (self: A, that: A) => boolean +): Monoid> => { + const S = getUnionSemigroup(isEquivalent) + return ({ + combine: S.combine, + combineMany: S.combineMany, + combineAll: (collection) => S.combineMany([], collection), + empty: [] + }) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const getIntersectionSemigroup = ( + isEquivalent: (self: A, that: A) => boolean +): Semigroup> => semigroup.make(intersection(isEquivalent)) as any + +/** + * Returns a `Semigroup` for `ReadonlyArray`. + * + * @category instances + * @since 1.0.0 + */ +export const getSemigroup: () => Semigroup> = semigroup.array + +/** + * Returns a `Monoid` for `ReadonlyArray`. + * + * @category instances + * @since 1.0.0 + */ +export const getMonoid: () => Monoid> = monoid.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. + * + * @category lifting + * @since 1.0.0 + */ +export const getOrder: (O: Order) => Order> = order.array + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + (name: N): (self: ReadonlyArray) => Array<{ [K in N]: A }> + (self: ReadonlyArray, name: N): Array<{ [K in N]: A }> +} = invariant.bindTo(Invariant) as any + +const let_: { + ( + name: Exclude, + f: (a: A) => B + ): (self: ReadonlyArray) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: ReadonlyArray, + name: Exclude, + f: (a: A) => B + ): Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = covariant.let(Covariant) as any + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: ReadonlyArray<{}> = of_.Do(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: { + ( + name: Exclude, + f: (a: A) => ReadonlyArray + ): (self: ReadonlyArray) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: ReadonlyArray, + name: Exclude, + f: (a: A) => ReadonlyArray + ): Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = chainable.bind(Chainable) as any + +/** + * A variant of `bind` that sequentially ignores the scope. + * + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: { + ( + name: Exclude, + that: ReadonlyArray + ): (self: ReadonlyArray) => Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: ReadonlyArray, + name: Exclude, + that: ReadonlyArray + ): Array<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = semiProduct.andThenBind(SemiProduct) as any diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts new file mode 100644 index 000000000..2ff8e96ea --- /dev/null +++ b/src/ReadonlyRecord.ts @@ -0,0 +1,835 @@ +/** + * This module provides utility functions for working with records in TypeScript. + * + * @since 1.0.0 + */ + +import type { Either } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" +import { dual, identity } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import type { Option } from "@fp-ts/core/Option" +import * as O from "@fp-ts/core/Option" +import type * as applicative from "@fp-ts/core/typeclass/Applicative" +import * as covariant from "@fp-ts/core/typeclass/Covariant" +import type * as filterable from "@fp-ts/core/typeclass/Filterable" +import * as invariant from "@fp-ts/core/typeclass/Invariant" +import * as traversable from "@fp-ts/core/typeclass/Traversable" +import * as traversableFilterable from "@fp-ts/core/typeclass/TraversableFilterable" + +// ------------------------------------------------------------------------------------- +// models +// ------------------------------------------------------------------------------------- + +/** + * @category models + * @since 1.0.0 + */ +export interface ReadonlyRecord { + readonly [x: string]: A +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface ReadonlyRecordTypeLambda extends TypeLambda { + readonly type: ReadonlyRecord +} + +// ------------------------------------------------------------------------------------- +// constructors +// ------------------------------------------------------------------------------------- + +/** + * Creates a new, empty record. + * + * @category constructors + * @since 1.0.0 + */ +export const empty = (): Record => ({}) + +/** + * Takes an iterable and a projection function and returns a record. + * The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record. + * + * @param self - An iterable of values to be mapped to a record. + * @param f - A projection function that maps values of the iterable to a tuple of a key and a value. + * + * @example + * import { fromIterable } from '@fp-ts/core/ReadonlyRecord' + * + * const input = [1, 2, 3, 4] + * + * assert.deepStrictEqual( + * fromIterable(input, a => [String(a), a * 2]), + * { '1': 2, '2': 4, '3': 6, '4': 8 } + * ) + * + * @category constructors + * @since 1.0.0 + */ +export const fromIterable: { + (f: (a: A) => readonly [string, B]): (self: Iterable) => Record + (self: Iterable, f: (a: A) => readonly [string, B]): Record +} = dual(2, (self: Iterable, f: (a: A) => readonly [string, B]): Record => { + const out: Record = {} + for (const a of self) { + const [k, b] = f(a) + out[k] = b + } + return out +}) + +// ------------------------------------------------------------------------------------- +// guards +// ------------------------------------------------------------------------------------- + +/** + * Determine if a `ReadonlyRecord` is empty. + * + * @param self - `ReadonlyRecord` to test for emptiness. + * + * @example + * import { isEmpty } from "@fp-ts/core/ReadonlyRecord" + * + * assert.deepStrictEqual(isEmpty({}), true); + * assert.deepStrictEqual(isEmpty({ a: 3 }), false); + * + * @category guards + * @since 1.0.0 + */ +export const isEmpty = (self: ReadonlyRecord): self is Record => { + for (const k in self) { + if (has(self, k)) { + return false + } + } + return true +} + +// ------------------------------------------------------------------------------------- +// conversions +// ------------------------------------------------------------------------------------- + +/** + * Transforms the values of a `ReadonlyRecord` into an `Array` with a custom mapping function. + * + * @param self - The `ReadonlyRecord` to transform. + * @param f - The custom mapping function to apply to each key/value of the `ReadonlyRecord`. + * + * @example + * import { collect } from '@fp-ts/core/ReadonlyRecord' + * + * const x = { a: 1, b: 2, c: 3 } + * assert.deepStrictEqual(collect(x, (key, n) => [key, n]), [["a", 1], ["b", 2], ["c", 3]]) + * + * @category conversions + * @since 1.0.0 + */ +export const collect: { + (f: (key: string, a: A) => B): (self: ReadonlyRecord) => Array + (self: ReadonlyRecord, f: (key: string, a: A) => B): Array +} = dual( + 2, + (self: ReadonlyRecord, f: (key: string, a: A) => B): Array => { + const out: Array = [] + for (const key of Object.keys(self)) { + out.push(f(key, self[key])) + } + return out + } +) + +/** + * Converts a `ReadonlyRecord` to an `Array` of key-value pairs. + * + * @param self - A `ReadonlyRecord` to convert to an `Array`. + * + * @example + * import { toArray } from '@fp-ts/core/ReadonlyRecord' + * + * const x = { a: 1, b: 2 } + * assert.deepStrictEqual(toArray(x), [["a", 1], ["b", 2]]) + * + * @category conversions + * @since 1.0.0 + */ +export const toArray: (self: ReadonlyRecord) => Array<[string, A]> = collect(( + key, + a +) => [key, a]) + +// ------------------------------------------------------------------------------------- +// utils +// ------------------------------------------------------------------------------------- + +/** + * Returns the number of key/value pairs in a `ReadonlyRecord`. + * + * @param self - A `ReadonlyRecord` to calculate the number of key/value pairs in. + * + * @example + * import { size } from "@fp-ts/core/ReadonlyRecord"; + * + * assert.deepStrictEqual(size({ a: "a", b: 1, c: true }), 3); + * + * @since 1.0.0 + */ +export const size = (self: ReadonlyRecord): number => Object.keys(self).length + +/** + * Check if a given `key` exists in a `ReadonlyRecord`. + * + * @param self - the `ReadonlyRecord` to look in. + * @param key - the key to look for in the `ReadonlyRecord`. + * + * @example + * import { has } from '@fp-ts/core/ReadonlyRecord' + * + * assert.deepStrictEqual(has({ a: 1, b: 2 }, "a"), true); + * assert.deepStrictEqual(has({ a: 1, b: 2 }, "c"), false); + * + * @since 1.0.0 + */ +export const has: { + (key: string): (self: ReadonlyRecord) => boolean + (self: ReadonlyRecord, key: string): boolean +} = dual( + 2, + (self: ReadonlyRecord, key: string): boolean => + Object.prototype.hasOwnProperty.call(self, key) +) + +/** + * Retrieve a value at a particular key from a `ReadonlyRecord`, returning it wrapped in an `Option`. + * + * @param self - The `ReadonlyRecord` to retrieve value from. + * @param key - Key to retrieve from `ReadonlyRecord`. + * + * @example + * import { get } from "@fp-ts/core/ReadonlyRecord" + * import { some, none } from "@fp-ts/core/Option" + * + * const person = { name: "John Doe", age: 35 } + * + * assert.deepStrictEqual(get(person, "name"), some("John Doe")) + * assert.deepStrictEqual(get(person, "email"), none()) + * + * @since 1.0.0 + */ +export const get: { + (key: string): (self: ReadonlyRecord) => Option + (self: ReadonlyRecord, key: string): Option +} = dual( + 2, + (self: ReadonlyRecord, key: string): Option => + has(self, key) ? O.some(self[key]) : O.none() +) + +/** + * Apply a function to the element at the specified key, creating a new record, + * or return `None` if the key doesn't exist. + * + * @param self - The `ReadonlyRecord` to be updated. + * @param key - The key of the element to modify. + * @param f - The function to apply to the element. + * + * @example + * import { modifyOption } from "@fp-ts/core/ReadonlyRecord" + * import { some, none } from "@fp-ts/core/Option" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * modifyOption({ a: 3 }, 'a', f), + * some({ a: 6 }) + * ) + * assert.deepStrictEqual( + * modifyOption({ a: 3 }, 'b', f), + * none() + * ) + * + * @since 1.0.0 + */ +export const modifyOption: { + (key: string, f: (a: A) => B): (self: ReadonlyRecord) => Option> + (self: ReadonlyRecord, key: string, f: (a: A) => B): Option> +} = dual( + 3, + (self: ReadonlyRecord, key: string, f: (a: A) => B): Option> => { + if (!has(self, key)) { + return O.none() + } + const out: Record = { ...self } + out[key] = f(self[key]) + return O.some(out) + } +) + +/** + * Replaces a value in the record with the new value passed as parameter. + * + * @param self - The `ReadonlyRecord` to be updated. + * @param key - The key to search for in the record. + * @param b - The new value to replace the existing value with. + * + * @example + * import { replaceOption } from "@fp-ts/core/ReadonlyRecord" + * import { some, none } from "@fp-ts/core/Option" + * + * assert.deepStrictEqual( + * replaceOption({ a: 1, b: 2, c: 3 }, 'a', 10), + * some({ a: 10, b: 2, c: 3 }) + * ) + * assert.deepStrictEqual(replaceOption({}, 'a', 10), none()) + * + * @since 1.0.0 + */ +export const replaceOption: { + (key: string, b: B): (self: ReadonlyRecord) => Option> + (self: ReadonlyRecord, key: string, b: B): Option> +} = dual( + 3, + (self: ReadonlyRecord, key: string, b: B): Option> => + modifyOption(self, key, () => b) +) + +/** + * Removes a key from a `ReadonlyRecord` and returns a new `Record` + * + * @param self - the `ReadonlyRecord` to remove the key from. + * @param key - the key to remove from the `ReadonlyRecord`. + * + * @example + * import { remove } from '@fp-ts/core/ReadonlyRecord' + * + * assert.deepStrictEqual(remove({ a: 1, b: 2 }, "a"), { b: 2 }) + * + * @since 1.0.0 + */ +export const remove: { + (key: string): (self: ReadonlyRecord) => Record + (self: ReadonlyRecord, key: string): Record +} = dual(2, (self: ReadonlyRecord, key: string): Record => { + const out: Record = { ...self } + delete out[key] + return out +}) + +/** + * Retrieves the value of the property with the given `key` from a `ReadonlyRecord` and returns an `Option` + * of a tuple with the value and the `ReadonlyRecord` with the removed property. + * If the key is not present, returns `O.none`. + * + * @param self - The input `ReadonlyRecord`. + * @param key - The key of the property to retrieve. + * + * @example + * import { pop } from '@fp-ts/core/ReadonlyRecord' + * import { some, none } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual(pop({ a: 1, b: 2 }, "a"), some([1, { b: 2 }])) + * assert.deepStrictEqual(pop({ a: 1, b: 2 }, "c"), none()) + * + * @category record + * @since 1.0.0 + */ +export const pop: { + (key: string): (self: ReadonlyRecord) => Option]> + (self: ReadonlyRecord, key: string): Option]> +} = dual(2, ( + self: ReadonlyRecord, + key: string +): Option]> => + has(self, key) ? O.some([self[key], remove(self, key)]) : O.none()) + +/** + * Maps a `ReadonlyRecord` into another `Record` by applying a transformation function to each of its values. + * + * @param self - The `ReadonlyRecord` to be mapped. + * @param f - A transformation function that will be applied to each of the values in the `ReadonlyRecord`. + * + * @example + * import { map } from "@fp-ts/core/ReadonlyRecord" + * + * const f = (n: number) => `-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, f), { a: "-3", b: "-5" }) + * + * const g = (n: number, key: string) => `${key.toUpperCase()}-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, g), { a: "A-3", b: "B-5" }) + * + * @since 1.0.0 + */ +export const map: { + (f: (a: A, key: K) => B): (self: Readonly>) => Record + (self: Readonly>, f: (a: A, key: K) => B): Record +} = dual( + 2, + (self: ReadonlyRecord, f: (a: A, key: string) => B): Record => { + const out: Record = {} + for (const key of Object.keys(self)) { + out[key] = f(self[key], key) + } + return out + } +) + +/** + * Transforms a `ReadonlyRecord` into a `Record` by applying the function `f` to each key and value in the original `ReadonlyRecord`. + * If the function returns `Some`, the key-value pair is included in the output `Record`. + * + * @param self - The input `ReadonlyRecord`. + * @param f - The transformation function. + * + * @example + * import { filterMap } from '@fp-ts/core/ReadonlyRecord' + * import { some, none } from '@fp-ts/core/Option' + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (a: number, key: string) => a > 2 ? some(a * 2) : none() + * assert.deepStrictEqual(filterMap(x, f), { c: 6 }) + * + * @since 1.0.0 + */ +export const filterMap: { + (f: (a: A, key: string) => Option): (self: ReadonlyRecord) => Record + (self: ReadonlyRecord, f: (a: A, key: string) => Option): Record +} = dual(2, ( + self: ReadonlyRecord, + f: (a: A, key: string) => Option +): Record => { + const out: Record = {} + for (const key of Object.keys(self)) { + const o = f(self[key], key) + if (O.isSome(o)) { + out[key] = o.value + } + } + return out +}) + +/** + * Selects properties from a record whose values match the given predicate. + * + * @param self - The `ReadonlyRecord` to filter. + * @param predicate - A function that returns a `boolean` value to determine if the entry should be included in the new record. + * + * @example + * import { filter } from '@fp-ts/core/ReadonlyRecord' + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * + * @category filtering + * @since 1.0.0 + */ +export const filter: { + ( + refinement: (a: A, key: string) => a is B + ): (self: ReadonlyRecord) => Record + ( + predicate: (a: A, key: string) => boolean + ): (self: ReadonlyRecord) => Record + ( + self: ReadonlyRecord, + refinement: (a: A, key: string) => a is B + ): Record + ( + self: ReadonlyRecord, + predicate: (a: A, key: string) => boolean + ): Record +} = dual( + 2, + ( + self: ReadonlyRecord, + predicate: (a: A, key: string) => boolean + ): Record => { + const out: Record = {} + for (const key of Object.keys(self)) { + if (predicate(self[key], key)) { + out[key] = self[key] + } + } + return out + } +) + +/** + * Given a `ReadonlyRecord` with `Option` values, returns a `Record` with only the `Some` values, with the same keys. + * + * @param self - A `ReadonlyRecord` with `Option` values. + * + * @example + * import { compact } from '@fp-ts/core/ReadonlyRecord' + * import { some, none } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual( + * compact({ a: some(1), b: none(), c: some(2) }), + * { a: 1, c: 2 } + * ) + * + * @category filtering + * @since 1.0.0 + */ +export const compact: (self: ReadonlyRecord>) => Record = filterMap( + identity +) + +/** + * Partitions the elements of a `ReadonlyRecord` into two groups: those that match a predicate, and those that don't. + * + * @param self - The `ReadonlyRecord` to partition. + * @param f - The predicate function to apply to each element. + * + * @example + * import { partitionMap } from '@fp-ts/core/ReadonlyRecord' + * import { left, right } from '@fp-ts/core/Either' + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (n: number) => (n % 2 === 0 ? right(n) : left(n)) + * assert.deepStrictEqual(partitionMap(x, f), [{ a: 1, c: 3 }, { b: 2}]) + * + * @category filtering + * @since 1.0.0 + */ +export const partitionMap: { + ( + f: (a: A, key: string) => Either + ): (self: ReadonlyRecord) => [Record, Record] + ( + self: ReadonlyRecord, + f: (a: A, key: string) => Either + ): [Record, Record] +} = dual( + 2, + ( + self: ReadonlyRecord, + f: (a: A, key: string) => Either + ): [Record, Record] => { + const left: Record = {} + const right: Record = {} + for (const key of Object.keys(self)) { + const e = f(self[key], key) + if (E.isLeft(e)) { + left[key] = e.left + } else { + right[key] = e.right + } + } + return [left, right] + } +) + +/** + * Partitions a `ReadonlyRecord` of `Either` values into two separate records, + * one with the `Left` values and one with the `Right` values. + * + * @param self - the `ReadonlyRecord` to partition. + * + * @example + * import { separate } from '@fp-ts/core/ReadonlyRecord' + * import { left, right } from '@fp-ts/core/Either' + * + * assert.deepStrictEqual( + * separate({ a: left("e"), b: right(1) }), + * [{ a: "e" }, { b: 1 }] + * ) + * + * @category filtering + * @since 1.0.0 + */ +export const separate: ( + self: ReadonlyRecord> +) => [Record, Record] = partitionMap(identity) + +/** + * Partitions a `ReadonlyRecord` into two separate `Record`s based on the result of a predicate function. + * + * @param self - The input `ReadonlyRecord` to partition. + * @param predicate - The partitioning function to determine the partitioning of each value of the `ReadonlyRecord`. + * + * @example + * import { partition } from '@fp-ts/core/ReadonlyRecord' + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * + * @category filtering + * @since 1.0.0 + */ +export const partition: { + (refinement: (a: A, key: string) => a is B): ( + self: ReadonlyRecord + ) => [Record, Record] + ( + predicate: (a: A, key: string) => boolean + ): (self: ReadonlyRecord) => [Record, Record] + ( + self: ReadonlyRecord, + refinement: (a: A, key: string) => a is B + ): [Record, Record] + ( + self: ReadonlyRecord, + predicate: (a: A, key: string) => boolean + ): [Record, Record] +} = dual( + 2, + ( + self: ReadonlyRecord, + predicate: (a: A, key: string) => boolean + ): [Record, Record] => { + const left: Record = {} + const right: Record = {} + for (const key of Object.keys(self)) { + if (predicate(self[key], key)) { + right[key] = self[key] + } else { + left[key] = self[key] + } + } + return [left, right] + } +) + +/** + * Maps each entry of a `ReadonlyRecord` to an effect and collects the results into a new record. + * + * @param F - an {@link applicative.Applicative Applicative} instance. + * @param self - a `ReadonlyRecord` to map over. + * @param f - the mapping function, which maps an entry `a` and its corresponding `key` to an effect. + * + * @example + * import { traverse } from '@fp-ts/core/ReadonlyRecord' + * import { some, none, Applicative } from '@fp-ts/core/Option' + * + * assert.deepStrictEqual( + * traverse(Applicative)({ a: 1, b: 2 }, (n: number) => (n <= 2 ? some(n) : none())), + * some({ a: 1, b: 2 }) + * ) + * assert.deepStrictEqual( + * traverse(Applicative)({ a: 1, b: 2 }, (n: number) => (n >= 2 ? some(n) : none())), + * none() + * ) + * + * @category traversing + * @since 1.0.0 + */ +export const traverse = (F: applicative.Applicative): { + ( + f: (a: A, key: string) => Kind + ): (self: ReadonlyRecord) => Kind> + ( + self: ReadonlyRecord, + f: (a: A, key: string) => Kind + ): Kind> +} => + dual(2, ( + self: ReadonlyRecord, + f: (a: A, key: string) => Kind + ): Kind> => + F.map( + F.productAll( + Object.entries(self).map(([key, a]) => F.map(f(a, key), b => [key, b] as const)) + ), + Object.fromEntries + )) + +/** + * Transforms a `ReadonlyRecord` of `Kind` values into a `Kind` of `Record` values. + * + * @param F - an {@link applicative.Applicative Applicative} instance. + * @param self - the `ReadonlyRecord` of `Kind` values. + * + * @example + * import * as RR from '@fp-ts/core/ReadonlyRecord' + * import { some, none, Applicative } from '@fp-ts/core/Option' + * + * const sequence = RR.sequence(Applicative) + * + * assert.deepStrictEqual(sequence({ a: some(1), b: some(2) }), some({ a: 1, b: 2 })) + * assert.deepStrictEqual(sequence({ a: none(), b: some(2) }), none()) + * + * @category traversing + * @since 1.0.0 + */ +export const sequence = ( + F: applicative.Applicative +): ( + self: ReadonlyRecord> +) => Kind> => traverse(F)(identity) + +const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.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: ReadonlyRecord) => Record = invariant.tupled( + Invariant +) + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: { + (self: ReadonlyRecord<(a: A) => B>): (a: A) => Record + (a: A, self: ReadonlyRecord<(a: A) => B>): Record +} = 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: ReadonlyRecord<_>) => Record + <_, B>(self: ReadonlyRecord<_>, b: B): Record +} = covariant.as(Covariant) + +/** + * @category instances + * @since 1.0.0 + */ +export const Filterable: filterable.Filterable = { + partitionMap, + filterMap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => { + ( + f: (a: A) => Kind + ): (self: ReadonlyRecord) => Kind> + ( + self: ReadonlyRecord, + f: (a: A) => Kind + ): Kind> +} = traversable.traverseTap(Traversable) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traversePartitionMap = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind> + ): ( + self: ReadonlyRecord + ) => Kind, Record]> + ( + self: ReadonlyRecord, + f: (a: A) => Kind> + ): Kind, Record]> +} => + dual(2, ( + self: ReadonlyRecord, + f: (a: A) => Kind> + ): Kind, Record]> => { + return F.map(traverse(F)(self, f), separate) + }) + +/** + * @category filtering + * @since 1.0.0 + */ +export const traverseFilterMap = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind> + ): (self: ReadonlyRecord) => Kind> + ( + self: ReadonlyRecord, + f: (a: A) => Kind> + ): Kind> +} => + dual(2, ( + self: ReadonlyRecord, + f: (a: A) => Kind> + ): Kind> => { + return F.map(traverse(F)(self, f), compact) + }) + +/** + * @category instances + * @since 1.0.0 + */ +export const TraversableFilterable: traversableFilterable.TraversableFilterable< + ReadonlyRecordTypeLambda +> = { + traversePartitionMap, + traverseFilterMap +} + +/** + * Filter values inside a context. + * + * @since 1.0.0 + */ +export const traverseFilter: ( + F: applicative.Applicative +) => { + ( + predicate: (a: A) => Kind + ): (self: ReadonlyRecord) => Kind> + ( + self: ReadonlyRecord, + predicate: (a: A) => Kind + ): Kind> +} = traversableFilterable.traverseFilter(TraversableFilterable) + +/** + * @since 1.0.0 + */ +export const traversePartition: ( + F: applicative.Applicative +) => { + ( + predicate: (a: A) => Kind + ): ( + self: ReadonlyRecord + ) => Kind, Record]> + ( + self: ReadonlyRecord, + predicate: (a: A) => Kind + ): Kind, Record]> +} = traversableFilterable.traversePartition(TraversableFilterable) diff --git a/src/String.ts b/src/String.ts new file mode 100644 index 000000000..d89a7b7de --- /dev/null +++ b/src/String.ts @@ -0,0 +1,508 @@ +/** + * 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 + */ + +import { dual } from "@fp-ts/core/Function" +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 { NonEmptyArray } 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 semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * Tests if a value is a `string`. + * + * @param input - The value to test. + * + * @example + * import { isString } from '@fp-ts/core/String' + * + * assert.deepStrictEqual(isString("a"), true) + * assert.deepStrictEqual(isString(1), false) + * + * @category guards + * @since 1.0.0 + */ +export const isString: Refinement = predicate.isString + +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.string + +/** + * @category instances + * @since 1.0.0 + */ +export const Order: order.Order = order.string + +/** + * `string` semigroup under concatenation. + * + * @category instances + * @since 1.0.0 + */ +export const Semigroup: semigroup.Semigroup = semigroup.string + +/** + * `string` monoid under concatenation. + * + * The `empty` value is `''`. + * + * @category instances + * @since 1.0.0 + */ +export const Monoid: monoid.Monoid = monoid.string + +/** + * The empty string `""`. + * + * @since 1.0.0 + */ +export const empty: "" = "" as const + +/** + * @since 1.0.0 + */ +export const concat: { + (that: string): (self: string) => string + (self: string, that: string): string +} = dual(2, Semigroup.combine) + +/** + * @example + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual(pipe('a', S.toUpperCase), 'A') + * + * @since 1.0.0 + */ +export const toUpperCase = (self: string): string => self.toUpperCase() + +/** + * @example + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual(pipe('A', S.toLowerCase), 'a') + * + * @since 1.0.0 + */ +export const toLowerCase = (self: string): string => self.toLowerCase() + +/** + * @example + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual(pipe('abc', S.replace('b', 'd')), 'adc') + * + * @since 1.0.0 + */ +export const replace: { + (searchValue: string | RegExp, replaceValue: string): (self: string) => string + (self: string, searchValue: string | RegExp, replaceValue: string): string +} = dual( + 3, + (self: string, searchValue: string | RegExp, replaceValue: string): string => + self.replace(searchValue, replaceValue) +) + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.trim(' a '), 'a') + * + * @since 1.0.0 + */ +export const trim = (self: string): string => self.trim() + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.trimStart(' a '), 'a ') + * + * @since 1.0.0 + */ +export const trimStart = (self: string): string => self.trimStart() + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.trimEnd(' a '), ' a') + * + * @since 1.0.0 + */ +export const trimEnd = (self: string): string => self.trimEnd() + +/** + * @example + * import * as S from '@fp-ts/core/String' + * import { pipe } from '@fp-ts/core/Function' + * + * assert.deepStrictEqual(pipe('abcd', S.slice(1, 3)), 'bc') + * + * @since 1.0.0 + */ +export const slice: { + (start: number, end: number): (self: string) => string + (self: string, start: number, end: number): string +} = dual(3, (self: string, start: number, end: number): string => self.slice(start, end)) + +/** + * Test whether a `string` is empty. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.isEmpty(''), true) + * assert.deepStrictEqual(S.isEmpty('a'), false) + * + * @since 1.0.0 + */ +export const isEmpty = (self: string): self is "" => self.length === 0 + +/** + * Test whether a `string` is non empty. + * + * @since 1.0.0 + */ +export const isNonEmpty = (self: string): boolean => self.length > 0 + +/** + * Calculate the number of characters in a `string`. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.length('abc'), 3) + * + * @since 1.0.0 + */ +export const length = (self: string): number => self.length + +/** + * @example + * 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('')), ['']) + * + * @since 1.0.0 + */ +export const split: { + (separator: string | RegExp): (self: string) => NonEmptyArray + (self: string, separator: string | RegExp): NonEmptyArray +} = dual(2, (self: string, separator: string | RegExp): NonEmptyArray => { + const out = self.split(separator) + return readonlyArray.isNonEmptyArray(out) ? out : [self] +}) + +/** + * Returns `true` if `searchString` appears as a substring of `self`, at one or more positions that are + * greater than or equal to `0`; otherwise, returns `false`. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.includes("abc", "b"), true) + * assert.deepStrictEqual(S.includes("abc", "d"), false) + * + * @since 1.0.0 + */ +export const includes: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} = dual(2, (self: string, searchString: string): boolean => self.includes(searchString)) + +/** + * Returns `true` if `searchString` appears as a substring of `self`, at one or more positions that are + * greater than or equal to `position`; otherwise, returns `false`. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.includesWithPosition("abc", "b", 1), true) + * assert.deepStrictEqual(S.includesWithPosition("abc", "a", 1), false) + * + * @since 1.0.0 + */ +export const includesWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} = dual( + 3, + (self: string, searchString: string, position: number): boolean => + self.includes(searchString, position) +) + +/** + * Returns `true` if the sequence of elements of `searchString` is the + * same as the corresponding elements of `s` starting at + * position. Otherwise returns false. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.startsWith("abc", "a"), true) + * assert.deepStrictEqual(S.startsWith("bc", "a"), false) + * + * @since 1.0.0 + */ +export const startsWith: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} = dual( + 2, + (self: string, searchString: string): boolean => self.startsWith(searchString) +) + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.startsWithPosition("abc", "b", 1), true) + * assert.deepStrictEqual(S.startsWithPosition("bc", "a", 1), false) + * + * @since 1.0.0 + */ +export const startsWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} = dual( + 3, + (self: string, searchString: string, position: number): boolean => + self.startsWith(searchString, position) +) + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.endsWith("abc", "c"), true) + * assert.deepStrictEqual(S.endsWith("ab", "c"), false) + * + * @since 1.0.0 + */ +export const endsWith: { + (searchString: string): (self: string) => boolean + (self: string, searchString: string): boolean +} = dual(2, (self: string, searchString: string): boolean => self.endsWith(searchString)) + +/** + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.endsWithPosition("abc", "b", 2), true) + * assert.deepStrictEqual(S.endsWithPosition("abc", "c", 2), false) + * + * @since 1.0.0 + */ +export const endsWithPosition: { + (searchString: string, position: number): (self: string) => boolean + (self: string, searchString: string, position: number): boolean +} = dual( + 3, + (self: string, searchString: string, position: number): boolean => + self.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. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.takeLeft("Hello World", 5), "Hello") + * + * @since 1.0.0 + */ +export const takeLeft: { + (n: number): (self: string) => string + (self: string, n: number): string +} = dual(2, (self: string, n: number): 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. + * + * @example + * import * as S from '@fp-ts/core/String' + * + * assert.deepStrictEqual(S.takeRight("Hello World", 5), "World") + * + * @since 1.0.0 + */ +export const takeRight: { + (n: number): (self: string) => string + (self: string, n: number): string +} = dual( + 2, + (self: string, n: number): string => + self.slice(Math.max(0, self.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 + +// /** +// * 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/Struct.ts b/src/Struct.ts new file mode 100644 index 000000000..79255cfa6 --- /dev/null +++ b/src/Struct.ts @@ -0,0 +1,156 @@ +/** + * This module provides utility functions for working with structs in TypeScript. + * + * @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 semigroup from "@fp-ts/core/typeclass/Semigroup" + +/** + * Create a new object by picking properties of an existing object. + * + * @example + * import { pick } from "@fp-ts/core/Struct" + * import { pipe } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, pick("a", "b")), { a: "a", b: 1 }) + * + * @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 + } + +/** + * Create a new object by omitting properties of an existing object. + * + * @example + * import { omit } from "@fp-ts/core/Struct" + * import { pipe } from "@fp-ts/core/Function" + * + * assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, omit("c")), { a: "a", b: 1 }) + * + * @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 + } + +/** + * 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. + * + * Alias of {@link equivalence.struct}. + * + * @example + * import { getEquivalence } from "@fp-ts/core/Struct" + * import * as S from "@fp-ts/core/String" + * import * as N from "@fp-ts/core/Number" + * + * const PersonEquivalence = getEquivalence({ + * name: S.Equivalence, + * age: N.Equivalence + * }) + * + * assert.deepStrictEqual( + * PersonEquivalence({ name: "John", age: 25 }, { name: "John", age: 25 }), + * true + * ) + * assert.deepStrictEqual( + * PersonEquivalence({ name: "John", age: 25 }, { name: "John", age: 40 }), + * false + * ) + * + * @category combinators + * @since 1.0.0 + */ +export const getEquivalence: >>( + predicates: R +) => equivalence.Equivalence< + { readonly [K in keyof R]: [R[K]] extends [equivalence.Equivalence] ? A : never } +> = equivalence.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. + * + * Alias of {@link order.struct}. + * + * @category combinators + * @since 1.0.0 + */ +export const getOrder: }>( + fields: R +) => order.Order<{ [K in keyof R]: [R[K]] extends [order.Order] ? A : never }> = + order.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. + * + * See also {@link getMonoid}. + * + * @example + * import { getSemigroup } from "@fp-ts/core/Struct" + * import * as Semigroup from "@fp-ts/core/typeclass/Semigroup" + * import * as O from "@fp-ts/core/Option" + * + * const PersonSemigroup = getSemigroup({ + * name: Semigroup.last(), + * age: O.getOptionalMonoid(Semigroup.last()) + * }) + * + * assert.deepStrictEqual( + * PersonSemigroup.combine({ name: "John", age: O.none() }, { name: "John", age: O.some(25) }), + * { name: "John", age: O.some(25) } + * ) + * assert.deepStrictEqual( + * PersonSemigroup.combine({ name: "John", age: O.some(25) }, { name: "John", age: O.none() }), + * { name: "John", age: O.some(25) } + * ) + * + * @category combinators + * @since 1.0.0 + */ +export const getSemigroup: }>( + fields: R +) => semigroup.Semigroup< + { [K in keyof R]: [R[K]] extends [semigroup.Semigroup] ? A : never } +> = semigroup.struct + +/** + * This function creates and returns a new `Monoid` for a struct of values based on the given `Monoid`s for each property in the struct. + * The returned `Monoid` combines two structs of the same type by applying the corresponding `Monoid` passed as arguments to each property in the struct. + * + * The `empty` value of the returned `Monoid` is a struct where each property is the `empty` value of the corresponding `Monoid` in the input `monoids` object. + * + * 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. + * + * See also {@link getSemigroup}. + * + * @category combinators + * @since 1.0.0 + */ +export const getMonoid: }>( + fields: R +) => monoid.Monoid<{ [K in keyof R]: [R[K]] extends [monoid.Monoid] ? A : never }> = + monoid.struct diff --git a/src/Symbol.ts b/src/Symbol.ts new file mode 100644 index 000000000..82d3781be --- /dev/null +++ b/src/Symbol.ts @@ -0,0 +1,28 @@ +/** + * @since 1.0.0 + */ + +import * as predicate from "@fp-ts/core/Predicate" +import * as equivalence from "@fp-ts/core/typeclass/Equivalence" + +/** + * Tests if a value is a `symbol`. + * + * @param input - The value to test. + * + * @example + * import { isSymbol } from "@fp-ts/core/Predicate" + * + * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true) + * assert.deepStrictEqual(isSymbol("a"), false) + * + * @category guards + * @since 1.0.0 + */ +export const isSymbol: (u: unknown) => u is symbol = predicate.isSymbol + +/** + * @category instances + * @since 1.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.symbol diff --git a/src/These.ts b/src/These.ts new file mode 100644 index 000000000..e669dbaad --- /dev/null +++ b/src/These.ts @@ -0,0 +1,1652 @@ +/** + * @since 1.0.0 + */ + +import type { Either, Left, Right } from "@fp-ts/core/Either" +import * as E from "@fp-ts/core/Either" +import type { LazyArg } from "@fp-ts/core/Function" +import { constNull, constUndefined, dual, identity } from "@fp-ts/core/Function" +import type { Kind, TypeLambda } from "@fp-ts/core/HKT" +import { proto, structural } from "@fp-ts/core/internal/effect" +import * as N from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/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 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 interface 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 = E.left + +/** + * @category constructors + * @since 1.0.0 + */ +export const right: (right: A) => These = E.right + +/** + * Alias of {@link right}. + * + * @category constructors + * @since 1.0.0 + */ +export const of: (right: A) => These = right + +/** + * @category constructors + * @since 1.0.0 + */ +export const both = (left: E, right: A): These => + Object.setPrototypeOf({ _tag: "Both", left, right }, proto) + +/** + * @category constructors + * @since 1.0.0 + */ +export const leftOrBoth: { + (onSome: LazyArg): (self: Option) => These + (self: Option, onSome: LazyArg): These +} = dual( + 2, + (self: Option, onSome: LazyArg): These => + O.isNone(self) ? left(onSome()) : both(onSome(), self.value) +) + +/** + * @category constructors + * @since 1.0.0 + */ +export const rightOrBoth: { + (onNone: LazyArg): (self: Option) => These + (self: Option, onNone: LazyArg): These +} = dual( + 2, + (self: Option, onNone: LazyArg): These => + O.isNone(self) ? right(onNone()) : both(self.value, onNone()) +) + +/** + * @category constructors + * @since 1.0.0 + */ +export const fail = (e: E): Validated => left([e]) + +/** + * @category constructors + * @since 1.0.0 + */ +export const warn = (e: E, a: A): Validated => both([e], a) + +// ------------------------------------------------------------------------------------- +// equivalence +// ------------------------------------------------------------------------------------- + +/** + * @category equivalence + * @since 1.0.0 + */ +export const getEquivalence = ( + EE: Equivalence, + EA: Equivalence +): Equivalence> => + equivalence.make((x, y) => + isLeft(x) + ? isLeft(y) && EE(x.left, y.left) + : isRight(x) + ? isRight(y) && EA(x.right, y.right) + : isBoth(y) && EE(x.left, y.left) && EA(x.right, y.right) + ) + +/** + * @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 + ( + self: These, + onLeft: (e: E) => B, + onRight: (a: A) => C, + onBoth: (e: E, a: A) => D + ): B | C | D +} = dual(4, ( + self: These, + onLeft: (e: E) => B, + onRight: (a: A) => C, + onBoth: (e: E, a: A) => D +): 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) +) + +// ------------------------------------------------------------------------------------- +// guards +// ------------------------------------------------------------------------------------- + +/** + * Tests if a value is a `These`. + * + * @param input - The value to check. + * + * @category guards + * @since 1.0.0 + */ +export const isThese = (input: unknown): input is These => + typeof input === "object" && input != null && structural in input && "_tag" in input && + (input["_tag"] === "Left" || input["_tag"] === "Right" || input["_tag"] === "Both") + +/** + * Determine if a `These` is a `Left`. + * + * @param self - The `These` to check. + * + * @example + * import { isLeft, left, right, both } from '@fp-ts/core/These' + * + * assert.deepStrictEqual(isLeft(right(1)), false) + * assert.deepStrictEqual(isLeft(left("error")), true) + * assert.deepStrictEqual(isLeft(both("error", 1)), false) + * + * @category guards + * @since 1.0.0 + */ +export const isLeft = (self: These): self is Left => self._tag === "Left" + +/** + * Determine if a `These` is a `Left` or a `Both`. + * + * @param self - The `These` to check. + * + * @example + * import { isLeftOrBoth, left, right, both } from '@fp-ts/core/These' + * + * assert.deepStrictEqual(isLeftOrBoth(right(1)), false) + * assert.deepStrictEqual(isLeftOrBoth(left("error")), true) + * assert.deepStrictEqual(isLeftOrBoth(both("error", 1)), true) + * + * @category guards + * @since 1.0.0 + */ +export const isLeftOrBoth = (self: These): self is Left | Both => + self._tag !== "Right" + +/** + * Determine if a `These` is a `Right`. + * + * @param self - The `These` to check. + * + * @example + * import { isRight, left, right, both } from '@fp-ts/core/These' + * + * assert.deepStrictEqual(isRight(right(1)), true) + * assert.deepStrictEqual(isRight(left("error")), false) + * assert.deepStrictEqual(isRight(both("error", 1)), false) + * + * @category guards + * @since 1.0.0 + */ +export const isRight = (self: These): self is Right => self._tag === "Right" + +/** + * Determine if a `These` is a `Right` or a `Both`. + * + * @param self - The `These` to check. + * + * @example + * import { isRightOrBoth, left, right, both } from '@fp-ts/core/These' + * + * assert.deepStrictEqual(isRightOrBoth(right(1)), true) + * assert.deepStrictEqual(isRightOrBoth(left("error")), false) + * assert.deepStrictEqual(isRightOrBoth(both("error", 1)), true) + * + * @category guards + * @since 1.0.0 + */ +export const isRightOrBoth = (self: These): self is Right | Both => + self._tag !== "Left" + +/** + * Determine if a `These` is a `Both`. + * + * @param self - The `These` to check. + * + * @example + * import { isBoth, left, right, both } from '@fp-ts/core/These' + * + * assert.deepStrictEqual(isBoth(right(1)), false) + * assert.deepStrictEqual(isBoth(left("error")), false) + * assert.deepStrictEqual(isBoth(both("error", 1)), true) + * + * @category guards + * @since 1.0.0 + */ +export const isBoth = (self: These): self is Both => self._tag === "Both" + +/** + * 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) = E.liftThrowable + +/** + * Extracts the value of a `These` or throws if the `These` is `Left`. + * + * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + * + * @param self - The `These` to extract the value from. + * @param onLeft - A function that will be called if the `These` is `Left`. It returns the error to be thrown. + * + * @example + * import * as E from "@fp-ts/core/These" + * + * assert.deepStrictEqual( + * E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), + * 1 + * ) + * assert.deepStrictEqual( + * E.getOrThrowWith(E.both("warning", 1), () => new Error('Unexpected Left')), + * 1 + * ) + * assert.throws(() => E.getOrThrowWith(E.left("error"), () => new Error('Unexpected Left'))) + * + * @category interop + * @since 1.0.0 + */ +export const getOrThrowWith: { + (onLeft: (e: E) => unknown): (self: These) => A + (self: These, onLeft: (e: E) => unknown): A +} = dual(2, (self: These, onLeft: (e: E) => unknown): A => { + if (isRightOrBoth(self)) { + return self.right + } + throw onLeft(self.left) +}) + +/** + * Extracts the value of a `These` or throws if the `These` is `Left`. + * + * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. + * + * @param self - The `These` to extract the value from. + * @throws `Error("getOrThrow called on a Left")` + * + * @example + * import * as T from "@fp-ts/core/These" + * + * assert.deepStrictEqual(T.getOrThrow(T.right(1)), 1) + * assert.deepStrictEqual(T.getOrThrow(T.both("warning", 1)), 1) + * assert.throws(() => T.getOrThrow(T.left("error"))) + * + * @category interop + * @since 1.0.0 + */ +export const getOrThrow: (self: These) => A = getOrThrowWith(() => + new Error("getOrThrow called on a Left") +) + +/** + * Extracts the value of a `These` or throws if the `These` is `Left`. + * + * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. + * + * @param self - The `These` to extract the value from. + * @param onLeft - A function that will be called if the `These` is `Left`. It returns the error to be thrown. + * + * @example + * import * as E from "@fp-ts/core/These" + * + * assert.deepStrictEqual( + * E.getRightOnlyOrThrowWith( + * E.right(1), + * () => new Error("Unexpected Left or Both") + * ), + * 1 + * ) + * assert.throws(() => E.getRightOnlyOrThrowWith(E.both("warning", 1), () => new Error("Unexpected Left or Both"))) + * assert.throws(() => E.getRightOnlyOrThrowWith(E.left("error"), () => new Error("Unexpected Left or Both"))) + * + * @category interop + * @since 1.0.0 + */ +export const getRightOnlyOrThrowWith: { + (onLeftOrBoth: (e: E) => unknown): (self: These) => A + (self: These, onLeftOrBoth: (e: E) => unknown): A +} = dual(2, (self: These, onLeftOrBoth: (e: E) => unknown): A => { + if (isRight(self)) { + return self.right + } + throw onLeftOrBoth(self.left) +}) + +/** + * Extracts the value of a `These` or throws if the `These` is not a `Right`. + * + * The thrown error is a default error. To configure the error thrown, see {@link getRightOnlyOrThrowWith}. + * + * @param self - The `These` to extract the value from. + * @throws `Error("getOrThrow called on a Left")` + * + * @example + * import * as T from "@fp-ts/core/These" + * + * assert.deepStrictEqual(T.getRightOnlyOrThrow(T.right(1)), 1) + * assert.throws(() => T.getRightOnlyOrThrow(T.both("error", 1))) + * assert.throws(() => T.getRightOnlyOrThrow(T.left("error"))) + * + * @category interop + * @since 1.0.0 + */ +export const getRightOnlyOrThrow: (self: These) => A = getRightOnlyOrThrowWith(() => + new Error("getRightOnlyOrThrow called on a Left or a Both") +) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromNullable: { + (onNullable: LazyArg): (a: A) => These> + (a: A, onNullable: LazyArg): These> +} = dual( + 2, + (a: A, onNullable: LazyArg): These> => + a == null ? left(onNullable()) : right(a as NonNullable) +) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromEither = (self: Either): Validated => + E.isLeft(self) ? left([self.left]) : self + +/** + * @category conversions + * @since 1.0.0 + */ +export const toEither: { + (onBoth: (e: E, a: A) => Either): (self: These) => Either + (self: These, onBoth: (e: E, a: A) => Either): Either +} = dual( + 2, + (self: These, onBoth: (e: E, a: A) => Either): Either => + isBoth(self) ? onBoth(self.left, self.right) : self +) + +/** + * @category conversions + * @since 1.0.0 + */ +export const absolve: (self: These) => Either = toEither(( + _, + a +) => E.right(a)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const condemn: (self: These) => Either = toEither(( + e, + _ +) => E.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 combining + * @since 1.0.0 + */ +export const flatMapNullable: { + ( + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 + ): (self: Validated) => Validated> + ( + self: Validated, + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 + ): Validated> +} = dual(3, ( + self: Validated, + f: (a: A) => B | null | undefined, + onNullable: (a: A) => E2 +): Validated> => flatMap(self, liftNullable(f, (a) => [onNullable(a)]))) + +/** + * @category lifting + * @since 1.0.0 + */ +export const liftPredicate: { + ( + refinement: Refinement, + onFalse: (c: C) => E + ): (c: C) => These + (predicate: Predicate, onFalse: (b: B) => E): (b: B) => These +} = (predicate: Predicate, onFalse: (b: B) => E) => + (b: B) => predicate(b) ? right(b) : left(onFalse(b)) + +/** + * @category conversions + * @since 1.0.0 + */ +export const fromIterable: { + (onEmpty: LazyArg): (collection: Iterable) => These + (collection: Iterable, onEmpty: LazyArg): These +} = dual(2, (collection: Iterable, onEmpty: LazyArg): 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 + (self: Option, onNone: LazyArg): These +} = dual( + 2, + (self: Option, onNone: LazyArg): These => + O.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 => toValidated(f(...a)) + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMapOption: { + ( + f: (a: A) => Option, + onNone: (a: A) => E2 + ): (self: Validated) => Validated + ( + self: Validated, + f: (a: A) => Option, + onNone: (a: A) => E2 + ): Validated +} = dual(3, ( + self: Validated, + f: (a: A) => Option, + onNone: (a: A) => E2 +): Validated => flatMap(self, liftOption(f, (a) => [onNone(a)]))) + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMapEither: { + (f: (a: A) => Either): (self: Validated) => Validated + (self: Validated, f: (a: A) => Either): Validated +} = dual( + 2, + (self: Validated, f: (a: A) => Either): Validated => + flatMap(self, liftEither(f)) +) + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMapThese: { + (f: (a: A) => These): (self: Validated) => Validated + (self: Validated, f: (a: A) => These): Validated +} = dual( + 2, + (self: Validated, f: (a: A) => These): Validated => + flatMap(self, liftThese(f)) +) + +/** + * Converts a `These` to an `Option` discarding the error (`Both` included). + * + * @category getters + * @since 1.0.0 + */ +export const getRight = ( + self: These +): Option => isLeft(self) ? O.none() : O.some(self.right) + +/** + * Returns the value if and only if the value is a `Right` (i.e. `Both` is excluded). + * + * @category getters + * @since 1.0.0 + */ +export const getRightOnly = ( + self: These +): Option => isRight(self) ? O.some(self.right) : O.none() + +/** + * Converts a `These` to an `Option` discarding the value (`Both` included). + * + * @category getters + * @since 1.0.0 + */ +export const getLeft = ( + self: These +): Option => isRight(self) ? O.none() : O.some(self.left) + +/** + * Returns the error if and only if the value is a `Left` (i.e. `Both` is excluded). + * + * @category getters + * @since 1.0.0 + */ +export const getLeftOnly = ( + self: These +): Option => isLeft(self) ? O.some(self.left) : O.none() + +/** + * @category getters + * @since 1.0.0 + */ +export const getBoth = ( + self: These +): Option => isBoth(self) ? O.some([self.left, self.right]) : O.none() + +/** + * @category getters + * @since 1.0.0 + */ +export const getBothOrElse: { + (e: LazyArg, a: LazyArg): (self: These) => [E, A] + (self: These, e: LazyArg, a: LazyArg): [E, A] +} = dual(3, (self: These, e: LazyArg, a: LazyArg): [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 + (self: These, onLeft: LazyArg): A | B +} = dual( + 2, + (self: These, onLeft: LazyArg): 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 + (self: These, onRight: (a: A) => void): These +} = dual(2, (self: These, onRight: (a: A) => void): 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 + (self: These, onRightOrBoth: (a: A) => void): These +} = dual(2, (self: These, onRightOrBoth: (a: A) => void): 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 + (self: These, onLeft: (e: E) => void): These +} = dual(2, (self: These, onLeft: (e: E) => void): 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 + (self: These, onBoth: (e: E, a: A) => void): These +} = dual(2, (self: These, onBoth: (e: E, a: A) => void): These => { + if (isBoth(self)) { + onBoth(self.left, self.right) + } + return self +}) + +/** + * @category mapping + * @since 1.0.0 + */ +export const bimap: { + (f: (e: E1) => E2, g: (a: A) => B): (self: These) => These + (self: These, f: (e: E1) => E2, g: (a: A) => B): These +} = dual( + 3, + (self: These, f: (e: E1) => E2, g: (a: A) => B): These => + isLeft(self) ? + left(f(self.left)) : + isRight(self) ? + right(g(self.right)) : + both(f(self.left), g(self.right)) +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bicovariant: bicovariant.Bicovariant = { + bimap +} + +/** + * Maps the `Left` side of an `These` value to a new `These` value. + * + * @param self - The input `These` value to map. + * @param f - A transformation function to apply to the `Left` value of the input `These`. + * + * @category error handling + * @since 1.0.0 + */ +export const mapLeft: { + (f: (e: E) => G): (self: These) => These + (self: These, f: (e: E) => G): These +} = bicovariant.mapLeft(Bicovariant) + +/** + * @category conversions + * @since 1.0.0 + */ +export const toValidated: (self: These) => Validated = mapLeft((e) => [e]) + +/** + * Maps the `Right` side of an `These` value to a new `These` value. + * + * @param self - An `These` to map + * @param f - The function to map over the value of the `These` + * + * @category mapping + * @since 1.0.0 + */ +export const map: { + (self: These, f: (a: A) => B): These + (f: (a: A) => B): (self: These) => These +} = bicovariant.map(Bicovariant) + +const imap = covariant.imap(map) + +/** + * @category instances + * @since 1.0.0 + */ +export const Covariant: covariant.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: These) => These = invariant.tupled( + Invariant +) + +/** + * @category mapping + * @since 1.0.0 + */ +export const flap: { + (a: A, self: These B>): These + (self: These B>): (a: A) => 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: { + (self: These, b: B): These + (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) + +/** + * @category instances + * @since 1.0.0 + */ +export const Of: of_.Of = { + of +} + +/** + * @since 1.0.0 + */ +export const unit: These = of_.unit(Of) + +/** + * @category instances + * @since 1.0.0 + */ +export const Pointed: pointed.Pointed = { + of, + imap, + map +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverse = ( + F: applicative.Applicative +): { + ( + f: (a: A) => Kind + ): (self: These) => Kind> + ( + self: These, + f: (a: A) => Kind + ): Kind> +} => + dual(2, ( + self: These, + f: (a: A) => Kind + ): Kind> => + isLeft(self) + ? F.of>(self) + : isRight(self) + ? F.map>(f(self.right), right) + : F.map(f(self.right), (b) => both(self.left, b))) + +/** + * @category instances + * @since 1.0.0 + */ +export const Traversable: traversable.Traversable = { + traverse +} + +/** + * @category traversing + * @since 1.0.0 + */ +export const sequence: ( + F: applicative.Applicative +) => ( + self: These> +) => Kind> = traversable.sequence(Traversable) + +/** + * @category traversing + * @since 1.0.0 + */ +export const traverseTap: ( + F: applicative.Applicative +) => { + ( + self: These, + f: (a: A) => Kind + ): Kind> + ( + 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 = (isEquivalent: (self: A, that: A) => boolean): { + (a: A): (self: These) => boolean + (self: These, a: A): boolean +} => + dual( + 2, + (self: These, a: A): boolean => isLeft(self) ? false : isEquivalent(self.right, a) + ) + +/** + * @category predicates + * @since 1.0.0 + */ +export const exists: { + (predicate: Predicate): (self: These) => boolean + (self: These, predicate: Predicate): boolean +} = dual( + 2, + (self: These, predicate: Predicate): boolean => + isLeft(self) ? false : predicate(self.right) +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Foldable: foldable.Foldable = { + reduce: dual( + 3, + (self: These, b: B, f: (b: B, a: A) => B): B => + isLeft(self) ? b : f(b, self.right) + ) +} + +/** + * 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: (e1: E1) => These): (self: These) => These + (self: These, that: (e1: E1) => These): These +} = dual( + 2, + (self: These, that: (e1: E1) => These): These => + isLeft(self) ? that(self.left) : 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: (e1: E1) => These + ): (self: These) => These> + ( + self: These, + that: (e1: E1) => These + ): These> +} = dual(2, ( + self: These, + that: (e1: E1) => These +): These> => + isLeft(self) ? + map(that(self.left), E.right) : + map(self, E.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 + (self: These, onLeft: LazyArg): These +} = dual( + 2, + (self: These, onLeft: LazyArg): These => + orElse(self, () => left(onLeft())) +) + +const coproduct = (self: These, that: These): These => + isRightOrBoth(self) ? self : that + +const coproductMany = (self: These, collection: Iterable>): These => { + let out = self + if (isRightOrBoth(out)) { + return out + } + for (out of collection) { + if (isRightOrBoth(out)) { + return out + } + } + return out +} + +/** + * @category error handling + * @since 1.0.0 + */ +export const firstRightOrBothOf: { + (collection: Iterable>): (self: These) => These + (self: These, collection: Iterable>): These +} = dual(2, coproductMany) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiCoproduct: semiCoproduct.SemiCoproduct = { + imap, + coproduct, + coproductMany +} + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstRightOrBothSemigroup: () => Semigroup> = semiCoproduct + .getSemigroup(SemiCoproduct) + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiAlternative: semiAlternative.SemiAlternative = { + map, + imap, + coproduct, + coproductMany: firstRightOrBothOf +} + +/** + * @category filtering + * @since 1.0.0 + */ +export const filter: { + (refinement: Refinement, onFalse: LazyArg): ( + self: These + ) => These + ( + predicate: Predicate, + onFalse: LazyArg + ): (self: These) => These + ( + self: These, + refinement: Refinement, + onFalse: LazyArg + ): These + ( + self: These, + predicate: Predicate, + onFalse: LazyArg + ): These +} = dual(3, ( + self: These, + predicate: Predicate, + onFalse: LazyArg +): 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 + ( + self: These, + f: (a: A) => Option, + onNone: LazyArg + ): These +} = dual(3, ( + self: These, + f: (a: A) => Option, + onNone: LazyArg +): These => { + if (isLeft(self)) { + return self + } + if (isRight(self)) { + const ob = f(self.right) + return O.isNone(ob) ? left(onNone()) : right(ob.value) + } + const ob = f(self.right) + return O.isNone(ob) ? left(onNone()) : both(self.left, ob.value) +}) + +/** + * @category filtering + * @since 1.0.0 + */ +export const compact: { + (onNone: LazyArg): (self: These>) => These + (self: These>, onNone: LazyArg): These +} = dual( + 2, + (self: These>, onNone: LazyArg): These => + filterMap(self, identity, onNone) +) + +const product = ( + self: Validated, + that: 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(self.left, that.left)) + } + if (isRight(that)) { + return both(self.left, [self.right, that.right]) + } + return both(RA.appendAllNonEmpty(self.left, that.left), [self.right, that.right]) +} + +/** + * Similar to `Promise.all` but operates on `These`s. + * + * ``` + * Iterable> -> These + * ``` + * + * @category combining + * @since 1.0.0 + */ +const all = ( + 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) +} + +const productMany = ( + self: Validated, + collection: Iterable> +): Validated]> => map(product(self, all(collection)), ([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 = { + imap, + map, + product, + productMany +} + +/** + * Lifts a binary function into `These`. + * + * @param f - The function to lift. + * + * @category lifting + * @since 1.0.0 + */ +export const lift2: (f: (a: A, b: B) => C) => { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = semiApplicative.lift2(SemiApplicative) + +/** + * @category combining + * @since 1.0.0 + */ +export const zipWith: { + ( + self: Validated, + that: Validated, + f: (a: A, b: B) => C + ): Validated + ( + that: Validated, + f: (a: A, b: B) => C + ): (self: Validated) => Validated +} = semiApplicative.zipWith(SemiApplicative) + +/** + * @since 1.0.0 + */ +export const ap: { + (self: Validated B>, that: Validated): Validated + ( + that: Validated + ): (self: Validated B>) => Validated +} = semiApplicative.ap(SemiApplicative) + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> = + semiApplicative.getSemigroup(SemiApplicative) + +/** + * Appends an element to the end of a tuple. + * + * @since 1.0.0 + */ +export const appendElement: { + , E2, B>( + self: Validated, + that: Validated + ): Validated + ( + that: Validated + ): >(self: Validated) => Validated +} = semiProduct.appendElement(SemiProduct) + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll: all +} + +/** + * Similar to `Promise.all` but operates on `These`s. + * + * ``` + * [These, These, ...] -> These + * ``` + * + * @since 1.0.0 + */ +export const tuple: >>(...tuple: T) => Validated< + [T[number]] extends [Validated] ? E : never, + { [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, + { [K in keyof R]: [R[K]] extends [Validated] ? A : never } +> = product_ + .struct(Product) + +/** + * @category combining + * @since 1.0.0 + */ +export const flatMap: { + (f: (a: A) => Validated): (self: Validated) => Validated + (self: Validated, f: (a: A) => Validated): Validated +} = dual( + 2, + (self: Validated, f: (a: A) => 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 = { + imap, + of, + map, + product, + productMany, + productAll: all +} + +/** + * @category combining + * @since 1.0.0 + */ +export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative + .getMonoid( + 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: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = flatMap_.andThen(FlatMap) + +/** + * @since 1.0.0 + */ +export const composeKleisliArrow: { + ( + afb: (a: A) => Validated, + bfc: (b: B) => Validated + ): (a: A) => Validated + ( + 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 +} + +/** + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. + * + * @category combining + * @since 1.0.0 + */ +export const andThenDiscard: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = chainable.andThenDiscard(Chainable) + +/** + * Returns an effect that effectfully "peeks" at the success of this effect. + * + * @category combinators + * @since 1.0.0 + */ +export const tap: { + (self: Validated, f: (a: A) => Validated): Validated + (f: (a: A) => Validated): (self: Validated) => Validated +} = chainable.tap(Chainable) + +/** + * @category instances + * @since 1.0.0 + */ +export const Monad: monad.Monad = { + imap, + of, + map, + flatMap +} + +// ------------------------------------------------------------------------------------- +// math +// ------------------------------------------------------------------------------------- + +/** + * @category math + * @since 1.0.0 + */ +export const sum: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = lift2(N.sum) + +/** + * @category math + * @since 1.0.0 + */ +export const multiply: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = lift2(N.multiply) + +/** + * @category math + * @since 1.0.0 + */ +export const subtract: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = lift2(N.subtract) + +/** + * @category math + * @since 1.0.0 + */ +export const divide: { + (self: Validated, that: Validated): Validated + (that: Validated): (self: Validated) => Validated +} = lift2(N.divide) + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindTo: { + (name: N): (self: These) => These + (self: These, name: N): These +} = invariant.bindTo(Invariant) + +const let_: { + ( + name: Exclude, + f: (a: A) => B + ): (self: These) => These + ( + self: These, + name: Exclude, + f: (a: A) => B + ): These +} = covariant.let(Covariant) + +export { + /** + * @category do notation + * @since 1.0.0 + */ + let_ as let +} + +/** + * @category do notation + * @since 1.0.0 + */ +export const Do: These = of_.Do(Of) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bind: { + ( + name: Exclude, + f: (a: A) => Validated + ): ( + self: Validated + ) => Validated + ( + self: Validated, + name: Exclude, + f: (a: A) => Validated + ): Validated +} = chainable.bind(Chainable) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindEither: { + ( + name: Exclude, + f: (a: A) => Either + ): ( + self: Validated + ) => Validated + ( + self: Validated, + name: Exclude, + f: (a: A) => Either + ): Validated +} = dual(3, ( + self: Validated, + name: Exclude, + f: (a: A) => Either +): Validated => + bind(self, name, (a) => fromEither(f(a)))) + +/** + * @category do notation + * @since 1.0.0 + */ +export const bindThese: { + ( + name: Exclude, + f: (a: A) => These + ): ( + self: Validated + ) => Validated + ( + self: Validated, + name: Exclude, + f: (a: A) => These + ): Validated +} = dual(3, ( + self: Validated, + name: Exclude, + f: (a: A) => These +): Validated => + bind(self, name, (a) => toValidated(f(a)))) + +/** + * @category do notation + * @since 1.0.0 + */ +export const andThenBind: { + ( + name: Exclude, + that: Validated + ): ( + self: Validated + ) => Validated + ( + self: Validated, + name: Exclude, + that: Validated + ): Validated +} = semiProduct.andThenBind(SemiProduct) diff --git a/src/Tuple.ts b/src/Tuple.ts new file mode 100644 index 000000000..429272fcc --- /dev/null +++ b/src/Tuple.ts @@ -0,0 +1,231 @@ +/** + * This module provides utility functions for working with tuples in TypeScript. + * + * @since 1.0.0 + */ +import { dual } from "@fp-ts/core/Function" +import type { TypeLambda } from "@fp-ts/core/HKT" +import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" +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 type lambdas + * @since 1.0.0 + */ +export interface TupleTypeLambda extends TypeLambda { + readonly type: [this["Out1"], this["Target"]] +} + +/** + * Constructs a new tuple from the provided values. + * + * @param elements - The list of elements to create the tuple from. + * + * @example + * import { tuple } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual(tuple(1, 'hello', true), [1, 'hello', true]) + * + * @category constructors + * @since 1.0.0 + */ +export const tuple = >(...elements: A): A => elements + +/** + * Return the first element of a tuple. + * + * @param self - A tuple of length `2`. + * + * @example + * import { getFirst } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual(getFirst(["hello", 42]), "hello") + * + * @category getters + * @since 1.0.0 + */ +export const getFirst = (self: readonly [L, R]): L => self[0] + +/** + * Return the second element of a tuple. + * + * @param self - A tuple of length `2`. + * + * @example + * import { getSecond } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual(getSecond(["hello", 42]), 42) + * + * @category getters + * @since 1.0.0 + */ +export const getSecond = (self: readonly [L, R]): R => self[1] + +/** + * Transforms both elements of a tuple using the given functions. + * + * @param self - A tuple of length `2`. + * @param f - The function to transform the first element of the tuple. + * @param g - The function to transform the second element of the tuple. + * + * @example + * import { bimap } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual( + * bimap(["hello", 42], s => s.toUpperCase(), n => n.toString()), + * ["HELLO", "42"] + * ) + * + * @category mapping + * @since 1.0.0 + */ +export const bimap: { + (f: (e: L1) => L2, g: (a: R1) => R2): (self: readonly [L1, R1]) => [L2, R2] + (self: readonly [L1, R1], f: (e: L1) => L2, g: (a: R1) => R2): [L2, R2] +} = dual( + 3, + ( + self: readonly [L1, R1], + f: (e: L1) => L2, + g: (a: R1) => R2 + ): [L2, R2] => [f(self[0]), g(self[1])] +) + +/** + * @category instances + * @since 1.0.0 + */ +export const Bicovariant: bicovariant.Bicovariant = { + bimap +} + +/** + * Transforms the first component of a tuple using a given function. + * + * @param self - A tuple of length `2`. + * @param f - The function to transform the first element of the tuple. + * + * @example + * import { mapFirst } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual( + * mapFirst(["hello", 42], s => s.toUpperCase()), + * ["HELLO", 42] + * ) + * + * @category mapping + * @since 1.0.0 + */ +export const mapFirst: { + (f: (left: L1) => L2): (self: readonly [L1, R]) => [L2, R] + (self: readonly [L1, R], f: (left: L1) => L2): [L2, R] +} = bicovariant.mapLeft(Bicovariant) as any + +/** + * Transforms the second component of a tuple using a given function. + * + * @param self - A tuple of length `2`. + * @param f - The function to transform the second element of the tuple. + * + * @example + * import { mapSecond } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual( + * mapSecond(["hello", 42], n => n.toString()), + * ["hello", "42"] + * ) + * + * @category mapping + * @since 1.0.0 + */ +export const mapSecond: { + (f: (right: R1) => R2): (self: readonly [L, R1]) => [L, R2] + (self: readonly [L, R1], f: (right: R1) => R2): [L, R2] +} = bicovariant.map(Bicovariant) as any + +/** + * Swaps the two elements of a tuple. + * + * @param self - A tuple of length `2`. + * + * @example + * import { swap } from "@fp-ts/core/Tuple" + * + * assert.deepStrictEqual(swap(["hello", 42]), [42, "hello"]) + * + * @since 1.0.0 + */ +export const swap = (self: readonly [L, R]): [R, L] => [self[1], self[0]] + +/** + * 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 combinators + * @since 1.0.0 + */ +export const getEquivalence: >>( + ...predicates: T +) => equivalence.Equivalence< + Readonly<{ [I in keyof T]: [T[I]] extends [equivalence.Equivalence] ? A : never }> +> = equivalence.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 getOrder: >>( + ...elements: T +) => order.Order<{ [I in keyof T]: [T[I]] extends [order.Order] ? A : never }> = + order.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. + * + * @category combinators + * @since 1.0.0 + */ +export const getSemigroup = semigroup.tuple + +/** + * This function creates and returns a new `Monoid` for a tuple of values based on the given `Monoid`s for each element in the tuple. + * The returned `Monoid` combines two tuples of the same type by applying the corresponding `Monoid` passed as arguments to each element in the tuple. + * + * The `empty` value of the returned `Monoid` is the tuple of `empty` values of the input `Monoid`s. + * + * 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 getMonoid = monoid.tuple + +/** + * Appends an element to the end of a tuple. + * + * @since 1.0.0 + */ +export const appendElement: { + (that: B): >(self: A) => [...A, B] + , B>(self: A, that: B): [...A, B] +} = dual(2, , B>(self: A, that: B): [...A, B] => [...self, that]) + +/* + + TODO: + + - at + - swap + +*/ diff --git a/src/index.ts b/src/index.ts index cd437d1bb..f03ebc373 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,27 @@ 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" +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" +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" +import * as tuple from "@fp-ts/core/Tuple" + // ------------------------------------------------------------------------------------- // typeclasses // ------------------------------------------------------------------------------------- @@ -20,12 +41,13 @@ 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 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 * 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" @@ -36,8 +58,13 @@ 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 { + /** + * @since 1.0.0 + */ + _function as function, /** * @category typeclass * @since 1.0.0 @@ -53,6 +80,14 @@ export { * @since 1.0.0 */ bicovariant, + /** + * @since 1.0.0 + */ + bigint, + /** + * @since 1.0.0 + */ + boolean, /** * @category typeclass * @since 1.0.0 @@ -78,6 +113,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 @@ -92,6 +141,10 @@ export { * @since 1.0.0 */ hkt, + /** + * @since 1.0.0 + */ + identity, /** * @category typeclass * @since 1.0.0 @@ -108,30 +161,49 @@ export { */ monoid, /** - * @category typeclass * @since 1.0.0 */ - nonEmptyTraversable, + number, /** * @category typeclass * @since 1.0.0 */ of, + /** + * @since 1.0.0 + */ + option, /** * @category typeclass * @since 1.0.0 */ order, + /** + * @since 1.0.0 + */ + ordering, /** * @category typeclass * @since 1.0.0 */ pointed, + /** + * @since 1.0.0 + */ + predicate, /** * @category typeclass * @since 1.0.0 */ product, + /** + * @since 1.0.0 + */ + readonlyArray, + /** + * @since 1.0.0 + */ + readonlyRecord, /** * @category typeclass * @since 1.0.0 @@ -157,9 +229,34 @@ export { * @since 1.0.0 */ semiProduct, + /** + * @since 1.0.0 + */ + string, + /** + * @since 1.0.0 + */ + struct, + /** + * @since 1.0.0 + */ + symbol, + /** + * @since 1.0.0 + */ + these, /** * @category typeclass * @since 1.0.0 */ - traversable + traversable, + /** + * @category typeclass + * @since 1.0.0 + */ + traversableFilterable, + /** + * @since 1.0.0 + */ + tuple } diff --git a/src/internal/Either.ts b/src/internal/Either.ts new file mode 100644 index 000000000..94335436a --- /dev/null +++ b/src/internal/Either.ts @@ -0,0 +1,40 @@ +/** + * @since 1.0.0 + */ + +import type { Either, Left, Right } from "@fp-ts/core/Either" +import { dual } from "@fp-ts/core/Function" +import { proto } 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 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 => + Object.setPrototypeOf({ _tag: "Left", left: e }, proto) + +/** @internal */ +export const right = (a: A): Either => + Object.setPrototypeOf({ _tag: "Right", right: a }, proto) + +/** @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 = dual( + 2, + (self: Option, onNone: () => E): Either => + option.isNone(self) ? left(onNone()) : right(self.value) +) 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/internal/Option.ts b/src/internal/Option.ts new file mode 100644 index 000000000..f444fb8d6 --- /dev/null +++ b/src/internal/Option.ts @@ -0,0 +1,18 @@ +/** + * @since 1.0.0 + */ + +import { proto } from "@fp-ts/core/internal/effect" +import type { None, Option, Some } from "@fp-ts/core/Option" + +/** @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 = Object.setPrototypeOf({ _tag: "None" }, proto) + +/** @internal */ +export const some = (a: A): Option => Object.setPrototypeOf({ _tag: "Some", value: a }, proto) diff --git a/src/internal/ReadonlyArray.ts b/src/internal/ReadonlyArray.ts new file mode 100644 index 000000000..40acb91f4 --- /dev/null +++ b/src/internal/ReadonlyArray.ts @@ -0,0 +1,13 @@ +/** + * @since 1.0.0 + */ + +import type { NonEmptyArray } from "@fp-ts/core/ReadonlyArray" + +/** @internal */ +export const isNonEmptyArray = (self: ReadonlyArray): self is NonEmptyArray => + self.length > 0 + +/** @internal */ +export const fromIterable = (collection: Iterable): Array => + Array.isArray(collection) ? collection : Array.from(collection) diff --git a/src/internal/effect.ts b/src/internal/effect.ts new file mode 100644 index 000000000..dab15a880 --- /dev/null +++ b/src/internal/effect.ts @@ -0,0 +1,9 @@ +/** + * @since 1.0.0 + */ + +/** @internal */ +export const structural = Symbol.for("@effect/data/Equal/structural") + +/** @internal */ +export const proto = Object.setPrototypeOf({ [structural]: true }, Object.prototype) diff --git a/src/typeclass/Applicative.ts b/src/typeclass/Applicative.ts index 626835845..0c370b53f 100644 --- a/src/typeclass/Applicative.ts +++ b/src/typeclass/Applicative.ts @@ -15,13 +15,19 @@ import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" export interface Applicative extends SemiApplicative, Product {} /** - * Lift a monoid into 'F', the inner values are combined using the provided `Monoid`. + * Lift a `Monoid` into `F`, combining the inner values using the provided `Monoid`: + * + * - `combine` is provided by {@link semiApplicative.getSemigroup}. + * - `empty` is `F.of(M.empty)` + * + * @param F - The `Applicative` instance for `F`. + * @param M - The `Monoid` instance for `A`. * * @since 1.0.0 */ -export const liftMonoid = (F: Applicative) => +export const getMonoid = (F: Applicative) => (M: Monoid): Monoid> => monoid.fromSemigroup( - semiApplicative.liftSemigroup(F)(M), + semiApplicative.getSemigroup(F)(M), F.of(M.empty) ) diff --git a/src/typeclass/Bicovariant.ts b/src/typeclass/Bicovariant.ts index 7de7e2576..1f1a67c20 100644 --- a/src/typeclass/Bicovariant.ts +++ b/src/typeclass/Bicovariant.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { dual, 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" /** @@ -10,14 +10,21 @@ import type { Covariant } from "@fp-ts/core/typeclass/Covariant" * @since 1.0.0 */ export interface Bicovariant extends TypeClass { - readonly bimap: ( - f: (e: E1) => E2, - g: (a: A) => B - ) => (self: Kind) => Kind + readonly bimap: { + ( + f: (e: E1) => E2, + g: (a: A) => B + ): (self: Kind) => Kind + ( + self: Kind, + f: (e: E1) => E2, + g: (a: A) => B + ): Kind + } } /** - * Returns a default `bimap` composition. + * Returns a default ternary `bimap` composition. * * @since 1.0.0 */ @@ -25,30 +32,39 @@ export const bimapComposition = ( CovariantF: Covariant, BicovariantG: Bicovariant ) => - ( + ( + self: Kind>, f: (e: E1) => E2, g: (a: A) => B - ): (( - self: Kind> - ) => Kind>) => CovariantF.map(BicovariantG.bimap(f, g)) + ): Kind> => CovariantF.map(self, BicovariantG.bimap(f, g)) /** + * Returns a default `mapLeft` implementation. + * * @since 1.0.0 */ export const mapLeft = ( F: Bicovariant -): (( - f: (e: E1) => E2 -) => (self: Kind) => Kind) => - (f: (e: E) => G): ((self: Kind) => Kind) => - F.bimap(f, identity) +): { + (f: (e: E) => G): (self: Kind) => Kind + (self: Kind, f: (e: E) => G): Kind +} => + dual( + 2, + (self: Kind, f: (e: E) => G): Kind => + F.bimap(self, f, identity) + ) /** * Returns a default `map` implementation. * * @since 1.0.0 */ -export const map = (F: Bicovariant): Covariant["map"] => - ( - f: (a: A) => B - ): ((self: Kind) => Kind) => F.bimap(identity, f) +export const map = ( + F: Bicovariant +): Covariant["map"] => + dual( + 2, + (self: Kind, f: (a: A) => B): Kind => + F.bimap(self, identity, f) + ) diff --git a/src/typeclass/Bounded.ts b/src/typeclass/Bounded.ts index 2459fa434..d0a7f7d17 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,34 @@ 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 + */ +export const number: Bounded = { + compare: order.number.compare, + maxBound: Infinity, + minBound: -Infinity +} + /** * Clamp a value between `minBound` and `maxBound` values. * @@ -36,6 +67,6 @@ export const clamp = (B: Bounded): (a: A) => A => order.clamp(B)(B.minBoun */ export const reverse = (B: Bounded): Bounded => ({ ...order.reverse(B), - minBound: B.minBound, - maxBound: B.maxBound + minBound: B.maxBound, + maxBound: B.minBound }) diff --git a/src/typeclass/Chainable.ts b/src/typeclass/Chainable.ts index ff67fcdc6..a10bbf3da 100644 --- a/src/typeclass/Chainable.ts +++ b/src/typeclass/Chainable.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { dual } 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" @@ -13,54 +13,68 @@ import type { FlatMap } from "@fp-ts/core/typeclass/FlatMap" export interface Chainable extends FlatMap, Covariant {} /** - * Returns an effect that effectfully "peeks" at the success of this effect. + * Sequences the specified effect after this effect, but ignores the value + * produced by the effect. * + * @category combining * @since 1.0.0 */ -export const tap = (F: Chainable) => - ( - f: (a: A) => Kind - ): ((self: Kind) => Kind) => - F.flatMap(a => - pipe( - f(a), - F.map(() => a) - ) - ) +export const andThenDiscard = (F: Chainable): { + ( + that: Kind + ): (self: Kind) => Kind + ( + self: Kind, + that: Kind + ): Kind +} => + dual(2, ( + self: Kind, + that: Kind + ): Kind => tap(F)(self, () => that)) /** - * Sequences the specified effect after this effect, but ignores the value - * produced by the effect. + * Returns an effect that effectfully "peeks" at the success of this effect. * - * @category sequencing * @since 1.0.0 */ -export const andThenDiscard = (F: Chainable) => - ( - that: Kind - ): (( - self: Kind - ) => Kind) => tap(F)(() => that) +export const tap = (F: Chainable): { + ( + f: (a: A) => Kind + ): (self: Kind) => Kind + ( + self: Kind, + f: (a: A) => Kind + ): Kind +} => + dual( + 2, + ( + self: Kind, + f: (a: A) => Kind + ): Kind => F.flatMap(self, a => F.map(f(a), () => a)) + ) /** + * @category do notation * @since 1.0.0 */ -export const bind = (F: Chainable) => +export const bind = (F: Chainable): { ( name: Exclude, f: (a: A) => Kind ): ( self: Kind - ) => Kind< - F, - R1 & R2, - O1 | O2, - E1 | E2, - { readonly [K in keyof A | N]: K extends keyof A ? A[K] : B } - > => - F.flatMap(a => - pipe( - f(a), - F.map(b => Object.assign({}, a, { [name]: b }) as any) - ) - ) + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: A) => Kind + ): Kind +} => + dual(3, ( + self: Kind, + name: Exclude, + f: (a: A) => Kind + ): Kind => + F.flatMap(self, a => F.map(f(a), b => Object.assign({}, a, { [name]: b }) as any))) diff --git a/src/typeclass/Contravariant.ts b/src/typeclass/Contravariant.ts index 414bc890f..6d70cd2c6 100644 --- a/src/typeclass/Contravariant.ts +++ b/src/typeclass/Contravariant.ts @@ -1,6 +1,7 @@ /** * @since 1.0.0 */ +import { dual } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import type { Invariant } from "@fp-ts/core/typeclass/Invariant" @@ -9,26 +10,27 @@ import type { Invariant } from "@fp-ts/core/typeclass/Invariant" * @since 1.0.0 */ export interface Contravariant extends Invariant { - readonly contramap: ( - f: (b: B) => A - ) => (self: Kind) => Kind + readonly contramap: { + (f: (b: B) => A): (self: Kind) => Kind + (self: Kind, f: (b: B) => A): Kind + } } /** * Composing two contravariant functors yields a Covariant functor. * - * Returns a default `map` composition. + * Returns a default binary `map` composition. * * @since 1.0.0 */ export const contramapComposition = ( F: Contravariant, G: Contravariant -): (( - f: (a: A) => B -) => ( - self: Kind> -) => Kind>) => f => F.contramap(G.contramap(f)) +) => + ( + self: Kind>, + f: (a: A) => B + ): Kind> => F.contramap(self, G.contramap(f)) /** * Returns a default `imap` implementation. @@ -36,16 +38,8 @@ export const contramapComposition = * @since 1.0.0 */ export const imap = ( - contramap: Contravariant["contramap"] -): Invariant["imap"] => (_, from) => contramap(from) - -/** - * @category constructors - * @since 1.0.0 - */ -export const make = ( - contramap: Contravariant["contramap"] -): Contravariant => ({ - contramap, - imap: imap(contramap) -}) + contramap: ( + self: Kind, + f: (b: B) => A + ) => Kind +): Invariant["imap"] => dual(3, (self, _, from) => contramap(self, from)) diff --git a/src/typeclass/Covariant.ts b/src/typeclass/Covariant.ts index e5baed6ea..bca0b2457 100644 --- a/src/typeclass/Covariant.ts +++ b/src/typeclass/Covariant.ts @@ -1,6 +1,7 @@ /** * @since 1.0.0 */ +import { dual } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import type { Invariant } from "@fp-ts/core/typeclass/Invariant" @@ -9,9 +10,10 @@ import type { Invariant } from "@fp-ts/core/typeclass/Invariant" * @since 1.0.0 */ export interface Covariant extends Invariant { - readonly map: ( - f: (a: A) => B - ) => (self: Kind) => Kind + readonly map: { + (f: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (a: A) => B): Kind + } } /** @@ -22,43 +24,46 @@ export interface Covariant extends Invariant { export const mapComposition = ( F: Covariant, G: Covariant -): (( +): (( + self: Kind>, f: (a: A) => B -) => ( - self: Kind> -) => Kind>) => f => F.map(G.map(f)) +) => Kind>) => (self, f) => F.map(self, G.map(f)) /** * Returns a default `imap` implementation. * * @since 1.0.0 */ -export const imap = (map: Covariant["map"]): Invariant["imap"] => - (to, _) => map(to) - -/** - * @category constructors - * @since 1.0.0 - */ -export const make = (map: Covariant["map"]): Covariant => ({ - map, - imap: imap(map) -}) +export const imap = ( + map: (self: Kind, f: (a: A) => B) => Kind +): Invariant["imap"] => dual(3, (self, to, _) => map(self, to)) /** * @category mapping * @since 1.0.0 */ -export const flap = (F: Covariant) => - (a: A): ((self: Kind B>) => Kind) => - F.map(f => f(a)) +export const flap = (F: Covariant): { + (self: Kind B>): (a: A) => Kind + (a: A, self: Kind B>): Kind +} => + dual( + 2, + (a: A, self: Kind B>): Kind => + F.map(self, f => f(a)) + ) /** * @category mapping * @since 1.0.0 */ -export const as = (F: Covariant) => - (b: B): ((self: Kind) => Kind) => F.map(() => b) +export const as = (F: Covariant): { + (b: B): (self: Kind) => Kind + (self: Kind, b: B): Kind +} => + dual( + 2, + (self: Kind, b: B): Kind => F.map(self, () => b) + ) /** * @category mapping @@ -70,16 +75,29 @@ export const asUnit = ( const let_ = ( F: Covariant -): (( - name: Exclude, - f: (a: A) => B -) => ( - self: Kind -) => Kind) => - (name, f) => F.map(a => Object.assign({}, a, { [name]: f(a) }) as any) +): { + ( + name: Exclude, + f: (a: A) => B + ): ( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: A) => B + ): Kind +} => + dual(3, ( + self: Kind, + name: Exclude, + f: (a: A) => B + ): Kind => + F.map(self, a => Object.assign({}, a, { [name]: f(a) }) as any)) export { /** + * @category do notation * @since 1.0.0 */ let_ as let diff --git a/src/typeclass/Equivalence.ts b/src/typeclass/Equivalence.ts new file mode 100644 index 000000000..5ab635aa6 --- /dev/null +++ b/src/typeclass/Equivalence.ts @@ -0,0 +1,220 @@ +/** + * 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 + */ +import { dual } 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" +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 * as product_ from "@fp-ts/core/typeclass/Product" +import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as 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 { + (self: A, that: A): boolean +} + +/** + * @category type lambdas + * @since 1.0.0 + */ +export interface EquivalenceTypeLambda extends TypeLambda { + readonly type: Equivalence +} + +/** + * @category constructors + * @since 1.0.0 + */ +export const make = (isEquivalent: (self: A, that: A) => boolean): Equivalence => + (self: A, that: A): boolean => self === that || isEquivalent(self, that) + +const isStrictEquivalent = (x: unknown, y: unknown) => x === y + +/** + * Return an `Equivalence` that uses strict equality (===) to compare values. + * + * @since 1.0.0 + * @category constructors + */ +export const strict: () => Equivalence = () => isStrictEquivalent + +/** + * @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() + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroup = (): Semigroup> => + semigroup.make( + (self, that) => make((x, y) => self(x, y) && that(x, y)), + (self, collection) => + make((x, y) => { + if (!self(x, y)) { + return false + } + for (const equivalence of collection) { + if (!equivalence(x, y)) { + return false + } + } + return true + }) + ) + +const isAlwaysEquivalent: Equivalence = (_x, _y) => true + +/** + * @category instances + * @since 1.0.0 + */ +export const getMonoid = (): Monoid> => + monoid.fromSemigroup(getSemigroup(), isAlwaysEquivalent) + +/** + * @category combinators + * @since 1.0.0 + */ +export const contramap: { + (f: (b: B) => A): (self: Equivalence) => Equivalence + (self: Equivalence, f: (b: B) => A): Equivalence +} = dual( + 2, + (self: Equivalence, f: (b: B) => A): Equivalence => make((x, y) => self(f(x), f(y))) +) + +const imap = contravariant.imap(contramap) + +/** + * @category instances + * @since 1.0.0 + */ +export const Contravariant: contravariant.Contravariant = { + imap, + contramap +} + +/** + * @category instances + * @since 1.0.0 + */ +export const Invariant: invariant.Invariant = { + imap +} + +const product = (self: Equivalence, that: Equivalence): Equivalence<[A, B]> => + make(([xa, xb], [ya, yb]) => self(xa, ya) && that(xb, yb)) + +const productAll = (collection: Iterable>): Equivalence> => { + const equivalences = readonlyArray.fromIterable(collection) + return make((x, y) => { + const len = Math.min(x.length, y.length, equivalences.length) + for (let i = 0; i < len; i++) { + if (!equivalences[i](x[i], y[i])) { + return false + } + } + return true + }) +} + +const productMany = ( + self: Equivalence, + collection: Iterable> +): Equivalence<[A, ...Array]> => { + const equivalence = productAll(collection) + return make((x, y) => !self(x[0], y[0]) ? false : equivalence(x.slice(1), y.slice(1))) +} + +/** + * @category instances + * @since 1.0.0 + */ +export const SemiProduct: semiProduct.SemiProduct = { + imap, + product, + productMany +} + +const of: (a: A) => Equivalence = () => isAlwaysEquivalent + +/** + * @category instances + * @since 1.0.0 + */ +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll +} + +/** + * Similar to `Promise.all` but operates on `Equivalence`s. + * + * ``` + * [Equivalence, Equivalence, ...] -> Equivalence<[A, B, ...]> + * ``` + * + * 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 combinators + * @since 1.0.0 + */ +export const tuple: >>( + ...predicates: T +) => Equivalence] ? A : never }>> = + product_.tuple(Product) + +/** + * 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 combinators + * @since 1.0.0 + */ +export const struct: >>( + predicates: R +) => Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence] ? A : never }> = + product_.struct(Product) diff --git a/src/typeclass/Filterable.ts b/src/typeclass/Filterable.ts new file mode 100644 index 000000000..1db789e02 --- /dev/null +++ b/src/typeclass/Filterable.ts @@ -0,0 +1,137 @@ +/** + * `Filterable` represents data structures which can be _partitioned_/_filtered_. + * + * @since 1.0.0 + */ +import type { Either } from "@fp-ts/core/Either" +import { dual, identity } 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 partitionMap: { + ( + f: (a: A) => Either + ): (self: Kind) => [Kind, Kind] + ( + self: Kind, + f: (a: A) => Either + ): [Kind, Kind] + } + + readonly filterMap: { + (f: (a: A) => Option): (self: Kind) => Kind + (self: Kind, f: (a: A) => Option): Kind + } +} + +/** + * Returns a default binary `partitionMap` composition. + * + * @since 1.0.0 + */ +export const partitionMapComposition = ( + F: Covariant, + G: Filterable +) => + ( + self: Kind>, + f: (a: A) => Either + ): [Kind>, Kind>] => { + const filterMap = filterMapComposition(F, G) + return [filterMap(self, a => either.getLeft(f(a))), filterMap(self, a => either.getRight(f(a)))] + } + +/** + * Returns a default binary `filterMap` composition. + * + * @since 1.0.0 + */ +export const filterMapComposition = ( + F: Covariant, + G: Filterable +) => + ( + self: Kind>, + f: (a: A) => Option + ): Kind> => F.map(self, G.filterMap(f)) + +/** + * @since 1.0.0 + */ +export const compact = ( + F: Filterable +): (self: Kind>) => Kind => F.filterMap(identity) + +/** + * @since 1.0.0 + */ +export const separate = ( + F: Filterable +): ( + self: Kind> +) => [Kind, Kind] => F.partitionMap(identity) + +/** + * @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 + ( + self: Kind, + refinement: (a: A) => a is B + ): Kind + ( + self: Kind, + predicate: (a: A) => boolean + ): Kind +} = (Filterable: Filterable) => + dual( + 2, + (self: Kind, predicate: (a: A) => boolean): Kind => + Filterable.filterMap(self, (b) => (predicate(b) ? option.some(b) : option.none)) + ) + +/** + * @since 1.0.0 + */ +export const partition = ( + F: Filterable +): { + (refinement: (a: A) => a is B): ( + self: Kind + ) => [Kind, Kind] + (predicate: (a: A) => boolean): ( + self: Kind + ) => [Kind, Kind] + ( + self: Kind, + refinement: (a: A) => a is B + ): [Kind, Kind] + ( + self: Kind, + predicate: (a: A) => boolean + ): [Kind, Kind] +} => + dual( + 2, + ( + self: Kind, + predicate: (a: A) => boolean + ): [Kind, Kind] => + F.partitionMap(self, (b) => (predicate(b) ? either.right(b) : either.left(b))) + ) diff --git a/src/typeclass/FlatMap.ts b/src/typeclass/FlatMap.ts index 6830e5946..680f15119 100644 --- a/src/typeclass/FlatMap.ts +++ b/src/typeclass/FlatMap.ts @@ -1,17 +1,23 @@ /** * @since 1.0.0 */ +import { dual, identity } 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 * @since 1.0.0 */ export interface FlatMap extends TypeClass { - readonly flatMap: ( - f: (a: A) => Kind - ) => (self: Kind) => Kind + readonly flatMap: { + ( + f: (a: A) => Kind + ): (self: Kind) => Kind + ( + self: Kind, + f: (a: A) => Kind + ): Kind + } } /** @@ -20,27 +26,47 @@ export interface FlatMap extends TypeClass { export const flatten = (F: FlatMap) => ( self: Kind> - ): Kind => pipe(self, F.flatMap(identity)) + ): Kind => F.flatMap(self, identity) /** * A variant of `flatMap` that ignores the value produced by this effect. * * @since 1.0.0 */ -export const andThen = (F: FlatMap) => +export const andThen = (F: FlatMap): { ( that: Kind - ): (( - self: Kind - ) => Kind) => F.flatMap(() => that) + ): (self: Kind) => Kind + ( + self: Kind, + that: Kind + ): Kind +} => + dual(2, ( + self: Kind, + that: Kind + ): Kind => F.flatMap(self, () => that)) /** * @since 1.0.0 */ export const composeKleisliArrow = ( F: FlatMap -): ( - bfc: (b: B) => Kind -) => ( - afb: (a: A) => Kind -) => (a: A) => Kind => bc => ab => a => pipe(ab(a), F.flatMap(bc)) +): { + ( + bfc: (b: B) => Kind + ): ( + afb: (a: A) => Kind + ) => (a: A) => Kind + ( + afb: (a: A) => Kind, + bfc: (b: B) => Kind + ): (a: A) => Kind +} => + dual( + 2, + ( + afb: (a: A) => Kind, + bfc: (b: B) => Kind + ): ((a: A) => Kind) => a => F.flatMap(afb(a), bfc) + ) diff --git a/src/typeclass/Foldable.ts b/src/typeclass/Foldable.ts index 4df612622..16d49adfb 100644 --- a/src/typeclass/Foldable.ts +++ b/src/typeclass/Foldable.ts @@ -2,8 +2,8 @@ * @since 1.0.0 */ +import { dual, identity } 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" @@ -13,14 +13,14 @@ import type { Monoid } from "@fp-ts/core/typeclass/Monoid" * @since 1.0.0 */ export interface Foldable extends TypeClass { - readonly reduce: ( - b: B, - f: (b: B, a: A) => B - ) => (self: Kind) => B + readonly reduce: { + (b: B, f: (b: B, a: A) => B): (self: Kind) => B + (self: Kind, b: B, f: (b: B, a: A) => B): B + } } /** - * Returns a default `reduce` composition. + * Returns a default ternary `reduce` composition. * * @since 1.0.0 */ @@ -28,87 +28,87 @@ export const reduceComposition = ( F: Foldable, G: Foldable ) => - (b: B, f: (b: B, a: A) => B) => - ( - self: Kind> - ): B => pipe(self, F.reduce(b, (b, ga) => pipe(ga, G.reduce(b, f)))) - -/** - * @since 1.0.0 - */ -export const reduceRight = ( - F: Foldable -) => - ( + ( + self: Kind>, b: B, f: (b: B, a: A) => B - ) => (self: Kind): B => toReadonlyArray(F)(self).reduceRight(f, b) + ): B => F.reduce(self, b, (b, ga) => G.reduce(ga, b, f)) /** * @since 1.0.0 */ -export const foldMap = (F: Foldable) => - (M: Monoid) => - (f: (a: A) => M) => - (self: Kind): M => M.combineAll(toReadonlyArrayWith(F)(f)(self)) +export const toArrayMap = ( + F: Foldable +): { + (f: (a: A) => B): (self: Kind) => Array + (self: Kind, f: (a: A) => B): Array +} => + dual( + 2, + (self: Kind, f: (a: A) => B): Array => + F.reduce(self, [], (out: Array, a) => [...out, f(a)]) + ) /** * @since 1.0.0 */ -export const toReadonlyArrayWith = ( +export const toArray = ( F: Foldable -) => - (f: (a: A) => B) => - (self: Kind): ReadonlyArray => - F.reduce>([], (out, a) => { - out.push(f(a)) - return out - })(self) +): (self: Kind) => Array => toArrayMap(F)(identity) /** * @since 1.0.0 */ -export const toReadonlyArray = ( - F: Foldable -): (self: Kind) => ReadonlyArray => toReadonlyArrayWith(F)(identity) +export const combineMap = (F: Foldable) => + (M: Monoid): { + (f: (a: A) => M): (self: Kind) => M + (self: Kind, f: (a: A) => M): M + } => + dual(2, (self: Kind, f: (a: A) => M): M => + F.reduce(self, M.empty, (m, a) => + M.combine(m, f(a)))) /** * @since 1.0.0 */ export const reduceKind = (F: Foldable) => - (G: Monad) => + (G: Monad): { ( b: B, f: (b: B, a: A) => Kind - ): (self: Kind) => Kind => - F.reduce>( - G.of(b), - (gb, a) => pipe(gb, G.flatMap(b => f(b, a))) - ) - -/** - * @since 1.0.0 - */ -export const reduceRightKind = (F: Foldable) => - (G: Monad) => - ( + ): (self: Kind) => Kind + ( + self: Kind, + b: B, + f: (b: B, a: A) => Kind + ): Kind + } => + dual(3, ( + self: Kind, b: B, f: (b: B, a: A) => Kind - ): (self: Kind) => Kind => - reduceRight(F)>( + ): Kind => + F.reduce( + self, G.of(b), - (gb, a) => pipe(gb, G.flatMap(b => f(b, a))) - ) + (gb: Kind, a) => G.flatMap(gb, b => f(b, a)) + )) /** * @since 1.0.0 */ -export const foldMapKind = (F: Foldable) => - (G: Coproduct) => +export const coproductMapKind = (F: Foldable) => + (G: Coproduct): { ( f: (a: A) => Kind - ): (self: Kind) => Kind => - F.reduce>( - G.zero(), - (gb, a) => pipe(gb, G.coproduct(f(a))) - ) + ): (self: Kind) => Kind + ( + self: Kind, + f: (a: A) => Kind + ): Kind + } => + dual(2, ( + self: Kind, + f: (a: A) => Kind + ): Kind => + F.reduce(self, G.zero(), (gb: Kind, a) => G.coproduct(gb, f(a)))) diff --git a/src/typeclass/Invariant.ts b/src/typeclass/Invariant.ts index c0c41dfba..5fe81b80b 100644 --- a/src/typeclass/Invariant.ts +++ b/src/typeclass/Invariant.ts @@ -1,6 +1,12 @@ /** + * The `Invariant` typeclass is a higher-order abstraction over types that allow mapping the contents of a type in both directions. + * It is similar to the `Covariant` typeclass but provides an `imap` opration, which allows transforming a value in both directions. + * This typeclass is useful when dealing with data types that can be converted to and from some other types. + * The `imap` operation provides a way to convert such data types to other types that they can interact with while preserving their invariants. + * * @since 1.0.0 */ +import { dual } from "@fp-ts/core/Function" import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" /** @@ -8,43 +14,59 @@ import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" * @since 1.0.0 */ export interface Invariant extends TypeClass { - readonly imap: ( - to: (a: A) => B, - from: (b: B) => A - ) => (self: Kind) => Kind + readonly imap: { + ( + to: (a: A) => B, + from: (b: B) => A + ): (self: Kind) => Kind + ( + self: Kind, + to: (a: A) => B, + from: (b: B) => A + ): Kind + } } /** - * Returns a default `imap` composition. + * Returns a default ternary `imap` composition. * * @since 1.0.0 */ export const imapComposition = ( F: Invariant, G: Invariant -): (( - to: (a: A) => B, - from: (b: B) => A -) => ( - self: Kind> -) => Kind>) => - (to, from) => F.imap(G.imap(to, from), G.imap(from, to)) +) => + ( + self: Kind>, + to: (a: A) => B, + from: (b: B) => A + ): Kind> => F.imap(self, G.imap(to, from), G.imap(from, to)) /** + * @category do notation * @since 1.0.0 */ -export const bindTo = (F: Invariant) => +export const bindTo = (F: Invariant): { ( name: N - ): (( - self: Kind - ) => Kind) => - F.imap(a => ({ [name]: a } as any), ({ [name]: a }) => a) + ): (self: Kind) => Kind + ( + self: Kind, + name: N + ): Kind +} => + dual(2, ( + self: Kind, + name: N + ): Kind => + F.imap(self, a => ({ [name]: a } as any), ({ [name]: a }) => a)) /** + * Convert a value in a singleton array in a given effect. + * * @since 1.0.0 */ 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/src/typeclass/Monoid.ts b/src/typeclass/Monoid.ts index d24fc3c52..68b90b1ca 100644 --- a/src/typeclass/Monoid.ts +++ b/src/typeclass/Monoid.ts @@ -15,15 +15,14 @@ export interface Monoid extends Semigroup { } /** - * Optimised. - * * @category constructors * @since 1.0.0 */ export const fromSemigroup = (S: Semigroup, empty: Monoid["empty"]): Monoid => ({ - ...S, + combine: S.combine, + combineMany: S.combineMany, empty, - combineAll: collection => S.combineMany(collection)(empty) + combineAll: collection => S.combineMany(empty, collection) }) /** @@ -49,35 +48,150 @@ 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) /** - * Given a struct of monoids returns a monoid for the struct. + * @category instances + * @since 1.0.0 + */ +export const string: Monoid = fromSemigroup(semigroup.string, "") + +/** + * `number` monoid under addition. * + * The `empty` value is `0`. + * + * @category instances * @since 1.0.0 */ -export const struct = ( - monoids: { [K in keyof A]: Monoid } -): Monoid<{ readonly [K in keyof A]: A[K] }> => { - const empty: A = {} as any - for (const k in monoids) { - if (Object.prototype.hasOwnProperty.call(monoids, k)) { - empty[k] = monoids[k].empty - } - } - return fromSemigroup(semigroup.struct(monoids), empty) +export const numberSum: Monoid = fromSemigroup(semigroup.numberSum, 0) + +/** + * `number` monoid under multiplication. + * + * The `empty` value is `1`. + * + * @category instances + * @since 1.0.0 + */ +export const numberMultiply: Monoid = fromSemigroup(semigroup.numberMultiply, 1) + +/** + * `number` monoid under addition. + * + * The `bigint` value is `0n`. + * + * @category instances + * @since 1.0.0 + */ +export const bigintSum: Monoid = fromSemigroup(semigroup.bigintSum, 0n) + +/** + * `bigint` monoid under multiplication. + * + * The `empty` value is `1n`. + * + * @category instances + * @since 1.0.0 + */ +export const bigintMultiply: Monoid = fromSemigroup(semigroup.bigintMultiply, 1n) + +/** + * `boolean` monoid under conjunction. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAll: Monoid = fromSemigroup(semigroup.booleanAll, true) + +/** + * `boolean` monoid under disjunction. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAny: Monoid = fromSemigroup(semigroup.booleanAny, false) + +/** + * `boolean` monoid under exclusive disjunction. + * + * The `empty` value is `false`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanXor: Monoid = fromSemigroup(semigroup.booleanXor, false) + +/** + * `boolean` monoid under equivalence. + * + * The `empty` value is `true`. + * + * @category instances + * @since 1.0.0 + */ +export const booleanEqv: Monoid = fromSemigroup(semigroup.booleanEqv, true) + +/** + * Similar to `Promise.all` but operates on `Monoid`s. + * + * ``` + * [Monoid, Monoid, ...] -> Monoid<[A, B, ...]> + * ``` + * + * This function creates and returns a new `Monoid` for a tuple of values based on the given `Monoid`s for each element in the tuple. + * The returned `Monoid` combines two tuples of the same type by applying the corresponding `Monoid` passed as arguments to each element in the tuple. + * + * The `empty` value of the returned `Monoid` is the tuple of `empty` values of the input `Monoid`s. + * + * 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 = >>( + ...elements: T +): Monoid<{ readonly [I in keyof T]: [T[I]] extends [Monoid] ? A : never }> => { + const empty = elements.map((m) => m.empty) as any + return fromSemigroup(semigroup.tuple(...elements), empty) } /** - * Given a tuple of monoids returns a monoid for the tuple. + * Given a type `A`, this function creates and returns a `Semigroup` for `ReadonlyArray`. * + * The `empty` value is the empty array. + * + * @category combinators * @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) +export const array = (): Monoid> => fromSemigroup(semigroup.array(), []) + +/** + * This function creates and returns a new `Monoid` for a struct of values based on the given `Monoid`s for each property in the struct. + * The returned `Monoid` combines two structs of the same type by applying the corresponding `Monoid` passed as arguments to each property in the struct. + * + * The `empty` value of the returned `Monoid` is a struct where each property is the `empty` value of the corresponding `Monoid` in the input `monoids` object. + * + * 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 = }>( + fields: R +): Monoid<{ readonly [K in keyof R]: [R[K]] extends [Monoid] ? A : never }> => { + const empty = {} as any + for (const k in fields) { + if (Object.prototype.hasOwnProperty.call(fields, k)) { + empty[k] = fields[k].empty + } + } + return fromSemigroup(semigroup.struct(fields), empty) } diff --git a/src/typeclass/Of.ts b/src/typeclass/Of.ts index e749399b3..69ac52b33 100644 --- a/src/typeclass/Of.ts +++ b/src/typeclass/Of.ts @@ -29,6 +29,7 @@ export const unit = ( ): Kind => F.of(undefined) /** + * @category do notation * @since 1.0.0 */ export const Do = ( diff --git a/src/typeclass/Order.ts b/src/typeclass/Order.ts index e6a17b131..d61086c8c 100644 --- a/src/typeclass/Order.ts +++ b/src/typeclass/Order.ts @@ -1,13 +1,16 @@ /** * @since 1.0.0 */ +import { dual } 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" 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 * as product_ from "@fp-ts/core/typeclass/Product" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" +import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" /** @@ -15,7 +18,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 } /** @@ -27,84 +30,75 @@ export interface OrderTypeLambda extends TypeLambda { } /** - * Main constructor. - * * @category constructors * @since 1.0.0 */ -export const fromCompare = (compare: Order["compare"]): Order => ({ - compare: that => self => self === that ? 0 : compare(that)(self) +export const make = ( + compare: Order["compare"] +): Order => ({ + compare: (self, that) => self === that ? 0 : compare(self, that) }) /** - * Given a tuple of `Compare`s returns a `Compare` for the tuple. - * + * @category instances * @since 1.0.0 */ -export const tuple = >( - ...orders: { [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 - } - } - return orders[i].compare(that[i])(self[i]) - } - ) +export const string: Order = make((self, that) => self < that ? -1 : 1) /** + * @category instances * @since 1.0.0 */ -export const reverse = (O: Order): Order => - fromCompare(that => self => O.compare(self)(that)) +export const number: Order = make((self, that) => self < that ? -1 : 1) /** + * @category instances * @since 1.0.0 */ -export const contramap = (f: (b: B) => A) => - (self: Order): Order => fromCompare((b2) => (b1) => self.compare(f(b2))(f(b1))) +export const boolean: Order = make((self, that) => self < that ? -1 : 1) /** * @category instances * @since 1.0.0 */ -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) +export const bigint: Order = make((self, that) => self < that ? -1 : 1) + +/** + * @since 1.0.0 + */ +export const reverse = (O: Order): Order => make((self, that) => O.compare(that, self)) + +/** + * @category instances + * @since 1.0.0 + */ +export const getSemigroup = (): Semigroup> => + semigroup.make( + (O1, O2) => + make((self, that) => { + const out = O1.compare(self, that) + if (out !== 0) { + return out } - ), - combineMany: (collection) => - (self) => - fromCompare(a2 => - a1 => { - let out = self.compare(a2)(a1) + return O2.compare(self, that) + }), + (self, collection) => + make((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 = make(() => 0) /** * @category instances @@ -112,20 +106,66 @@ const empty: Order = fromCompare(() => () => 0) */ export const getMonoid = (): Monoid> => monoid.fromSemigroup(getSemigroup(), empty) +/** + * @category combinators + * @since 1.0.0 + */ +export const contramap: { + (f: (b: B) => A): (self: Order) => Order + (self: Order, f: (b: B) => A): Order +} = dual( + 2, + (self: Order, f: (b: B) => A): Order => make((b1, b2) => self.compare(f(b1), f(b2))) +) + +const imap = contravariant.imap(contramap) + /** * @category instances * @since 1.0.0 */ -export const Contravariant: contravariant.Contravariant = contravariant.make( +export const Contravariant: contravariant.Contravariant = { + imap, contramap -) +} /** * @category instances * @since 1.0.0 */ export const Invariant: invariant.Invariant = { - imap: Contravariant.imap + imap +} + +const product = (self: Order, that: Order): Order<[A, B]> => + make(([xa, xb], [ya, yb]) => { + const o = self.compare(xa, ya) + return o !== 0 ? o : that.compare(xb, yb) + }) + +const productAll = (collection: Iterable>): Order> => { + const orders = readonlyArray.fromIterable(collection) + return make((x, y) => { + const len = Math.min(x.length, y.length, orders.length) + for (let i = 0; i < len; i++) { + const o = orders[i].compare(x[i], y[i]) + if (o !== 0) { + return o + } + } + return 0 + }) +} + +const productMany = ( + self: Order, + collection: Iterable> +): Order<[A, ...Array]> => { + const order = productAll(collection) + return make((x, y) => { + const o = self.compare(x[0], y[0]) + return o !== 0 ? o : order.compare(x.slice(1), y.slice(1)) + }) } /** @@ -133,80 +173,167 @@ export const Invariant: invariant.Invariant = { * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { - imap: Contravariant.imap, - product: that => self => tuple(self, that), - productMany: collection => self => tuple(self, ...collection) + imap, + product, + productMany } +const of: (a: A) => Order = () => empty + /** * @category instances * @since 1.0.0 */ -export const Product: product.Product = { - ...SemiProduct, - of: () => empty, - productAll: (collection: Iterable>) => tuple>(...collection) +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll } +/** + * Similar to `Promise.all` but operates on `Order`s. + * + * ``` + * [Order, Order, ...] -> Order<[A, B, ...]> + * ``` + * + * 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: >>( + ...elements: T +) => Order<{ [I in keyof T]: [T[I]] extends [Order] ? A : never }> = product_.tuple( + Product +) + +/** + * 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> => + make((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(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: }>( + fields: R +) => Order<{ [K in keyof R]: [R[K]] extends [Order] ? A : never }> = product_.struct( + Product +) + /** * Test whether one value is _strictly less than_ another. * * @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) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: 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) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O.compare(self, that) === 1) /** * Test whether one value is _non-strictly less than_ another. * * @since 1.0.0 */ -export const lessThanOrEqualTo = (O: Order) => - (that: A) => (self: A) => O.compare(that)(self) !== 1 +export const lessThanOrEqualTo = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O.compare(self, that) !== 1) /** * Test whether one value is _non-strictly greater than_ another. * * @since 1.0.0 */ -export const greaterThanOrEqualTo = (O: Order) => - (that: A) => (self: A) => O.compare(that)(self) !== -1 +export const greaterThanOrEqualTo = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O.compare(self, that) !== -1) /** * Take the minimum of two values. If they are considered equal, the first argument is chosen. * * @since 1.0.0 */ -export const min = (O: Order) => - (that: A) => (self: A): A => self === that || O.compare(that)(self) < 1 ? self : that +export const min = (O: Order): { + (that: A): (self: A) => A + (self: A, that: A): A +} => dual(2, (self: A, that: 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. * * @since 1.0.0 */ -export const max = (O: Order) => - (that: A) => (self: A): A => self === that || O.compare(that)(self) > -1 ? self : that +export const max = (O: Order): { + (that: A): (self: A) => A + (self: A, that: A): A +} => dual(2, (self: A, that: A) => self === that || O.compare(self, that) > -1 ? self : that) /** * Clamp a value between a minimum and a maximum. * * @since 1.0.0 */ -export const clamp = (O: Order) => - (minimum: A, maximum: A) => (a: A) => min(O)(max(O)(a)(minimum))(maximum) +export const clamp = (O: Order): { + (minimum: A, maximum: A): (self: A) => A + (self: A, minimum: A, maximum: A): A +} => + dual( + 3, + (self: A, minimum: A, maximum: A): A => min(O)(maximum, max(O)(minimum, self)) + ) /** * Test whether a value is between a minimum and a maximum (inclusive). * * @since 1.0.0 */ -export const between = (O: Order) => - (minimum: A, maximum: A) => - (a: A): boolean => !lessThan(O)(minimum)(a) && !greaterThan(O)(maximum)(a) +export const between = (O: Order): { + (minimum: A, maximum: A): (self: A) => boolean + (self: A, minimum: A, maximum: A): boolean +} => + dual( + 3, + (self: A, minimum: A, maximum: A): boolean => + !lessThan(O)(self, minimum) && !greaterThan(O)(self, maximum) + ) diff --git a/src/typeclass/Product.ts b/src/typeclass/Product.ts index 3ad68f9a5..0aa9d7c6c 100644 --- a/src/typeclass/Product.ts +++ b/src/typeclass/Product.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ + 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" @@ -13,41 +13,38 @@ import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" export interface Product extends SemiProduct, Of { readonly productAll: ( collection: Iterable> - ) => Kind> + ) => Kind> } /** * @since 1.0.0 */ export const tuple = (F: Product) => - >>(...components: T): Kind< + >>(...elements: 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 }> - > => F.productAll(components) as any + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } + > => F.productAll(elements) as any /** * @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), ([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( - F.productAll(keys.map(k => fields[k])), - F.imap(values => { - const out: any = {} - for (let i = 0; i < values.length; i++) { - out[keys[i]] = values[i] - } - return out - }, (r) => keys.map(k => r[k])) - ) + return F.imap(F.productAll(keys.map(k => fields[k])), values => { + const out: any = {} + for (let i = 0; i < values.length; i++) { + out[keys[i]] = values[i] + } + return out + }, (r) => keys.map(k => r[k])) } diff --git a/src/typeclass/SemiApplicative.ts b/src/typeclass/SemiApplicative.ts index 9bbf4ffb2..1a9bfb969 100644 --- a/src/typeclass/SemiApplicative.ts +++ b/src/typeclass/SemiApplicative.ts @@ -1,10 +1,11 @@ /** * @since 1.0.0 */ +import { dual, identity, SK } 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 * as semigroup from "@fp-ts/core/typeclass/Semigroup" import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" /** @@ -16,80 +17,118 @@ export interface SemiApplicative extends SemiProduct, C /** * Lift a `Semigroup` into 'F', the inner values are combined using the provided `Semigroup`. * + * @category lifting * @since 1.0.0 */ -export const liftSemigroup = (F: SemiApplicative) => - (S: Semigroup): Semigroup> => ({ - combine: that => self => pipe(self, F.product(that), F.map(([a1, a2]) => S.combine(a2)(a1))), - combineMany: collection => - self => - pipe( - self, - F.productMany(collection), - F.map(([head, ...tail]) => pipe(head, S.combineMany(tail))) - ) - }) +export const getSemigroup = (F: SemiApplicative) => + (S: Semigroup): Semigroup> => + semigroup.make( + (self, that) => F.map(F.product(self, that), ([a1, a2]) => S.combine(a1, a2)), + (self, collection) => + F.map(F.productMany(self, collection), ([head, ...tail]) => S.combineMany(head, tail)) + ) + +/** + * Zips two `F` values together using a provided function, returning a new `F` of the result. + * + * @param self - The left-hand side of the zip operation + * @param that - The right-hand side of the zip operation + * @param f - The function used to combine the values of the two `Option`s + * + * @since 1.0.0 + */ +export const zipWith = (F: SemiApplicative): { + ( + that: Kind, + f: (a: A, b: B) => C + ): (self: Kind) => Kind + ( + self: Kind, + that: Kind, + f: (a: A, b: B) => C + ): Kind +} => + dual( + 3, + ( + self: Kind, + that: Kind, + f: (a: A, b: B) => C + ): Kind => F.map(F.product(self, that), ([a, b]) => f(a, b)) + ) /** * @since 1.0.0 */ -export const ap = (F: SemiApplicative) => +export const ap = (F: SemiApplicative): { ( - fa: Kind - ) => - ( - self: Kind B> - ): Kind => pipe(self, F.product(fa), F.map(([f, a]) => f(a))) + that: Kind + ): ( + self: Kind B> + ) => Kind + ( + self: Kind B>, + that: Kind + ): Kind +} => + dual(2, ( + self: Kind B>, + that: Kind + ): Kind => zipWith(F)(self, that, (f, a) => f(a))) /** * @since 1.0.0 */ -export const andThenDiscard = (F: SemiApplicative) => +export const andThenDiscard = (F: SemiApplicative): { ( that: Kind - ) => - ( - self: Kind - ): Kind => pipe(self, F.product(that), F.map(([a]) => a)) + ): (self: Kind) => Kind + ( + self: Kind, + that: Kind + ): Kind +} => + dual(2, ( + self: Kind, + that: Kind + ): Kind => zipWith(F)(self, that, identity)) /** * @since 1.0.0 */ -export const andThen = (F: SemiApplicative) => +export const andThen = (F: SemiApplicative): { ( that: Kind - ) => - ( - self: Kind - ): Kind => pipe(self, F.product(that), F.map(([_, a]) => a)) + ): (self: Kind) => Kind + ( + self: Kind, + that: Kind + ): Kind +} => + dual(2, ( + self: Kind, + that: Kind + ): Kind => zipWith(F)(self, that, SK)) /** * Lifts a binary function into `F`. * + * @param f - The function to lift. + * + * @category lifting * @since 1.0.0 */ export const lift2 = (F: SemiApplicative) => - (f: (a: A, b: B) => C) => + (f: (a: A, b: B) => C): { + ( + that: Kind + ): (self: Kind) => Kind ( - fa: Kind, - fb: Kind - ): Kind => pipe(fa, F.product(fb), F.map(([a, b]) => f(a, b))) - -/** - * Lifts a ternary function into 'F'. - * - * @since 1.0.0 - */ -export const lift3 = (F: SemiApplicative) => - (f: (a: A, b: B, c: C) => D) => - ( - fa: Kind, - fb: Kind, - fc: Kind - ): Kind => - pipe( - fa, - F.product(fb), - F.product(fc), - F.map(([[a, b], c]) => f(a, b, c)) - ) + self: Kind, + that: Kind + ): Kind + } => + dual(2, ( + self: Kind, + that: Kind + ): Kind => zipWith(F)(self, that, f)) diff --git a/src/typeclass/SemiCoproduct.ts b/src/typeclass/SemiCoproduct.ts index d13d6e044..57d85526d 100644 --- a/src/typeclass/SemiCoproduct.ts +++ b/src/typeclass/SemiCoproduct.ts @@ -1,6 +1,7 @@ /** * @since 1.0.0 */ + 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" @@ -10,24 +11,22 @@ 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: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind + ) => Kind } /** * @since 1.0.0 */ export const getSemigroup = (F: SemiCoproduct) => - (): Semigroup< - Kind - > => ({ + (): Semigroup> => ({ combine: F.coproduct, combineMany: F.coproductMany }) diff --git a/src/typeclass/SemiProduct.ts b/src/typeclass/SemiProduct.ts index c8b6958dd..b1dc41b1c 100644 --- a/src/typeclass/SemiProduct.ts +++ b/src/typeclass/SemiProduct.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ +import { dual } 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" @@ -12,17 +12,41 @@ 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 + ) => Kind readonly productMany: ( + self: Kind, collection: Iterable> - ) => (self: Kind) => Kind]> + ) => Kind]> } +/** + * Returns a default `productMany` implementation. + * + * @category constructors + * @since 1.0.0 + */ +export const productMany = ( + map: Covariant["map"], + product: SemiProduct["product"] +): SemiProduct["productMany"] => + ( + self: Kind, + collection: Iterable> + ) => { + let out = map(self, (a): [A, ...Array] => [a]) + for (const fa of collection) { + out = map( + product(out, fa), + ([[head, ...tail], a]): [A, ...Array] => [head, ...tail, a] + ) + } + return out + } + /** * Returns a default `product` composition. * @@ -32,18 +56,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 + > => F.map(F.product(self, that), ([ga, gb]) => G.product(ga, gb)) /** * Returns a default `productMany` composition. @@ -55,100 +77,75 @@ export const productManyComposition = ) => ( + self: Kind>, collection: Iterable>> - ) => - ( - self: Kind> - ): Kind]>> => - pipe( - self, - F.productMany(collection), - F.map(([ga, ...gas]) => pipe(ga, G.productMany(gas))) - ) - -/** - * Returns a default `productMany` implementation (useful for tests). - * - * @category constructors - * @since 1.0.0 - */ -export const productMany = ( - Covariant: Covariant, - product: SemiProduct["product"] -): SemiProduct["productMany"] => - ( - collection: Iterable> - ) => - (self: Kind) => { - let out = pipe( - self, - Covariant.map((a): readonly [A, ...Array] => [a]) - ) - for (const fa of collection) { - out = pipe( - out, - product(fa), - Covariant.map(([[head, ...tail], a]): readonly [A, ...Array] => [head, ...tail, a]) - ) - } - return out - } + ): Kind]>> => + F.map(F.productMany(self, collection), ([ga, ...gas]) => G.productMany(ga, gas)) /** + * @category do notation * @since 1.0.0 */ -export const andThenBind = (F: SemiProduct) => +export const andThenBind = (F: SemiProduct): { ( name: Exclude, that: Kind - ) => - ( - self: Kind - ): Kind< - F, - R1 & R2, - O1 | O2, - E1 | E2, - { readonly [K in keyof A | N]: K extends keyof A ? A[K] : B } - > => - pipe( - self, - F.product(that), - F.imap( - ([a, b]) => Object.assign({}, a, { [name]: b }) as any, - ({ [name]: b, ...rest }) => [rest, b] as any - ) - ) + ): ( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + that: Kind + ): Kind +} => + dual(3, ( + self: Kind, + name: Exclude, + that: Kind + ): Kind => + F.imap( + F.product(self, that), + ([a, b]) => Object.assign({}, a, { [name]: b }) as any, + ({ [name]: b, ...rest }) => [rest, b] as any + )) /** + * Appends an element to the end of a tuple. + * * @since 1.0.0 */ -export const productFlatten = (F: SemiProduct) => +export const appendElement = (F: SemiProduct): { ( that: Kind - ) => - >( - self: 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) - ) + ): >( + self: Kind + ) => Kind + , R2, O2, E2, B>( + self: Kind, + that: Kind + ): Kind +} => + dual(2, , R2, O2, E2, B>( + self: Kind, + that: Kind + ): Kind => + F.imap(F.product(self, that), ([a, b]) => [...a, b], ab => + [ab.slice(0, -1), ab[ab.length - 1]] as any)) /** * @since 1.0.0 */ export const nonEmptyTuple = (F: SemiProduct) => - , ...ReadonlyArray>]>( - ...components: T + , ...Array>]>( + ...elements: 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 }> - > => F.productMany(components.slice(1))(components[0]) as any + { [I in keyof T]: [T[I]] extends [Kind] ? A : never } + > => F.productMany(elements[0], elements.slice(1)) as any type EnforceNonEmptyRecord = keyof R extends never ? never : R @@ -156,24 +153,25 @@ type EnforceNonEmptyRecord = keyof R extends never ? never : R * @since 1.0.0 */ export const nonEmptyStruct = (F: SemiProduct) => - >>( - fields: EnforceNonEmptyRecord & Record> + }>( + fields: EnforceNonEmptyRecord & { readonly [x: string]: Kind } ): 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 } + { [K in keyof R]: [R[K]] extends [Kind] ? A : never } > => { const keys = Object.keys(fields) - return pipe( - F.productMany(keys.slice(1).map(k => fields[k]))(fields[keys[0]]), - F.imap(([value, ...values]) => { + return F.imap( + F.productMany(fields[keys[0]], keys.slice(1).map(k => fields[k])), + ([value, ...values]) => { const out: any = { [keys[0]]: value } for (let i = 0; i < values.length; i++) { out[keys[i + 1]] = values[i] } return out - }, (r) => keys.map(k => r[k]) as any) + }, + (r) => keys.map(k => r[k]) as any ) } diff --git a/src/typeclass/Semigroup.ts b/src/typeclass/Semigroup.ts index 37f2c4579..f9866e1db 100644 --- a/src/typeclass/Semigroup.ts +++ b/src/typeclass/Semigroup.ts @@ -1,30 +1,13 @@ /** - * `Semigroup` describes a way of combining two values of type `A` that is associative. - * - * ```ts - * export interface Semigroup { - * readonly combine: (that: A) => (self: A) => A - * readonly combineMany: (collection: Iterable) => (self: A) => A - * } - * ``` - * - * The combine operator must be associative, meaning that if we combine `a` with `b` and then combine the result - * with `c` we must get the same value as if we combine `b` with `c` and then combine `a` with the result. - * - * ``` - * (a <> b) <> c === a <> (b <> c) - * ``` - * - * The `Semigroup` abstraction allows us to combine values of a data type to build a new value of that data type - * with richer structure. - * * @since 1.0.0 */ +import { dual } from "@fp-ts/core/Function" import type { TypeLambda } from "@fp-ts/core/HKT" -import { identity } from "@fp-ts/core/internal/Function" +import { fromIterable } from "@fp-ts/core/internal/ReadonlyArray" +import * as readonlyArray 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" +import * as product_ from "@fp-ts/core/typeclass/Product" import type * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" /** @@ -32,8 +15,8 @@ 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 combineMany: (collection: Iterable) => (self: A) => A + readonly combine: (self: A, that: A) => A + readonly combineMany: (self: A, collection: Iterable) => A } /** @@ -45,106 +28,197 @@ export interface SemigroupTypeLambda extends TypeLambda { } /** - * Useful when `combineMany` can't be optimised. + * @param combineMany - Useful when `combineMany` can be optimised * * @category constructors * @since 1.0.0 */ -export const fromCombine = (combine: Semigroup["combine"]): Semigroup => ({ +export const make = ( + combine: Semigroup["combine"], + combineMany: Semigroup["combineMany"] = (self, collection) => + fromIterable(collection).reduce(combine, self) +): Semigroup => ({ combine, - combineMany: (collection) => - (self) => { - let out: A = self - for (const a of collection) { - out = combine(a)(out) - } - return out - } + combineMany }) /** - * `Semigroup` that returns last minimum of elements. + * @category instances + * @since 1.0.0 + */ +export const string: Semigroup = make((self, that) => self + that) + +/** + * `number` semigroup under addition. * - * @category constructors + * @category instances * @since 1.0.0 */ -export const min = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(that)(self) === -1 ? self : that) +export const numberSum: Semigroup = make((self, that) => self + that) /** - * `Semigroup` that returns last maximum of elements. + * `number` semigroup under multiplication. * - * @category constructors + * @category instances * @since 1.0.0 */ -export const max = (O: Order): Semigroup => - fromCombine((that) => (self) => O.compare(that)(self) === 1 ? self : that) +export const numberMultiply: Semigroup = make( + (self, that) => self * that, + (self, collection) => { + if (self === 0) { + return 0 + } + let out = self + for (const n of collection) { + if (n === 0) { + return 0 + } + out = out * n + } + return out + } +) /** - * @category constructors + * `bigint` semigroup under addition. + * + * @category instances * @since 1.0.0 */ -export const constant = (a: A): Semigroup => ({ - combine: () => () => a, - combineMany: () => () => a -}) +export const bigintSum: Semigroup = make((self, that) => self + that) /** - * The dual of a `Semigroup`, obtained by flipping the arguments of `combine`. + * `bigint` semigroup under multiplication. * + * @category instances * @since 1.0.0 */ -export const reverse = (S: Semigroup): Semigroup => ({ - combine: (that) => (self) => S.combine(self)(that), - combineMany: (collection) => - (self) => { - const reversed = Array.from(collection).reverse() - return reversed.length > 0 ? - S.combine(self)(S.combineMany(reversed.slice(1))(reversed[0])) : - self +export const bigintMultiply: Semigroup = make( + (self, that) => self * that, + (self, collection) => { + if (self === 0n) { + return 0n } -}) + let out = self + for (const n of collection) { + if (n === 0n) { + return 0n + } + out = out * n + } + return out + } +) /** - * Given a struct of associatives returns an associative for the struct. + * `boolean` semigroup under conjunction. * + * @category instances * @since 1.0.0 */ -export const struct = (semigroups: { [K in keyof A]: Semigroup }): Semigroup< - { - readonly [K in keyof A]: A[K] +export const booleanAll: Semigroup = make( + (self, that) => self && that, + (self, collection) => { + if (self === false) { + return false + } + for (const b of collection) { + if (b === false) { + return false + } + } + return true } -> => - 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]) - } +) + +/** + * `boolean` semigroup under disjunction. + * + * @category instances + * @since 1.0.0 + */ +export const booleanAny: Semigroup = make( + (self, that) => self || that, + (self, collection) => { + if (self === true) { + return true + } + for (const b of collection) { + if (b === true) { + return true } - return r } - ) + return false + } +) /** - * Given a tuple of associatives returns an associative for the tuple. + * `boolean` semigroup under exclusive disjunction. * + * @category instances * @since 1.0.0 */ -export const tuple = >( - ...semigroups: { [K in keyof A]: Semigroup } -): Semigroup> => - fromCombine((that) => (self) => semigroups.map((S, i) => S.combine(that[i])(self[i])) as any) +export const booleanXor: Semigroup = make((self, that) => self !== that) /** + * `boolean` semigroup under equivalence. + * + * @category instances * @since 1.0.0 */ -export const intercalate = (separator: A) => - (S: Semigroup): Semigroup => - fromCombine( - (that) => S.combineMany([separator, that]) - ) +export const booleanEqv: Semigroup = make((self, that) => self === that) + +/** + * `Semigroup` that returns last minimum of elements. + * + * @category constructors + * @since 1.0.0 + */ +export const min = (O: Order): Semigroup => + make((self, that) => O.compare(self, that) === -1 ? self : that) + +/** + * `Semigroup` that returns last maximum of elements. + * + * @category constructors + * @since 1.0.0 + */ +export const max = (O: Order): Semigroup => + make((self, that) => O.compare(self, that) === 1 ? self : that) + +/** + * @category constructors + * @since 1.0.0 + */ +export const constant = (a: A): Semigroup => make(() => a, () => a) + +/** + * The dual of a `Semigroup`, obtained by flipping the arguments of `combine`. + * + * @since 1.0.0 + */ +export const reverse = (S: Semigroup): Semigroup => + make( + (self, that) => S.combine(that, self), + (self, collection) => { + const reversed = Array.from(collection).reverse() + return reversed.length > 0 ? + S.combine(S.combineMany(reversed[0], reversed.slice(1)), self) : + self + } + ) + +/** + * @since 1.0.0 + */ +export const intercalate: { + (separator: A): (S: Semigroup) => Semigroup + (S: Semigroup, separator: A): Semigroup +} = dual( + 2, + (S: Semigroup, separator: A): Semigroup => + make((self, that) => S.combineMany(self, [separator, that])) +) /** * Always return the first argument. @@ -152,10 +226,7 @@ export const intercalate = (separator: A) => * @category instances * @since 1.0.0 */ -export const first = (): Semigroup => ({ - combine: () => identity, - combineMany: () => identity -}) +export const first = (): Semigroup => make((a) => a, (a) => a) /** * Always return the last argument. @@ -163,34 +234,28 @@ export const first = (): Semigroup => ({ * @category instances * @since 1.0.0 */ -export const last = (): Semigroup => ({ - combine: second => () => second, - combineMany: collection => - self => { +export const last = (): Semigroup => + make( + (_, second) => second, + (self, collection) => { let a: A = self // eslint-disable-next-line no-empty for (a of collection) {} return a } -}) + ) /** * @since 1.0.0 */ -export const imap = ( - to: (a: A) => B, - from: (b: B) => A -) => - (S: Semigroup): Semigroup => ({ - combine: that => self => to(S.combine(from(that))(from(self))), - combineMany: (collection) => - self => - to( - S.combineMany( - (Array.isArray(collection) ? collection : Array.from(collection)).map(from) - )(from(self)) - ) - }) +export const imap: { + (to: (a: A) => B, from: (b: B) => A): (self: Semigroup) => Semigroup + (self: Semigroup, to: (a: A) => B, from: (b: B) => A): Semigroup +} = dual(3, (S: Semigroup, to: (a: A) => B, from: (b: B) => A): Semigroup => + make( + (self, that) => to(S.combine(from(self), from(that))), + (self, collection) => to(S.combineMany(from(self), (fromIterable(collection)).map(from))) + )) /** * @category instances @@ -200,22 +265,92 @@ export const Invariant: invariant.Invariant = { imap } +const product = (self: Semigroup, that: Semigroup): Semigroup<[A, B]> => + make(([xa, xb], [ya, yb]) => [self.combine(xa, ya), that.combine(xb, yb)]) + +const productAll = (collection: Iterable>): Semigroup> => { + const semigroups = readonlyArray.fromIterable(collection) + return make((x, y) => { + const len = Math.min(x.length, y.length, semigroups.length) + const out: Array = [] + for (let i = 0; i < len; i++) { + out.push(semigroups[i].combine(x[i], y[i])) + } + return out + }) +} + +const productMany = ( + self: Semigroup, + collection: Iterable> +): Semigroup<[A, ...Array]> => { + const semigroup = productAll(collection) + return make((x, y) => [self.combine(x[0], y[0]), ...semigroup.combine(x.slice(1), y.slice(1))]) +} + /** * @category instances * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { - ...Invariant, - product: that => self => tuple(self, that), - productMany: collection => self => tuple(self, ...collection) + imap, + product, + productMany } +const of: (a: A) => Semigroup = constant + /** * @category instances * @since 1.0.0 */ -export const Product: product.Product = { - ...SemiProduct, - of: constant, - productAll: (collection: Iterable>) => tuple>(...collection) +export const Product: product_.Product = { + of, + imap, + product, + productMany, + productAll } + +/** + * Similar to `Promise.all` but operates on `Semigroup`s. + * + * ``` + * [Semigroup, Semigroup, ...] -> Semigroup<[A, B, ...]> + * ``` + * + * 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: >>( + ...elements: T +) => Semigroup<{ readonly [I in keyof T]: [T[I]] extends [Semigroup] ? A : never }> = + product_.tuple(Product) + +/** + * 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 array = (): Semigroup> => make((self, that) => self.concat(that)) + +/** + * 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: }>( + fields: R +) => Semigroup<{ readonly [K in keyof R]: [R[K]] extends [Semigroup] ? A : never }> = + product_.struct(Product) diff --git a/src/typeclass/Traversable.ts b/src/typeclass/Traversable.ts index 91b386a98..442e9d4e4 100644 --- a/src/typeclass/Traversable.ts +++ b/src/typeclass/Traversable.ts @@ -1,10 +1,9 @@ /** * @since 1.0.0 */ +import { dual, identity } 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" /** * @category type class @@ -13,17 +12,19 @@ import type { Covariant } from "@fp-ts/core/typeclass/Covariant" export interface Traversable extends TypeClass { readonly traverse: ( F: Applicative - ) => ( - f: (a: A) => Kind - ) => (self: Kind) => Kind> - - readonly sequence: (F: Applicative) => ( - self: Kind> - ) => Kind> + ) => { + ( + f: (a: A) => Kind + ): (self: Kind) => Kind> + ( + self: Kind, + f: (a: A) => Kind + ): Kind> + } } /** - * Returns a default `traverse` composition. + * Returns a default binary `traverse` composition. * * @since 1.0.0 */ @@ -32,39 +33,22 @@ export const traverseComposition = ( G: Traversable ) => (F: Applicative) => - ( + ( + self: Kind>, f: (a: A) => Kind - ): (( - self: Kind> - ) => Kind>>) => - T.traverse(F)(G.traverse(F)(f)) - -/** - * Returns a default `sequence` composition. - * - * @since 1.0.0 - */ -export const sequenceComposition = ( - T: Traversable & Covariant, - G: Traversable -) => - (F: Applicative) => - ( - self: Kind>> - ): Kind>> => - T.sequence(F)(pipe(self, T.map(G.sequence(F)))) + ): Kind>> => + T.traverse(F)(self, G.traverse(F)(f)) /** * Returns a default `sequence` implementation. * * @since 1.0.0 */ -export const sequence = ( - traverse: Traversable["traverse"] -): Traversable["sequence"] => - (F: Applicative): (( - self: Kind> - ) => Kind>) => traverse(F)(identity) +export const sequence = (T: Traversable) => + (F: Applicative) => + ( + self: Kind> + ): Kind> => T.traverse(F)(self, identity) /** * Given a function which returns a `F` effect, thread this effect @@ -75,7 +59,16 @@ export const sequence = ( * @since 1.0.0 */ export const traverseTap = (T: Traversable) => - (F: Applicative) => - (f: (a: A) => Kind): ( - self: Kind - ) => Kind> => T.traverse(F)(a => pipe(f(a), F.map(() => a))) + (F: Applicative): { + ( + f: (a: A) => Kind + ): (self: Kind) => Kind> + ( + self: Kind, + f: (a: A) => Kind + ): Kind> + } => + dual(2, ( + self: Kind, + f: (a: A) => Kind + ): Kind> => T.traverse(F)(self, a => F.map(f(a), () => a))) diff --git a/src/typeclass/TraversableFilterable.ts b/src/typeclass/TraversableFilterable.ts new file mode 100644 index 000000000..4e93bdb62 --- /dev/null +++ b/src/typeclass/TraversableFilterable.ts @@ -0,0 +1,128 @@ +/** + * `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 { dual } 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 { TraversableFilterable } from "@fp-ts/core/ReadonlyArray" +import type { Applicative } from "@fp-ts/core/typeclass/Applicative" +import type { Covariant } from "@fp-ts/core/typeclass/Covariant" +import * as filterable from "@fp-ts/core/typeclass/Filterable" +import type { Filterable } from "@fp-ts/core/typeclass/Filterable" +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]> + ( + self: Kind, + f: (a: A) => Kind> + ): Kind, Kind]> + } + + readonly traverseFilterMap: ( + F: Applicative + ) => { + ( + f: (a: A) => Kind> + ): (self: Kind) => Kind> + ( + self: Kind, + f: (a: A) => Kind> + ): Kind> + } +} + +/** + * Returns a default binary `traversePartitionMap` implementation. + * + * @since 1.0.0 + */ +export const traversePartitionMap = ( + T: Traversable & Covariant & Filterable +): ( + F: Applicative +) => ( + self: Kind, + f: (a: A) => Kind> +) => Kind, Kind]> => + (F) => (self, f) => F.map(T.traverse(F)(self, f), filterable.separate(T)) + +/** + * Returns a default binary `traverseFilterMap` implementation. + * + * @since 1.0.0 + */ +export const traverseFilterMap = ( + T: Traversable & Filterable +): ( + F: Applicative +) => ( + self: Kind, + f: (a: A) => Kind> +) => Kind> => + (F) => (self, f) => F.map(T.traverse(F)(self, f), filterable.compact(T)) + +/** + * @since 1.0.0 + */ +export const traverseFilter = ( + T: TraversableFilterable +) => + ( + F: Applicative + ): { + ( + predicate: (a: A) => Kind + ): ( + self: Kind + ) => Kind> + ( + self: Kind, + predicate: (a: A) => Kind + ): Kind> + } => + dual(2, ( + self: Kind, + predicate: (a: A) => Kind + ): Kind> => + T.traverseFilterMap(F)(self, (b) => + F.map(predicate(b), (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]> + ( + self: Kind, + predicate: (a: A) => Kind + ): Kind, Kind]> + } => + dual(2, (self, predicate) => + T.traversePartitionMap(F)(self, (b) => + F.map(predicate(b), (keep) => (keep ? E.right(b) : E.left(b))))) diff --git a/test/Bigint.ts b/test/Bigint.ts new file mode 100644 index 000000000..a6ffc7fee --- /dev/null +++ b/test/Bigint.ts @@ -0,0 +1,86 @@ +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("exports", () => { + expect(Bigint.SemigroupMax).exists + expect(Bigint.SemigroupMin).exists + expect(Bigint.sumAll).exists + expect(Bigint.multiplyAll).exists + expect(Bigint.lessThan).exists + expect(Bigint.lessThanOrEqualTo).exists + expect(Bigint.greaterThan).exists + expect(Bigint.greaterThanOrEqualTo).exists + expect(Bigint.between).exists + expect(Bigint.clamp).exists + expect(Bigint.min).exists + expect(Bigint.max).exists + }) + + it("sign", () => { + assert.deepStrictEqual(Bigint.sign(-5n), -1) + assert.deepStrictEqual(Bigint.sign(0n), 0) + assert.deepStrictEqual(Bigint.sign(5n), 1) + }) + + it("isBigint", () => { + 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(pipe(1n, Bigint.sum(2n)), 3n) + }) + + it("multiply", () => { + deepStrictEqual(pipe(2n, Bigint.multiply(3n)), 6n) + }) + + it("subtract", () => { + deepStrictEqual(pipe(3n, Bigint.subtract(1n)), 2n) + }) + + it("divide", () => { + deepStrictEqual(pipe(6n, Bigint.divide(2n)), 3n) + }) + + 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(Bigint.Order.compare(1n, 2n), -1) + deepStrictEqual(Bigint.Order.compare(2n, 1n), 1) + deepStrictEqual(Bigint.Order.compare(2n, 2n), 0) + }) + + it("SemigroupSum", () => { + deepStrictEqual(Bigint.SemigroupSum.combine(2n, 3n), 5n) + }) + + it("MonoidSum", () => { + deepStrictEqual(Bigint.MonoidSum.combineAll([1n, 2n, 3n]), 6n) + }) + + it("SemigroupMultiply", () => { + deepStrictEqual(Bigint.SemigroupMultiply.combine(2n, 3n), 6n) + deepStrictEqual(Bigint.SemigroupMultiply.combineMany(0n, [1n, 2n, 3n]), 0n) + deepStrictEqual(Bigint.SemigroupMultiply.combineMany(2n, [1n, 0n, 3n]), 0n) + }) + + it("MonoidMultiply", () => { + deepStrictEqual(Bigint.MonoidMultiply.combineAll([2n, 3n, 4n]), 24n) + }) +}) diff --git a/test/Boolean.ts b/test/Boolean.ts new file mode 100644 index 000000000..fdbf97c24 --- /dev/null +++ b/test/Boolean.ts @@ -0,0 +1,178 @@ +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("exports", () => { + expect(Boolean.SemigroupAll).exist + expect(Boolean.MonoidAll).exist + expect(Boolean.SemigroupAny).exist + expect(Boolean.MonoidAny).exist + expect(Boolean.SemigroupXor).exist + expect(Boolean.MonoidXor).exist + expect(Boolean.SemigroupEqv).exist + expect(Boolean.MonoidEqv).exist + expect(Boolean.all).exist + expect(Boolean.any).exist + expect(Boolean.xor).exist + expect(Boolean.eqv).exist + expect(Boolean.nand).exist + expect(Boolean.nor).exist + expect(Boolean.implies).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("nand", () => { + deepStrictEqual(pipe(true, Boolean.nand(true)), false) + deepStrictEqual(pipe(true, Boolean.nand(false)), true) + deepStrictEqual(pipe(false, Boolean.nand(true)), true) + deepStrictEqual(pipe(false, Boolean.nand(false)), true) + }) + + 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) + }) + + it("nor", () => { + deepStrictEqual(pipe(true, Boolean.nor(true)), false) + deepStrictEqual(pipe(true, Boolean.nor(false)), false) + deepStrictEqual(pipe(false, Boolean.nor(true)), false) + deepStrictEqual(pipe(false, Boolean.nor(false)), true) + }) + + it("xor", () => { + deepStrictEqual(pipe(true, Boolean.xor(true)), false) + deepStrictEqual(pipe(true, Boolean.xor(false)), true) + deepStrictEqual(pipe(false, Boolean.xor(true)), true) + deepStrictEqual(pipe(false, Boolean.xor(false)), false) + }) + + it("eqv", () => { + deepStrictEqual(pipe(true, Boolean.eqv(true)), true) + deepStrictEqual(pipe(true, Boolean.eqv(false)), false) + deepStrictEqual(pipe(false, Boolean.eqv(true)), false) + deepStrictEqual(pipe(false, Boolean.eqv(false)), true) + }) + + it("implies", () => { + deepStrictEqual(pipe(true, Boolean.implies(true)), true) + deepStrictEqual(pipe(true, Boolean.implies(false)), false) + deepStrictEqual(pipe(false, Boolean.implies(true)), true) + deepStrictEqual(pipe(false, Boolean.implies(false)), true) + }) + + it("not", () => { + deepStrictEqual(pipe(true, Boolean.not), false) + deepStrictEqual(pipe(false, Boolean.not), true) + }) + + describe.concurrent("MonoidXor", () => { + it("baseline", () => { + deepStrictEqual(Boolean.MonoidXor.combineMany(true, []), true) + deepStrictEqual(Boolean.MonoidXor.combineMany(false, []), false) + deepStrictEqual(Boolean.MonoidXor.combineMany(false, [true]), true) + deepStrictEqual(Boolean.MonoidXor.combineMany(false, [false]), false) + deepStrictEqual(Boolean.MonoidXor.combineMany(true, [true]), false) + deepStrictEqual(Boolean.MonoidXor.combineMany(true, [false]), true) + deepStrictEqual(Boolean.MonoidXor.combineMany(true, [true, false]), false) + deepStrictEqual(Boolean.MonoidXor.combineMany(true, [false, true]), false) + deepStrictEqual(Boolean.MonoidXor.combineAll([true, false]), true) + deepStrictEqual(Boolean.MonoidXor.combineAll([false, true]), true) + }) + + it("should handle iterables", () => { + deepStrictEqual(Boolean.MonoidXor.combineAll(new Set([true, true])), true) + deepStrictEqual(Boolean.MonoidXor.combineAll(new Set([true, false])), true) + deepStrictEqual(Boolean.MonoidXor.combineAll(new Set([false, false])), false) + }) + }) + + describe.concurrent("MonoidEqv", () => { + it("baseline", () => { + deepStrictEqual(Boolean.MonoidEqv.combineMany(true, []), true) + deepStrictEqual(Boolean.MonoidEqv.combineMany(false, []), false) + deepStrictEqual(Boolean.MonoidEqv.combineMany(false, [true]), false) + deepStrictEqual(Boolean.MonoidEqv.combineMany(false, [false]), true) + deepStrictEqual(Boolean.MonoidEqv.combineMany(true, [true]), true) + deepStrictEqual(Boolean.MonoidEqv.combineMany(true, [false]), false) + deepStrictEqual(Boolean.MonoidEqv.combineMany(true, [true, false]), false) + deepStrictEqual(Boolean.MonoidEqv.combineMany(true, [false, true]), false) + deepStrictEqual(Boolean.MonoidEqv.combineAll([true, false]), false) + deepStrictEqual(Boolean.MonoidEqv.combineAll([false, true]), false) + }) + + it("should handle iterables", () => { + deepStrictEqual(Boolean.MonoidEqv.combineAll(new Set([true, true])), true) + deepStrictEqual(Boolean.MonoidEqv.combineAll(new Set([true, false])), false) + deepStrictEqual(Boolean.MonoidEqv.combineAll(new Set([false, false])), false) + }) + }) + + describe.concurrent("MonoidAll", () => { + it("baseline", () => { + 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) + }) + + 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, [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) + }) + + 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("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(Boolean.Order.compare(false, true), -1) + deepStrictEqual(Boolean.Order.compare(true, false), 1) + deepStrictEqual(Boolean.Order.compare(true, true), 0) + }) +}) diff --git a/test/Either.ts b/test/Either.ts new file mode 100644 index 000000000..ccbbdda5f --- /dev/null +++ b/test/Either.ts @@ -0,0 +1,528 @@ +import * as E from "@fp-ts/core/Either" +import { flow, identity, pipe } from "@fp-ts/core/Function" +import { structural } from "@fp-ts/core/internal/effect" +import * as N from "@fp-ts/core/Number" +import * as O from "@fp-ts/core/Option" +import * as S from "@fp-ts/core/String" +import * as Util from "@fp-ts/core/test/util" + +describe.concurrent("Either", () => { + it("exports", () => { + expect(E.toOption).exist + expect(E.getRight).exist + expect(E.getLeft).exist + + expect(E.Invariant).exist + expect(E.tupled).exist + expect(E.bindTo).exist + + expect(E.Covariant).exist + expect(E.map).exist + expect(E.let).exist + expect(E.flap).exist + expect(E.as).exist + expect(E.asUnit).exist + + expect(E.Bicovariant).exist + expect(E.bimap).exist + expect(E.mapLeft).exist + + expect(E.Of).exist + expect(E.of).exist + expect(E.unit).exist + expect(E.Do).exist + + expect(E.Pointed).exist + + expect(E.FlatMap).exist + expect(E.flatMap).exist + expect(E.flatten).exist + expect(E.andThen).exist + expect(E.composeKleisliArrow).exist + + expect(E.Chainable).exist + expect(E.bind).exist + expect(E.tap).exist + expect(E.andThenDiscard).exist + + expect(E.Monad).exist + + expect(E.SemiProduct).exist + + expect(E.Product).exist + expect(E.all).exist + expect(E.tuple).exist + expect(E.struct).exist + + expect(E.SemiApplicative).exist + expect(E.getFirstLeftSemigroup).exist // liftSemigroup + expect(E.lift2).exist + expect(E.ap).exist + expect(E.andThenDiscard).exist + expect(E.andThen).exist + + expect(E.Applicative).exist + expect(E.getFirstLeftMonoid).exist // liftMonoid + + expect(E.SemiCoproduct).exist + expect(E.getFirstRightSemigroup).exist // getSemigroup + + expect(E.SemiAlternative).exist + + expect(E.Foldable).exist + + expect(E.Traversable).exist + expect(E.traverse).exist + expect(E.sequence).exist + expect(E.traverseTap).exist + }) + + it("structural tracking", () => { + expect(Util.ownKeys(E.left("a"))).toEqual(["_tag", "left"]) + expect(Util.ownKeys(E.right(1))).toEqual(["_tag", "right"]) + + expect(Object.prototype.hasOwnProperty.call(E.left("a"), structural)).toEqual(false) + expect(Object.prototype.hasOwnProperty.call(E.right(1), structural)).toEqual(false) + + expect(Util.isStructural(E.left("a"))).toEqual(true) + expect(Util.isStructural(E.right(1))).toEqual(true) + }) + + it("toRefinement", () => { + const f = (s: string | number): E.Either => + typeof s === "string" ? E.right(s) : E.left("not a string") + const isString = E.toRefinement(f) + Util.deepStrictEqual(isString("s"), true) + Util.deepStrictEqual(isString(1), false) + type A = { readonly type: "A" } + type B = { readonly type: "B" } + type C = A | B + const isA = E.toRefinement(( + c: C + ) => (c.type === "A" ? E.right(c) : E.left("not as A"))) + Util.deepStrictEqual(isA({ type: "A" }), true) + Util.deepStrictEqual(isA({ type: "B" }), false) + }) + + it("isEither", () => { + Util.deepStrictEqual(pipe(E.right(1), E.isEither), true) + Util.deepStrictEqual(pipe(E.left("e"), E.isEither), true) + Util.deepStrictEqual(pipe(O.some(1), E.isEither), false) + }) + + it("orElseFail", () => { + Util.deepStrictEqual(pipe(E.right(1), E.orElseFail(() => "e2")), E.right(1)) + Util.deepStrictEqual(pipe(E.left("e1"), E.orElseFail(() => "e2")), E.left("e2")) + }) + + it("reduce", () => { + Util.deepStrictEqual(pipe(E.right("bar"), E.Foldable.reduce("foo", (b, a) => b + a)), "foobar") + Util.deepStrictEqual(pipe(E.left("bar"), E.Foldable.reduce("foo", (b, a) => b + a)), "foo") + }) + + it("getRight", () => { + Util.deepStrictEqual(pipe(E.right(1), E.getRight), O.some(1)) + Util.deepStrictEqual(pipe(E.left("a"), E.getRight), O.none()) + }) + + it("getLeft", () => { + Util.deepStrictEqual(pipe(E.right(1), E.getLeft), O.none()) + Util.deepStrictEqual(pipe(E.left("e"), E.getLeft), O.some("e")) + }) + + it("getOrNull", () => { + Util.deepStrictEqual(pipe(E.right(1), E.getOrNull), 1) + Util.deepStrictEqual(pipe(E.left("a"), E.getOrNull), null) + }) + + it("getOrUndefined", () => { + Util.deepStrictEqual(pipe(E.right(1), E.getOrUndefined), 1) + Util.deepStrictEqual(pipe(E.left("a"), E.getOrUndefined), undefined) + }) + + it("compact", () => { + Util.deepStrictEqual(pipe(E.right(O.some(1)), E.compact(() => "e2")), E.right(1)) + Util.deepStrictEqual(pipe(E.right(O.none()), E.compact(() => "e2")), E.left("e2")) + Util.deepStrictEqual(pipe(E.left("e1"), E.compact(() => "e2")), E.left("e1")) + }) + + it("inspectRight", () => { + const log: Array = [] + pipe(E.right(1), E.inspectRight((e) => log.push(e))) + pipe(E.left("e"), E.inspectRight((e) => log.push(e))) + Util.deepStrictEqual(log, [1]) + }) + + it("tapError", () => { + Util.deepStrictEqual(pipe(E.right(1), E.tapError(() => E.right(2))), E.right(1)) + Util.deepStrictEqual(pipe(E.left("a"), E.tapError(() => E.right(2))), E.left("a")) + Util.deepStrictEqual(pipe(E.left("a"), E.tapError(() => E.left("b"))), E.left("b")) + }) + + it("inspectLeft", () => { + const log: Array = [] + pipe(E.right(1), E.inspectLeft((e) => log.push(e))) + pipe(E.left("e"), E.inspectLeft((e) => log.push(e))) + Util.deepStrictEqual(log, ["e"]) + }) + + it("getOrThrow", () => { + expect(pipe(E.right(1), E.getOrThrow)).toEqual(1) + expect(() => pipe(E.left("e"), E.getOrThrow)).toThrowError( + new Error("getOrThrow called on a Left") + ) + }) + + it("getOrThrowWith", () => { + expect(pipe(E.right(1), E.getOrThrowWith((e) => new Error(`Unexpected Left: ${e}`)))).toEqual(1) + expect(() => pipe(E.left("e"), E.getOrThrowWith((e) => new Error(`Unexpected Left: ${e}`)))) + .toThrowError( + new Error("Unexpected Left: e") + ) + }) + + it("andThenDiscard", () => { + Util.deepStrictEqual(pipe(E.right(1), E.andThenDiscard(E.right("a"))), E.right(1)) + Util.deepStrictEqual(pipe(E.right(1), E.andThenDiscard(E.left(true))), E.left(true)) + Util.deepStrictEqual(pipe(E.left(1), E.andThenDiscard(E.right("a"))), E.left(1)) + Util.deepStrictEqual(pipe(E.left(1), E.andThenDiscard(E.left(true))), E.left(1)) + }) + + it("andThen", () => { + Util.deepStrictEqual(pipe(E.right(1), E.andThen(E.right("a"))), E.right("a")) + Util.deepStrictEqual(pipe(E.right(1), E.andThen(E.left(true))), E.left(true)) + Util.deepStrictEqual(pipe(E.left(1), E.andThen(E.right("a"))), E.left(1)) + Util.deepStrictEqual(pipe(E.left(1), E.andThen(E.left(true))), E.left(1)) + }) + + it("orElse", () => { + Util.deepStrictEqual(pipe(E.right(1), E.orElse(() => E.right(2))), E.right(1)) + Util.deepStrictEqual(pipe(E.right(1), E.orElse(() => E.left("b"))), E.right(1)) + Util.deepStrictEqual(pipe(E.left("a"), E.orElse(() => E.right(2))), E.right(2)) + Util.deepStrictEqual(pipe(E.left("a"), E.orElse(() => E.left("b"))), E.left("b")) + }) + + it("orElseEither", () => { + expect(pipe(E.right(1), E.orElseEither(() => E.right(2)))).toEqual(E.right(E.left(1))) + expect(pipe(E.right(1), E.orElseEither(() => E.left("b")))).toEqual(E.right(E.left(1))) + expect(pipe(E.left("a"), E.orElseEither(() => E.right(2)))).toEqual(E.right(E.right(2))) + expect(pipe(E.left("a"), E.orElseEither(() => E.left("b")))).toEqual(E.left("b")) + }) + + it("map", () => { + const f = E.map(S.length) + Util.deepStrictEqual(pipe(E.right("abc"), f), E.right(3)) + Util.deepStrictEqual(pipe(E.left("s"), f), E.left("s")) + }) + + it("flatMap", () => { + const f = E.flatMap(flow(S.length, E.right)) + Util.deepStrictEqual(pipe(E.right("abc"), f), E.right(3)) + Util.deepStrictEqual(pipe(E.left("maError"), f), E.left("maError")) + }) + + it("bimap", () => { + const f = E.bimap(S.length, (n: number) => n > 2) + Util.deepStrictEqual(pipe(E.right(1), f), E.right(false)) + }) + + it("mapLeft", () => { + const f = E.mapLeft(Util.double) + Util.deepStrictEqual(pipe(E.right("a"), f), E.right("a")) + Util.deepStrictEqual(pipe(E.left(1), f), E.left(2)) + }) + + it("traverse", () => { + const traverse = E.traverse(O.Applicative)(( + n: number + ) => (n >= 2 ? O.some(n) : O.none())) + Util.deepStrictEqual(pipe(E.left("a"), traverse), O.some(E.left("a"))) + Util.deepStrictEqual(pipe(E.right(1), traverse), O.none()) + Util.deepStrictEqual(pipe(E.right(3), traverse), O.some(E.right(3))) + }) + + it("sequence", () => { + const sequence = E.sequence(O.Applicative) + Util.deepStrictEqual(sequence(E.right(O.some(1))), O.some(E.right(1))) + Util.deepStrictEqual(sequence(E.left("a")), O.some(E.left("a"))) + Util.deepStrictEqual(sequence(E.right(O.none())), O.none()) + }) + + it("match", () => { + const f = (s: string) => `left${s.length}` + const g = (s: string) => `right${s.length}` + const match = E.match(f, g) + Util.deepStrictEqual(match(E.left("abc")), "left3") + Util.deepStrictEqual(match(E.right("abc")), "right3") + }) + + it("getOrElse", () => { + Util.deepStrictEqual(pipe(E.right(12), E.getOrElse(() => 17)), 12) + Util.deepStrictEqual(pipe(E.left("a"), E.getOrElse(() => 17)), 17) + }) + + it("contains", () => { + const contains = E.contains(N.Equivalence) + Util.deepStrictEqual(pipe(E.left("a"), contains(2)), false) + Util.deepStrictEqual(pipe(E.right(2), contains(2)), true) + Util.deepStrictEqual(pipe(E.right(2), contains(1)), false) + }) + + it("filter", () => { + const predicate = (n: number) => n > 10 + Util.deepStrictEqual(pipe(E.right(12), E.filter(predicate, () => -1)), E.right(12)) + Util.deepStrictEqual(pipe(E.right(7), E.filter(predicate, () => -1)), E.left(-1)) + Util.deepStrictEqual(pipe(E.left(12), E.filter(predicate, () => -1)), E.left(12)) + }) + + it("isLeft", () => { + Util.deepStrictEqual(E.isLeft(E.right(1)), false) + Util.deepStrictEqual(E.isLeft(E.left(1)), true) + }) + + it("isRight", () => { + Util.deepStrictEqual(E.isRight(E.right(1)), true) + Util.deepStrictEqual(E.isRight(E.left(1)), false) + }) + + it("swap", () => { + Util.deepStrictEqual(E.reverse(E.right("a")), E.left("a")) + Util.deepStrictEqual(E.reverse(E.left("b")), E.right("b")) + }) + + it("liftPredicate", () => { + const f = E.liftPredicate((n: number) => n >= 2, () => "e") + Util.deepStrictEqual(f(3), E.right(3)) + Util.deepStrictEqual(f(1), E.left("e")) + }) + + it("fromNullable", () => { + Util.deepStrictEqual(E.fromNullable(() => "default")(null), E.left("default")) + Util.deepStrictEqual(E.fromNullable(() => "default")(undefined), E.left("default")) + Util.deepStrictEqual(E.fromNullable(() => "default")(1), E.right(1)) + }) + + it("filterMap", () => { + const p = (n: number) => n > 2 + const f = (n: number) => (p(n) ? O.some(n + 1) : O.none()) + Util.deepStrictEqual(pipe(E.left("123"), E.filterMap(f, () => "")), E.left("123")) + Util.deepStrictEqual(pipe(E.right(1), E.filterMap(f, () => "")), E.left(S.Monoid.empty)) + Util.deepStrictEqual(pipe(E.right(3), E.filterMap(f, () => "")), E.right(4)) + }) + + it("fromIterable", () => { + Util.deepStrictEqual(E.fromIterable(() => "e")([]), E.left("e")) + Util.deepStrictEqual(E.fromIterable(() => "e")(["a"]), E.right("a")) + }) + + it("firstRightOf", () => { + Util.deepStrictEqual(pipe(E.right(1), E.firstRightOf([])), E.right(1)) + Util.deepStrictEqual(pipe(E.left("e"), E.firstRightOf([])), E.left("e")) + Util.deepStrictEqual( + pipe(E.left("e1"), E.firstRightOf([E.left("e2"), E.left("e3"), E.left("e4"), E.right(1)])), + E.right(1) + ) + Util.deepStrictEqual( + pipe(E.left("e1"), E.firstRightOf([E.left("e2"), E.left("e3"), E.left("e4")])), + E.left("e4") + ) + }) + + it("fromOption", () => { + Util.deepStrictEqual(E.fromOption(() => "none")(O.none()), E.left("none")) + Util.deepStrictEqual(E.fromOption(() => "none")(O.some(1)), E.right(1)) + }) + + it("liftOption", () => { + const f = E.liftOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "a") + Util.deepStrictEqual(f(1), E.right(1)) + Util.deepStrictEqual(f(-1), E.left("a")) + }) + + it("flatMapOption", () => { + const f = E.flatMapOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "a") + Util.deepStrictEqual(f(E.right(1)), E.right(1)) + Util.deepStrictEqual(f(E.right(-1)), E.left("a")) + Util.deepStrictEqual(f(E.left("b")), E.left("b")) + }) + + it("exists", () => { + const gt2 = E.exists((n: number) => n > 2) + Util.deepStrictEqual(gt2(E.left("a")), false) + Util.deepStrictEqual(gt2(E.right(1)), false) + Util.deepStrictEqual(gt2(E.right(3)), true) + }) + + it("do notation", () => { + Util.deepStrictEqual( + pipe( + E.right(1), + E.bindTo("a"), + E.bind("b", () => E.right("b")), + E.let("c", ({ a, b }) => [a, b]) + ), + E.right({ a: 1, b: "b", c: [1, "b"] }) + ) + }) + + it("andThenBind", () => { + Util.deepStrictEqual( + pipe(E.right(1), E.bindTo("a"), E.andThenBind("b", E.right("b"))), + E.right({ a: 1, b: "b" }) + ) + }) + + it("product", () => { + const product = E.SemiProduct.product + Util.deepStrictEqual(product(E.right(1), E.right("a")), E.right([1, "a"])) + Util.deepStrictEqual(product(E.right(1), E.left("e2")), E.left("e2")) + Util.deepStrictEqual(product(E.left("e1"), E.right("a")), E.left("e1")) + Util.deepStrictEqual(product(E.left("e1"), E.left("2")), E.left("e1")) + }) + + it("productMany", () => { + const productMany: ( + self: E.Either, + collection: Iterable> + ) => E.Either]> = E.SemiProduct.productMany + + Util.deepStrictEqual(productMany(E.right(1), []), E.right([1])) + Util.deepStrictEqual( + productMany(E.right(1), [E.right(2), E.right(3)]), + E.right([1, 2, 3]) + ) + Util.deepStrictEqual( + productMany(E.right(1), [E.left("e"), E.right(3)]), + E.left("e") + ) + expect( + productMany(E.left("e"), [E.right(2), E.right(3)]) + ).toEqual(E.left("e")) + }) + + it("productAll", () => { + const productAll = E.Product.productAll + Util.deepStrictEqual(productAll([]), E.right([])) + Util.deepStrictEqual( + productAll([E.right(1), E.right(2), E.right(3)]), + E.right([1, 2, 3]) + ) + Util.deepStrictEqual( + productAll([E.left("e"), E.right(2), E.right(3)]), + E.left("e") + ) + }) + + it("coproduct", () => { + const coproduct = E.SemiCoproduct.coproduct + Util.deepStrictEqual(coproduct(E.right(1), E.right(2)), E.right(1)) + Util.deepStrictEqual(coproduct(E.right(1), E.left("e2")), E.right(1)) + Util.deepStrictEqual(coproduct(E.left("e1"), E.right(2)), E.right(2)) + Util.deepStrictEqual(coproduct(E.left("e1"), E.left("e2")), E.left("e2")) + }) + + it("coproductMany", () => { + const coproductMany = E.SemiCoproduct.coproductMany + Util.deepStrictEqual(coproductMany(E.right(1), [E.right(2)]), E.right(1)) + Util.deepStrictEqual( + coproductMany(E.right(1), [E.left("e2")]), + E.right(1) + ) + Util.deepStrictEqual(coproductMany(E.left("e1"), [E.right(2)]), E.right(2)) + Util.deepStrictEqual(coproductMany(E.left("e1"), [E.left("e2")]), E.left("e2")) + }) + + it("element", () => { + expect(pipe(E.right(1), E.tupled, E.appendElement(E.right("b")))).toEqual( + E.right([1, "b"]) + ) + }) + + it("liftNullable", () => { + const f = E.liftNullable((n: number) => (n > 0 ? n : null), () => "error") + Util.deepStrictEqual(f(1), E.right(1)) + Util.deepStrictEqual(f(-1), E.left("error")) + }) + + it("flatMapNullable", () => { + const f = E.flatMapNullable((n: number) => (n > 0 ? n : null), () => "error") + Util.deepStrictEqual(f(E.right(1)), E.right(1)) + Util.deepStrictEqual(f(E.right(-1)), E.left("error")) + Util.deepStrictEqual(f(E.left("a")), E.left("a")) + }) + + it("merge", () => { + Util.deepStrictEqual(E.merge(E.right(1)), 1) + Util.deepStrictEqual(E.merge(E.left("a")), "a") + }) + + it("liftThrowable", () => { + const f = E.liftThrowable((s: string) => { + const len = s.length + if (len > 0) { + return len + } + throw new Error("empty string") + }, identity) + Util.deepStrictEqual(f("a"), E.right(1)) + Util.deepStrictEqual(f(""), E.left(new Error("empty string"))) + }) + + it("zipWith", () => { + expect(pipe(E.left("a"), E.zipWith(E.right(2), (a, b) => a + b))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.zipWith(E.left("a"), (a, b) => a + b))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.zipWith(E.right(2), (a, b) => a + b))).toEqual(E.right(3)) + }) + + it("sum", () => { + expect(pipe(E.left("a"), E.sum(E.right(2)))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.sum(E.left("a")))).toEqual(E.left("a")) + expect(pipe(E.right(2), E.sum(E.right(3)))).toEqual(E.right(5)) + }) + + it("multiply", () => { + expect(pipe(E.left("a"), E.multiply(E.right(2)))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.multiply(E.left("a")))).toEqual(E.left("a")) + expect(pipe(E.right(2), E.multiply(E.right(3)))).toEqual(E.right(6)) + }) + + it("subtract", () => { + expect(pipe(E.left("a"), E.subtract(E.right(2)))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.subtract(E.left("a")))).toEqual(E.left("a")) + expect(pipe(E.right(2), E.subtract(E.right(3)))).toEqual(E.right(-1)) + }) + + it("divide", () => { + expect(pipe(E.left("a"), E.divide(E.right(2)))).toEqual(E.left("a")) + expect(pipe(E.right(1), E.divide(E.left("a")))).toEqual(E.left("a")) + expect(pipe(E.right(6), E.divide(E.right(3)))).toEqual(E.right(2)) + }) + + it("getOptionalSemigroup", () => { + const OS = E.getOptionalSemigroup(S.Semigroup) + Util.deepStrictEqual(OS.combine(E.left("e"), E.left("e")), E.left("e")) + Util.deepStrictEqual(OS.combine(E.left("e"), E.right("a")), E.right("a")) + Util.deepStrictEqual(OS.combine(E.right("a"), E.left("e")), E.right("a")) + Util.deepStrictEqual(OS.combine(E.right("b"), E.right("a")), E.right("ba")) + Util.deepStrictEqual(OS.combine(E.right("a"), E.right("b")), E.right("ab")) + + Util.deepStrictEqual(OS.combineMany(E.right("a"), [E.right("b")]), E.right("ab")) + Util.deepStrictEqual(OS.combineMany(E.left("e"), [E.right("b")]), E.right("b")) + Util.deepStrictEqual(OS.combineMany(E.right("a"), [E.left("e")]), E.right("a")) + }) + + it("getEquivalence", () => { + const isEquivalent = E.getEquivalence(S.Equivalence, N.Equivalence) + Util.deepStrictEqual(isEquivalent(E.right(1), E.right(1)), true) + Util.deepStrictEqual(isEquivalent(E.right(1), E.right(2)), false) + Util.deepStrictEqual(isEquivalent(E.right(1), E.left("foo")), false) + Util.deepStrictEqual(isEquivalent(E.left("foo"), E.left("foo")), true) + Util.deepStrictEqual(isEquivalent(E.left("foo"), E.left("bar")), false) + Util.deepStrictEqual(isEquivalent(E.left("foo"), E.right(1)), false) + }) + + it("toArray", () => { + expect(E.toArray(E.right(1))).toEqual([1]) + expect(E.toArray(E.left("error"))).toEqual([]) + }) +}) diff --git a/test/Function.ts b/test/Function.ts new file mode 100644 index 000000000..1fc57f71c --- /dev/null +++ b/test/Function.ts @@ -0,0 +1,134 @@ +import * as _ from "@fp-ts/core/Function" +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 + +describe.concurrent("Function", () => { + it("apply", () => { + deepStrictEqual(_.pipe(String.length, _.apply("a")), 1) + }) + + it("compose", () => { + deepStrictEqual(_.pipe(String.length, _.compose(double))("aaa"), 6) + deepStrictEqual(_.compose(String.length, double)("aaa"), 6) + }) + + it("flip", () => { + const f = (a: number) => (b: string) => a - b.length + const g = (a: number, i = 0) => (b: number) => a ** b + i + + deepStrictEqual(_.flip(f)("aaa")(2), -1) + deepStrictEqual(_.flip(g)(2)(2, 1), 5) + }) + + it("unsafeCoerce", () => { + deepStrictEqual(_.unsafeCoerce, _.identity) + }) + + it("constant", () => { + deepStrictEqual(_.constant("a")(), "a") + }) + + it("constTrue", () => { + deepStrictEqual(_.constTrue(), true) + }) + + it("constFalse", () => { + deepStrictEqual(_.constFalse(), false) + }) + + it("constNull", () => { + deepStrictEqual(_.constNull(), null) + }) + + it("constUndefined", () => { + deepStrictEqual(_.constUndefined(), undefined) + }) + + it("constVoid", () => { + deepStrictEqual(_.constVoid(), undefined) + }) + + it("absurd", () => { + assert.throws(() => _.absurd(null as any as never)) + }) + + it("hole", () => { + assert.throws(() => _.hole()) + }) + + it("SK", () => { + expect(_.SK(1, 2)).toEqual(2) + }) + + it("flow", () => { + deepStrictEqual(_.flow(f)(2), 3) + deepStrictEqual(_.flow(f, g)(2), 6) + deepStrictEqual(_.flow(f, g, f)(2), 7) + deepStrictEqual(_.flow(f, g, f, g)(2), 14) + deepStrictEqual(_.flow(f, g, f, g, f)(2), 15) + deepStrictEqual(_.flow(f, g, f, g, f, g)(2), 30) + deepStrictEqual(_.flow(f, g, f, g, f, g, f)(2), 31) + deepStrictEqual(_.flow(f, g, f, g, f, g, f, g)(2), 62) + deepStrictEqual(_.flow(f, g, f, g, f, g, f, g, f)(2), 63) + // this is just to satisfy noImplicitReturns and 100% coverage + deepStrictEqual((_.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 = _.tupled(f1) + const u2 = _.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 = _.untupled(f1) + const u2 = _.untupled(f2) + deepStrictEqual(u1(1), 2) + deepStrictEqual(u2(1, 2), 3) + }) + + it("pipe", () => { + deepStrictEqual(_.pipe(2), 2) + deepStrictEqual(_.pipe(2, f), 3) + deepStrictEqual(_.pipe(2, f, g), 6) + deepStrictEqual(_.pipe(2, f, g, f), 7) + deepStrictEqual(_.pipe(2, f, g, f, g), 14) + deepStrictEqual(_.pipe(2, f, g, f, g, f), 15) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g), 30) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f), 31) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g), 62) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f), 63) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g), 126) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f), 127) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g), 254) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f), 255) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 510) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 511) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 1022) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 1023) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 2046) + deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 2047) + 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 + ) + }) + + it("dual", () => { + const f = _.dual< + (that: number) => (self: number) => number, + (self: number, that: number) => number + >(2, (a: number, b: number): number => a - b) + deepStrictEqual(f(3, 2), 1) + deepStrictEqual(_.pipe(3, f(2)), 1) + }) +}) diff --git a/test/Identity.ts b/test/Identity.ts new file mode 100644 index 000000000..6d8f7cbd2 --- /dev/null +++ b/test/Identity.ts @@ -0,0 +1,78 @@ +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(_.Covariant).exist + expect(_.Of).exist + expect(_.Pointed).exist + expect(_.FlatMap).exist + expect(_.Chainable).exist + expect(_.Monad).exist + expect(_.SemiProduct).exist + expect(_.Product).exist + expect(_.SemiApplicative).exist + expect(_.Applicative).exist + expect(_.Foldable).exist + expect(_.Traversable).exist + + expect(_.bindTo).exist + expect(_.let).exist + expect(_.Do).exist + expect(_.bind).exist + }) + + it("Of", () => { + U.deepStrictEqual(_.Of.of("a"), "a") + }) + + it("SemiProduct", () => { + U.deepStrictEqual(_.SemiProduct.productMany("a", ["b", "c"]), ["a", "b", "c"]) + }) + + it("Product", () => { + U.deepStrictEqual(_.Product.productAll([]), []) + U.deepStrictEqual(_.Product.productAll(["a", "b", "c"]), ["a", "b", "c"]) + }) + + it("Covariant", () => { + assert.deepStrictEqual(_.Covariant.map(1, n => n * 2), 2) + }) + + it("FlatMap", () => { + U.deepStrictEqual( + pipe("a", _.FlatMap.flatMap((a) => a + "b")), + "ab" + ) + }) + + it("SemiProduct", () => { + const product = _.SemiProduct.product + U.deepStrictEqual(product("a", "b"), ["a", "b"]) + }) + + it("getSemiCoproduct", () => { + const F = _.getSemiCoproduct(String.Semigroup) + U.deepStrictEqual(F.coproduct("a", "b"), "ab") + U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") + }) + + it("getSemiAlternative", () => { + const F = _.getSemiAlternative(String.Semigroup) + U.deepStrictEqual(F.coproduct("a", "b"), "ab") + U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") + }) + + it("Foldable", () => { + U.deepStrictEqual(pipe("b", _.Foldable.reduce("a", (b, a) => b + a)), "ab") + }) + + it("Traversable", () => { + 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/Number.ts b/test/Number.ts new file mode 100644 index 000000000..b5d61d3e9 --- /dev/null +++ b/test/Number.ts @@ -0,0 +1,112 @@ +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 + expect(Number.sumAll).exists + expect(Number.multiplyAll).exists + expect(Number.lessThan).exists + expect(Number.lessThanOrEqualTo).exists + expect(Number.greaterThan).exists + expect(Number.greaterThanOrEqualTo).exists + expect(Number.between).exists + expect(Number.clamp).exists + expect(Number.min).exists + expect(Number.max).exists + }) + + it("isNumber", () => { + expect(Number.isNumber(1)).toEqual(true) + expect(Number.isNumber("a")).toEqual(false) + expect(Number.isNumber(true)).toEqual(false) + }) + + it("sum", () => { + deepStrictEqual(pipe(1, Number.sum(2)), 3) + }) + + it("multiply", () => { + deepStrictEqual(pipe(2, Number.multiply(3)), 6) + }) + + it("subtract", () => { + deepStrictEqual(pipe(3, Number.subtract(1)), 2) + }) + + it("divide", () => { + deepStrictEqual(pipe(6, Number.divide(2)), 3) + }) + + it("increment", () => { + deepStrictEqual(Number.increment(2), 3) + }) + + it("decrement", () => { + deepStrictEqual(Number.decrement(2), 1) + }) + + it("Equivalence", () => { + expect(Number.Equivalence(1, 1)).toBe(true) + expect(Number.Equivalence(1, 2)).toBe(false) + }) + + it("Order", () => { + deepStrictEqual(Number.Order.compare(1, 2), -1) + deepStrictEqual(Number.Order.compare(2, 1), 1) + deepStrictEqual(Number.Order.compare(2, 2), 0) + }) + + it("Bounded", () => { + expect(Number.Bounded.maxBound).toEqual(Infinity) + expect(Number.Bounded.minBound).toEqual(-Infinity) + }) + + it("SemigroupSum", () => { + deepStrictEqual(Number.SemigroupSum.combine(2, 3), 5) + }) + + it("MonoidSum", () => { + deepStrictEqual(Number.MonoidSum.combineAll([1, 2, 3]), 6) + }) + + it("SemigroupMultiply", () => { + deepStrictEqual(Number.SemigroupMultiply.combine(2, 3), 6) + deepStrictEqual(Number.SemigroupMultiply.combineMany(0, [1, 2, 3]), 0) + deepStrictEqual(Number.SemigroupMultiply.combineMany(2, [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) + }) + + it("remainder", () => { + assert.deepStrictEqual(Number.remainder(2, 2), 0) + assert.deepStrictEqual(Number.remainder(3, 2), 1) + assert.deepStrictEqual(Number.remainder(4, 2), 0) + assert.deepStrictEqual(Number.remainder(2.5, 2), 0.5) + assert.deepStrictEqual(Number.remainder(-2, 2), -0) + assert.deepStrictEqual(Number.remainder(-3, 2), -1) + assert.deepStrictEqual(Number.remainder(-4, 2), -0) + assert.deepStrictEqual(Number.remainder(-2.8, -.2), -0) + assert.deepStrictEqual(Number.remainder(-2, -.2), -0) + assert.deepStrictEqual(Number.remainder(-1.5, -.2), -0.1) + assert.deepStrictEqual(Number.remainder(0, -.2), 0) + assert.deepStrictEqual(Number.remainder(1, -.2), 0) + assert.deepStrictEqual(Number.remainder(2.6, -.2), 0) + assert.deepStrictEqual(Number.remainder(3.1, -.2), 0.1) + }) +}) diff --git a/test/Option.ts b/test/Option.ts new file mode 100644 index 000000000..dca618056 --- /dev/null +++ b/test/Option.ts @@ -0,0 +1,619 @@ +import { equivalence } from "@fp-ts/core" +import * as E from "@fp-ts/core/Either" +import { pipe } from "@fp-ts/core/Function" +import { structural } from "@fp-ts/core/internal/effect" +import * as N from "@fp-ts/core/Number" +import * as _ from "@fp-ts/core/Option" +import * as ReadonlyArray from "@fp-ts/core/ReadonlyArray" +import * as S from "@fp-ts/core/String" +import * as Util from "@fp-ts/core/test/util" + +const p = (n: number): boolean => n > 2 + +describe.concurrent("Option", () => { + it("exports", () => { + expect(_.toEither).exist + expect(_.getRight).exist + expect(_.getLeft).exist + + expect(_.Invariant).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(_.all).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.getFailureSemigroup).exist // liftSemigroup + expect(_.lift2).exist + expect(_.ap).exist + expect(_.andThenDiscard).exist + expect(_.andThen).exist + + expect(_.Applicative).exist + expect(_.getFailureMonoid).exist // liftMonoid + + expect(_.SemiCoproduct).exist + expect(_.getFirstSomeSemigroup).exist // getSemigroup + + expect(_.Coproduct).exist + + expect(_.SemiAlternative).exist + + expect(_.Alternative).exist + + expect(_.Foldable).exist + expect(_.reduceCompact).exist + expect(_.toArray).exist + + expect(_.Traversable).exist + expect(_.traverse).exist + expect(_.sequence).exist + expect(_.traverseTap).exist + + expect(_.Filterable).exist + expect(_.filterMap).exist + expect(_.filter).exist + }) + + it("structural tracking", () => { + expect(Util.ownKeys(_.none())).toEqual(["_tag"]) + expect(Util.ownKeys(_.some(1))).toEqual(["_tag", "value"]) + + expect(Object.prototype.hasOwnProperty.call(_.none(), structural)).toEqual(false) + expect(Object.prototype.hasOwnProperty.call(_.some(1), structural)).toEqual(false) + + expect(Util.isStructural(_.none())).toEqual(true) + expect(Util.isStructural(_.some(1))).toEqual(true) + }) + + it("toRefinement", () => { + const f = ( + s: string | number + ): _.Option => (typeof s === "string" ? _.some(s) : _.none()) + const isString = _.toRefinement(f) + Util.deepStrictEqual(isString("s"), true) + Util.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())) + Util.deepStrictEqual(isA({ type: "A" }), true) + Util.deepStrictEqual(isA({ type: "B" }), false) + }) + + it("isOption", () => { + Util.deepStrictEqual(pipe(_.some(1), _.isOption), true) + Util.deepStrictEqual(pipe(_.none(), _.isOption), true) + Util.deepStrictEqual(pipe(E.right(1), _.isOption), false) + }) + + it("coproductMany", () => { + const coproductMany = _.SemiCoproduct.coproductMany + Util.deepStrictEqual(coproductMany(_.some(1), []), _.some(1)) + Util.deepStrictEqual(coproductMany(_.none(), []), _.none()) + Util.deepStrictEqual( + coproductMany(_.none(), [_.none(), _.none(), _.none(), _.some(1)]), + _.some(1) + ) + Util.deepStrictEqual( + coproductMany(_.none(), [_.none(), _.none(), _.none()]), + _.none() + ) + }) + + it("firstSomeOf", () => { + Util.deepStrictEqual(_.firstSomeOf([]), _.none()) + Util.deepStrictEqual(_.firstSomeOf([_.some(1)]), _.some(1)) + Util.deepStrictEqual(_.firstSomeOf([_.none()]), _.none()) + Util.deepStrictEqual( + _.firstSomeOf([_.none(), _.none(), _.none(), _.none(), _.some(1)]), + _.some(1) + ) + Util.deepStrictEqual( + _.firstSomeOf([_.none(), _.none(), _.none(), _.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("inspectSome", () => { + const log: Array = [] + pipe( + _.some(1), + _.inspectSome(() => log.push(1)) + ) + pipe( + _.none(), + _.inspectSome(() => log.push(2)) + ) + Util.deepStrictEqual( + log, + [1] + ) + }) + + it("inspectNone", () => { + const log: Array = [] + pipe( + _.some(1), + _.inspectNone(() => log.push(1)) + ) + pipe( + _.none(), + _.inspectNone(() => log.push(2)) + ) + Util.deepStrictEqual( + log, + [2] + ) + }) + + it("getOrThrow", () => { + expect(pipe(_.some(1), _.getOrThrow)).toEqual(1) + expect(() => pipe(_.none(), _.getOrThrow)).toThrowError( + new Error("getOrThrow called on a None") + ) + }) + + it("getOrThrowWith", () => { + expect(pipe(_.some(1), _.getOrThrowWith(() => new Error("Unexpected None")))).toEqual(1) + expect(() => pipe(_.none(), _.getOrThrowWith(() => new Error("Unexpected None")))).toThrowError( + new Error("Unexpected None") + ) + }) + + it("of", () => { + Util.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", () => { + const coproductAll = _.Coproduct.coproductAll + Util.deepStrictEqual(coproductAll([]), _.none()) + Util.deepStrictEqual(coproductAll([_.some(1)]), _.some(1)) + Util.deepStrictEqual(coproductAll([_.none(), _.some(1)]), _.some(1)) + Util.deepStrictEqual(coproductAll([_.some(1), _.some(2)]), _.some(1)) + }) + + it("unit", () => { + Util.deepStrictEqual(_.unit, _.some(undefined)) + }) + + it("product", () => { + const product = _.SemiProduct.product + Util.deepStrictEqual(product(_.none(), _.none()), _.none()) + Util.deepStrictEqual(product(_.some(1), _.none()), _.none()) + Util.deepStrictEqual(product(_.none(), _.some("a")), _.none()) + Util.deepStrictEqual( + product(_.some(1), _.some("a")), + _.some([1, "a"]) + ) + }) + + it("productMany", () => { + const productMany = _.SemiProduct.productMany + Util.deepStrictEqual(productMany(_.none(), []), _.none()) + Util.deepStrictEqual(productMany(_.some(1), []), _.some([1])) + Util.deepStrictEqual(productMany(_.some(1), [_.none()]), _.none()) + Util.deepStrictEqual(productMany(_.some(1), [_.some(2)]), _.some([1, 2])) + }) + + it("productAll", () => { + const productAll = _.Applicative.productAll + Util.deepStrictEqual(productAll([]), _.some([])) + Util.deepStrictEqual(productAll([_.none()]), _.none()) + Util.deepStrictEqual(productAll([_.some(1), _.some(2)]), _.some([1, 2])) + Util.deepStrictEqual(productAll([_.some(1), _.none()]), _.none()) + }) + + it("SemiCoproduct", () => { + const coproduct = _.SemiCoproduct.coproduct + Util.deepStrictEqual(coproduct(_.none(), _.none()), _.none()) + Util.deepStrictEqual(coproduct(_.none(), _.some(2)), _.some(2)) + Util.deepStrictEqual(coproduct(_.some(1), _.none()), _.some(1)) + Util.deepStrictEqual(coproduct(_.some(1), _.some(2)), _.some(1)) + + const coproductMany = _.SemiCoproduct.coproductMany + Util.deepStrictEqual(coproductMany(_.none(), []), _.none()) + Util.deepStrictEqual(coproductMany(_.none(), [_.none()]), _.none()) + Util.deepStrictEqual(coproductMany(_.none(), [_.some(2)]), _.some(2)) + Util.deepStrictEqual(coproductMany(_.some(1), []), _.some(1)) + Util.deepStrictEqual(coproductMany(_.some(1), [_.none() as _.Option]), _.some(1)) + Util.deepStrictEqual(coproductMany(_.some(1), [_.some(2)]), _.some(1)) + }) + + it("fromIterable", () => { + Util.deepStrictEqual(_.fromIterable([]), _.none()) + Util.deepStrictEqual(_.fromIterable(["a"]), _.some("a")) + }) + + it("map", () => { + Util.deepStrictEqual(pipe(_.some(2), _.map(Util.double)), _.some(4)) + Util.deepStrictEqual(pipe(_.none(), _.map(Util.double)), _.none()) + }) + + it("flatMap", () => { + const f = (n: number) => _.some(n * 2) + const g = () => _.none() + Util.deepStrictEqual(pipe(_.some(1), _.flatMap(f)), _.some(2)) + Util.deepStrictEqual(pipe(_.none(), _.flatMap(f)), _.none()) + Util.deepStrictEqual(pipe(_.some(1), _.flatMap(g)), _.none()) + Util.deepStrictEqual(pipe(_.none(), _.flatMap(g)), _.none()) + }) + + it("orElse", () => { + const assertAlt = ( + a: _.Option, + b: _.Option, + expected: _.Option + ) => { + Util.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("partitionMap", () => { + const f = (n: number) => (p(n) ? E.right(n + 1) : E.left(n - 1)) + assert.deepStrictEqual(pipe(_.none(), _.partitionMap(f)), [_.none(), _.none()]) + assert.deepStrictEqual(pipe(_.some(1), _.partitionMap(f)), [_.some(0), _.none()]) + assert.deepStrictEqual(pipe(_.some(3), _.partitionMap(f)), [_.none(), _.some(4)]) + }) + + it("filterMap", () => { + const f = (n: number) => (p(n) ? _.some(n + 1) : _.none()) + Util.deepStrictEqual(pipe(_.none(), _.filterMap(f)), _.none()) + Util.deepStrictEqual(pipe(_.some(1), _.filterMap(f)), _.none()) + Util.deepStrictEqual(pipe(_.some(3), _.filterMap(f)), _.some(4)) + }) + + it("traverse", () => { + Util.deepStrictEqual( + pipe( + _.some("hello"), + _.traverse(ReadonlyArray.Applicative)(() => []) + ), + [] + ) + Util.deepStrictEqual( + pipe( + _.some("hello"), + _.traverse(ReadonlyArray.Applicative)((s) => [s.length]) + ), + [_.some(5)] + ) + Util.deepStrictEqual( + pipe( + _.none(), + _.traverse(ReadonlyArray.Applicative)((s) => [s]) + ), + [_.none()] + ) + }) + + it("toEither", () => { + Util.deepStrictEqual(pipe(_.none(), _.toEither(() => "e")), E.left("e")) + Util.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) + Util.deepStrictEqual(match(_.none()), "none") + Util.deepStrictEqual(match(_.some("abc")), "some3") + }) + + it("getOrElse", () => { + Util.deepStrictEqual(pipe(_.some(1), _.getOrElse(() => 0)), 1) + Util.deepStrictEqual(pipe(_.none(), _.getOrElse(() => 0)), 0) + }) + + it("getOrNull", () => { + Util.deepStrictEqual(_.getOrNull(_.none()), null) + Util.deepStrictEqual(_.getOrNull(_.some(1)), 1) + }) + + it("getOrUndefined", () => { + Util.deepStrictEqual(_.getOrUndefined(_.none()), undefined) + Util.deepStrictEqual(_.getOrUndefined(_.some(1)), 1) + }) + + it("getOrder", () => { + const OS = _.getOrder(S.Order) + Util.deepStrictEqual(OS.compare(_.none(), _.none()), 0) + Util.deepStrictEqual(OS.compare(_.some("a"), _.none()), 1) + Util.deepStrictEqual(OS.compare(_.none(), _.some("a")), -1) + Util.deepStrictEqual(OS.compare(_.some("a"), _.some("a")), 0) + Util.deepStrictEqual(OS.compare(_.some("a"), _.some("b")), -1) + Util.deepStrictEqual(OS.compare(_.some("b"), _.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 } } } } + Util.deepStrictEqual( + pipe( + _.fromNullable(x1.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.none() + ) + Util.deepStrictEqual( + pipe( + _.fromNullable(x2.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.none() + ) + Util.deepStrictEqual( + pipe( + _.fromNullable(x3.a), + _.flatMapNullable((x) => x.b), + _.flatMapNullable((x) => x.c), + _.flatMapNullable((x) => x.d) + ), + _.some(1) + ) + }) + + it("getOptionalMonoid", () => { + const M = _.getOptionalMonoid(S.Semigroup) + Util.deepStrictEqual(M.combine(_.none(), _.none()), _.none()) + Util.deepStrictEqual(M.combine(_.none(), _.some("a")), _.some("a")) + Util.deepStrictEqual(M.combine(_.some("a"), _.none()), _.some("a")) + Util.deepStrictEqual(M.combine(_.some("b"), _.some("a")), _.some("ba")) + Util.deepStrictEqual(M.combine(_.some("a"), _.some("b")), _.some("ab")) + + Util.deepStrictEqual(M.combineMany(_.some("a"), [_.some("b")]), _.some("ab")) + Util.deepStrictEqual(M.combineMany(_.none(), [_.some("b")]), _.some("b")) + Util.deepStrictEqual(M.combineMany(_.some("a"), [_.none()]), _.some("a")) + + Util.deepStrictEqual(pipe(M.combineAll([])), _.none()) + Util.deepStrictEqual(pipe(M.combineAll([_.some("a")])), _.some("a")) + Util.deepStrictEqual(pipe(M.combineAll([_.some("a"), _.some("b")])), _.some("ab")) + Util.deepStrictEqual(pipe(M.combineAll([_.some("a"), _.none()])), _.some("a")) + }) + + it("fromNullable", () => { + Util.deepStrictEqual(_.fromNullable(2), _.some(2)) + Util.deepStrictEqual(_.fromNullable(null), _.none()) + Util.deepStrictEqual(_.fromNullable(undefined), _.none()) + }) + + it("liftPredicate", () => { + const f = _.liftPredicate(p) + Util.deepStrictEqual(f(1), _.none()) + Util.deepStrictEqual(f(3), _.some(3)) + + type Direction = "asc" | "desc" + const parseDirection = _.liftPredicate((s: string): s is Direction => + s === "asc" || s === "desc" + ) + Util.deepStrictEqual(parseDirection("asc"), _.some("asc")) + Util.deepStrictEqual(parseDirection("foo"), _.none()) + }) + + it("contains", () => { + const contains = _.contains(equivalence.number) + Util.deepStrictEqual(pipe(_.none(), contains(2)), false) + Util.deepStrictEqual(pipe(_.some(2), contains(2)), true) + Util.deepStrictEqual(pipe(_.some(2), contains(1)), false) + }) + + it("isNone", () => { + Util.deepStrictEqual(_.isNone(_.none()), true) + Util.deepStrictEqual(_.isNone(_.some(1)), false) + }) + + it("isSome", () => { + Util.deepStrictEqual(_.isSome(_.none()), false) + Util.deepStrictEqual(_.isSome(_.some(1)), true) + }) + + it("exists", () => { + const predicate = (a: number) => a === 2 + Util.deepStrictEqual(pipe(_.none(), _.exists(predicate)), false) + Util.deepStrictEqual(pipe(_.some(1), _.exists(predicate)), false) + Util.deepStrictEqual(pipe(_.some(2), _.exists(predicate)), true) + }) + + it("fromEither", () => { + Util.deepStrictEqual(_.fromEither(E.right(1)), _.some(1)) + Util.deepStrictEqual(_.fromEither(E.left("e")), _.none()) + }) + + it("do notation", () => { + Util.deepStrictEqual( + pipe( + _.some(1), + _.bindTo("a"), + _.bind("b", () => _.some("b")) + ), + _.some({ a: 1, b: "b" }) + ) + }) + + it("andThenBind", () => { + Util.deepStrictEqual( + pipe(_.some(1), _.bindTo("a"), _.andThenBind("b", _.some("b"))), + _.some({ a: 1, b: "b" }) + ) + }) + + it("element", () => { + expect(pipe(_.some(1), _.tupled, _.appendElement(_.some("b")))).toEqual( + _.some([1, "b"]) + ) + }) + + it("liftNullable", () => { + const f = _.liftNullable((n: number) => (n > 0 ? n : null)) + Util.deepStrictEqual(f(1), _.some(1)) + Util.deepStrictEqual(f(-1), _.none()) + }) + + it("liftThrowable", () => { + const parse = _.liftThrowable(JSON.parse) + Util.deepStrictEqual(parse("1"), _.some(1)) + Util.deepStrictEqual(parse(""), _.none()) + }) + + it("liftEither", () => { + const f = _.liftEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + Util.deepStrictEqual(f(1), _.some(1)) + Util.deepStrictEqual(f(-1), _.none()) + }) + + it("flatMapEither", () => { + const f = _.flatMapEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + Util.deepStrictEqual(pipe(_.none(), f), _.none()) + Util.deepStrictEqual(pipe(_.some(0), f), _.none()) + Util.deepStrictEqual(pipe(_.some(1), f), _.some(1)) + }) + + it("guard", () => { + Util.deepStrictEqual( + pipe( + _.Do, + _.bind("x", () => _.some("a")), + _.bind("y", () => _.some("a")), + _.filter(({ x, y }) => x === y) + ), + _.some({ x: "a", y: "a" }) + ) + Util.deepStrictEqual( + pipe( + _.Do, + _.bind("x", () => _.some("a")), + _.bind("y", () => _.some("b")), + _.filter(({ x, y }) => x === y) + ), + _.none() + ) + }) + + it("zipWith", () => { + expect(pipe(_.none(), _.zipWith(_.some(2), (a, b) => a + b))).toEqual(_.none()) + expect(pipe(_.some(1), _.zipWith(_.none(), (a, b) => a + b))).toEqual(_.none()) + expect(pipe(_.some(1), _.zipWith(_.some(2), (a, b) => a + b))).toEqual(_.some(3)) + }) + + it("reduceCompact", () => { + const sumCompact = _.reduceCompact(0, N.sum) + expect(sumCompact([])).toEqual(0) + expect(sumCompact([_.some(2), _.some(3)])).toEqual(5) + expect(sumCompact([_.some(2), _.none(), _.some(3)])).toEqual(5) + }) + + it("sum", () => { + expect(pipe(_.none(), _.sum(_.some(2)))).toEqual(_.none()) + expect(pipe(_.some(1), _.sum(_.none()))).toEqual(_.none()) + expect(pipe(_.some(2), _.sum(_.some(3)))).toEqual(_.some(5)) + }) + + it("multiply", () => { + expect(pipe(_.none(), _.multiply(_.some(2)))).toEqual(_.none()) + expect(pipe(_.some(1), _.multiply(_.none()))).toEqual(_.none()) + expect(pipe(_.some(2), _.multiply(_.some(3)))).toEqual(_.some(6)) + }) + + it("subtract", () => { + expect(pipe(_.none(), _.subtract(_.some(2)))).toEqual(_.none()) + expect(pipe(_.some(1), _.subtract(_.none()))).toEqual(_.none()) + expect(pipe(_.some(2), _.subtract(_.some(3)))).toEqual(_.some(-1)) + }) + + it("divide", () => { + expect(pipe(_.none(), _.divide(_.some(2)))).toEqual(_.none()) + expect(pipe(_.some(1), _.divide(_.none()))).toEqual(_.none()) + expect(pipe(_.some(6), _.divide(_.some(3)))).toEqual(_.some(2)) + }) + + it("reduce", () => { + expect(pipe(_.none(), _.Foldable.reduce(0, (b, a) => b + a))).toEqual(0) + expect(pipe(_.some(1), _.Foldable.reduce(0, (b, a) => b + a))).toEqual(1) + }) + + it("sumCompact", () => { + expect(_.sumCompact([])).toEqual(0) + expect(_.sumCompact([_.some(2), _.some(3)])).toEqual(5) + expect(_.sumCompact([_.some(2), _.none(), _.some(3)])).toEqual(5) + }) + + it("multiplyCompact", () => { + expect(_.multiplyCompact([])).toEqual(1) + expect(_.multiplyCompact([_.some(2), _.some(3)])).toEqual(6) + expect(_.multiplyCompact([_.some(2), _.none(), _.some(3)])).toEqual(6) + expect(_.multiplyCompact([_.some(2), _.some(0), _.some(3)])).toEqual(0) + }) + + it("getEquivalence", () => { + const isEquivalent = _.getEquivalence(N.Equivalence) + expect(isEquivalent(_.none(), _.none())).toEqual(true) + expect(isEquivalent(_.none(), _.some(1))).toEqual(false) + expect(isEquivalent(_.some(1), _.none())).toEqual(false) + expect(isEquivalent(_.some(2), _.some(1))).toEqual(false) + expect(isEquivalent(_.some(1), _.some(2))).toEqual(false) + expect(isEquivalent(_.some(2), _.some(2))).toEqual(true) + }) +}) diff --git a/test/Ordering.ts b/test/Ordering.ts new file mode 100644 index 000000000..5f2682083 --- /dev/null +++ b/test/Ordering.ts @@ -0,0 +1,45 @@ +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(_.Semigroup.combine(0, 0), 0) + deepStrictEqual(_.Semigroup.combine(0, 1), 1) + deepStrictEqual(_.Semigroup.combine(1, -1), 1) + deepStrictEqual(_.Semigroup.combine(-1, 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", () => { + 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 new file mode 100644 index 000000000..39806fb32 --- /dev/null +++ b/test/Predicate.ts @@ -0,0 +1,259 @@ +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(_.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(_.andThenBind).exist + expect(_.appendElement).exist + + expect(_.Product).exist + expect(_.tuple).exist + expect(_.struct).exist + }) + + 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 product = _.SemiProduct.product + const p = product(isPositive, isNegative) + deepStrictEqual(p([1, -1]), true) + deepStrictEqual(p([1, 1]), false) + deepStrictEqual(p([-1, -1]), false) + deepStrictEqual(p([-1, 1]), false) + }) + + it("productMany", () => { + const productMany = _.SemiProduct.productMany + const p = productMany(isPositive, [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 = _.Product.productAll([isPositive, isNegative]) + deepStrictEqual(p([1]), true) + 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 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", () => { + const M = _.getMonoidAny() + const predicate = M.combine(isPositive, M.empty) + deepStrictEqual(predicate(0), isPositive(0)) + deepStrictEqual(predicate(-1), isPositive(-1)) + deepStrictEqual(predicate(1), isPositive(1)) + }) + + it("getSemigroupAll", () => { + const S = _.getSemigroupAll() + 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", () => { + const M = _.getMonoidAll() + const predicate = M.combine(isPositive, 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) + }) + + it("isFunction", () => { + assert.deepStrictEqual(_.isFunction(_.isFunction), true) + assert.deepStrictEqual(_.isFunction("function"), false) + }) + + it("isUndefined", () => { + assert.deepStrictEqual(_.isUndefined(undefined), true) + assert.deepStrictEqual(_.isUndefined(null), false) + assert.deepStrictEqual(_.isUndefined("undefined"), false) + }) + + it("isNotUndefined", () => { + assert.deepStrictEqual(_.isNotUndefined(undefined), false) + assert.deepStrictEqual(_.isNotUndefined(null), true) + assert.deepStrictEqual(_.isNotUndefined("undefined"), true) + }) + + it("isNull", () => { + assert.deepStrictEqual(_.isNull(null), true) + assert.deepStrictEqual(_.isNull(undefined), false) + assert.deepStrictEqual(_.isNull("null"), false) + }) + + it("isNotNull", () => { + assert.deepStrictEqual(_.isNotNull(null), false) + assert.deepStrictEqual(_.isNotNull(undefined), true) + assert.deepStrictEqual(_.isNotNull("null"), true) + }) + + it("isNever", () => { + assert.deepStrictEqual(_.isNever(null), false) + assert.deepStrictEqual(_.isNever(undefined), false) + assert.deepStrictEqual(_.isNever({}), false) + assert.deepStrictEqual(_.isNever([]), false) + }) + + it("isUnknown", () => { + assert.deepStrictEqual(_.isUnknown(null), true) + assert.deepStrictEqual(_.isUnknown(undefined), true) + assert.deepStrictEqual(_.isUnknown({}), true) + assert.deepStrictEqual(_.isUnknown([]), true) + }) + + it("isObject", () => { + assert.deepStrictEqual(_.isObject({}), true) + assert.deepStrictEqual(_.isObject([]), true) + assert.deepStrictEqual(_.isObject(null), false) + assert.deepStrictEqual(_.isObject(undefined), false) + }) + + it("isNullable", () => { + assert.deepStrictEqual(_.isNullable(null), true) + assert.deepStrictEqual(_.isNullable(undefined), true) + assert.deepStrictEqual(_.isNullable({}), false) + assert.deepStrictEqual(_.isNullable([]), false) + }) + + it("isNotNullable", () => { + assert.deepStrictEqual(_.isNotNullable({}), true) + assert.deepStrictEqual(_.isNotNullable([]), true) + assert.deepStrictEqual(_.isNotNullable(null), false) + assert.deepStrictEqual(_.isNotNullable(undefined), false) + }) + + it("isError", () => { + assert.deepStrictEqual(_.isError(new Error()), true) + assert.deepStrictEqual(_.isError(null), false) + assert.deepStrictEqual(_.isError({}), false) + }) + + it("isDate", () => { + assert.deepStrictEqual(_.isDate(new Date()), true) + assert.deepStrictEqual(_.isDate(null), false) + assert.deepStrictEqual(_.isDate({}), false) + }) + + it("isRecord", () => { + assert.deepStrictEqual(_.isRecord({}), true) + assert.deepStrictEqual(_.isRecord({ a: 1 }), true) + + assert.deepStrictEqual(_.isRecord([]), false) + assert.deepStrictEqual(_.isRecord([1, 2, 3]), false) + assert.deepStrictEqual(_.isRecord(null), false) + assert.deepStrictEqual(_.isRecord(undefined), false) + }) + + it("isReadonlyRecord", () => { + assert.deepStrictEqual(_.isReadonlyRecord({}), true) + assert.deepStrictEqual(_.isReadonlyRecord({ a: 1 }), true) + + assert.deepStrictEqual(_.isReadonlyRecord([]), false) + assert.deepStrictEqual(_.isReadonlyRecord([1, 2, 3]), false) + assert.deepStrictEqual(_.isReadonlyRecord(null), false) + assert.deepStrictEqual(_.isReadonlyRecord(undefined), false) + }) +}) diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts new file mode 100644 index 000000000..e74063adf --- /dev/null +++ b/test/ReadonlyArray.ts @@ -0,0 +1,1599 @@ +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 assert from "assert" +import * as fc from "fast-check" + +describe.concurrent("ReadonlyArray", () => { + it("exports", () => { + expect(RA.Invariant).exist + expect(RA.tupled).exist + expect(RA.bindTo).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.andThenBind).exist + + expect(RA.Product).exist + + expect(RA.SemiApplicative).exist + expect(RA.lift2).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.reduceKind).exist + expect(RA.coproductMapKind).exist + + expect(RA.Traversable).exist + expect(RA.traverse).exist + expect(RA.sequence).exist + expect(RA.traverseTap).exist + + expect(RA.compact).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.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])) + deepStrictEqual( + pipe( + ["a", "bb"], + RA.traverse(O.Applicative)(( + s, + i + ) => (s.length >= 1 ? O.some(s + i) : O.none())) + ), + O.some(["a0", "bb1"]) + ) + deepStrictEqual( + pipe( + ["a", "bb"], + RA.traverse(O.Applicative)(( + s, + i + ) => (s.length > 1 ? O.some(s + i) : O.none())) + ), + O.none() + ) + }) + + 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("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] + ) + deepStrictEqual( + pipe( + ["a", "b"], + RA.map((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] + ) + const f = RA.flatMap((n: number, i) => [n + i]) + deepStrictEqual(pipe([], f), []) + deepStrictEqual(pipe([1, 2, 3], f), [1, 3, 5]) + }) + + it("extend", () => { + deepStrictEqual(pipe([1, 2, 3, 4], RA.extend(Number.sumAll)), [10, 9, 7, 4]) + deepStrictEqual(pipe([1, 2, 3, 4], RA.extend(identity)), [ + [1, 2, 3, 4], + [2, 3, 4], + [3, 4], + [4] + ]) + }) + + it("combineMap", () => { + deepStrictEqual(pipe(["a", "b", "c"], RA.combineMap(String.Monoid)(identity)), "abc") + deepStrictEqual(pipe([], RA.combineMap(String.Monoid)(identity)), "") + }) + + it("compact", () => { + assert.deepStrictEqual(RA.compact([]), []) + assert.deepStrictEqual(RA.compact([O.some(1), O.some(2), O.some(3)]), [ + 1, + 2, + 3 + ]) + assert.deepStrictEqual(RA.compact([O.some(1), O.none(), O.some(3)]), [ + 1, + 3 + ]) + }) + + it("separate", () => { + assert.deepStrictEqual(RA.separate([]), [[], []]) + assert.deepStrictEqual(pipe([E.right(1), E.left("e"), E.right(2)], RA.separate), [ + ["e"], + [1, 2] + ]) + }) + + 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)]) + + const f = (n: number) => n % 2 === 0 + deepStrictEqual(pipe(["a", "b", "c"], RA.filter((_, 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)), []) + const g = (n: number, i: number) => ((i + n) % 2 === 0 ? O.none() : O.some(n)) + deepStrictEqual(pipe([1, 2, 4], RA.filterMap(g)), [1, 2]) + deepStrictEqual(pipe([], RA.filterMap(g)), []) + }) + + it("combineMap", () => { + deepStrictEqual( + pipe( + ["a", "b"], + RA.combineMap(String.Monoid)((a, i) => i + a) + ), + "0a1b" + ) + }) + + it("partitionMap", () => { + deepStrictEqual(pipe([], RA.partitionMap(identity)), [[], []]) + deepStrictEqual( + pipe( + [E.right(1), E.left("foo"), E.right(2)], + RA.partitionMap(identity) + ), + [["foo"], [1, 2]] + ) + deepStrictEqual( + pipe( + [E.right(1), E.left("foo"), E.right(2)], + RA.partitionMap((a, i) => pipe(a, E.filter((n) => n > i, () => "err"))) + ), + [["foo", "err"], [1]] + ) + }) + + it("partition", () => { + deepStrictEqual( + pipe([], RA.partition((n) => n > 2)), + [[], []] + ) + deepStrictEqual( + pipe([1, 3], RA.partition((n) => n > 2)), + [[1], [3]] + ) + deepStrictEqual( + pipe([], RA.partition((i, n) => i + n > 2)), + [[], []] + ) + deepStrictEqual( + pipe([1, 2], RA.partition((i, n) => i + n > 2)), + [[1], [2]] + ) + }) + + it("reduce", () => { + deepStrictEqual(pipe(["a", "b", "c"], RA.reduce("", (b, a) => b + a)), "abc") + deepStrictEqual( + pipe( + ["a", "b"], + RA.reduce("", (b, a, i) => b + i + a) + ), + "0a1b" + ) + }) + + it("reduceRight", () => { + const f = (b: string, a: string) => b + a + deepStrictEqual(pipe(["a", "b", "c"], RA.reduceRight("", f)), "cba") + deepStrictEqual(pipe([], RA.reduceRight("", f)), "") + deepStrictEqual( + pipe( + ["a", "b"], + RA.reduceRight("", (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() + ) + deepStrictEqual( + pipe( + RA.make("a", "bb"), + RA.traverseNonEmpty(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.traverseNonEmpty(O.Applicative)((s, i) => (s.length > 1 ? O.some(s + i) : O.none())) + ), + O.none() + ) + }) + + it("getMonoid", () => { + const M = RA.getMonoid() + deepStrictEqual(M.combine([1, 2], [3, 4]), [1, 2, 3, 4]) + const x = [1, 2] + 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]) + }) + + it("getOrder", () => { + const O = RA.getOrder(String.Order) + deepStrictEqual(O.compare([], []), 0) + deepStrictEqual(O.compare(["a"], ["a"]), 0) + + 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(["b"], ["a", "a"]), 1) + + deepStrictEqual(O.compare(["a", "a"], ["a", "a"]), 0) + deepStrictEqual(O.compare(["a", "b"], ["a", "b"]), 0) + + deepStrictEqual(O.compare(["a", "b"], ["a", "a"]), 1) + deepStrictEqual(O.compare(["a", "a"], ["a", "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", () => { + deepStrictEqual(RA.isEmpty([1, 2, 3]), false) + deepStrictEqual(RA.isEmpty([]), true) + }) + + it("isEmptyArray", () => { + deepStrictEqual(RA.isEmptyArray([1, 2, 3]), false) + deepStrictEqual(RA.isEmptyArray([]), true) + }) + + it("isNotEmpty", () => { + deepStrictEqual(RA.isNonEmpty([1, 2, 3]), true) + deepStrictEqual(RA.isNonEmpty([]), false) + }) + + it("isNonEmptyArray", () => { + deepStrictEqual(RA.isNonEmptyArray([1, 2, 3]), true) + deepStrictEqual(RA.isNonEmptyArray([]), 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]) + const g = (a: number, i: number): RA.NonEmptyReadonlyArray => [a + i, 4] + deepStrictEqual(pipe(RA.make(1, 2), RA.flatMapNonEmpty(g)), [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] + ) + deepStrictEqual( + pipe(RA.make("a", "b"), RA.mapNonEmpty((s: string, i: number) => s + i)), + ["a0", "b1"] + ) + }) + + 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("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, + (as) => 1 + len(as.slice(1)) + ) + deepStrictEqual(len([1, 2, 3]), 3) + }) + + it("matchLeft", () => { + const len: (as: ReadonlyArray) => number = RA.matchLeft( + () => 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(5, n => n * 2), [0, 2, 4, 6, 8]) + deepStrictEqual(RA.makeBy(2.2, n => n * 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(S.combine([1, 2], [2, 3])).toEqual([1, 2, 2, 3]) + }) + + it("getUnionSemigroup", () => { + const S = RA.getUnionSemigroup(Number.Equivalence) + expect(S.combine([1, 2], [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(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(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([3, 4], [1, 2]), []) + deepStrictEqual(S.combine([2, 3], [1, 2]), [2]) + deepStrictEqual(S.combine([1, 2], [1, 2]), [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("combineMapNonEmpty", () => { + deepStrictEqual( + pipe( + RA.make("a", "b", "c"), + RA.combineMapNonEmpty(String.Semigroup)(identity) + ), + "abc" + ) + deepStrictEqual( + pipe( + RA.make("a", "b"), + RA.combineMapNonEmpty(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) + }) + + it("length", () => { + deepStrictEqual(RA.length(RA.empty()), 0) + deepStrictEqual(RA.length([]), 0) + deepStrictEqual(RA.length(["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", () => { + 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"], + [2, "b"] + ]) + }) + + test("productAll", () => { + 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 + ]]) + }) + + it("traversePartitionMap", () => { + const traversePartitionMap = RA.traversePartitionMap(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(E.right(s)) : s.length > 0 ? O.some(E.left(s)) : O.none() + assert.deepStrictEqual(traversePartitionMap([], f), O.some([[], []])) + assert.deepStrictEqual(traversePartitionMap([""], f), O.none()) + assert.deepStrictEqual(traversePartitionMap(["a"], f), O.some([["a"], []])) + assert.deepStrictEqual(traversePartitionMap(["aa"], f), O.some([[], ["aa"]])) + assert.deepStrictEqual(traversePartitionMap(["aa", "a", ""], f), O.none()) + assert.deepStrictEqual( + traversePartitionMap(["aa", "a", "aaa"], f), + O.some([["a"], ["aa", "aaa"]]) + ) + }) + + it("traverseFilterMap", () => { + const traverseFilterMap = RA.traverseFilterMap(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(O.some(s)) : s.length > 0 ? O.some(O.none()) : O.none() + assert.deepStrictEqual(traverseFilterMap([], f), O.some([])) + assert.deepStrictEqual(traverseFilterMap([""], f), O.none()) + assert.deepStrictEqual(traverseFilterMap(["a"], f), O.some([])) + assert.deepStrictEqual(traverseFilterMap(["aa"], f), O.some(["aa"])) + assert.deepStrictEqual(traverseFilterMap(["aa", "a", ""], f), O.none()) + assert.deepStrictEqual( + traverseFilterMap(["aa", "a", "aaa"], f), + O.some(["aa", "aaa"]) + ) + }) +}) diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts new file mode 100644 index 000000000..5810c5087 --- /dev/null +++ b/test/ReadonlyRecord.ts @@ -0,0 +1,201 @@ +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 RR from "@fp-ts/core/ReadonlyRecord" + +describe.concurrent("ReadonlyRecord", () => { + it("exports", () => { + expect(RR.Invariant).exist + expect(RR.tupled).exist + expect(RR.Covariant).exist + expect(RR.flap).exist + expect(RR.as).exist + expect(RR.Filterable).exist + expect(RR.Traversable).exist + expect(RR.traverseTap).exist + expect(RR.TraversableFilterable).exist + expect(RR.traverseFilter).exist + expect(RR.traversePartition).exist + }) + + 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" }) + ) + }) + + it("map", () => { + expect(pipe({ a: 1, b: 2 }, RR.map(n => n * 2))).toEqual({ a: 2, b: 4 }) + expect(pipe({ a: 1, b: 2 }, RR.map((n, k) => `${k}-${n}`))).toEqual({ + a: "a-1", + b: "b-2" + }) + }) + + it("fromIterable", () => { + const input = [1, 2, 3, 4] + expect(RR.fromIterable(input, a => [String(a), a * 2])).toEqual({ + "1": 2, + "2": 4, + "3": 6, + "4": 8 + }) + }) + + it("collect", () => { + const x = { a: 1, b: 2, c: 3 } + assert.deepStrictEqual(RR.collect(x, (key, n) => [key, n]), [["a", 1], ["b", 2], ["c", 3]]) + }) + + it("toArray", () => { + const x = { a: 1, b: 2 } + assert.deepStrictEqual(RR.toArray(x), [["a", 1], ["b", 2]]) + }) + + it("remove", () => { + assert.deepStrictEqual(RR.remove({ a: 1, b: 2 }, "a"), { b: 2 }) + assert.deepStrictEqual(RR.remove({ a: 1, b: 2 }, "c"), { a: 1, b: 2 }) + }) + + describe("pop", () => { + it("should return the value associated with the given key, if the key is present in the record", () => { + const record = { a: 1, b: 2 } + const result = RR.pop("a")(record) + + assert.deepStrictEqual(result, O.some([1, { b: 2 }])) + }) + + it("should return none if the key is not present in the record", () => { + const record = { a: 1, b: 2 } + const result = RR.pop("c")(record) + + assert.deepStrictEqual(result, O.none()) + }) + }) + + describe("filterMap", () => { + it("should filter the properties of an object", () => { + const obj = { a: 1, b: 2, c: 3 } + const filtered = RR.filterMap(obj, (value, key) => (value > 2 ? O.some(key) : O.none())) + expect(filtered).toEqual({ c: "c" }) + }) + }) + + it("compact", () => { + const x = { a: O.some(1), b: O.none(), c: O.some(2) } + assert.deepStrictEqual(RR.compact(x), { a: 1, c: 2 }) + }) + + it("filter", () => { + const x = { a: 1, b: 2, c: 3, d: 4 } + assert.deepStrictEqual(RR.filter(x, (value) => value > 2), { c: 3, d: 4 }) + }) + + it("partitionMap", () => { + const f = (n: number) => (n > 2 ? E.right(n + 1) : E.left(n - 1)) + assert.deepStrictEqual(RR.partitionMap({}, f), [{}, {}]) + assert.deepStrictEqual(RR.partitionMap({ a: 1, b: 3 }, f), [{ a: 0 }, { b: 4 }]) + }) + + it("partition", () => { + const f = (n: number) => n > 2 + assert.deepStrictEqual(RR.partition({}, f), [{}, {}]) + assert.deepStrictEqual(RR.partition({ a: 1, b: 3 }, f), [{ a: 1 }, { b: 3 }]) + }) + + it("separate", () => { + assert.deepStrictEqual( + RR.separate({ a: E.left("e"), b: E.right(1) }), + [{ a: "e" }, { b: 1 }] + ) + // should ignore non own properties + const o: RR.ReadonlyRecord> = Object.create({ a: 1 }) + assert.deepStrictEqual(pipe(o, RR.separate), [{}, {}]) + }) + + it("traverse", () => { + assert.deepStrictEqual( + RR.traverse(O.Applicative)({ a: 1, b: 2 }, (n: number) => (n <= 2 ? O.some(n) : O.none())), + O.some({ a: 1, b: 2 }) + ) + assert.deepStrictEqual( + RR.traverse(O.Applicative)((n: number) => (n <= 2 ? O.some(n) : O.none()))({ a: 1, b: 2 }), + O.some({ a: 1, b: 2 }) + ) + assert.deepStrictEqual( + RR.traverse(O.Applicative)({ a: 1, b: 2 }, (n: number) => (n >= 2 ? O.some(n) : O.none())), + O.none() + ) + assert.deepStrictEqual( + RR.traverse(O.Applicative)((n: number) => (n >= 2 ? O.some(n) : O.none()))({ a: 1, b: 2 }), + O.none() + ) + }) + + it("sequence", () => { + const sequence = RR.sequence(O.Applicative) + assert.deepStrictEqual(sequence({ a: O.some(1), b: O.some(2) }), O.some({ a: 1, b: 2 })) + assert.deepStrictEqual(sequence({ a: O.none(), b: O.some(2) }), O.none()) + }) + + it("empty", () => { + expect(RR.empty()).toEqual({}) + }) + + it("isEmpty", () => { + assert.deepStrictEqual(RR.isEmpty({}), true) + assert.deepStrictEqual(RR.isEmpty({ a: 3 }), false) + }) + + it("size", () => { + assert.deepStrictEqual(RR.size({ a: "a", b: 1, c: true }), 3) + }) + + it("has", () => { + assert.deepStrictEqual(RR.has({ a: 1, b: 2 }, "a"), true) + assert.deepStrictEqual(RR.has({ a: 1, b: 2 }, "c"), false) + }) + + it("traversePartitionMap", () => { + const traversePartitionMap = RR.traversePartitionMap(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(E.right(s)) : s.length > 0 ? O.some(E.left(s)) : O.none() + assert.deepStrictEqual(traversePartitionMap({}, f), O.some([{}, {}])) + assert.deepStrictEqual(traversePartitionMap({ "": "" }, f), O.none()) + assert.deepStrictEqual(traversePartitionMap({ a: "a" }, f), O.some([{ a: "a" }, {}])) + assert.deepStrictEqual(traversePartitionMap({ aa: "aa" }, f), O.some([{}, { aa: "aa" }])) + assert.deepStrictEqual(traversePartitionMap({ aa: "aa", a: "a", "": "" }, f), O.none()) + assert.deepStrictEqual( + traversePartitionMap({ aa: "aa", a: "a", aaa: "aaa" }, f), + O.some([{ a: "a" }, { aa: "aa", aaa: "aaa" }]) + ) + }) + + it("traverseFilterMap", () => { + const traverseFilterMap = RR.traverseFilterMap(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(O.some(s)) : s.length > 0 ? O.some(O.none()) : O.none() + assert.deepStrictEqual(traverseFilterMap({}, f), O.some({})) + assert.deepStrictEqual(traverseFilterMap({ "": "" }, f), O.none()) + assert.deepStrictEqual(traverseFilterMap({ a: "a" }, f), O.some({})) + assert.deepStrictEqual(traverseFilterMap({ aa: "aa" }, f), O.some({ aa: "aa" })) + assert.deepStrictEqual(traverseFilterMap({ aa: "aa", a: "a", "": "" }, f), O.none()) + assert.deepStrictEqual( + traverseFilterMap({ aa: "aa", a: "a", aaa: "aaa" }, f), + O.some({ aa: "aa", aaa: "aaa" }) + ) + }) +}) diff --git a/test/String.ts b/test/String.ts new file mode 100644 index 000000000..8aa07bba0 --- /dev/null +++ b/test/String.ts @@ -0,0 +1,217 @@ +import { pipe } from "@fp-ts/core/Function" +import * as S 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(S.isString("a")).toEqual(true) + expect(S.isString(1)).toEqual(false) + expect(S.isString(true)).toEqual(false) + }) + + it("empty", () => { + expect(S.empty).toEqual("") + }) + + it("Semigroup", () => { + expect(S.Semigroup.combine("a", "b")).toEqual("ab") + expect(S.Semigroup.combineMany("a", ["b", "c"])).toEqual("abc") + expect(S.Semigroup.combineMany("a", [])).toEqual("a") + }) + + it("Monoid", () => { + expect(S.Monoid.combineAll([])).toEqual("") + }) + + it("Equivalence", () => { + expect(S.Equivalence("a", "a")).toBe(true) + expect(S.Equivalence("a", "b")).toBe(false) + }) + + it("Order", () => { + const lessThan = Order.lessThan(S.Order) + const lessThanOrEqualTo = Order.lessThanOrEqualTo(S.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", S.concat("b")), "ab") + }) + + it("isEmpty", () => { + deepStrictEqual(S.isEmpty(""), true) + deepStrictEqual(S.isEmpty("a"), false) + }) + + it("isNonEmpty", () => { + deepStrictEqual(S.isNonEmpty(""), false) + deepStrictEqual(S.isNonEmpty("a"), true) + }) + + it("length", () => { + deepStrictEqual(S.length(""), 0) + deepStrictEqual(S.length("a"), 1) + deepStrictEqual(S.length("aaa"), 3) + }) + + it("toUpperCase", () => { + deepStrictEqual(S.toUpperCase("a"), "A") + }) + + it("toLowerCase", () => { + deepStrictEqual(S.toLowerCase("A"), "a") + }) + + it("replace", () => { + deepStrictEqual(pipe("abc", S.replace("b", "d")), "adc") + }) + + it("split", () => { + deepStrictEqual(pipe("abc", S.split("")), ["a", "b", "c"]) + deepStrictEqual(pipe("", S.split("")), [""]) + }) + + it("trim", () => { + deepStrictEqual(pipe(" a ", S.trim), "a") + }) + + it("trimStart", () => { + deepStrictEqual(pipe(" a ", S.trimStart), "a ") + }) + + it("trimEnd", () => { + deepStrictEqual(pipe(" a ", S.trimEnd), " a") + }) + + it("includes", () => { + assert.deepStrictEqual(S.includes("abc", "b"), true) + assert.deepStrictEqual(S.includes("abc", "d"), false) + }) + + it("includesWithPosition", () => { + assert.deepStrictEqual(S.includesWithPosition("abc", "b", 1), true) + assert.deepStrictEqual(S.includesWithPosition("abc", "a", 1), false) + }) + + it("startsWith", () => { + assert.deepStrictEqual(S.startsWith("abc", "a"), true) + assert.deepStrictEqual(S.startsWith("bc", "a"), false) + }) + + it("startsWithPosition", () => { + assert.deepStrictEqual(S.startsWithPosition("abc", "b", 1), true) + assert.deepStrictEqual(S.startsWithPosition("bc", "a", 1), false) + }) + + it("endsWith", () => { + assert.deepStrictEqual(S.endsWith("abc", "c"), true) + assert.deepStrictEqual(S.endsWith("ab", "c"), false) + }) + + it("endsWithPosition", () => { + assert.deepStrictEqual(S.endsWithPosition("abc", "b", 2), true) + assert.deepStrictEqual(S.endsWithPosition("abc", "c", 2), false) + }) + + it("slice", () => { + deepStrictEqual(pipe("abcd", S.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, S.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, S.takeLeft(100)) + + assert.strictEqual(result, string) + }) + + it("should return the empty string for a negative `n`", () => { + const string = "Hello, World!" + + const result = pipe(string, S.takeLeft(-1)) + + assert.strictEqual(result, "") + }) + + it("should round down if `n` is a float", () => { + const string = "Hello, World!" + + const result = pipe(string, S.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, S.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, S.takeRight(100)) + + assert.strictEqual(result, string) + }) + + it("should return the empty string for a negative `n`", () => { + const string = "Hello, World!" + + const result = pipe(string, S.takeRight(-1)) + + assert.strictEqual(result, "") + }) + + it("should round down if `n` is a float", () => { + const string = "Hello, World!" + + const result = pipe(string, S.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") + // }) + // }) +}) diff --git a/test/Struct.ts b/test/Struct.ts new file mode 100644 index 000000000..c9e2e7740 --- /dev/null +++ b/test/Struct.ts @@ -0,0 +1,19 @@ +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 + }) + + 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 }) + }) +}) diff --git a/test/Symbol.ts b/test/Symbol.ts new file mode 100644 index 000000000..ad271c9a2 --- /dev/null +++ b/test/Symbol.ts @@ -0,0 +1,17 @@ +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) + }) + + it("Equivalence", () => { + const eq = S.Equivalence + 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) + }) +}) diff --git a/test/These.ts b/test/These.ts new file mode 100644 index 000000000..0ad9170cd --- /dev/null +++ b/test/These.ts @@ -0,0 +1,809 @@ +import * as E from "@fp-ts/core/Either" +import { identity, pipe } from "@fp-ts/core/Function" +import { structural } from "@fp-ts/core/internal/effect" +import * as N from "@fp-ts/core/Number" +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 Util from "./util" + +describe("These", () => { + it("instances and derived exports", () => { + expect(_.Invariant).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(_.andThenBind).exist + expect(_.appendElement).exist + + expect(_.Product).exist + expect(_.tuple).exist + expect(_.struct).exist + + expect(_.SemiApplicative).exist + expect(_.getFirstLeftSemigroup).exist // liftSemigroup + expect(_.lift2).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("structural tracking", () => { + expect(Util.ownKeys(_.left("a"))).toEqual(["_tag", "left"]) + expect(Util.ownKeys(_.right(1))).toEqual(["_tag", "right"]) + expect(Util.ownKeys(_.both("a", 1))).toEqual(["_tag", "left", "right"]) + + expect(Object.prototype.hasOwnProperty.call(_.left("a"), structural)).toEqual(false) + expect(Object.prototype.hasOwnProperty.call(_.right(1), structural)).toEqual(false) + expect(Object.prototype.hasOwnProperty.call(_.both("a", 1), structural)).toEqual(false) + + expect(Util.isStructural(_.left("a"))).toEqual(true) + expect(Util.isStructural(_.right(1))).toEqual(true) + expect(Util.isStructural(_.both("a", 1))).toEqual(true) + }) + + it("reduce", () => { + Util.deepStrictEqual(pipe(_.right("a"), _.Foldable.reduce("-", (b, a) => b + a)), "-a") + Util.deepStrictEqual(pipe(_.left("e"), _.Foldable.reduce("-", (b, a) => b + a)), "-") + Util.deepStrictEqual(pipe(_.both("e", "a"), _.Foldable.reduce("-", (b, a) => b + a)), "-a") + }) + + it("map", () => { + Util.deepStrictEqual(pipe(_.left("e"), _.map(Util.double)), _.left("e")) + Util.deepStrictEqual(pipe(_.right(2), _.map(Util.double)), _.right(4)) + Util.deepStrictEqual(pipe(_.both("e", 2), _.map(Util.double)), _.both("e", 4)) + }) + + it("bimap", () => { + const f = _.bimap(S.length, Util.double) + Util.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) + Util.deepStrictEqual(pipe(_.right(2), f), _.right(4)) + Util.deepStrictEqual(pipe(_.both("eee", 1), f), _.both(3, 2)) + }) + + it("mapLeft", () => { + const f = _.mapLeft(S.length) + Util.deepStrictEqual(pipe(_.left("e"), f), _.left(1)) + Util.deepStrictEqual(pipe(_.right(2), f), _.right(2)) + Util.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())) + Util.deepStrictEqual(pipe(_.left("a"), traverse), O.some(_.left("a"))) + Util.deepStrictEqual(pipe(_.right(2), traverse), O.some(_.right(2))) + Util.deepStrictEqual(pipe(_.right(1), traverse), O.none()) + Util.deepStrictEqual(pipe(_.both("a", 2), traverse), O.some(_.both("a", 2))) + Util.deepStrictEqual( + pipe( + _.both("a", 1), + _.traverse(O.Applicative)((n) => (n >= 2 ? O.some(n) : O.none())) + ), + O.none() + ) + }) + + it("andThenBind", () => { + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.right(1)), _.bindEither("b", () => E.right(2))), + _.right({ a: 1, b: 2 }) + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.right(1)), _.bindEither("b", () => E.left("e2"))), + _.fail("e2") + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindEither("b", () => E.right(2))), + _.fail("e1") + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindEither("b", () => E.left("e2"))), + _.fail("e1") + ) + Util.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", () => { + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.right(1)), _.bindThese("b", () => _.right(2))), + _.right({ a: 1, b: 2 }) + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.right(1)), _.bindThese("b", () => _.left("e2"))), + _.fail("e2") + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.right(1)), _.bindThese("b", () => _.both("e2", 2))), + _.warn("e2", { a: 1, b: 2 }) + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.right(2))), + _.fail("e1") + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.left("e2"))), + _.fail("e1") + ) + Util.deepStrictEqual( + pipe(_.Do, _.bind("a", () => _.fail("e1")), _.bindThese("b", () => _.both("e2", 2))), + _.fail("e1") + ) + Util.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) + Util.deepStrictEqual(sequence(_.left("a")), O.some(_.left("a"))) + Util.deepStrictEqual(sequence(_.right(O.some(1))), O.some(_.right(1))) + Util.deepStrictEqual(sequence(_.right(O.none())), O.none()) + Util.deepStrictEqual(sequence(_.both("a", O.some(1))), O.some(_.both("a", 1))) + Util.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 + + const product = _.SemiProduct.product + expect(product(_.right(1), _.right(2))).toEqual(_.right([1, 2])) + Util.deepStrictEqual(product(_.right(1), _.left(b)), _.left(b)) + expect(product(_.right(1), _.both(b, 2))).toEqual(_.both(b, [1, 2])) + + Util.deepStrictEqual(product(_.left(a), _.right(2)), _.left(a)) + Util.deepStrictEqual(product(_.left(a), _.left(b)), _.left(a)) + Util.deepStrictEqual(product(_.left(a), _.both(b, 2)), _.left(a)) + + 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", () => { + const a = ["a"] as const + const b = ["b"] as const + const ab = ["a", "b"] as const + + const productMany: ( + self: _.Validated, + collection: Iterable<_.Validated> + ) => _.Validated]> = _.SemiProduct.productMany + + expect(productMany(_.right(1), [_.right(2)])).toEqual(_.right([1, 2])) + Util.deepStrictEqual( + productMany(_.right(1), [_.left(b)]), + _.left(b) + ) + expect( + productMany(_.right(1), [_.both(b, 2)]) + ).toEqual( + _.both(b, [1, 2]) + ) + + Util.deepStrictEqual(productMany(_.left(a), [_.right(2)]), _.left(a)) + Util.deepStrictEqual(productMany(_.left(a), [_.left(b)]), _.left(a)) + Util.deepStrictEqual( + productMany(_.left(a), [_.both(b, 2)]), + _.left(a) + ) + + 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]) + ) + }) + + it("productAll", () => { + const a = ["a"] as const + const b = ["b"] as const + const ab = ["a", "b"] as const + + const productAll = _.Product.productAll + Util.deepStrictEqual(productAll([_.right(1), _.right(2)]), _.right([1, 2])) + Util.deepStrictEqual(productAll([_.right(1), _.left(b)]), _.left(b)) + Util.deepStrictEqual(productAll([_.right(1), _.both(b, 2)]), _.both(b, [1, 2])) + + Util.deepStrictEqual(productAll([_.left(a), _.right(2)]), _.left(a)) + Util.deepStrictEqual(productAll([_.left(a), _.left(b)]), _.left(a)) + Util.deepStrictEqual(productAll([_.left(a), _.both(b, 2)]), _.left(a)) + + Util.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 ? _.right(n * 2) : _.warn("e2", n)) : + _.fail("e3")) + Util.deepStrictEqual( + pipe(_.fail("e1"), _.flatMap(f)), + _.fail("e1") + ) + Util.deepStrictEqual(pipe(_.right(2), _.flatMap(f)), _.right(4)) + Util.deepStrictEqual(pipe(_.right(1), _.flatMap(f)), _.fail("e3")) + Util.deepStrictEqual(pipe(_.right(6), _.flatMap(f)), _.warn("e2", 6)) + Util.deepStrictEqual( + pipe(_.warn("e1", 2), _.flatMap(f)), + _.warn("e1", 4) + ) + 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", () => { + const f = _.flatMapNullable((n: number) => (n > 0 ? n : null), () => "e2") + Util.deepStrictEqual(f(_.right(1)), _.right(1)) + Util.deepStrictEqual(f(_.right(-1)), _.fail("e2")) + Util.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + Util.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") + Util.deepStrictEqual(f(_.right(1)), _.right(1)) + Util.deepStrictEqual(f(_.right(-1)), _.fail("e2")) + Util.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + Util.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"))) + Util.deepStrictEqual(f(_.right(1)), _.right(1)) + Util.deepStrictEqual(f(_.right(-1)), _.fail("e2")) + Util.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + Util.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"))) + Util.deepStrictEqual(f(_.right(1)), _.right(1)) + Util.deepStrictEqual(f(_.right(-1)), _.fail("e2")) + Util.deepStrictEqual(f(_.right(11)), _.warn("e3", 11)) + Util.deepStrictEqual(f(_.fail("e1")), _.fail("e1")) + Util.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", () => { + Util.deepStrictEqual(_.leftOrBoth(() => "a")(O.none()), _.left("a")) + Util.deepStrictEqual(_.leftOrBoth(() => "a")(O.some(1)), _.both("a", 1)) + }) + + it("rightOrBoth", () => { + Util.deepStrictEqual(_.rightOrBoth(() => 1)(O.none()), _.right(1)) + Util.deepStrictEqual(_.rightOrBoth(() => 1)(O.some("a")), _.both("a", 1)) + }) + + it("match", () => { + const f = (s: string, n: number) => S.length(s) + Util.double(n) + const match = _.match(S.length, Util.double, f) + Util.deepStrictEqual(match(_.left("foo")), 3) + Util.deepStrictEqual(match(_.right(1)), 2) + Util.deepStrictEqual(match(_.both("foo", 1)), 5) + }) + + it("getBothOrElse", () => { + const f = _.getBothOrElse(() => "a", () => 1) + Util.deepStrictEqual(pipe(_.left("b"), f), ["b", 1]) + Util.deepStrictEqual(pipe(_.right(2), f), ["a", 2]) + Util.deepStrictEqual(pipe(_.both("b", 2), f), ["b", 2]) + }) + + it("getBoth", () => { + Util.deepStrictEqual(pipe(_.left("e"), _.getBoth), O.none()) + Util.deepStrictEqual(pipe(_.right(1), _.getBoth), O.none()) + expect(pipe(_.both("e", 1), _.getBoth)).toEqual(O.some(["e", 1])) + }) + + it("getLeft", () => { + Util.deepStrictEqual(_.getLeft(_.left("e")), O.some("e")) + Util.deepStrictEqual(_.getLeft(_.right(1)), O.none()) + Util.deepStrictEqual(_.getLeft(_.both("e", 1)), O.some("e")) + }) + + it("getRight", () => { + Util.deepStrictEqual(_.getRight(_.left("e")), O.none()) + Util.deepStrictEqual(_.getRight(_.right(1)), O.some(1)) + Util.deepStrictEqual(_.getRight(_.both("e", 1)), O.some(1)) + }) + + it("getLeftOnly", () => { + Util.deepStrictEqual(_.getLeftOnly(_.left("e")), O.some("e")) + Util.deepStrictEqual(_.getLeftOnly(_.right(1)), O.none()) + Util.deepStrictEqual(_.getLeftOnly(_.both("e", 1)), O.none()) + }) + + it("getRightOnly", () => { + Util.deepStrictEqual(_.getRightOnly(_.left("e")), O.none()) + Util.deepStrictEqual(_.getRightOnly(_.right(1)), O.some(1)) + Util.deepStrictEqual(_.getRightOnly(_.both("e", 1)), O.none()) + }) + + it("isLeft", () => { + Util.deepStrictEqual(_.isLeft(_.left("e")), true) + Util.deepStrictEqual(_.isLeft(_.right(1)), false) + Util.deepStrictEqual(_.isLeft(_.both("e", 1)), false) + }) + + it("isLeftOrBoth", () => { + Util.deepStrictEqual(_.isLeftOrBoth(_.left("e")), true) + Util.deepStrictEqual(_.isLeftOrBoth(_.right(1)), false) + Util.deepStrictEqual(_.isLeftOrBoth(_.both("e", 1)), true) + }) + + it("isRight", () => { + Util.deepStrictEqual(_.isRight(_.left("e")), false) + Util.deepStrictEqual(_.isRight(_.right(1)), true) + Util.deepStrictEqual(_.isRight(_.both("", 1)), false) + }) + + it("isRightOrBoth", () => { + Util.deepStrictEqual(_.isRightOrBoth(_.left("e")), false) + Util.deepStrictEqual(_.isRightOrBoth(_.right(1)), true) + Util.deepStrictEqual(_.isRightOrBoth(_.both("e", 1)), true) + }) + + it("isThese", () => { + Util.deepStrictEqual(_.isThese(_.left("e")), true) + Util.deepStrictEqual(_.isThese(_.right(1)), true) + Util.deepStrictEqual(_.isThese(_.both("e", 1)), true) + Util.deepStrictEqual(_.isThese(E.left("e")), true) + Util.deepStrictEqual(_.isThese(E.right(1)), true) + Util.deepStrictEqual(_.isThese(O.some(1)), false) + }) + + it("isBoth", () => { + Util.deepStrictEqual(_.isBoth(_.left("e")), false) + Util.deepStrictEqual(_.isBoth(_.right(1)), false) + Util.deepStrictEqual(_.isBoth(_.both("e", 1)), true) + }) + + it("liftThrowable", () => { + const f = _.liftThrowable((s: string) => { + const len = s.length + if (len > 0) { + return len + } + throw new Error("empty string") + }, identity) + Util.deepStrictEqual(f("a"), _.right(1)) + Util.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))) + Util.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))) + Util.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))) + Util.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))) + Util.deepStrictEqual(log, ["e1"]) + }) + + it("getOrThrow", () => { + expect(pipe(_.right(1), _.getOrThrow)).toEqual(1) + expect(pipe(_.both("e", 1), _.getOrThrow)).toEqual(1) + expect(() => pipe(_.left("e"), _.getOrThrow)).toThrowError( + new Error("getOrThrow called on a Left") + ) + }) + + it("getOrThrowWith", () => { + expect(pipe(_.right(1), _.getOrThrowWith((e) => new Error(`Unexpected Left: ${e}`)))).toEqual(1) + expect(pipe(_.both("w", 1), _.getOrThrowWith((e) => new Error(`Unexpected Left: ${e}`)))) + .toEqual(1) + expect(() => pipe(_.left("e"), _.getOrThrowWith((e) => new Error(`Unexpected Left: ${e}`)))) + .toThrowError( + new Error("Unexpected Left: e") + ) + }) + + it("getRightOnlyOrThrow", () => { + expect(pipe(_.right(1), _.getRightOnlyOrThrow)).toEqual(1) + expect(() => pipe(_.left("e"), _.getRightOnlyOrThrow)).toThrow( + new Error("getRightOnlyOrThrow called on a Left or a Both") + ) + expect(() => pipe(_.both("e", 1), _.getRightOnlyOrThrow)).toThrow( + new Error("getRightOnlyOrThrow called on a Left or a Both") + ) + }) + + it("getRightOnlyOrThrowWith", () => { + expect( + pipe(_.right(1), _.getRightOnlyOrThrowWith((e) => new Error(`Unexpected Left or Both: ${e}`))) + ).toEqual(1) + expect(() => + pipe( + _.left("e"), + _.getRightOnlyOrThrowWith((e) => new Error(`Unexpected Left or Both: ${e}`)) + ) + ).toThrow( + new Error("Unexpected Left or Both: e") + ) + expect(() => + pipe( + _.both("e", 1), + _.getRightOnlyOrThrowWith((e) => new Error(`Unexpected Left or Both: ${e}`)) + ) + ).toThrow( + new Error("Unexpected Left or Both: e") + ) + }) + + it("getOrElse", () => { + Util.deepStrictEqual(pipe(_.right(1), _.getOrElse(() => 2)), 1) + Util.deepStrictEqual(pipe(_.left("e"), _.getOrElse(() => 2)), 2) + Util.deepStrictEqual(pipe(_.both("e", 1), _.getOrElse(() => 2)), 1) + }) + + it("getOrNull", () => { + Util.deepStrictEqual(pipe(_.right(1), _.getOrNull), 1) + Util.deepStrictEqual(pipe(_.left("e"), _.getOrNull), null) + Util.deepStrictEqual(pipe(_.both("e", 1), _.getOrNull), 1) + }) + + it("getOrUndefined", () => { + Util.deepStrictEqual(pipe(_.right(1), _.getOrUndefined), 1) + Util.deepStrictEqual(pipe(_.left("e"), _.getOrUndefined), undefined) + Util.deepStrictEqual(pipe(_.both("e", 1), _.getOrUndefined), 1) + }) + + it("fromNullable", () => { + Util.deepStrictEqual(_.fromNullable(() => "default")(null), _.left("default")) + Util.deepStrictEqual(_.fromNullable(() => "default")(undefined), _.left("default")) + Util.deepStrictEqual(_.fromNullable(() => "default")(1), _.right(1)) + }) + + it("liftNullable", () => { + const f = _.liftNullable((n: number) => (n > 0 ? n : null), () => "error") + Util.deepStrictEqual(f(1), _.right(1)) + Util.deepStrictEqual(f(-1), _.left("error")) + }) + + it("liftPredicate", () => { + const f = _.liftPredicate((n: number) => n >= 2, () => "e") + Util.deepStrictEqual(f(3), _.right(3)) + Util.deepStrictEqual(f(1), _.left("e")) + }) + + it("fromIterable", () => { + Util.deepStrictEqual(_.fromIterable(() => "e")([]), _.left("e")) + Util.deepStrictEqual(_.fromIterable(() => "e")(["a"]), _.right("a")) + }) + + it("fromOption", () => { + Util.deepStrictEqual(_.fromOption(() => "e")(O.none()), _.left("e")) + Util.deepStrictEqual(_.fromOption(() => "e")(O.some(1)), _.right(1)) + }) + + it("fromEither", () => { + Util.deepStrictEqual(_.fromEither(E.right(1)), _.right(1)) + expect(_.fromEither(E.left("e"))).toEqual(_.left(["e"])) + }) + + it("toValidated", () => { + Util.deepStrictEqual(_.toValidated(_.right(1)), _.right(1)) + Util.deepStrictEqual(_.toValidated(_.left("e")), _.fail("e")) + Util.deepStrictEqual(_.toValidated(_.both("e", 1)), _.warn("e", 1)) + }) + + it("toEither", () => { + expect(_.toEither).exist + }) + + it("absolve", () => { + Util.deepStrictEqual(_.absolve(_.right(1)), E.right(1)) + Util.deepStrictEqual(_.absolve(_.left("e")), E.left("e")) + Util.deepStrictEqual(_.absolve(_.both("e", 1)), E.right(1)) + }) + + it("condemn", () => { + Util.deepStrictEqual(_.condemn(_.right(1)), E.right(1)) + Util.deepStrictEqual(_.condemn(_.left("e")), E.left("e")) + Util.deepStrictEqual(_.condemn(_.both("e", 1)), E.left("e")) + }) + + it("liftOption", () => { + const f = _.liftOption((n: number) => (n > 0 ? O.some(n) : O.none()), () => "e") + Util.deepStrictEqual(f(1), _.right(1)) + Util.deepStrictEqual(f(-1), _.left("e")) + }) + + it("liftEither", () => { + const f = _.liftEither((n: number) => (n > 0 ? E.right(n) : E.left("e"))) + Util.deepStrictEqual(f(1), _.right(1)) + Util.deepStrictEqual(f(-1), _.fail("e")) + }) + + it("liftThese", () => { + const f = _.liftThese((n: number) => (n > 0 ? _.right(n) : _.left("e"))) + Util.deepStrictEqual(f(1), _.right(1)) + Util.deepStrictEqual(f(-1), _.fail("e")) + }) + + it("fromTuple", () => { + expect(pipe(["e", 1] as [string, number], _.fromTuple)).toEqual(_.both("e", 1)) + }) + + it("reverse", () => { + Util.deepStrictEqual(_.reverse(_.left("e")), _.right("e")) + Util.deepStrictEqual(_.reverse(_.right(1)), _.left(1)) + Util.deepStrictEqual(_.reverse(_.both("e", 1)), _.both(1, "e")) + }) + + it("exists", () => { + const gt2 = _.exists((n: number) => n > 2) + Util.deepStrictEqual(gt2(_.left("a")), false) + Util.deepStrictEqual(gt2(_.right(1)), false) + Util.deepStrictEqual(gt2(_.right(3)), true) + Util.deepStrictEqual(gt2(_.both("a", 1)), false) + Util.deepStrictEqual(gt2(_.both("a", 3)), true) + }) + + it("contains", () => { + const contains = _.contains(number) + Util.deepStrictEqual(contains(2)(_.left("a")), false) + Util.deepStrictEqual(contains(2)(_.right(2)), true) + Util.deepStrictEqual(contains(1)(_.right(2)), false) + Util.deepStrictEqual(contains(2)(_.both("a", 2)), true) + Util.deepStrictEqual(contains(1)(_.both("a", 2)), false) + }) + + it("of", () => { + Util.deepStrictEqual(_.of(1), _.right(1)) + }) + + it("orElse", () => { + Util.deepStrictEqual(pipe(_.right(1), _.orElse(() => _.right(2))), _.right(1)) + Util.deepStrictEqual(pipe(_.right(1), _.orElse(() => _.left("b"))), _.right(1)) + Util.deepStrictEqual(pipe(_.right(1), _.orElse(() => _.both("b", 2))), _.right(1)) + Util.deepStrictEqual(pipe(_.left("a"), _.orElse(() => _.right(2))), _.right(2)) + Util.deepStrictEqual(pipe(_.left("a"), _.orElse(() => _.left("b"))), _.left("b")) + Util.deepStrictEqual(pipe(_.left("a"), _.orElse(() => _.both("b", 2))), _.both("b", 2)) + Util.deepStrictEqual(pipe(_.both("a", 1), _.orElse(() => _.right(2))), _.both("a", 1)) + Util.deepStrictEqual(pipe(_.both("a", 1), _.orElse(() => _.left("b"))), _.both("a", 1)) + Util.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", () => { + Util.deepStrictEqual(pipe(_.right(1), _.orElseFail(() => "e2")), _.right(1)) + Util.deepStrictEqual(pipe(_.left("e1"), _.orElseFail(() => "e2")), _.left("e2")) + Util.deepStrictEqual(pipe(_.both("e1", 1), _.orElseFail(() => "e2")), _.both("e1", 1)) + }) + + it("firstSuccessOf", () => { + Util.deepStrictEqual(pipe(_.right(1), _.firstRightOrBothOf([])), _.right(1)) + Util.deepStrictEqual(pipe(_.left("e"), _.firstRightOrBothOf([])), _.left("e")) + Util.deepStrictEqual( + pipe( + _.left("e1"), + _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4"), _.right(1)]) + ), + _.right(1) + ) + Util.deepStrictEqual( + pipe( + _.left("e1"), + _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4"), _.both("e5", 1)]) + ), + _.both("e5", 1) + ) + Util.deepStrictEqual( + pipe(_.left("e1"), _.firstRightOrBothOf([_.left("e2"), _.left("e3"), _.left("e4")])), + _.left("e4") + ) + }) + + it("coproduct", () => { + const coproduct = _.SemiCoproduct.coproduct + Util.deepStrictEqual(coproduct(_.right(1), _.right(2)), _.right(1)) + Util.deepStrictEqual(coproduct(_.right(1), _.left("e2")), _.right(1)) + Util.deepStrictEqual(coproduct(_.left("e1"), _.right(2)), _.right(2)) + Util.deepStrictEqual(coproduct(_.left("e1"), _.left("e2")), _.left("e2")) + Util.deepStrictEqual(coproduct(_.both("e1", 1), _.right(2)), _.both("e1", 1)) + Util.deepStrictEqual(coproduct(_.both("e1", 1), _.left("e2")), _.both("e1", 1)) + }) + + it("coproduct", () => { + const coproductMany = _.SemiCoproduct.coproductMany + Util.deepStrictEqual(coproductMany(_.right(1), [_.right(2)]), _.right(1)) + Util.deepStrictEqual(coproductMany(_.right(1), [_.left("e2")]), _.right(1)) + Util.deepStrictEqual(coproductMany(_.left("e1"), [_.right(2)]), _.right(2)) + Util.deepStrictEqual(coproductMany(_.left("e1"), [_.left("e2")]), _.left("e2")) + Util.deepStrictEqual(coproductMany(_.both("e1", 1), [_.right(2)]), _.both("e1", 1)) + Util.deepStrictEqual(coproductMany(_.both("e1", 1), [_.left("e2")]), _.both("e1", 1)) + }) + + it("compact", () => { + Util.deepStrictEqual(pipe(_.right(O.some(1)), _.compact(() => "e2")), _.right(1)) + Util.deepStrictEqual(pipe(_.right(O.none()), _.compact(() => "e2")), _.left("e2")) + Util.deepStrictEqual(pipe(_.left("e1"), _.compact(() => "e2")), _.left("e1")) + Util.deepStrictEqual(pipe(_.both("e1", O.some(1)), _.compact(() => "e2")), _.both("e1", 1)) + Util.deepStrictEqual(pipe(_.both("e1", O.none()), _.compact(() => "e2")), _.left("e2")) + }) + + it("filter", () => { + const predicate = (n: number) => n > 10 + Util.deepStrictEqual(pipe(_.right(12), _.filter(predicate, () => "e2")), _.right(12)) + Util.deepStrictEqual(pipe(_.right(7), _.filter(predicate, () => "e2")), _.left("e2")) + Util.deepStrictEqual(pipe(_.left("e1"), _.filter(predicate, () => "e2")), _.left("e1")) + Util.deepStrictEqual(pipe(_.both("e1", 12), _.filter(predicate, () => "e2")), _.both("e1", 12)) + Util.deepStrictEqual(pipe(_.both("e1", 7), _.filter(predicate, () => "e2")), _.left("e2")) + }) + + it("filterMap", () => { + const f = (n: number) => (n > 2 ? O.some(n + 1) : O.none()) + Util.deepStrictEqual(pipe(_.left("e1"), _.filterMap(f, () => "e2")), _.left("e1")) + Util.deepStrictEqual(pipe(_.right(1), _.filterMap(f, () => "e2")), _.left("e2")) + Util.deepStrictEqual(pipe(_.right(3), _.filterMap(f, () => "e2")), _.right(4)) + Util.deepStrictEqual(pipe(_.both("e1", 1), _.filterMap(f, () => "e2")), _.left("e2")) + Util.deepStrictEqual(pipe(_.both("e1", 3), _.filterMap(f, () => "e2")), _.both("e1", 4)) + }) + + it("zipWith", () => { + const e: _.Validated = _.left(["a"]) + expect(pipe(e, _.zipWith(_.right(2), (a, b) => a + b))).toEqual(e) + expect(pipe(_.right(1), _.zipWith(e, (a, b) => a + b))).toEqual(e) + expect(pipe(_.right(1), _.zipWith(_.right(2), (a, b) => a + b))).toEqual(_.right(3)) + }) + + it("sum", () => { + const e: _.Validated = _.left(["a"]) + expect(pipe(e, _.sum(_.right(2)))).toEqual(e) + expect(pipe(_.right(1), _.sum(e))).toEqual(e) + expect(pipe(_.right(2), _.sum(_.right(3)))).toEqual(_.right(5)) + }) + + it("multiply", () => { + const e: _.Validated = _.left(["a"]) + expect(pipe(e, _.multiply(_.right(2)))).toEqual(e) + expect(pipe(_.right(1), _.multiply(e))).toEqual(e) + expect(pipe(_.right(2), _.multiply(_.right(3)))).toEqual(_.right(6)) + }) + + it("subtract", () => { + const e: _.Validated = _.left(["a"]) + expect(pipe(e, _.subtract(_.right(2)))).toEqual(e) + expect(pipe(_.right(1), _.subtract(e))).toEqual(e) + expect(pipe(_.right(2), _.subtract(_.right(3)))).toEqual(_.right(-1)) + }) + + it("divide", () => { + const e: _.Validated = _.left(["a"]) + expect(pipe(e, _.divide(_.right(2)))).toEqual(e) + expect(pipe(_.right(1), _.divide(e))).toEqual(e) + expect(pipe(_.right(6), _.divide(_.right(3)))).toEqual(_.right(2)) + }) + + it("getEquivalence", () => { + const isEquivalent = _.getEquivalence(N.Equivalence, N.Equivalence) + Util.deepStrictEqual(isEquivalent(_.left(2), _.left(2)), true) + Util.deepStrictEqual(isEquivalent(_.left(2), _.left(3)), false) + Util.deepStrictEqual(isEquivalent(_.left(3), _.left(2)), false) + Util.deepStrictEqual(isEquivalent(_.left(2), _.right(2)), false) + Util.deepStrictEqual(isEquivalent(_.left(2), _.both(2, 2)), false) + Util.deepStrictEqual(isEquivalent(_.right(2), _.right(2)), true) + Util.deepStrictEqual(isEquivalent(_.right(2), _.right(3)), false) + Util.deepStrictEqual(isEquivalent(_.right(3), _.right(2)), false) + Util.deepStrictEqual(isEquivalent(_.right(2), _.both(2, 2)), false) + Util.deepStrictEqual(isEquivalent(_.both(2, 2), _.both(2, 2)), true) + Util.deepStrictEqual(isEquivalent(_.both(2, 3), _.both(3, 2)), false) + }) +}) diff --git a/test/Tuple.ts b/test/Tuple.ts new file mode 100644 index 000000000..127ffb84e --- /dev/null +++ b/test/Tuple.ts @@ -0,0 +1,39 @@ +import { pipe } from "@fp-ts/core/Function" +import * as T from "@fp-ts/core/Tuple" + +describe.concurrent("Tuple", () => { + it("exports", () => { + expect(T.Bicovariant).exists + expect(T.mapFirst).exists + expect(T.mapSecond).exists + expect(T.appendElement).exists + expect(T.getEquivalence).exists + expect(T.getOrder).exists + expect(T.getSemigroup).exists + expect(T.getMonoid).exists + }) + + it("tuple", () => { + expect(T.tuple("a", 1, true)).toEqual(["a", 1, true]) + }) + + it("appendElement", () => { + expect(pipe(T.tuple("a", 1), T.appendElement(true))).toEqual(["a", 1, true]) + }) + + it("getFirst", () => { + expect(T.getFirst(T.tuple("a", 1))).toEqual("a") + }) + + it("getSecond", () => { + expect(T.getSecond(T.tuple("a", 1))).toEqual(1) + }) + + it("bimap", () => { + expect(T.bimap(T.tuple("a", 1), s => s + "!", n => n * 2)).toEqual(["a!", 2]) + }) + + it("swap", () => { + expect(T.swap(T.tuple("a", 1))).toEqual([1, "a"]) + }) +}) 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 775a2c355..000000000 --- a/test/data/NonEmptyReadonlyArray.ts +++ /dev/null @@ -1,69 +0,0 @@ -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" - -/** - * @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 c3890a22f..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 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" -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/data/Option' - * import { pipe } from '@fp-ts/data/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/data/Option' - * import { pipe } from '@fp-ts/data/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/data/Option' - * import * as N from '@fp-ts/data/number' - * import { pipe } from '@fp-ts/data/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/data/Option' - * import * as N from '@fp-ts/data/number' - * import { pipe } from '@fp-ts/data/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 a11bea077..000000000 --- a/test/data/ReadonlyArray.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { semiProduct } from "@fp-ts/core" -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" -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/index.ts b/test/index.ts index 84cf06572..a4954f336 100644 --- a/test/index.ts +++ b/test/index.ts @@ -7,9 +7,6 @@ const getExportName = (name: string): string => { if (name === "HKT") { return name.toLowerCase() } - if (name === "Const") { - return "const_" - } return name.substring(0, 1).toLowerCase() + name.substring(1) } 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 deleted file mode 100644 index 0f271d061..000000000 --- a/test/limbo/CovariantWithIndex.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @since 1.0.0 - */ -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" - -/** - * @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/limbo/FoldableWithIndex.ts b/test/limbo/FoldableWithIndex.ts deleted file mode 100644 index f6ff25738..000000000 --- a/test/limbo/FoldableWithIndex.ts +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @since 1.0.0 - */ - -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" - -/** - * @category type class - * @since 1.0.0 - */ -export interface FoldableWithIndex extends TypeClass { - readonly reduceWithIndex: ( - b: B, - f: (b: B, a: A, i: I) => B - ) => (self: Kind) => B - - readonly reduceRightWithIndex: ( - b: B, - f: (b: B, a: A, i: I) => B - ) => (self: Kind) => B -} - -/** - * Returns a default `reduceWithIndex` composition. - * - * @since 1.0.0 - */ -export const reduceWithIndexComposition = ( - F: FoldableWithIndex, - G: FoldableWithIndex -) => - (b: B, f: (b: B, a: A, ij: readonly [I, J]) => B) => - ( - self: Kind> - ): B => - pipe( - self, - F.reduceWithIndex(b, (b, ga, i) => - pipe(ga, G.reduceWithIndex(b, (b, a, j) => f(b, a, [i, j])))) - ) - -/** - * Returns a default `reduceRightWithIndex` composition. - * - * @since 1.0.0 - */ -export const reduceRightWithIndexComposition = ( - F: FoldableWithIndex, - G: FoldableWithIndex -) => - (b: B, f: (b: B, a: A, ij: readonly [I, J]) => B) => - ( - self: Kind> - ): B => - pipe( - self, - F.reduceRightWithIndex(b, (b, ga, i) => - pipe(ga, G.reduceRightWithIndex(b, (b, a, j) => f(b, a, [i, j])))) - ) - -/** - * Returns a default `reduce` implementation. - * - * @since 1.0.0 - */ -export const reduce = ( - F: FoldableWithIndex -): Foldable["reduce"] => (b, f) => F.reduceWithIndex(b, f) - -/** - * @since 1.0.0 - */ -export const toReadonlyArray = ( - F: FoldableWithIndex -): (self: Kind) => ReadonlyArray => toReadonlyArrayWith(F)(identity) - -/** - * @since 1.0.0 - */ -export const toReadonlyArrayWith = ( - F: FoldableWithIndex -) => - (f: (a: A, i: I) => B) => - (self: Kind): ReadonlyArray => - F.reduceWithIndex>([], (out, a, i) => { - out.push(f(a, i)) - return out - })(self) - -/** - * @since 1.0.0 - */ -export const foldMapWithIndex = ( - F: FoldableWithIndex -) => - (M: Monoid) => - (f: (a: A, i: I) => M) => - (self: Kind): M => M.combineAll(toReadonlyArrayWith(F)(f)(self)) diff --git a/src/typeclass/NonEmptyTraversable.ts b/test/limbo/NonEmptyTraversable.ts similarity index 97% rename from src/typeclass/NonEmptyTraversable.ts rename to test/limbo/NonEmptyTraversable.ts index 7ec2cdd34..7f9aaf96d 100644 --- a/src/typeclass/NonEmptyTraversable.ts +++ b/test/limbo/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/test/limbo/TraversableWithIndex.ts b/test/limbo/TraversableWithIndex.ts deleted file mode 100644 index 009e5ec52..000000000 --- a/test/limbo/TraversableWithIndex.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @since 1.0.0 - */ -import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" -import type { Applicative } from "@fp-ts/core/typeclass/Applicative" -import type { Traversable } from "@fp-ts/core/typeclass/Traversable" - -/** - * @category type class - * @since 1.0.0 - */ -export interface TraversableWithIndex extends TypeClass { - readonly traverseWithIndex: ( - F: Applicative - ) => ( - f: (a: A, i: I) => Kind - ) => ( - self: Kind - ) => Kind> -} - -/** - * Returns a default `traverseWithIndex` composition. - * - * @since 1.0.0 - */ -export const traverseWithIndexComposition = ( - F: TraversableWithIndex, - G: TraversableWithIndex -) => - (H: Applicative) => - ( - f: (a: A, ij: readonly [I, J]) => Kind - ): (( - fga: Kind> - ) => Kind>>) => - F.traverseWithIndex(H)((ga, i) => - G.traverseWithIndex(H)((a, j) => f(a, [i, j]))(ga) - ) - -/** - * Returns a default `traverse` implementation. - * - * @since 1.0.0 - */ -export const traverse = ( - F: TraversableWithIndex -): Traversable["traverse"] => f => F.traverseWithIndex(f) diff --git a/test/typeclass/Applicative.ts b/test/typeclass/Applicative.ts index 97e5610d5..cdf5b8d6a 100644 --- a/test/typeclass/Applicative.ts +++ b/test/typeclass/Applicative.ts @@ -1,16 +1,15 @@ -import { pipe } from "@fp-ts/core/internal/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 liftMonoid = _.getMonoid(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/Bicovariant.ts b/test/typeclass/Bicovariant.ts index 712cf4352..b6a0110d3 100644 --- a/test/typeclass/Bicovariant.ts +++ b/test/typeclass/Bicovariant.ts @@ -1,34 +1,29 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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 = { - bimap: (f, g) => (self) => E.isLeft(self) ? E.left(f(self.left)) : E.right(g(self.right)) -} - describe("Bicovariant", () => { it("mapLeft", () => { - const mapLeft = _.mapLeft(Bicovariant) + const mapLeft = _.mapLeft(E.Bicovariant) const f = (s: string) => s.length U.deepStrictEqual(pipe(E.right(1), mapLeft(f)), E.right(1)) U.deepStrictEqual(pipe(E.left("eee"), mapLeft(f)), E.left(3)) }) it("map", () => { - const map = _.map(Bicovariant) + const map = _.map(E.Bicovariant) const g = (n: number) => n * 2 U.deepStrictEqual(pipe(E.right(1), map(g)), E.right(2)) U.deepStrictEqual(pipe(E.left("eee"), map(g)), E.left("eee")) }) it("bimapComposition", () => { - const bimap = _.bimapComposition(RA.Covariant, Bicovariant) + const bimap = _.bimapComposition(RA.Covariant, E.Bicovariant) const f = (s: string) => s.length const g = (n: number) => n * 2 - U.deepStrictEqual(pipe([E.right(1), E.right(2), E.left("eee")], bimap(f, g)), [ + U.deepStrictEqual(bimap([E.right(1), E.right(2), E.left("eee")], f, g), [ E.right(2), E.right(4), E.left(3) diff --git a/test/typeclass/Bounded.ts b/test/typeclass/Bounded.ts index 7c02285dc..1e4d5c4bc 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,8 +13,8 @@ describe("Bounded", () => { }) it("reverse", () => { - const B = _.reverse({ ...number.Order, minBound: 10, maxBound: 1 }) - U.deepStrictEqual(B.maxBound, 1) - U.deepStrictEqual(B.minBound, 10) + const B = _.reverse({ ...Number.Order, minBound: 10, maxBound: 1 }) + U.deepStrictEqual(B.maxBound, 10) + U.deepStrictEqual(B.minBound, 1) }) }) diff --git a/test/typeclass/Chainable.ts b/test/typeclass/Chainable.ts index 4fffd748f..944c8f118 100644 --- a/test/typeclass/Chainable.ts +++ b/test/typeclass/Chainable.ts @@ -1,28 +1,28 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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 a843e650b..e7008f948 100644 --- a/test/typeclass/Contravariant.ts +++ b/test/typeclass/Contravariant.ts @@ -1,28 +1,27 @@ -import { pipe } from "@fp-ts/core/internal/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", () => { it("mapComposition", () => { 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) + const a = map(emptyString, s => s.length) + U.deepStrictEqual(a(S.isString), false) U.deepStrictEqual(a(n => n === 0), true) }) it("imap", () => { const O = _.imap(order.contramap)( - (s: string) => [s] as const, + (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) - 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/Coproduct.ts b/test/typeclass/Coproduct.ts index 2bc212210..93c2fe019 100644 --- a/test/typeclass/Coproduct.ts +++ b/test/typeclass/Coproduct.ts @@ -1,18 +1,17 @@ -import { pipe } from "@fp-ts/core/internal/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.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/Covariant.ts b/test/typeclass/Covariant.ts index e2be796ff..1ecc7ca6e 100644 --- a/test/typeclass/Covariant.ts +++ b/test/typeclass/Covariant.ts @@ -1,18 +1,18 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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", () => { it("mapComposition", () => { const map = _.mapComposition(RA.Covariant, RA.Covariant) const f = (a: string) => a + "!" - U.deepStrictEqual(pipe([], map(f)), []) - U.deepStrictEqual(pipe([[]], map(f)), [[]]) - U.deepStrictEqual(pipe([["a"]], map(f)), [["a!"]]) - U.deepStrictEqual(pipe([["a"], ["b"]], map(f)), [["a!"], ["b!"]]) - U.deepStrictEqual(pipe([["a", "c"], ["b", "d", "e"]], map(f)), [["a!", "c!"], [ + U.deepStrictEqual(map([], f), []) + U.deepStrictEqual(map([[]], f), [[]]) + U.deepStrictEqual(map([["a"]], f), [["a!"]]) + U.deepStrictEqual(map([["a"], ["b"]], f), [["a!"], ["b!"]]) + U.deepStrictEqual(map([["a", "c"], ["b", "d", "e"]], f), [["a!", "c!"], [ "b!", "d!", "e!" @@ -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.some(U.double), flap(1)), O.some(2)) + U.deepStrictEqual(pipe(1, flap(O.none())), O.none()) + U.deepStrictEqual(pipe(1, flap(O.some(U.double))), 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)) }) @@ -46,8 +46,8 @@ describe("Covariant", () => { }) it("imap", () => { - const f = _.imap(O.map)((s: string) => [s] as const, ([s]) => s) - U.deepStrictEqual(pipe(O.none, f), O.none) - U.deepStrictEqual(pipe(O.some("a"), f), O.some(["a"] as const)) + const f = _.imap(O.map)((s: string) => [s], ([s]) => s) + 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 deleted file mode 100644 index c2f630bfb..000000000 --- a/test/typeclass/CovariantWithIndex.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { pipe } from "@fp-ts/core/internal/Function" -import * as RA from "../data/ReadonlyArray" -import * as _ from "../limbo/CovariantWithIndex" -import * as U from "../util" - -describe("FunctorWithIndex", () => { - it("mapWithIndexComposition", () => { - const mapWithIndex = _.mapWithIndexComposition(RA.CovariantWithIndex, RA.CovariantWithIndex) - const f = (a: string, [i, j]: readonly [number, number]) => a + i + j - U.deepStrictEqual(pipe([], mapWithIndex(f)), []) - U.deepStrictEqual(pipe([[]], mapWithIndex(f)), [[]]) - U.deepStrictEqual(pipe([["a"]], mapWithIndex(f)), [["a00"]]) - U.deepStrictEqual(pipe([["a"], ["b"]], mapWithIndex(f)), [["a00"], ["b10"]]) - U.deepStrictEqual(pipe([["a", "c"], ["b", "d", "e"]], mapWithIndex(f)), [["a00", "c01"], [ - "b10", - "d11", - "e12" - ]]) - }) - - it("map", () => { - const map = _.map(RA.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/Equivalence.ts b/test/typeclass/Equivalence.ts new file mode 100644 index 000000000..21b365ab3 --- /dev/null +++ b/test/typeclass/Equivalence.ts @@ -0,0 +1,92 @@ +import { pipe } from "@fp-ts/core/Function" +import * as _ from "@fp-ts/core/typeclass/Equivalence" + +describe("Equivalence", () => { + it("exports", () => { + expect(_.Invariant).exists + expect(_.Contravariant).exists + expect(_.SemiProduct).exists + expect(_.Product).exists + expect(_.tuple).exists + expect(_.struct).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("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 = 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) + expect(eqE0E1(["a", 1, true], ["a", 2, false])).toEqual(false) + const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) + 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) + 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("of", () => { + const eq = _.Product.of([]) + expect(eq([], [])).toEqual(true) + }) + + it("product", () => { + const eq = _.Product.product(_.string, _.string) + expect(eq(["a", "b"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["c", "b"])).toEqual(false) + expect(eq(["a", "b"], ["a", "c"])).toEqual(false) + }) + + it("productMany", () => { + const eq = _.Product.productMany(_.string, [_.string]) + expect(eq(["a", "b"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["a", "b", "c"])).toEqual(true) + expect(eq(["a", "b", "c"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["c", "b"])).toEqual(false) + expect(eq(["a", "b"], ["a", "c"])).toEqual(false) + }) + + it("productAll", () => { + const eq = _.Product.productAll([_.string, _.string]) + expect(eq(["a"], ["a"])).toEqual(true) + expect(eq(["a"], ["b"])).toEqual(false) + expect(eq(["a", "b"], ["a", "b"])).toEqual(true) + expect(eq(["a", "b"], ["a", "c"])).toEqual(false) + }) +}) diff --git a/test/typeclass/Filterable.ts b/test/typeclass/Filterable.ts new file mode 100644 index 000000000..d0a76c22f --- /dev/null +++ b/test/typeclass/Filterable.ts @@ -0,0 +1,67 @@ +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 = (s: string) => s.length > 1 ? O.some(s.length) : O.none() + U.deepStrictEqual(filterMap([], f), []) + U.deepStrictEqual(filterMap([O.none()], f), [O.none()]) + U.deepStrictEqual(filterMap([O.some("a")], f), [O.none()]) + U.deepStrictEqual(filterMap([O.some("aa")], f), [O.some(2)]) + }) + + it("partitionMapComposition", () => { + const partitionMap = _.partitionMapComposition(RA.Covariant, O.Filterable) + const f = (s: string) => s.length > 1 ? E.right(s.length) : E.left(s + "!") + U.deepStrictEqual(partitionMap([], f), [[], []]) + U.deepStrictEqual(partitionMap([O.none()], f), [[O.none()], [O.none()]]) + U.deepStrictEqual(partitionMap([O.some("a")], f), [[O.some("a!")], [O.none()]]) + U.deepStrictEqual(partitionMap([O.some("aa")], f), [[O.none()], [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("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]]) + }) + + it("compact", () => { + const compact = _.compact(RA.Filterable) + assert.deepStrictEqual(compact([]), []) + assert.deepStrictEqual(compact([O.some(1), O.some(2), O.some(3)]), [ + 1, + 2, + 3 + ]) + assert.deepStrictEqual(compact([O.some(1), O.none(), O.some(3)]), [ + 1, + 3 + ]) + }) + + it("separate", () => { + const separate = _.separate(RA.Filterable) + 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/FlatMap.ts b/test/typeclass/FlatMap.ts index 331606b5b..4ce685825 100644 --- a/test/typeclass/FlatMap.ts +++ b/test/typeclass/FlatMap.ts @@ -1,31 +1,31 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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 4c0285898..26a469a2d 100644 --- a/test/typeclass/Foldable.ts +++ b/test/typeclass/Foldable.ts @@ -1,76 +1,57 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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", () => { it("reduceComposition", () => { const reduce = _.reduceComposition(RA.Foldable, RA.Foldable) const f = (b: string, a: string) => b + a - U.deepStrictEqual(pipe([], reduce("-", f)), "-") - U.deepStrictEqual(pipe([[]], reduce("-", f)), "-") - U.deepStrictEqual(pipe([["a", "c"], ["b", "d"]], reduce("-", f)), "-acbd") + U.deepStrictEqual(reduce([], "-", f), "-") + U.deepStrictEqual(reduce([[]], "-", f), "-") + U.deepStrictEqual(reduce([["a", "c"], ["b", "d"]], "-", f), "-acbd") }) - it("toReadonlyArray", () => { - const toReadonlyArray = _.toReadonlyArray(O.Foldable) - U.deepStrictEqual(toReadonlyArray(O.none), []) - U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) + it("toArray", () => { + const toArray = _.toArray(O.Foldable) + U.deepStrictEqual(toArray(O.none()), []) + U.deepStrictEqual(toArray(O.some(2)), [2]) }) - it("toReadonlyArrayWith", () => { - const toReadonlyArrayWith = _.toReadonlyArrayWith(O.Foldable) - U.deepStrictEqual(pipe(O.none, toReadonlyArrayWith(U.double)), []) - U.deepStrictEqual(pipe(O.some(2), toReadonlyArrayWith(U.double)), [4]) + it("toArrayMap", () => { + const toArrayMap = _.toArrayMap(O.Foldable) + U.deepStrictEqual(toArrayMap(O.none(), U.double), []) + U.deepStrictEqual(toArrayMap(O.some(2), U.double), [4]) }) - it("foldMap", () => { - const foldMap = _.foldMap(RA.Foldable) - U.deepStrictEqual(pipe([1, 2, 3], foldMap(number.MonoidSum)(U.double)), 12) - }) - - it("reduceRight", () => { - const reduceRight = _.reduceRight(RA.Foldable) - U.deepStrictEqual(pipe(["a", "b", "c"], reduceRight("-", (b, a) => b + a)), "-cba") + it("combineMap", () => { + const combineMap = _.combineMap(RA.Foldable) + U.deepStrictEqual(combineMap(N.MonoidSum)([1, 2, 3], U.double), 12) }) 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(reduceKind([], "-", () => O.none()), O.some("-")) + U.deepStrictEqual(reduceKind(["a"], "-", () => O.none()), O.none()) U.deepStrictEqual( - pipe(["a", "b", "c"], reduceKind("-", (b, a) => O.some(b + a))), + reduceKind(["a", "b", "c"], "-", (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 - ) - }) - - 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(["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 + reduceKind(["a", "b", "c"], "-", (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(["a", "b", "c"], foldMapKind((a) => O.some(a))), O.some("a")) + it("coproductMapKind", () => { + const coproductMapKind = _.coproductMapKind(RA.Foldable)(O.Alternative) + U.deepStrictEqual(pipe([], coproductMapKind(() => O.none())), O.none()) + U.deepStrictEqual(pipe(["a"], coproductMapKind(() => O.none())), O.none()) + U.deepStrictEqual(pipe(["a", "b", "c"], coproductMapKind((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"], coproductMapKind((a) => a === "b" ? O.none() : O.some(a))), O.some("a") ) }) diff --git a/test/typeclass/FoldableWithIndex.ts b/test/typeclass/FoldableWithIndex.ts deleted file mode 100644 index b1ef8a1af..000000000 --- a/test/typeclass/FoldableWithIndex.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { pipe } from "@fp-ts/core/internal/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" - -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("toReadonlyArray", () => { - const toReadonlyArray = foldableWithIndex.toReadonlyArray(O.FoldableWithIndex) - U.deepStrictEqual(toReadonlyArray(O.none), []) - U.deepStrictEqual(toReadonlyArray(O.some(2)), [2]) - }) - - it("toReadonlyArrayWith", () => { - const toReadonlyArrayWith = foldableWithIndex.toReadonlyArrayWith(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 - ) - }) -}) diff --git a/test/typeclass/Invariant.ts b/test/typeclass/Invariant.ts index 2c20678b1..2d2aae648 100644 --- a/test/typeclass/Invariant.ts +++ b/test/typeclass/Invariant.ts @@ -1,34 +1,34 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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 = imap(O.getOptionalMonoid(String.Semigroup), 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"]) ) }) 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.some(1), tupled), O.some([1] as const)) + U.deepStrictEqual(pipe(O.none(), tupled), O.none()) + U.deepStrictEqual(pipe(O.some(1), tupled), O.some([1])) }) 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 22a22d5b8..16a16393f 100644 --- a/test/typeclass/Monoid.ts +++ b/test/typeclass/Monoid.ts @@ -1,43 +1,42 @@ -import { pipe } from "@fp-ts/core/internal/Function" -import * as monoid from "@fp-ts/core/typeclass/Monoid" -import * as number from "../data/number" -import * as string from "../data/string" +import * as N from "@fp-ts/core/Number" +import * as String from "@fp-ts/core/String" +import * as _ from "@fp-ts/core/typeclass/Monoid" import * as U from "../util" describe("Monoid", () => { it("min", () => { - const M = monoid.min(number.Bounded) + const M = _.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 = _.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) - 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(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") + const M = _.reverse(String.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(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", () => { it("baseline", () => { - const M = monoid.struct({ - name: string.Monoid, - age: number.MonoidSum + const M = _.struct({ + 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 })), { + U.deepStrictEqual(M.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { name: "ab", age: 30 }) @@ -45,17 +44,17 @@ describe("Monoid", () => { it("should ignore non own properties", () => { const monoids = Object.create({ a: 1 }) - const M = monoid.struct(monoids) + const M = _.struct(monoids) U.deepStrictEqual(M.empty, {}) }) }) it("tuple", () => { - const M = monoid.tuple( - string.Monoid, - number.MonoidSum + const M = _.tuple( + String.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/NonEmptyTraversable.ts b/test/typeclass/NonEmptyTraversable.ts index ce901b412..9470d1c40 100644 --- a/test/typeclass/NonEmptyTraversable.ts +++ b/test/typeclass/NonEmptyTraversable.ts @@ -1,42 +1,74 @@ -import * as _ from "@fp-ts/core/typeclass/NonEmptyTraversable" -import * as NERA from "../data/NonEmptyReadonlyArray" -import * as O from "../data/Option" +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 = { + // @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 +} + +const imap = covariant.imap(RA.mapNonEmpty) + +/** + * @category instances + * @since 1.0.0 + */ +export const NonEmptyCovariant: covariant.Covariant = { + imap, + map: RA.mapNonEmpty +} + describe("NonEmptyTraversable", () => { it("traverseNonEmptyComposition", () => { const traverseNonEmpty = _.traverseNonEmptyComposition( - NERA.NonEmptyTraversable, - NERA.NonEmptyTraversable - )(O.SemiApplicative)((n: number) => (n > 0 ? O.some(n) : O.none)) + 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) + 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 + { ...NonEmptyTraversable, ...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) + 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( + 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 e24e4f0d6..d119c1d6c 100644 --- a/test/typeclass/Order.ts +++ b/test/typeclass/Order.ts @@ -1,38 +1,38 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import { pipe } from "@fp-ts/core/Function" +import { sort } from "@fp-ts/core/ReadonlyArray" 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", () => { - 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) + it("exports", () => { + expect(_.Invariant).exist + expect(_.Contravariant).exist + expect(_.string).exist + expect(_.number).exist + expect(_.boolean).exist + expect(_.bigint).exist }) - 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) + it("productAll", () => { + const O = _.Product.productAll([_.string, _.string]) + U.deepStrictEqual(O.compare(["a"], ["b"]), -1) + U.deepStrictEqual(O.compare(["a"], ["a"]), 0) + U.deepStrictEqual(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", "c"], ["a", "b"]), 1) }) - it("Invariant", () => { - const O = _.Invariant.imap((s: string) => [s] as const, ([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) + it("contramap", () => { + const O = _.contramap(_.number, (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) }) it("getSemigroup", () => { - type T = readonly [number, string] - const tuples: ReadonlyArray = [ + type T = [number, string] + const tuples: Array = [ [2, "c"], [1, "b"], [2, "a"], @@ -40,32 +40,32 @@ 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(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"], [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"], @@ -74,8 +74,8 @@ describe("Order", () => { }) it("getMonoid", () => { - type T = readonly [number, string] - const tuples: ReadonlyArray = [ + type T = [number, string] + const tuples: Array = [ [2, "c"], [1, "b"], [2, "a"], @@ -83,20 +83,20 @@ 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(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"], @@ -105,128 +105,122 @@ describe("Order", () => { }) it("clamp", () => { - const clamp = _.clamp(number.Order) - U.deepStrictEqual(clamp(1, 10)(2), 2) - U.deepStrictEqual(clamp(1, 10)(10), 10) - U.deepStrictEqual(clamp(1, 10)(20), 10) - U.deepStrictEqual(clamp(1, 10)(1), 1) - U.deepStrictEqual(clamp(1, 10)(-10), 1) + const clamp = _.clamp(_.number) + U.deepStrictEqual(clamp(2, 1, 10), 2) + U.deepStrictEqual(clamp(10, 1, 10), 10) + U.deepStrictEqual(clamp(20, 1, 10), 10) + U.deepStrictEqual(clamp(1, 1, 10), 1) + U.deepStrictEqual(clamp(-10, 1, 10), 1) }) it("between", () => { - const between = _.between(number.Order) - U.deepStrictEqual(between(1, 10)(2), true) - U.deepStrictEqual(between(1, 10)(10), true) - U.deepStrictEqual(between(1, 10)(20), false) - U.deepStrictEqual(between(1, 10)(1), true) - U.deepStrictEqual(between(1, 10)(-10), false) + const between = _.between(_.number) + U.deepStrictEqual(between(2, 1, 10), true) + U.deepStrictEqual(between(10, 1, 10), true) + U.deepStrictEqual(between(20, 1, 10), false) + U.deepStrictEqual(between(1, 1, 10), true) + U.deepStrictEqual(between(-10, 1, 10), false) }) 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) + 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) - U.deepStrictEqual(pipe(0, lessThan(1)), true) - U.deepStrictEqual(pipe(1, lessThan(1)), false) - U.deepStrictEqual(pipe(2, lessThan(1)), false) + const lessThan = _.lessThan(_.number) + U.deepStrictEqual(lessThan(0, 1), true) + U.deepStrictEqual(lessThan(1, 1), false) + U.deepStrictEqual(lessThan(2, 1), false) }) it("lessThanOrEqualTo", () => { - const lessThanOrEqualTo = _.lessThanOrEqualTo(number.Order) - U.deepStrictEqual(pipe(0, lessThanOrEqualTo(1)), true) - U.deepStrictEqual(pipe(1, lessThanOrEqualTo(1)), true) - U.deepStrictEqual(pipe(2, lessThanOrEqualTo(1)), false) + const lessThanOrEqualTo = _.lessThanOrEqualTo(_.number) + U.deepStrictEqual(lessThanOrEqualTo(0, 1), true) + U.deepStrictEqual(lessThanOrEqualTo(1, 1), true) + U.deepStrictEqual(lessThanOrEqualTo(2, 1), false) }) it("greaterThan", () => { - const greaterThan = _.greaterThan(number.Order) - U.deepStrictEqual(pipe(0, greaterThan(1)), false) - U.deepStrictEqual(pipe(1, greaterThan(1)), false) - U.deepStrictEqual(pipe(2, greaterThan(1)), true) + const greaterThan = _.greaterThan(_.number) + U.deepStrictEqual(greaterThan(0, 1), false) + U.deepStrictEqual(greaterThan(1, 1), false) + U.deepStrictEqual(greaterThan(2, 1), true) }) it("greaterThanOrEqualTo", () => { - const greaterThanOrEqualTo = _.greaterThanOrEqualTo(number.Order) - U.deepStrictEqual(pipe(0, greaterThanOrEqualTo(1)), false) - U.deepStrictEqual(pipe(1, greaterThanOrEqualTo(1)), true) - U.deepStrictEqual(pipe(2, greaterThanOrEqualTo(1)), true) + const greaterThanOrEqualTo = _.greaterThanOrEqualTo(_.number) + U.deepStrictEqual(greaterThanOrEqualTo(0, 1), false) + U.deepStrictEqual(greaterThanOrEqualTo(1, 1), true) + U.deepStrictEqual(greaterThanOrEqualTo(2, 1), true) }) it("min", () => { - type A = { readonly a: number } + type A = { a: number } const min = _.min( pipe( - number.Order, + _.number, _.contramap((a: A) => a.a) ) ) - U.deepStrictEqual(pipe({ a: 1 }, min({ a: 2 })), { a: 1 }) - U.deepStrictEqual(pipe({ a: 2 }, min({ a: 1 })), { a: 1 }) + U.deepStrictEqual(min({ a: 1 }, { a: 2 }), { a: 1 }) + U.deepStrictEqual(min({ a: 2 }, { a: 1 }), { a: 1 }) const first = { a: 1 } const second = { a: 1 } - U.strictEqual(pipe(first, min(second)), first) + U.strictEqual(min(first, second), first) }) it("max", () => { - type A = { readonly a: number } + type A = { a: number } const max = _.max( pipe( - number.Order, + _.number, _.contramap((a: A) => a.a) ) ) - U.deepStrictEqual(pipe({ a: 1 }, max({ a: 2 })), { a: 2 }) - U.deepStrictEqual(pipe({ a: 2 }, max({ a: 1 })), { a: 2 }) + U.deepStrictEqual(max({ a: 1 }, { a: 2 }), { a: 2 }) + U.deepStrictEqual(max({ a: 2 }, { a: 1 }), { a: 2 }) const first = { a: 1 } const second = { a: 1 } - U.strictEqual(pipe(first, max(second)), first) + U.strictEqual(max(first, second), first) }) describe("SemiProduct", () => { it("product", () => { - const O = pipe( - 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) + 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) + U.deepStrictEqual(O.compare(["a", 1], ["b", 1]), -1) }) it("productMany", () => { - const O = pipe( - 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) + 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) + 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]) + _.Product.productAll([_.string, _.string, _.string]) ) - 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) }) }) }) diff --git a/test/typeclass/Product.ts b/test/typeclass/Product.ts index 787a127b6..2f917c38d 100644 --- a/test/typeclass/Product.ts +++ b/test/typeclass/Product.ts @@ -1,36 +1,36 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import * as Boolean from "@fp-ts/core/Boolean" +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", () => { 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) + 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) - U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) + U.deepStrictEqual(tuple().combine([], []), []) + const S = tuple(String.Semigroup, Number.SemigroupSum) + U.deepStrictEqual(S.combine(["a", 2], ["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 +46,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 }) - U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) + U.deepStrictEqual(struct({}).combine({}, {}), {}) + const S = struct({ x: String.Semigroup, y: Number.SemigroupSum }) + U.deepStrictEqual(S.combine({ x: "a", y: 2 }, { 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 a5ed18cc2..9b0832eb1 100644 --- a/test/typeclass/SemiApplicative.ts +++ b/test/typeclass/SemiApplicative.ts @@ -1,60 +1,43 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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) - U.deepStrictEqual(pipe(O.some("a"), S.combine(O.some("b"))), O.some("ab")) + const liftSemigroup = _.getSemigroup(O.SemiApplicative) + const S = liftSemigroup(String.Semigroup) + 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")) - }) - - 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.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.some(1), O.some(2), O.some(3)), O.some(6)) + U.deepStrictEqual(S.combineMany(O.some("a"), [O.some("b"), O.some("c")]), O.some("abc")) }) }) diff --git a/test/typeclass/SemiCoproduct.ts b/test/typeclass/SemiCoproduct.ts index 3692c8384..7ac9c273b 100644 --- a/test/typeclass/SemiCoproduct.ts +++ b/test/typeclass/SemiCoproduct.ts @@ -1,14 +1,14 @@ -import { pipe } from "@fp-ts/core/internal/Function" +import * as E from "@fp-ts/core/Either" import * as _ from "@fp-ts/core/typeclass/SemiCoproduct" -import * as E from "../data/Either" 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")) + U.deepStrictEqual(S.combineMany(E.left("a"), [E.left("b")]), E.left("b")) }) }) diff --git a/test/typeclass/SemiProduct.ts b/test/typeclass/SemiProduct.ts index 3f708d7ee..4f0c26b5c 100644 --- a/test/typeclass/SemiProduct.ts +++ b/test/typeclass/SemiProduct.ts @@ -1,17 +1,18 @@ +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 { pipe } from "@fp-ts/core/internal/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 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", () => { - it("productMany", () => { + it("productMany should be equivalent to `ap`", () => { const curry = (f: Function, n: number, acc: ReadonlyArray) => (x: unknown) => { const combined = Array(acc.length + 1) @@ -34,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) @@ -44,59 +45,36 @@ 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) } - 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 - } - - const productMany = _.productMany( - RA.Covariant, - product - ) - - const SemiApplicative = { - ...RA.Covariant, - product, - productMany - } - - assertSameResult(SemiApplicative)([])([]) - assertSameResult(SemiApplicative)([])([1, 2, 3]) - assertSameResult(SemiApplicative)([[4]])([1, 2, 3]) - assertSameResult(SemiApplicative)([[4, 5, 6], [7, 8], [9, 10, 11]])([1, 2, 3]) + assertSameResult(RA.SemiApplicative)([])([]) + assertSameResult(RA.SemiApplicative)([])([1, 2, 3]) + assertSameResult(RA.SemiApplicative)([[4]])([1, 2, 3]) + assertSameResult(RA.SemiApplicative)([[4, 5, 6], [7, 8], [9, 10, 11]])([1, 2, 3]) }) 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([O.some(1)], product([O.some(2)])), [O.some([1, 2] as const)]) + 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)))), - O.some(O.some([1, 2] as const)) + product(O.some(O.some(1)), O.some(O.some(2))), + O.some(O.some([1, 2])) ) }) }) @@ -104,36 +82,43 @@ 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]])), [ - O.some([1, 2] as const), - O.none, - O.none, - O.none + expect(productMany([O.some(1), O.none()], [])).toEqual([ + O.some([1]), + O.none() ]) - U.deepStrictEqual( - pipe([O.some(1), O.some(2)], productMany([[O.some(3), O.some(4)], [O.some(5)]])), + expect(productMany([O.some(1), O.none()], [[O.some(2), O.none()]])).toEqual([ + O.some([1, 2]), + O.none(), + O.none(), + O.none() + ]) + expect( + productMany([O.some(1), O.some(2)], [[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]) ] ) }) 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] 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(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( + 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))])), - O.some(O.some([1, 2] as const)) + productMany(O.some(O.some(1)), [O.some(O.some(2))]), + O.some(O.some([1, 2])) ) }) }) @@ -141,31 +126,31 @@ 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) }) }) - describe("productFlatten", () => { + describe("appendElement", () => { 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)) + const appendElement = _.appendElement(O.SemiProduct) + U.deepStrictEqual(pipe(O.some([1, 2]), appendElement(O.none())), O.none()) + expect(pipe(O.some([1, 2]), appendElement(O.some(3)))).toEqual(O.some([1, 2, 3])) }) it("Contravariant (Predicate)", () => { - const productFlatten = _.productFlatten(P.SemiProduct) - const p = pipe(P.tuple(P.isString, P.isString), productFlatten(P.isNumber)) + const appendElement = _.appendElement(P.SemiProduct) + const p = pipe(P.tuple(String.isString, String.isString), appendElement(Number.isNumber)) U.deepStrictEqual(p(["a", "b", 3]), true) U.deepStrictEqual(p(["a", "b", "c"]), false) U.deepStrictEqual(p([1, "b", 1]), false) @@ -175,23 +160,24 @@ 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) + 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) - U.deepStrictEqual(pipe(["a", 2], S.combine(["b", 3])), ["ab", 5]) + const S = nonEmptyTuple(String.Semigroup, Number.SemigroupSum) + U.deepStrictEqual(S.combine(["a", 2], ["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 +192,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 }) - U.deepStrictEqual(pipe({ x: "a", y: 2 }, S.combine({ x: "b", y: 3 })), { x: "ab", y: 5 }) + const S = nonEmptyStruct({ x: String.Semigroup, y: Number.SemigroupSum }) + U.deepStrictEqual(S.combine({ x: "a", y: 2 }, { 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 afd9cb9c5..c5a9b10d8 100644 --- a/test/typeclass/Semigroup.ts +++ b/test/typeclass/Semigroup.ts @@ -1,153 +1,119 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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("exports", () => { + expect(_.Invariant).exist + }) + it("reverse", () => { - 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") - 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(pipe("a", A.combine("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(pipe("a", A.combine("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 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) - }) - }) - - it("struct", () => { - const A = _.struct({ - name: string.Semigroup, - age: number.SemigroupSum + U.strictEqual(S.combineMany({ a: 1 }, [{ a: 2 }, item]), item) + U.strictEqual(S.combineMany(item, []), item) }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combine({ name: "b", age: 20 })), { - name: "ab", - age: 30 - }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combineMany([])), { - name: "a", - age: 10 - }) - U.deepStrictEqual(pipe({ name: "a", age: 10 }, A.combineMany([{ 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 }])), - { - name: "abc", - age: 60 - } - ) - }) - - it("tuple", () => { - const A = _.tuple( - string.Semigroup, - number.SemigroupSum - ) - U.deepStrictEqual(pipe(["a", 10], A.combine(["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]) }) it("first", () => { - const A = _.first() - U.deepStrictEqual(pipe(1, A.combine(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(pipe(1, A.combine(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(pipe(["a"], To.combine(["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( - 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) + const S = pipe( + _.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] + ) ) - U.deepStrictEqual(pipe(["a", 2, 3], A.combine(["b", 3, 4])), ["ab", 5, 12]) + U.deepStrictEqual(S.combine(["a", 2, 3], ["b", 3, 4]), ["ab", 5, 12]) }) it("productMany", () => { - const A = pipe( - string.Semigroup, - _.SemiProduct.productMany([string.Semigroup, string.Semigroup]) - ) - U.deepStrictEqual(pipe(["a", "b", "c"], A.combine(["d", "e", "f"])), ["ad", "be", "cf"]) + const S = _.SemiProduct.productMany(String.Semigroup, [String.Semigroup, String.Semigroup]) + U.deepStrictEqual(S.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) + }) + + it("productAll", () => { + const S = _.Product.productAll([String.Semigroup, String.Semigroup]) + U.deepStrictEqual(S.combine(["a1", "b1"], ["a2", "b2"]), ["a1a2", "b1b2"]) }) }) diff --git a/test/typeclass/Traversable.ts b/test/typeclass/Traversable.ts index 2a78856f0..5f749befc 100644 --- a/test/typeclass/Traversable.ts +++ b/test/typeclass/Traversable.ts @@ -1,49 +1,35 @@ -import { pipe } from "@fp-ts/core/internal/Function" +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))), + traverse([[1, 2], [3]], (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) - }) - - it("sequenceComposition", () => { - const sequence = _.sequenceComposition({ ...RA.Traversable, ...RA.Covariant }, RA.Traversable)( - O.Applicative - ) U.deepStrictEqual( - pipe([[O.some(1), O.some(2)], [O.some(3)]], sequence), - O.some([[1, 2], [3]]) + traverse([[1, -2], [3]], (a) => (a > 0 ? O.some(a) : O.none())), + 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.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/TraversableFilterable.ts b/test/typeclass/TraversableFilterable.ts new file mode 100644 index 000000000..01e48ba8b --- /dev/null +++ b/test/typeclass/TraversableFilterable.ts @@ -0,0 +1,80 @@ +import * as E from "@fp-ts/core/Either" +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("traversePartitionMap", () => { + const traversePartitionMap: ( + self: ReadonlyArray, + f: (a: A) => O.Option> + ) => O.Option<[ReadonlyArray, ReadonlyArray]> = _.traversePartitionMap({ + ...RA.Traversable, + ...RA.Covariant, + ...RA.Filterable + })(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(E.right(s)) : s.length > 0 ? O.some(E.left(s)) : O.none() + assert.deepStrictEqual(traversePartitionMap([], f), O.some([[], []])) + assert.deepStrictEqual(traversePartitionMap([""], f), O.none()) + assert.deepStrictEqual(traversePartitionMap(["a"], f), O.some([["a"], []])) + assert.deepStrictEqual(traversePartitionMap(["aa"], f), O.some([[], ["aa"]])) + assert.deepStrictEqual(traversePartitionMap(["aa", "a", ""], f), O.none()) + assert.deepStrictEqual( + traversePartitionMap(["aa", "a", "aaa"], f), + O.some([["a"], ["aa", "aaa"]]) + ) + }) + + it("traverseFilterMap", () => { + const traverseFilterMap: ( + self: ReadonlyArray, + f: (a: A) => O.Option> + ) => O.Option> = _.traverseFilterMap({ + ...RA.Traversable, + ...RA.Filterable + })(O.Applicative) + const f = (s: string) => + s.length > 1 ? O.some(O.some(s)) : s.length > 0 ? O.some(O.none()) : O.none() + assert.deepStrictEqual(traverseFilterMap([], f), O.some([])) + assert.deepStrictEqual(traverseFilterMap([""], f), O.none()) + assert.deepStrictEqual(traverseFilterMap(["a"], f), O.some([])) + assert.deepStrictEqual(traverseFilterMap(["aa"], f), O.some(["aa"])) + assert.deepStrictEqual(traverseFilterMap(["aa", "a", ""], f), O.none()) + assert.deepStrictEqual( + traverseFilterMap(["aa", "a", "aaa"], f), + O.some(["aa", "aaa"]) + ) + }) + + 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() + ) + 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"]])) + }) +}) diff --git a/test/typeclass/TraversableWithIndex.ts b/test/typeclass/TraversableWithIndex.ts deleted file mode 100644 index 5a0cbbf1b..000000000 --- a/test/typeclass/TraversableWithIndex.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { pipe } from "@fp-ts/core/internal/Function" -import * as O from "../data/Option" -import * as RA from "../data/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 - ) - }) - - 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/test/util.ts b/test/util.ts index 007cbfc6c..c042e20e2 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,3 +1,4 @@ +import { structural } from "@fp-ts/core/internal/effect" import * as assert from "assert" export const deepStrictEqual = (actual: A, expected: A) => { @@ -9,3 +10,8 @@ export const strictEqual = (actual: A, expected: A) => { } export const double = (n: number): number => n * 2 + +export const ownKeys = (o: object): ReadonlyArray => + (Object.keys(o) as ReadonlyArray).concat(Object.getOwnPropertySymbols(o)) + +export const isStructural = (u: unknown) => typeof u === "object" && u != null && structural in u diff --git a/tsconfig.base.json b/tsconfig.base.json index 2bb5b91ee..cc688e6e5 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -41,7 +41,8 @@ "@fp-ts/core/test/*": ["./test/*"], "@fp-ts/core/examples/*": ["./examples/*"], "@fp-ts/core/*": ["./src/*"] - } + }, + "plugins": [{ "name": "@effect/language-service" }] }, "include": [], "exclude": ["node_modules", "build", "lib"] diff --git a/vitest.config.ts b/vitest.config.ts index 9469eb3d4..564ebd3fe 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,7 +7,6 @@ export default defineConfig({ include: ["./test/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], exclude: [ "./test/util.ts", - "./test/data/*.ts", "./test/limbo/*.ts" ], globals: true