├── dtslint ├── ts4.8 │ ├── index.d.ts │ ├── index.ts │ ├── Tuple.ts │ ├── SemiProduct.ts │ ├── Monoid.ts │ ├── Semigroup.ts │ ├── FlatMap.ts │ ├── SemiAlternative.ts │ ├── tsconfig.json │ ├── tslint.json │ ├── Product.ts │ ├── ReadonlyRecord.ts │ ├── Option.ts │ └── ReadonlyArray.ts └── index.d.ts ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── Feature_request.md │ ├── Documentation.md │ └── Bug_report.md ├── PULL_REQUEST_TEMPLATE.md ├── workflows │ ├── pr.yml │ └── main.yml └── CONTRIBUTING.md ├── .babel.mjs.json ├── docs ├── fp-ts-logo.png ├── modules │ ├── index.md │ ├── typeclass │ │ ├── Monad.ts.md │ │ ├── Pointed.ts.md │ │ ├── Alternative.ts.md │ │ ├── SemiAlternative.ts.md │ │ ├── Coproduct.ts.md │ │ ├── Applicative.ts.md │ │ ├── SemiCoproduct.ts.md │ │ ├── Of.ts.md │ │ ├── Product.ts.md │ │ ├── Contravariant.ts.md │ │ ├── Bicovariant.ts.md │ │ ├── Bounded.ts.md │ │ ├── FlatMap.ts.md │ │ ├── Invariant.ts.md │ │ ├── Chainable.ts.md │ │ ├── Traversable.ts.md │ │ ├── Foldable.ts.md │ │ ├── Covariant.ts.md │ │ ├── SemiApplicative.ts.md │ │ ├── TraversableFilterable.ts.md │ │ └── Filterable.ts.md │ ├── Symbol.ts.md │ ├── HKT.ts.md │ └── Ordering.ts.md ├── _sass │ └── custom │ │ └── custom.scss ├── _config.yml └── index.md ├── .changeset ├── beige-numbers-exist.md ├── hungry-comics-kneel.md ├── odd-hornets-wave.md ├── flat-news-whisper.md ├── tough-planets-lick.md ├── famous-spies-attend.md ├── sour-suits-press.md ├── mighty-meals-dream.md ├── config.json └── README.md ├── .babel.cjs.json ├── docs-ts.json ├── .gitignore ├── .gitpod.yml ├── tsconfig.json ├── README.md ├── src ├── internal │ ├── effect.ts │ ├── ReadonlyArray.ts │ ├── Option.ts │ └── Either.ts ├── typeclass │ ├── Pointed.ts │ ├── Monad.ts │ ├── Alternative.ts │ ├── SemiAlternative.ts │ ├── Coproduct.ts │ ├── Of.ts │ ├── SemiCoproduct.ts │ ├── Applicative.ts │ ├── Contravariant.ts │ ├── Product.ts │ ├── Bounded.ts │ ├── Bicovariant.ts │ ├── FlatMap.ts │ ├── Invariant.ts │ ├── Traversable.ts │ ├── Chainable.ts │ ├── Covariant.ts │ └── Foldable.ts ├── Symbol.ts ├── HKT.ts └── Ordering.ts ├── tsconfig.build.json ├── tsconfig.madge.json ├── tsconfig.build-test.json ├── tsconfig.eslint.json ├── benchmark ├── tsconfig.json ├── These │ └── product.ts ├── Number │ └── lessThan.ts ├── sumAll.ts └── dual.ts ├── test ├── limbo │ ├── Category.ts │ ├── Comonad.ts │ ├── Composable.ts │ ├── Extendable.ts │ └── NonEmptyTraversable.ts ├── typeclass │ ├── Of.ts │ ├── Applicative.ts │ ├── Bounded.ts │ ├── SemiCoproduct.ts │ ├── Coproduct.ts │ ├── Contravariant.ts │ ├── Bicovariant.ts │ ├── Chainable.ts │ ├── Traversable.ts │ ├── FlatMap.ts │ ├── Covariant.ts │ ├── Invariant.ts │ ├── Monoid.ts │ ├── SemiApplicative.ts │ ├── Foldable.ts │ ├── Filterable.ts │ ├── Product.ts │ ├── NonEmptyTraversable.ts │ ├── TraversableFilterable.ts │ ├── Equivalence.ts │ └── Semigroup.ts ├── Struct.ts ├── Symbol.ts ├── util.ts ├── Tuple.ts ├── index.ts ├── Ordering.ts ├── Identity.ts ├── Bigint.ts ├── Number.ts └── Function.ts ├── vitest.config.ts ├── guides ├── These.md └── FAQ.md ├── .vscode ├── tasks.json └── settings.json ├── LICENSE ├── tsconfig.base.json ├── patches └── docs-ts@0.6.10.patch ├── .eslintrc.cjs └── package.json /dtslint/ts4.8/index.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [gcanti] 2 | -------------------------------------------------------------------------------- /dtslint/index.d.ts: -------------------------------------------------------------------------------- 1 | // TypeScript Version: 4.8 2 | -------------------------------------------------------------------------------- /.babel.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [["annotate-pure-calls"]] 3 | } 4 | -------------------------------------------------------------------------------- /docs/fp-ts-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fp-ts/core/HEAD/docs/fp-ts-logo.png -------------------------------------------------------------------------------- /.changeset/beige-numbers-exist.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | Tuple: add more APIs 6 | -------------------------------------------------------------------------------- /.babel.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [["@babel/transform-modules-commonjs"], ["annotate-pure-calls"]] 3 | } 4 | -------------------------------------------------------------------------------- /docs-ts.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["src/internal/**/*.ts"], 3 | "theme": "mikearnaldi/just-the-docs" 4 | } 5 | -------------------------------------------------------------------------------- /.changeset/hungry-comics-kneel.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | Function: swap apply arguments 6 | -------------------------------------------------------------------------------- /.changeset/odd-hornets-wave.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | Bigint: feature parity with Number 6 | -------------------------------------------------------------------------------- /.changeset/flat-news-whisper.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | fix bounds flipping in reverse function 6 | -------------------------------------------------------------------------------- /docs/modules/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Modules 3 | has_children: true 4 | permalink: /docs/modules 5 | nav_order: 2 6 | --- -------------------------------------------------------------------------------- /.changeset/tough-planets-lick.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | ReadonlyRecord: map: add support for structs 6 | -------------------------------------------------------------------------------- /.changeset/famous-spies-attend.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | export productAll from Option, Either, Predicate 6 | -------------------------------------------------------------------------------- /.changeset/sour-suits-press.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | Number: add missing functions (derivable from Order) 6 | -------------------------------------------------------------------------------- /.changeset/mighty-meals-dream.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@fp-ts/core": patch 3 | --- 4 | 5 | add missing boolean semigroups, monoids and combinators 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.tsbuildinfo 3 | node_modules/ 4 | yarn-error.log 5 | .ultra.cache.json 6 | .DS_Store 7 | tmp/ 8 | build/ 9 | dist/ 10 | .idea/ -------------------------------------------------------------------------------- /docs/_sass/custom/custom.scss: -------------------------------------------------------------------------------- 1 | $fp-ts-blue-000: #0088e6; 2 | $fp-ts-blue-100: #0075c5; 3 | 4 | $link-color: $fp-ts-blue-000; 5 | $btn-primary-color: $fp-ts-blue-100; 6 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm install -g pnpm && pnpm && pnpm run build 3 | github: 4 | prebuilds: 5 | addCheck: true 6 | vscode: 7 | extensions: 8 | - dbaeumer.vscode-eslint 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "references": [ 4 | { 5 | "path": "./tsconfig.build.json" 6 | }, 7 | { 8 | "path": "./tsconfig.build-test.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **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. 2 | -------------------------------------------------------------------------------- /src/internal/effect.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | /** @internal */ 6 | export const structural = Symbol.for("@effect/data/Equal/structural") 7 | 8 | /** @internal */ 9 | export const proto = Object.setPrototypeOf({ [structural]: true }, Object.prototype) 10 | -------------------------------------------------------------------------------- /dtslint/ts4.8/index.ts: -------------------------------------------------------------------------------- 1 | import * as _ from '../../src/HKT' 2 | 3 | // issue #536 4 | function testIssue536( 5 | x: _.Kind 6 | ): _.Kind { 7 | // $ExpectError 8 | return x 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "build/esm", 5 | "declarationDir": "build/dts", 6 | "tsBuildInfoFile": "build/tsbuildinfo/esm.tsbuildinfo", 7 | "rootDir": "src" 8 | }, 9 | "include": ["src/**/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.madge.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "allowJs": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "@fp-ts/core": ["./build/esm/index.js"], 8 | "@fp-ts/core/*": ["./build/esm/*"] 9 | } 10 | }, 11 | "include": ["./build/esm/**/*.js"] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.build-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "build/tsbuildinfo/test.tsbuildinfo", 5 | "rootDir": "./", 6 | "noEmit": true, 7 | "types": ["vitest/globals", "node"] 8 | }, 9 | "include": ["test/**/*.ts", "src/**/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "allowJs": true, 5 | "baseUrl": "." 6 | }, 7 | "include": [ 8 | "./src/**/*.ts", 9 | "./test/**/*.ts", 10 | "./dtslint/**/*.ts", 11 | "./examples/**/*.ts", 12 | "./vitest.config.ts", 13 | "./.eslintrc.cjs" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /benchmark/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "module": "commonjs", 5 | "paths": { 6 | "@fp-ts/core": ["../src/index.ts"], 7 | "@fp-ts/core/test/*": ["../test/*"], 8 | "@fp-ts/core/examples/*": ["../examples/*"], 9 | "@fp-ts/core/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.6.4/schema.json", 3 | "changelog": ["@changesets/changelog-github", { "repo": "fp-ts/core" }], 4 | "commit": false, 5 | "linked": [], 6 | "access": "restricted", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": [] 10 | } 11 | -------------------------------------------------------------------------------- /dtslint/ts4.8/Tuple.ts: -------------------------------------------------------------------------------- 1 | import * as T from '@fp-ts/core/Tuple' 2 | import { pipe } from '@fp-ts/core/Function' 3 | 4 | // 5 | // tuple 6 | // 7 | 8 | // $ExpectType [string, number, boolean] 9 | T.tuple('a', 1, true) 10 | 11 | // 12 | // appendElement 13 | // 14 | 15 | // $ExpectType [string, number, boolean] 16 | pipe(T.tuple('a', 1), T.appendElement(true)) 17 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: mikearnaldi/just-the-docs 2 | 3 | # Enable or disable the site search 4 | search_enabled: true 5 | 6 | # Aux links for the upper right navigation 7 | aux_links: 8 | "Docs": 9 | - "//fp-ts.github.io/core" 10 | "API Reference": 11 | - "//fp-ts.github.io/core/docs/modules" 12 | "GitHub": 13 | - "//github.com/fp-ts/core" 14 | -------------------------------------------------------------------------------- /test/limbo/Category.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Composable } from "./Composable" 6 | 7 | /** 8 | * @category type class 9 | * @since 1.0.0 10 | */ 11 | export interface Category extends Composable { 12 | readonly identity: () => Kind 13 | } 14 | -------------------------------------------------------------------------------- /src/typeclass/Pointed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 6 | import type { Of } from "@fp-ts/core/typeclass/Of" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Pointed extends Covariant, Of {} 13 | -------------------------------------------------------------------------------- /src/typeclass/Monad.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { TypeLambda } from "@fp-ts/core/HKT" 5 | import type { FlatMap } from "@fp-ts/core/typeclass/FlatMap" 6 | import type { Pointed } from "@fp-ts/core/typeclass/Pointed" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Monad extends FlatMap, Pointed {} 13 | -------------------------------------------------------------------------------- /dtslint/ts4.8/SemiProduct.ts: -------------------------------------------------------------------------------- 1 | import { OptionTypeLambda } from "@fp-ts/core/Option" 2 | import * as _ from "@fp-ts/core/typeclass/SemiProduct" 3 | 4 | export declare const SemiProduct: _.SemiProduct 5 | 6 | // $ExpectError 7 | _.nonEmptyTuple(SemiProduct)() // should not allow empty tuples 8 | 9 | // $ExpectError 10 | _.nonEmptyStruct(SemiProduct)({}) // should not allow empty structs 11 | -------------------------------------------------------------------------------- /test/limbo/Comonad.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Extendable } from "@fp-ts/core/test/limbo/Extendable" 6 | 7 | /** 8 | * @category type class 9 | * @since 1.0.0 10 | */ 11 | export interface Comonad extends Extendable { 12 | readonly extract: (self: Kind) => A 13 | } 14 | -------------------------------------------------------------------------------- /src/typeclass/Alternative.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" 6 | import type { SemiAlternative } from "@fp-ts/core/typeclass/SemiAlternative" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Alternative extends SemiAlternative, Coproduct {} 13 | -------------------------------------------------------------------------------- /src/typeclass/SemiAlternative.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 6 | import type { SemiCoproduct } from "@fp-ts/core/typeclass/SemiCoproduct" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface SemiAlternative extends SemiCoproduct, Covariant {} 13 | -------------------------------------------------------------------------------- /test/limbo/Composable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 5 | 6 | /** 7 | * @category type class 8 | * @since 1.0.0 9 | */ 10 | export interface Composable extends TypeClass { 11 | readonly compose: ( 12 | bc: Kind 13 | ) => (ab: Kind) => Kind 14 | } 15 | -------------------------------------------------------------------------------- /src/internal/ReadonlyArray.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import type { NonEmptyArray } from "@fp-ts/core/ReadonlyArray" 6 | 7 | /** @internal */ 8 | export const isNonEmptyArray = (self: ReadonlyArray): self is NonEmptyArray => 9 | self.length > 0 10 | 11 | /** @internal */ 12 | export const fromIterable = (collection: Iterable): Array => 13 | Array.isArray(collection) ? collection : Array.from(collection) 14 | -------------------------------------------------------------------------------- /test/typeclass/Of.ts: -------------------------------------------------------------------------------- 1 | import * as O from "@fp-ts/core/Option" 2 | import * as RA from "@fp-ts/core/ReadonlyArray" 3 | import * as _ from "@fp-ts/core/typeclass/Of" 4 | import * as U from "../util" 5 | 6 | describe("Of", () => { 7 | it("ofComposition", () => { 8 | const of = _.ofComposition(RA.Of, O.Of) 9 | U.deepStrictEqual(of(1), [O.some(1)]) 10 | }) 11 | 12 | it("unit", () => { 13 | U.deepStrictEqual(_.unit(O.Pointed), O.some(undefined)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/limbo/Extendable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 6 | 7 | /** 8 | * @category type class 9 | * @since 1.0.0 10 | */ 11 | export interface Extendable extends Covariant { 12 | readonly extend: ( 13 | f: (self: Kind) => B 14 | ) => (self: Kind) => Kind 15 | } 16 | -------------------------------------------------------------------------------- /dtslint/ts4.8/Monoid.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/typeclass/Monoid" 2 | import * as Number from "@fp-ts/core/Number" 3 | import * as String from "@fp-ts/core/String" 4 | 5 | // 6 | // tuple 7 | // 8 | 9 | // $ExpectType Monoid 10 | _.tuple( 11 | String.Monoid, 12 | Number.MonoidSum 13 | ) 14 | 15 | // 16 | // struct 17 | // 18 | 19 | // $ExpectType Monoid<{ readonly a: string; readonly b: number; }> 20 | _.struct({ 21 | a: String.Monoid, 22 | b: Number.MonoidSum 23 | }) 24 | -------------------------------------------------------------------------------- /dtslint/ts4.8/Semigroup.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/typeclass/Semigroup" 2 | import * as Number from "@fp-ts/core/Number" 3 | import * as String from "@fp-ts/core/String" 4 | 5 | // 6 | // tuple 7 | // 8 | 9 | // $ExpectType Semigroup 10 | _.tuple( 11 | String.Semigroup, 12 | Number.SemigroupSum 13 | ) 14 | 15 | // 16 | // struct 17 | // 18 | 19 | // $ExpectType Semigroup<{ readonly a: string; readonly b: number; }> 20 | _.struct({ 21 | a: String.Semigroup, 22 | b: Number.SemigroupSum 23 | }) 24 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import path from "path" 3 | import { defineConfig } from "vite" 4 | 5 | export default defineConfig({ 6 | test: { 7 | include: ["./test/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], 8 | exclude: [ 9 | "./test/util.ts", 10 | "./test/limbo/*.ts" 11 | ], 12 | globals: true 13 | }, 14 | resolve: { 15 | alias: { 16 | "@fp-ts/core/test": path.resolve(__dirname, "/test"), 17 | "@fp-ts/core": path.resolve(__dirname, "/src") 18 | } 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Monad.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Monad.ts 3 | nav_order: 32 4 | parent: Modules 5 | --- 6 | 7 | ## Monad overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Monad (interface)](#monad-interface) 17 | 18 | --- 19 | 20 | # type class 21 | 22 | ## Monad (interface) 23 | 24 | **Signature** 25 | 26 | ```ts 27 | export interface Monad extends FlatMap, Pointed {} 28 | ``` 29 | 30 | Added in v1.0.0 31 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Pointed.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Pointed.ts 3 | nav_order: 36 4 | parent: Modules 5 | --- 6 | 7 | ## Pointed overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Pointed (interface)](#pointed-interface) 17 | 18 | --- 19 | 20 | # type class 21 | 22 | ## Pointed (interface) 23 | 24 | **Signature** 25 | 26 | ```ts 27 | export interface Pointed extends Covariant, Of {} 28 | ``` 29 | 30 | Added in v1.0.0 31 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | permalink: / 4 | nav_order: 1 5 | has_children: false 6 | has_toc: false 7 | --- 8 | 9 | fp-ts logo 10 | 11 | # Typed functional programming in TypeScript 12 | 13 | `@fp-ts/core` provides developers with popular patterns and reliable abstractions from typed functional languages in TypeScript. 14 | {: .fs-6 .fw-300 } 15 | 16 | **Disclaimer**. Teaching functional programming is out of scope of this project, so the documentation assumes you already know what FP is. 17 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Alternative.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Alternative.ts 3 | nav_order: 19 4 | parent: Modules 5 | --- 6 | 7 | ## Alternative overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Alternative (interface)](#alternative-interface) 17 | 18 | --- 19 | 20 | # type class 21 | 22 | ## Alternative (interface) 23 | 24 | **Signature** 25 | 26 | ```ts 27 | export interface Alternative extends SemiAlternative, Coproduct {} 28 | ``` 29 | 30 | Added in v1.0.0 31 | -------------------------------------------------------------------------------- /test/Struct.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as S from "@fp-ts/core/Struct" 3 | 4 | describe.concurrent("Struct", () => { 5 | it("exports", () => { 6 | expect(S.getEquivalence).exists 7 | expect(S.getOrder).exists 8 | expect(S.getSemigroup).exists 9 | expect(S.getMonoid).exists 10 | }) 11 | 12 | it("pick", () => { 13 | expect(pipe({ a: "a", b: 1, c: true }, S.pick("a", "b"))).toEqual({ a: "a", b: 1 }) 14 | }) 15 | 16 | it("omit", () => { 17 | expect(pipe({ a: "a", b: 1, c: true }, S.omit("c"))).toEqual({ a: "a", b: 1 }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /docs/modules/typeclass/SemiAlternative.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/SemiAlternative.ts 3 | nav_order: 38 4 | parent: Modules 5 | --- 6 | 7 | ## SemiAlternative overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [SemiAlternative (interface)](#semialternative-interface) 17 | 18 | --- 19 | 20 | # type class 21 | 22 | ## SemiAlternative (interface) 23 | 24 | **Signature** 25 | 26 | ```ts 27 | export interface SemiAlternative extends SemiCoproduct, Covariant {} 28 | ``` 29 | 30 | Added in v1.0.0 31 | -------------------------------------------------------------------------------- /src/internal/Option.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import { proto } from "@fp-ts/core/internal/effect" 6 | import type { None, Option, Some } from "@fp-ts/core/Option" 7 | 8 | /** @internal */ 9 | export const isNone =
(fa: Option): fa is None => fa._tag === "None" 10 | 11 | /** @internal */ 12 | export const isSome = (fa: Option): fa is Some => fa._tag === "Some" 13 | 14 | /** @internal */ 15 | export const none: Option = Object.setPrototypeOf({ _tag: "None" }, proto) 16 | 17 | /** @internal */ 18 | export const some = (a: A): Option => Object.setPrototypeOf({ _tag: "Some", value: a }, proto) 19 | -------------------------------------------------------------------------------- /dtslint/ts4.8/FlatMap.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/typeclass/FlatMap" 2 | import type { TypeLambda } from "@fp-ts/core/HKT" 3 | import { pipe } from "@fp-ts/core/Function" 4 | 5 | interface RAW { 6 | (r: R): () => Promise 7 | } 8 | 9 | interface RAWTypeLambda extends TypeLambda { 10 | readonly type: RAW 11 | } 12 | 13 | declare const FlatMap: _.FlatMap 14 | 15 | declare const ffa: RAW<{ a: string }, string, RAW<{ b: number }, number, 'a'>> 16 | 17 | // $ExpectType RAW<{ b: number; } & { a: string; }, string | number, "a"> 18 | pipe(ffa, _.flatten(FlatMap)) 19 | -------------------------------------------------------------------------------- /benchmark/These/product.ts: -------------------------------------------------------------------------------- 1 | import { Product, right, fail } from "@fp-ts/core/These" 2 | import * as Benchmark from "benchmark" 3 | 4 | /* 5 | */ 6 | 7 | const suite = new Benchmark.Suite() 8 | 9 | suite 10 | .add("Product.product", function() { 11 | Product.product(right(1), fail('e')) 12 | }) 13 | .add("Product.productAll", function() { 14 | Product.productAll([right(1), fail('e')]) 15 | }) 16 | .on("cycle", function(event: any) { 17 | console.log(String(event.target)) 18 | }) 19 | .on("complete", function(this: any) { 20 | console.log("Fastest is " + this.filter("fastest").map("name")) 21 | }) 22 | .run({ async: true }) 23 | -------------------------------------------------------------------------------- /test/typeclass/Applicative.ts: -------------------------------------------------------------------------------- 1 | import * as N from "@fp-ts/core/Number" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as _ from "@fp-ts/core/typeclass/Applicative" 4 | import * as U from "../util" 5 | 6 | describe("Applicative", () => { 7 | it("liftMonoid", () => { 8 | const liftMonoid = _.getMonoid(O.Applicative) 9 | const M = liftMonoid(N.MonoidSum) 10 | U.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) 11 | U.deepStrictEqual(M.combine(O.some(1), O.none()), O.none()) 12 | U.deepStrictEqual(M.combine(O.none(), O.some(2)), O.none()) 13 | U.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(3)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/Symbol.ts: -------------------------------------------------------------------------------- 1 | import * as S from "@fp-ts/core/Symbol" 2 | 3 | describe.concurrent("Symbol", () => { 4 | it("isSymbol", () => { 5 | expect(S.isSymbol(Symbol.for("@fp-ts/core/test/a"))).toEqual(true) 6 | expect(S.isSymbol(1n)).toEqual(false) 7 | expect(S.isSymbol(1)).toEqual(false) 8 | expect(S.isSymbol("a")).toEqual(false) 9 | expect(S.isSymbol(true)).toEqual(false) 10 | }) 11 | 12 | it("Equivalence", () => { 13 | const eq = S.Equivalence 14 | expect(eq(Symbol.for("@fp-ts/core/test/a"), Symbol.for("@fp-ts/core/test/a"))).toBe(true) 15 | expect(eq(Symbol.for("@fp-ts/core/test/a"), Symbol.for("@fp-ts/core/test/b"))).toBe(false) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | import { structural } from "@fp-ts/core/internal/effect" 2 | import * as assert from "assert" 3 | 4 | export const deepStrictEqual = (actual: A, expected: A) => { 5 | assert.deepStrictEqual(actual, expected) 6 | } 7 | 8 | export const strictEqual = (actual: A, expected: A) => { 9 | assert.strictEqual(actual, expected) 10 | } 11 | 12 | export const double = (n: number): number => n * 2 13 | 14 | export const ownKeys = (o: object): ReadonlyArray => 15 | (Object.keys(o) as ReadonlyArray).concat(Object.getOwnPropertySymbols(o)) 16 | 17 | export const isStructural = (u: unknown) => typeof u === "object" && u != null && structural in u 18 | -------------------------------------------------------------------------------- /test/typeclass/Bounded.ts: -------------------------------------------------------------------------------- 1 | import * as Number from "@fp-ts/core/Number" 2 | import * as _ from "@fp-ts/core/typeclass/Bounded" 3 | import * as U from "../util" 4 | 5 | describe("Bounded", () => { 6 | it("clamp", () => { 7 | const clamp = _.clamp({ ...Number.Order, minBound: 1, maxBound: 10 }) 8 | U.deepStrictEqual(clamp(2), 2) 9 | U.deepStrictEqual(clamp(10), 10) 10 | U.deepStrictEqual(clamp(20), 10) 11 | U.deepStrictEqual(clamp(1), 1) 12 | U.deepStrictEqual(clamp(-10), 1) 13 | }) 14 | 15 | it("reverse", () => { 16 | const B = _.reverse({ ...Number.Order, minBound: 10, maxBound: 1 }) 17 | U.deepStrictEqual(B.maxBound, 10) 18 | U.deepStrictEqual(B.minBound, 1) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/typeclass/SemiCoproduct.ts: -------------------------------------------------------------------------------- 1 | import * as E from "@fp-ts/core/Either" 2 | import * as _ from "@fp-ts/core/typeclass/SemiCoproduct" 3 | import * as U from "../util" 4 | 5 | describe("SemiCoproduct", () => { 6 | it("getSemigroup", () => { 7 | const S = _.getSemigroup(E.SemiCoproduct)() 8 | U.deepStrictEqual(S.combine(E.right(1), E.right(2)), E.right(1)) 9 | U.deepStrictEqual(S.combine(E.left("a"), E.right(2)), E.right(2)) 10 | U.deepStrictEqual(S.combine(E.right(1), E.left("b")), E.right(1)) 11 | U.deepStrictEqual(S.combine(E.left("a"), E.left("b")), E.left("b")) 12 | U.deepStrictEqual(S.combineMany(E.left("a"), [E.left("b")]), E.left("b")) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /dtslint/ts4.8/SemiAlternative.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/typeclass/SemiAlternative" 2 | import type { TypeLambda } from "@fp-ts/core/HKT" 3 | import { pipe } from "@fp-ts/core/Function" 4 | 5 | interface RAW { 6 | (r: R): () => Promise 7 | } 8 | 9 | interface RAWTypeLambda extends TypeLambda { 10 | readonly type: RAW 11 | } 12 | 13 | declare const fa: RAW<{ a: string }, string, "fa"> 14 | declare const fb: RAW<{ b: number }, number, "fb"> 15 | 16 | declare const SemiAlternative: _.SemiAlternative 17 | 18 | // $ExpectType RAW<{ a: string; } & { b: number; }, string | number, "fa" | "fb"> 19 | SemiAlternative.coproduct(fa, fb) 20 | -------------------------------------------------------------------------------- /dtslint/ts4.8/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "strict": true, 5 | "noImplicitAny": false, 6 | "noImplicitThis": true, 7 | "strictNullChecks": true, 8 | "strictFunctionTypes": true, 9 | "noImplicitReturns": false, 10 | "noUnusedLocals": false, 11 | "noUnusedParameters": false, 12 | "noFallthroughCasesInSwitch": true, 13 | "moduleResolution": "node", 14 | "target": "ES2021", 15 | "lib": ["ES2021"], 16 | "paths": { 17 | "@fp-ts/core": ["../../src/index.ts"], 18 | "@fp-ts/core/test/*": ["../../test/*"], 19 | "@fp-ts/core/examples/*": ["../../examples/*"], 20 | "@fp-ts/core/*": ["../../src/*"] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Symbol.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import * as predicate from "@fp-ts/core/Predicate" 6 | import * as equivalence from "@fp-ts/core/typeclass/Equivalence" 7 | 8 | /** 9 | * Tests if a value is a `symbol`. 10 | * 11 | * @param input - The value to test. 12 | * 13 | * @example 14 | * import { isSymbol } from "@fp-ts/core/Predicate" 15 | * 16 | * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true) 17 | * assert.deepStrictEqual(isSymbol("a"), false) 18 | * 19 | * @category guards 20 | * @since 1.0.0 21 | */ 22 | export const isSymbol: (u: unknown) => u is symbol = predicate.isSymbol 23 | 24 | /** 25 | * @category instances 26 | * @since 1.0.0 27 | */ 28 | export const Equivalence: equivalence.Equivalence = equivalence.symbol 29 | -------------------------------------------------------------------------------- /test/typeclass/Coproduct.ts: -------------------------------------------------------------------------------- 1 | import * as O from "@fp-ts/core/Option" 2 | import * as _ from "@fp-ts/core/typeclass/Coproduct" 3 | import * as U from "../util" 4 | 5 | describe("Coproduct", () => { 6 | it("getMonoid", () => { 7 | const M = _.getMonoid(O.Alternative)() 8 | U.deepStrictEqual(M.combine(O.none(), O.none()), O.none()) 9 | U.deepStrictEqual(M.combine(O.some(1), O.none()), O.some(1)) 10 | U.deepStrictEqual(M.combine(O.none(), O.some(2)), O.some(2)) 11 | U.deepStrictEqual(M.combine(O.some(1), O.some(2)), O.some(1)) 12 | 13 | U.deepStrictEqual(M.combine(M.empty, O.none()), O.none()) 14 | U.deepStrictEqual(M.combine(M.empty, O.some(2)), O.some(2)) 15 | U.deepStrictEqual(M.combine(O.some(1), M.empty), O.some(1)) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /dtslint/ts4.8/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dtslint.json", 3 | "rules": { 4 | "no-duplicate-imports": false, 5 | "semicolon": false, 6 | "array-type": false, 7 | "no-unnecessary-generics": false, 8 | "member-access": false, 9 | "no-empty-interface": false, 10 | "no-arg": false, 11 | "no-object-literal-type-assertion": false, 12 | "no-unnecessary-class": false, 13 | "radix": false, 14 | "no-angle-bracket-type-assertion": false, 15 | "object-literal-shorthand": false, 16 | "prefer-object-spread": false, 17 | "whitespace": false, 18 | "use-default-type-parameter": false, 19 | "no-relative-import-in-test": false, 20 | "no-null-undefined-union": false, 21 | "invalid-void": false, 22 | "max-line-length": false, 23 | "no-useless-files": false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /benchmark/Number/lessThan.ts: -------------------------------------------------------------------------------- 1 | import { dual } from "@fp-ts/core/Function"; 2 | import { lessThan } from "@fp-ts/core/Number" 3 | import * as Benchmark from "benchmark" 4 | 5 | /* 6 | */ 7 | 8 | const suite = new Benchmark.Suite() 9 | 10 | const lessThanBaseline: { 11 | (that: number): (self: number) => boolean; 12 | (self: number, that: number): boolean; 13 | } = dual(2, (self: number, that: number): boolean => self < that) 14 | 15 | suite 16 | .add("lessThanBaseline", function() { 17 | lessThanBaseline(2, 1) 18 | }) 19 | .add("lessThan", function() { 20 | lessThan(2, 1) 21 | }) 22 | .on("cycle", function(event: any) { 23 | console.log(String(event.target)) 24 | }) 25 | .on("complete", function(this: any) { 26 | console.log("Fastest is " + this.filter("fastest").map("name")) 27 | }) 28 | .run({ async: true }) 29 | -------------------------------------------------------------------------------- /guides/These.md: -------------------------------------------------------------------------------- 1 | # The `These` data type 2 | 3 | ## A data structure providing "inclusive-or" as opposed to `Either`'s "exclusive-or". 4 | 5 | If you interpret `Either` as suggesting the computation may either fail or succeed (exclusively), then 6 | `These` may fail, succeed, or do both at the same time. 7 | 8 | There are a few ways to interpret the `Both` case: 9 | 10 | 1. You can think of a computation that has a non-fatal error. 11 | 2. You can think of a computation that went as far as it could before erroring. 12 | 3. You can think of a computation that keeps track of errors as it completes. 13 | 14 | Another way you can think of `These` is saying that we want to handle `E` kind of data, `A` kind of data, or 15 | both `E` and `A` kind of data at the same time. This is particularly useful when it comes to displaying UI's. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | # Feature Request 2 | 3 | Thank you for submitting a feature request for `@fp-ts/core`. Your contributions help shape the future of this library. 4 | 5 | ## Describing the Feature 6 | 7 | Please provide a clear and concise description of the new feature you would like to see added to `@fp-ts/core`. 8 | 9 | ## Problem to Solve 10 | 11 | Please describe the problem that this new feature will solve, and why it's important. 12 | 13 | ## Use Case 14 | 15 | Please provide a use case or an example of how this feature will be used. 16 | 17 | ## Alternatives Considered 18 | 19 | Please describe any alternatives you have considered and why you believe this new feature is a better solution. 20 | 21 | ## Additional Context 22 | 23 | Please provide any additional information or context that might be relevant to this feature request. 24 | -------------------------------------------------------------------------------- /benchmark/sumAll.ts: -------------------------------------------------------------------------------- 1 | import { fromIterable } from "@fp-ts/core/internal/ReadonlyArray" 2 | import { sumAll } from "@fp-ts/core/Number" 3 | import * as Benchmark from "benchmark" 4 | 5 | /* 6 | sumAll x 98,318,148 ops/sec ±0.46% (91 runs sampled) 7 | sum x 104,495,149 ops/sec ±0.76% (90 runs sampled) 8 | */ 9 | 10 | const suite = new Benchmark.Suite() 11 | 12 | const sum = (collection: Iterable): number => fromIterable(collection).reduce((a, b) => a + b, 0) 13 | 14 | suite 15 | .add("sumAll", function() { 16 | sumAll([1, 2, 3, 4, 5]) 17 | }) 18 | .add("sum", function() { 19 | sum([1, 2, 3, 4, 5]) 20 | }) 21 | .on("cycle", function(event: any) { 22 | console.log(String(event.target)) 23 | }) 24 | .on("complete", function(this: any) { 25 | console.log("Fastest is " + this.filter("fastest").map("name")) 26 | }) 27 | .run({ async: true }) 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Documentation.md: -------------------------------------------------------------------------------- 1 | # Improving Documentation 2 | 3 | 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. 4 | 5 | ## How to Contribute 6 | 7 | To contribute to the documentation, follow these steps: 8 | 9 | 1. Make the desired changes to the documentation files. 10 | 2. Submit a pull request with a clear description of the changes and why they are beneficial. 11 | 12 | ## Content Guidelines 13 | 14 | When contributing to the documentation, please keep in mind the following guidelines: 15 | 16 | - Write clear and concise explanations of features and concepts. 17 | - Use examples and code snippets to help illustrate your explanations. 18 | - Follow the same formatting and style used in the existing documentation. 19 | 20 | Thank you for your contributions to the `@fp-ts/core` documentation! 21 | -------------------------------------------------------------------------------- /src/HKT.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | /** 6 | * @since 1.0.0 7 | */ 8 | export declare const URI: unique symbol 9 | 10 | /** 11 | * @since 1.0.0 12 | */ 13 | export interface TypeClass { 14 | readonly [URI]?: F 15 | } 16 | 17 | /** 18 | * @since 1.0.0 19 | */ 20 | export interface TypeLambda { 21 | readonly In: unknown 22 | readonly Out2: unknown 23 | readonly Out1: unknown 24 | readonly Target: unknown 25 | } 26 | 27 | /** 28 | * @since 1.0.0 29 | */ 30 | export type Kind = F extends { 31 | readonly type: unknown 32 | } ? (F & { 33 | readonly In: In 34 | readonly Out2: Out2 35 | readonly Out1: Out1 36 | readonly Target: Target 37 | })["type"] 38 | : { 39 | readonly F: F 40 | readonly In: (_: In) => void 41 | readonly Out2: () => Out2 42 | readonly Out1: () => Out1 43 | readonly Target: (_: Target) => Target 44 | } 45 | -------------------------------------------------------------------------------- /src/typeclass/Coproduct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Monoid } from "@fp-ts/core/typeclass/Monoid" 6 | import type { SemiCoproduct } from "@fp-ts/core/typeclass/SemiCoproduct" 7 | import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" 8 | 9 | /** 10 | * @category type class 11 | * @since 1.0.0 12 | */ 13 | export interface Coproduct extends SemiCoproduct { 14 | readonly zero: () => Kind 15 | 16 | readonly coproductAll: ( 17 | collection: Iterable> 18 | ) => Kind 19 | } 20 | 21 | /** 22 | * @since 1.0.0 23 | */ 24 | export const getMonoid = (F: Coproduct) => 25 | (): Monoid< 26 | Kind 27 | > => ({ 28 | ...semiCoproduct.getSemigroup(F)(), 29 | empty: F.zero(), 30 | combineAll: F.coproductAll 31 | }) 32 | -------------------------------------------------------------------------------- /docs/modules/Symbol.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Symbol.ts 3 | nav_order: 16 4 | parent: Modules 5 | --- 6 | 7 | ## Symbol overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [guards](#guards) 16 | - [isSymbol](#issymbol) 17 | - [instances](#instances) 18 | - [Equivalence](#equivalence) 19 | 20 | --- 21 | 22 | # guards 23 | 24 | ## isSymbol 25 | 26 | Tests if a value is a `symbol`. 27 | 28 | **Signature** 29 | 30 | ```ts 31 | export declare const isSymbol: (u: unknown) => u is symbol 32 | ``` 33 | 34 | **Example** 35 | 36 | ```ts 37 | import { isSymbol } from '@fp-ts/core/Predicate' 38 | 39 | assert.deepStrictEqual(isSymbol(Symbol.for('a')), true) 40 | assert.deepStrictEqual(isSymbol('a'), false) 41 | ``` 42 | 43 | Added in v1.0.0 44 | 45 | # instances 46 | 47 | ## Equivalence 48 | 49 | **Signature** 50 | 51 | ```ts 52 | export declare const Equivalence: equivalence.Equivalence 53 | ``` 54 | 55 | Added in v1.0.0 56 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | ## Description 4 | 5 | Please describe the changes you have made and the purpose of the changes. If applicable, provide context or links to relevant issues or discussions. 6 | 7 | ## Types of Changes 8 | 9 | What types of changes does your code introduce to the library? 10 | 11 | - [ ] Bugfix 12 | - [ ] New feature 13 | - [ ] Documentation update 14 | 15 | ## Checklist 16 | 17 | - [ ] I have read the [contributing guidelines](https://github.com/fp-ts/core/blob/main/.github/CONTRIBUTING.md) for this repository 18 | - [ ] I have written tests for the changes I have made 19 | - [ ] I have run `pnpm coverage` and my code is 100% covered 20 | - [ ] I have updated the documentation (if applicable) 21 | - [ ] I agree to license my contribution under the MIT license 22 | 23 | ## Additional Information 24 | 25 | Is there anything else you would like to add? Are there any questions that need to be answered before merging this pull request? 26 | -------------------------------------------------------------------------------- /src/typeclass/Of.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 5 | 6 | /** 7 | * @category type class 8 | * @since 1.0.0 9 | */ 10 | export interface Of extends TypeClass { 11 | readonly of:
(a: A) => Kind 12 | } 13 | 14 | /** 15 | * Returns a default `of` composition. 16 | * 17 | * @since 1.0.0 18 | */ 19 | export const ofComposition = ( 20 | F: Of, 21 | G: Of 22 | ) => (a: A): Kind> => F.of(G.of(a)) 23 | 24 | /** 25 | * @since 1.0.0 26 | */ 27 | export const unit = ( 28 | F: Of 29 | ): Kind => F.of(undefined) 30 | 31 | /** 32 | * @category do notation 33 | * @since 1.0.0 34 | */ 35 | export const Do = ( 36 | F: Of 37 | ): Kind => F.of({}) 38 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean", 6 | "type": "shell", 7 | "command": "yarn clean", 8 | "isBackground": false, 9 | "problemMatcher": [] 10 | }, 11 | { 12 | "label": "build-watch", 13 | "type": "shell", 14 | "command": "yarn build-watch", 15 | "problemMatcher": ["$tsc-watch"], 16 | "isBackground": true, 17 | "presentation": { 18 | "focus": false, 19 | "panel": "shared", 20 | "group": "dev", 21 | "showReuseMessage": true, 22 | "clear": false 23 | } 24 | }, 25 | { 26 | "label": "build", 27 | "type": "shell", 28 | "command": "yarn build", 29 | "problemMatcher": [], 30 | "isBackground": false 31 | }, 32 | { 33 | "label": "test-watch", 34 | "dependsOn": ["build-watch"], 35 | "type": "shell", 36 | "command": "yarn test", 37 | "problemMatcher": [] 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /dtslint/ts4.8/Product.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/typeclass/Product" 2 | import type { TypeLambda } from "@fp-ts/core/HKT" 3 | 4 | interface RAW { 5 | (r: R): () => Promise 6 | } 7 | 8 | interface RAWTypeLambda extends TypeLambda { 9 | readonly type: RAW 10 | } 11 | 12 | declare const fa: RAW<{ a: string }, "a", string> 13 | declare const fb: RAW<{ b: number }, "b", number> 14 | declare const fc: RAW<{ c: boolean }, "c", boolean> 15 | 16 | export declare const Product: _.Product 17 | 18 | // $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", [string, number, boolean]> 19 | _.tuple(Product)(fa, fb, fc) 20 | 21 | // $ExpectType RAW<{ a: string; } & { b: number; } & { c: boolean; }, "a" | "b" | "c", { fa: string; fb: number; fc: boolean; }> 22 | _.struct(Product)({ fa, fb, fc }) 23 | 24 | _.tuple(Product)() // should allow empty tuple 25 | _.struct(Product)({}) // should allow empty structs 26 | -------------------------------------------------------------------------------- /src/typeclass/SemiCoproduct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Invariant } from "@fp-ts/core/typeclass/Invariant" 7 | import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" 8 | 9 | /** 10 | * @category type class 11 | * @since 1.0.0 12 | */ 13 | export interface SemiCoproduct extends Invariant { 14 | readonly coproduct: ( 15 | self: Kind, 16 | that: Kind 17 | ) => Kind 18 | 19 | readonly coproductMany: ( 20 | self: Kind, 21 | collection: Iterable> 22 | ) => Kind 23 | } 24 | 25 | /** 26 | * @since 1.0.0 27 | */ 28 | export const getSemigroup = (F: SemiCoproduct) => 29 | (): Semigroup> => ({ 30 | combine: F.coproduct, 31 | combineMany: F.coproductMany 32 | }) 33 | -------------------------------------------------------------------------------- /test/typeclass/Contravariant.ts: -------------------------------------------------------------------------------- 1 | import * as P from "@fp-ts/core/Predicate" 2 | import * as S from "@fp-ts/core/String" 3 | import * as _ from "@fp-ts/core/typeclass/Contravariant" 4 | import * as order from "@fp-ts/core/typeclass/Order" 5 | import * as U from "../util" 6 | 7 | describe("Contravariant", () => { 8 | it("mapComposition", () => { 9 | const map = _.contramapComposition(P.Contravariant, P.Contravariant) 10 | const emptyString: P.Predicate> = p => p("") === true 11 | const a = map(emptyString, s => s.length) 12 | U.deepStrictEqual(a(S.isString), false) 13 | U.deepStrictEqual(a(n => n === 0), true) 14 | }) 15 | 16 | it("imap", () => { 17 | const O = _.imap(order.contramap)( 18 | (s: string) => [s], 19 | ([s]) => s 20 | )( 21 | S.Order 22 | ) 23 | U.deepStrictEqual(O.compare(["a"], ["b"]), -1) 24 | U.deepStrictEqual(O.compare(["a"], ["a"]), 0) 25 | U.deepStrictEqual(O.compare(["b"], ["a"]), 1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Coproduct.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Coproduct.ts 3 | nav_order: 25 4 | parent: Modules 5 | --- 6 | 7 | ## Coproduct overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Coproduct (interface)](#coproduct-interface) 17 | - [utils](#utils) 18 | - [getMonoid](#getmonoid) 19 | 20 | --- 21 | 22 | # type class 23 | 24 | ## Coproduct (interface) 25 | 26 | **Signature** 27 | 28 | ```ts 29 | export interface Coproduct extends SemiCoproduct { 30 | readonly zero:
() => Kind 31 | 32 | readonly coproductAll: (collection: Iterable>) => Kind 33 | } 34 | ``` 35 | 36 | Added in v1.0.0 37 | 38 | # utils 39 | 40 | ## getMonoid 41 | 42 | **Signature** 43 | 44 | ```ts 45 | export declare const getMonoid: (F: Coproduct) => () => Monoid> 46 | ``` 47 | 48 | Added in v1.0.0 49 | -------------------------------------------------------------------------------- /benchmark/dual.ts: -------------------------------------------------------------------------------- 1 | import { sum } from "@fp-ts/core/Number" 2 | import * as Benchmark from "benchmark" 3 | 4 | /* 5 | sum(a, b) x 39,807,035 ops/sec ±0.79% (87 runs sampled) 6 | binary(a, b) x 745,618,052 ops/sec ±0.53% (91 runs sampled) 7 | sum(b)(a) x 2,423,147 ops/sec ±1.50% (82 runs sampled) 8 | curried(b)(a) x 737,608,819 ops/sec ±0.60% (88 runs sampled) 9 | */ 10 | 11 | const suite = new Benchmark.Suite() 12 | 13 | const binary = (a: number, b: number): number => a + b 14 | 15 | const curried = (a: number) => (b: number): number => a + b 16 | 17 | suite 18 | .add("sum(a, b)", function() { 19 | sum(1, 2) 20 | }) 21 | .add("binary(a, b)", function() { 22 | binary(1, 2) 23 | }) 24 | .add("sum(b)(a)", function() { 25 | sum(2)(1) 26 | }) 27 | .add("curried(b)(a)", function() { 28 | curried(2)(1) 29 | }) 30 | .on("cycle", function(event: any) { 31 | console.log(String(event.target)) 32 | }) 33 | .on("complete", function(this: any) { 34 | console.log("Fastest is " + this.filter("fastest").map("name")) 35 | }) 36 | .run({ async: true }) 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | # Bug Report 2 | 3 | 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. 4 | 5 | ## Describing the Bug 6 | 7 | Please provide a clear and concise description of the issue you are encountering, including any error messages or unexpected behavior you have observed. 8 | 9 | ## Steps to Reproduce 10 | 11 | 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. 12 | 13 | ## Expected Behavior 14 | 15 | Please describe what you expected to happen, and how the current behavior differs from your expectations. 16 | 17 | ## Environment 18 | 19 | Please provide the following information to help us understand your setup: 20 | 21 | - Library version: [e.g. `0.8.1`] 22 | - TypeScript version: [e.g. `4.2.2`] 23 | 24 | ## Additional Context 25 | 26 | Any additional information or context that you think would be helpful in resolving the issue. 27 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Applicative.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Applicative.ts 3 | nav_order: 20 4 | parent: Modules 5 | --- 6 | 7 | ## Applicative overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Applicative (interface)](#applicative-interface) 17 | - [utils](#utils) 18 | - [getMonoid](#getmonoid) 19 | 20 | --- 21 | 22 | # type class 23 | 24 | ## Applicative (interface) 25 | 26 | **Signature** 27 | 28 | ```ts 29 | export interface Applicative extends SemiApplicative, Product {} 30 | ``` 31 | 32 | Added in v1.0.0 33 | 34 | # utils 35 | 36 | ## getMonoid 37 | 38 | Lift a `Monoid` into `F`, combining the inner values using the provided `Monoid`: 39 | 40 | - `combine` is provided by {@link semiApplicative.getSemigroup}. 41 | - `empty` is `F.of(M.empty)` 42 | 43 | **Signature** 44 | 45 | ```ts 46 | export declare const getMonoid: ( 47 | F: Applicative 48 | ) => (M: Monoid
) => Monoid> 49 | ``` 50 | 51 | Added in v1.0.0 52 | -------------------------------------------------------------------------------- /test/Tuple.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as T from "@fp-ts/core/Tuple" 3 | 4 | describe.concurrent("Tuple", () => { 5 | it("exports", () => { 6 | expect(T.Bicovariant).exists 7 | expect(T.mapFirst).exists 8 | expect(T.mapSecond).exists 9 | expect(T.appendElement).exists 10 | expect(T.getEquivalence).exists 11 | expect(T.getOrder).exists 12 | expect(T.getSemigroup).exists 13 | expect(T.getMonoid).exists 14 | }) 15 | 16 | it("tuple", () => { 17 | expect(T.tuple("a", 1, true)).toEqual(["a", 1, true]) 18 | }) 19 | 20 | it("appendElement", () => { 21 | expect(pipe(T.tuple("a", 1), T.appendElement(true))).toEqual(["a", 1, true]) 22 | }) 23 | 24 | it("getFirst", () => { 25 | expect(T.getFirst(T.tuple("a", 1))).toEqual("a") 26 | }) 27 | 28 | it("getSecond", () => { 29 | expect(T.getSecond(T.tuple("a", 1))).toEqual(1) 30 | }) 31 | 32 | it("bimap", () => { 33 | expect(T.bimap(T.tuple("a", 1), s => s + "!", n => n * 2)).toEqual(["a!", 2]) 34 | }) 35 | 36 | it("swap", () => { 37 | expect(T.swap(T.tuple("a", 1))).toEqual([1, "a"]) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present Giulio Canti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/typeclass/Bicovariant.ts: -------------------------------------------------------------------------------- 1 | import * as E from "@fp-ts/core/Either" 2 | import { pipe } from "@fp-ts/core/Function" 3 | import * as RA from "@fp-ts/core/ReadonlyArray" 4 | import * as _ from "@fp-ts/core/typeclass/Bicovariant" 5 | import * as U from "../util" 6 | 7 | describe("Bicovariant", () => { 8 | it("mapLeft", () => { 9 | const mapLeft = _.mapLeft(E.Bicovariant) 10 | const f = (s: string) => s.length 11 | U.deepStrictEqual(pipe(E.right(1), mapLeft(f)), E.right(1)) 12 | U.deepStrictEqual(pipe(E.left("eee"), mapLeft(f)), E.left(3)) 13 | }) 14 | 15 | it("map", () => { 16 | const map = _.map(E.Bicovariant) 17 | const g = (n: number) => n * 2 18 | U.deepStrictEqual(pipe(E.right(1), map(g)), E.right(2)) 19 | U.deepStrictEqual(pipe(E.left("eee"), map(g)), E.left("eee")) 20 | }) 21 | 22 | it("bimapComposition", () => { 23 | const bimap = _.bimapComposition(RA.Covariant, E.Bicovariant) 24 | const f = (s: string) => s.length 25 | const g = (n: number) => n * 2 26 | U.deepStrictEqual(bimap([E.right(1), E.right(2), E.left("eee")], f, g), [ 27 | E.right(2), 28 | E.right(4), 29 | E.left(3) 30 | ]) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core" 2 | import * as assert from "assert" 3 | import * as glob from "glob" 4 | import * as path from "path" 5 | 6 | const getExportName = (name: string): string => { 7 | if (name === "HKT") { 8 | return name.toLowerCase() 9 | } 10 | return name.substring(0, 1).toLowerCase() + name.substring(1) 11 | } 12 | 13 | function getModuleNames(): ReadonlyArray { 14 | return glob 15 | .sync("./src/**/*.ts") 16 | .map((file) => path.parse(file)) 17 | .filter((file) => !file.dir.startsWith("./src/internal")) 18 | .map((file) => file.name) 19 | } 20 | 21 | describe.concurrent("index", () => { 22 | it("check exported modules", () => { 23 | const moduleNames = getModuleNames() 24 | moduleNames.forEach((name) => { 25 | if (name !== "index") { 26 | const exportName = getExportName(name) 27 | assert.deepStrictEqual( 28 | // tslint:disable-next-line: strict-type-predicates 29 | (_ as Record)[exportName] !== undefined, 30 | true, 31 | `The "${name}" module is not exported in src/index.ts as ${exportName}` 32 | ) 33 | } 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/typeclass/Applicative.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Monoid } from "@fp-ts/core/typeclass/Monoid" 6 | import * as monoid from "@fp-ts/core/typeclass/Monoid" 7 | import type { Product } from "@fp-ts/core/typeclass/Product" 8 | import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" 9 | import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" 10 | 11 | /** 12 | * @category type class 13 | * @since 1.0.0 14 | */ 15 | export interface Applicative extends SemiApplicative, Product {} 16 | 17 | /** 18 | * Lift a `Monoid` into `F`, combining the inner values using the provided `Monoid`: 19 | * 20 | * - `combine` is provided by {@link semiApplicative.getSemigroup}. 21 | * - `empty` is `F.of(M.empty)` 22 | * 23 | * @param F - The `Applicative` instance for `F`. 24 | * @param M - The `Monoid` instance for `A`. 25 | * 26 | * @since 1.0.0 27 | */ 28 | export const getMonoid = (F: Applicative) => 29 | (M: Monoid): Monoid> => 30 | monoid.fromSemigroup( 31 | semiApplicative.getSemigroup(F)(M), 32 | F.of(M.empty) 33 | ) 34 | -------------------------------------------------------------------------------- /docs/modules/typeclass/SemiCoproduct.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/SemiCoproduct.ts 3 | nav_order: 40 4 | parent: Modules 5 | --- 6 | 7 | ## SemiCoproduct overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [SemiCoproduct (interface)](#semicoproduct-interface) 17 | - [utils](#utils) 18 | - [getSemigroup](#getsemigroup) 19 | 20 | --- 21 | 22 | # type class 23 | 24 | ## SemiCoproduct (interface) 25 | 26 | **Signature** 27 | 28 | ```ts 29 | export interface SemiCoproduct extends Invariant { 30 | readonly coproduct: ( 31 | self: Kind, 32 | that: Kind 33 | ) => Kind 34 | 35 | readonly coproductMany: ( 36 | self: Kind, 37 | collection: Iterable> 38 | ) => Kind 39 | } 40 | ``` 41 | 42 | Added in v1.0.0 43 | 44 | # utils 45 | 46 | ## getSemigroup 47 | 48 | **Signature** 49 | 50 | ```ts 51 | export declare const getSemigroup: ( 52 | F: SemiCoproduct 53 | ) => () => Semigroup> 54 | ``` 55 | 56 | Added in v1.0.0 57 | -------------------------------------------------------------------------------- /test/typeclass/Chainable.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as _ from "@fp-ts/core/typeclass/Chainable" 4 | import * as U from "../util" 5 | 6 | describe("Chainable", () => { 7 | it("andThenDiscard", () => { 8 | const andThenDiscard = _.andThenDiscard(O.Chainable) 9 | U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.none())), O.none()) 10 | U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.some(2))), O.none()) 11 | U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none())), O.none()) 12 | U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.some(2))), O.some(1)) 13 | }) 14 | 15 | it("bind", () => { 16 | const bind = _.bind(O.Chainable) 17 | U.deepStrictEqual(pipe(O.Do, bind("a", () => O.none())), O.none()) 18 | U.deepStrictEqual(pipe(O.Do, bind("a", () => O.some(1))), O.some({ a: 1 })) 19 | }) 20 | 21 | it("tap", () => { 22 | const tap = _.tap(O.Chainable) 23 | U.deepStrictEqual(pipe(O.none(), tap(() => O.none())), O.none()) 24 | U.deepStrictEqual(pipe(O.none(), tap(() => O.some(2))), O.none()) 25 | U.deepStrictEqual(pipe(O.some(1), tap(() => O.none())), O.none()) 26 | U.deepStrictEqual(pipe(O.some(1), tap(() => O.some(2))), O.some(1)) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/typeclass/Traversable.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as RA from "@fp-ts/core/ReadonlyArray" 4 | import * as _ from "@fp-ts/core/typeclass/Traversable" 5 | import * as U from "../util" 6 | 7 | describe("Traversable", () => { 8 | it("traverseComposition", () => { 9 | const traverse = _.traverseComposition(RA.Traversable, RA.Traversable)(O.Applicative) 10 | U.deepStrictEqual( 11 | traverse([[1, 2], [3]], (a) => (a > 0 ? O.some(a) : O.none())), 12 | O.some([[1, 2], [3]]) 13 | ) 14 | U.deepStrictEqual( 15 | traverse([[1, -2], [3]], (a) => (a > 0 ? O.some(a) : O.none())), 16 | O.none() 17 | ) 18 | }) 19 | 20 | it("traverseTap", () => { 21 | const traverseTap = _.traverseTap(RA.Traversable)(O.Applicative) 22 | U.deepStrictEqual( 23 | pipe([], traverseTap(n => n > 0 ? O.some(n) : O.none())), 24 | O.some([]) 25 | ) 26 | U.deepStrictEqual( 27 | pipe(["a", "b", "c"], traverseTap(s => s.length > 0 ? O.some(s.length) : O.none())), 28 | O.some(["a", "b", "c"]) 29 | ) 30 | U.deepStrictEqual( 31 | pipe(["a", "", "c"], traverseTap(s => s.length > 0 ? O.some(s) : O.none())), 32 | O.none() 33 | ) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/typeclass/FlatMap.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as _ from "@fp-ts/core/typeclass/FlatMap" 4 | import * as U from "../util" 5 | 6 | describe("FlatMap", () => { 7 | it("flatten", () => { 8 | const flatten = _.flatten(O.FlatMap) 9 | U.deepStrictEqual(pipe(O.none(), flatten), O.none()) 10 | U.deepStrictEqual(pipe(O.some(O.none()), flatten), O.none()) 11 | U.deepStrictEqual(pipe(O.some(O.some(1)), flatten), O.some(1)) 12 | }) 13 | 14 | it("andThen", () => { 15 | const andThen = _.andThen(O.FlatMap) 16 | U.deepStrictEqual(pipe(O.none(), andThen(O.none())), O.none()) 17 | U.deepStrictEqual(pipe(O.none(), andThen(O.some(2))), O.none()) 18 | U.deepStrictEqual(pipe(O.some(1), andThen(O.none())), O.none()) 19 | U.deepStrictEqual(pipe(O.some(1), andThen(O.some(2))), O.some(2)) 20 | }) 21 | 22 | it("composeKleisliArrow", () => { 23 | const composeKleisliArrow = _.composeKleisliArrow(O.FlatMap) 24 | const f = (s: string): O.Option => s.length > 0 ? O.some(s.length) : O.none() 25 | const g = (n: number): O.Option => n > 1 ? O.some(n) : O.none() 26 | const h = pipe(f, composeKleisliArrow(g)) 27 | U.deepStrictEqual(h(""), O.none()) 28 | U.deepStrictEqual(h("a"), O.none()) 29 | U.deepStrictEqual(h("aa"), O.some(2)) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/internal/Either.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import type { Either, Left, Right } from "@fp-ts/core/Either" 6 | import { dual } from "@fp-ts/core/Function" 7 | import { proto } from "@fp-ts/core/internal/effect" 8 | import * as option from "@fp-ts/core/internal/Option" 9 | import type { Option } from "@fp-ts/core/Option" 10 | 11 | /** @internal */ 12 | export const isLeft = (ma: Either): ma is Left => ma._tag === "Left" 13 | 14 | /** @internal */ 15 | export const isRight = (ma: Either): ma is Right
=> ma._tag === "Right" 16 | 17 | /** @internal */ 18 | export const left = (e: E): Either => 19 | Object.setPrototypeOf({ _tag: "Left", left: e }, proto) 20 | 21 | /** @internal */ 22 | export const right = (a: A): Either => 23 | Object.setPrototypeOf({ _tag: "Right", right: a }, proto) 24 | 25 | /** @internal */ 26 | export const getLeft = ( 27 | self: Either 28 | ): Option => (isRight(self) ? option.none : option.some(self.left)) 29 | 30 | /** @internal */ 31 | export const getRight = ( 32 | self: Either 33 | ): Option => (isLeft(self) ? option.none : option.some(self.right)) 34 | 35 | /** @internal */ 36 | export const fromOption = dual( 37 | 2, 38 | (self: Option, onNone: () => E): Either => 39 | option.isNone(self) ? left(onNone()) : right(self.value) 40 | ) 41 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR Flow 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - 'main' 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [16.17.1] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - uses: pnpm/action-setup@v2.0.1 28 | name: Install pnpm 29 | id: pnpm-install 30 | with: 31 | version: 7 32 | run_install: false 33 | - name: Get pnpm store directory 34 | id: pnpm-cache 35 | run: | 36 | echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" 37 | - uses: actions/cache@v3 38 | name: Setup pnpm cache 39 | with: 40 | path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} 41 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 42 | restore-keys: | 43 | ${{ runner.os }}-pnpm-store- 44 | - run: pnpm install 45 | - run: pnpm build 46 | - run: pnpm test 47 | - run: pnpm dtslint 48 | - run: pnpm lint 49 | - run: pnpm run docs 50 | -------------------------------------------------------------------------------- /docs/modules/HKT.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: HKT.ts 3 | nav_order: 5 4 | parent: Modules 5 | --- 6 | 7 | ## HKT overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [utils](#utils) 16 | - [Kind (type alias)](#kind-type-alias) 17 | - [TypeClass (interface)](#typeclass-interface) 18 | - [TypeLambda (interface)](#typelambda-interface) 19 | 20 | --- 21 | 22 | # utils 23 | 24 | ## Kind (type alias) 25 | 26 | **Signature** 27 | 28 | ```ts 29 | export type Kind = F extends { 30 | readonly type: unknown 31 | } 32 | ? (F & { 33 | readonly In: In 34 | readonly Out2: Out2 35 | readonly Out1: Out1 36 | readonly Target: Target 37 | })['type'] 38 | : { 39 | readonly F: F 40 | readonly In: (_: In) => void 41 | readonly Out2: () => Out2 42 | readonly Out1: () => Out1 43 | readonly Target: (_: Target) => Target 44 | } 45 | ``` 46 | 47 | Added in v1.0.0 48 | 49 | ## TypeClass (interface) 50 | 51 | **Signature** 52 | 53 | ```ts 54 | export interface TypeClass { 55 | readonly [URI]?: F 56 | } 57 | ``` 58 | 59 | Added in v1.0.0 60 | 61 | ## TypeLambda (interface) 62 | 63 | **Signature** 64 | 65 | ```ts 66 | export interface TypeLambda { 67 | readonly In: unknown 68 | readonly Out2: unknown 69 | readonly Out1: unknown 70 | readonly Target: unknown 71 | } 72 | ``` 73 | 74 | Added in v1.0.0 75 | -------------------------------------------------------------------------------- /src/typeclass/Contravariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual } from "@fp-ts/core/Function" 5 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Invariant } from "@fp-ts/core/typeclass/Invariant" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Contravariant extends Invariant { 13 | readonly contramap: { 14 | (f: (b: B) => A): (self: Kind) => Kind 15 | (self: Kind, f: (b: B) => A): Kind 16 | } 17 | } 18 | 19 | /** 20 | * Composing two contravariant functors yields a Covariant functor. 21 | * 22 | * Returns a default binary `map` composition. 23 | * 24 | * @since 1.0.0 25 | */ 26 | export const contramapComposition = ( 27 | F: Contravariant, 28 | G: Contravariant 29 | ) => 30 | ( 31 | self: Kind>, 32 | f: (a: A) => B 33 | ): Kind> => F.contramap(self, G.contramap(f)) 34 | 35 | /** 36 | * Returns a default `imap` implementation. 37 | * 38 | * @since 1.0.0 39 | */ 40 | export const imap = ( 41 | contramap: ( 42 | self: Kind, 43 | f: (b: B) => A 44 | ) => Kind 45 | ): Invariant["imap"] => dual(3, (self, _, from) => contramap(self, from)) 46 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Of.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Of.ts 3 | nav_order: 34 4 | parent: Modules 5 | --- 6 | 7 | ## Of overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [do notation](#do-notation) 16 | - [Do](#do) 17 | - [type class](#type-class) 18 | - [Of (interface)](#of-interface) 19 | - [utils](#utils) 20 | - [ofComposition](#ofcomposition) 21 | - [unit](#unit) 22 | 23 | --- 24 | 25 | # do notation 26 | 27 | ## Do 28 | 29 | **Signature** 30 | 31 | ```ts 32 | export declare const Do: (F: Of) => Kind 33 | ``` 34 | 35 | Added in v1.0.0 36 | 37 | # type class 38 | 39 | ## Of (interface) 40 | 41 | **Signature** 42 | 43 | ```ts 44 | export interface Of extends TypeClass { 45 | readonly of:
(a: A) => Kind 46 | } 47 | ``` 48 | 49 | Added in v1.0.0 50 | 51 | # utils 52 | 53 | ## ofComposition 54 | 55 | Returns a default `of` composition. 56 | 57 | **Signature** 58 | 59 | ```ts 60 | export declare const ofComposition: ( 61 | F: Of, 62 | G: Of 63 | ) => (a: A) => Kind> 64 | ``` 65 | 66 | Added in v1.0.0 67 | 68 | ## unit 69 | 70 | **Signature** 71 | 72 | ```ts 73 | export declare const unit: (F: Of) => Kind 74 | ``` 75 | 76 | Added in v1.0.0 77 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.preferences.importModuleSpecifier": "non-relative", 4 | "typescript.enablePromptUseWorkspaceTsdk": true, 5 | "editor.formatOnSave": true, 6 | "eslint.format.enable": true, 7 | "[json]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "[javascript]": { 11 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 12 | }, 13 | "[javascriptreact]": { 14 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 15 | }, 16 | "[typescript]": { 17 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 18 | }, 19 | "[typescriptreact]": { 20 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 21 | }, 22 | "eslint.validate": ["markdown", "javascript", "typescript"], 23 | "editor.codeActionsOnSave": { 24 | "source.fixAll.eslint": true 25 | }, 26 | "editor.quickSuggestions": { 27 | "other": true, 28 | "comments": false, 29 | "strings": false 30 | }, 31 | "editor.acceptSuggestionOnCommitCharacter": true, 32 | "editor.acceptSuggestionOnEnter": "on", 33 | "editor.quickSuggestionsDelay": 10, 34 | "editor.suggestOnTriggerCharacters": true, 35 | "editor.tabCompletion": "off", 36 | "editor.suggest.localityBonus": true, 37 | "editor.suggestSelection": "recentlyUsed", 38 | "editor.wordBasedSuggestions": true, 39 | "editor.parameterHints.enabled": true, 40 | "files.watcherExclude": { 41 | "**/target": true 42 | }, 43 | "files.insertFinalNewline": true 44 | } 45 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleDetection": "force", 4 | "composite": true, 5 | "downlevelIteration": true, 6 | "resolveJsonModule": true, 7 | "esModuleInterop": true, 8 | "declaration": true, 9 | "skipLibCheck": true, 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "preserveSymlinks": true, 13 | "moduleResolution": "node", 14 | "noEmit": false, 15 | "lib": ["ES2021"], 16 | "sourceMap": true, 17 | "declarationMap": true, 18 | "strict": true, 19 | "noImplicitReturns": false, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false, 22 | "noFallthroughCasesInSwitch": true, 23 | "noEmitOnError": false, 24 | "noErrorTruncation": false, 25 | "allowJs": false, 26 | "checkJs": false, 27 | "forceConsistentCasingInFileNames": true, 28 | "suppressImplicitAnyIndexErrors": true, 29 | "stripInternal": true, 30 | "noImplicitAny": true, 31 | "noImplicitThis": true, 32 | "noUncheckedIndexedAccess": false, 33 | "strictNullChecks": true, 34 | "baseUrl": ".", 35 | "target": "ES2021", 36 | "module": "ES6", 37 | "incremental": true, 38 | "removeComments": false, 39 | "paths": { 40 | "@fp-ts/core": ["./src/index.ts"], 41 | "@fp-ts/core/test/*": ["./test/*"], 42 | "@fp-ts/core/examples/*": ["./examples/*"], 43 | "@fp-ts/core/*": ["./src/*"] 44 | }, 45 | "plugins": [{ "name": "@effect/language-service" }] 46 | }, 47 | "include": [], 48 | "exclude": ["node_modules", "build", "lib"] 49 | } 50 | -------------------------------------------------------------------------------- /test/Ordering.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/Ordering" 2 | import { deepStrictEqual } from "./util" 3 | 4 | describe("Ordering", () => { 5 | it("match", () => { 6 | const f = _.match( 7 | () => "lt", 8 | () => "eq", 9 | () => "gt" 10 | ) 11 | deepStrictEqual(f(-1), "lt") 12 | deepStrictEqual(f(0), "eq") 13 | deepStrictEqual(f(1), "gt") 14 | }) 15 | 16 | it("reverse", () => { 17 | deepStrictEqual(_.reverse(-1), 1) 18 | deepStrictEqual(_.reverse(0), 0) 19 | deepStrictEqual(_.reverse(1), -1) 20 | }) 21 | 22 | it("Semigroup", () => { 23 | deepStrictEqual(_.Semigroup.combine(0, 0), 0) 24 | deepStrictEqual(_.Semigroup.combine(0, 1), 1) 25 | deepStrictEqual(_.Semigroup.combine(1, -1), 1) 26 | deepStrictEqual(_.Semigroup.combine(-1, 1), -1) 27 | 28 | deepStrictEqual(_.Semigroup.combineMany(0, []), 0) 29 | deepStrictEqual(_.Semigroup.combineMany(1, []), 1) 30 | deepStrictEqual(_.Semigroup.combineMany(-1, []), -1) 31 | deepStrictEqual(_.Semigroup.combineMany(0, [0, 0, 0]), 0) 32 | deepStrictEqual(_.Semigroup.combineMany(0, [0, 0, 1]), 1) 33 | deepStrictEqual(_.Semigroup.combineMany(1, [0, 0, -1]), 1) 34 | deepStrictEqual(_.Semigroup.combineMany(-1, [0, 0, 1]), -1) 35 | }) 36 | 37 | it("Monoid", () => { 38 | deepStrictEqual(_.Monoid.combine(_.Monoid.empty, 0), 0) 39 | deepStrictEqual(_.Monoid.combine(_.Monoid.empty, 1), 1) 40 | deepStrictEqual(_.Monoid.combine(_.Monoid.empty, -1), -1) 41 | deepStrictEqual(_.Monoid.combine(0, _.Monoid.empty), 0) 42 | deepStrictEqual(_.Monoid.combine(1, _.Monoid.empty), 1) 43 | deepStrictEqual(_.Monoid.combine(-1, _.Monoid.empty), -1) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /dtslint/ts4.8/ReadonlyRecord.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from '@fp-ts/core/Function' 2 | import * as RR from '@fp-ts/core/ReadonlyRecord' 3 | 4 | declare const r: Record 5 | declare const struct: Record<'a' | 'b', number> 6 | 7 | // 8 | // map 9 | // 10 | 11 | // $ExpectType Record 12 | RR.map(r, ( 13 | value, // $ExpectType number 14 | _key, // $ExpectType string 15 | ) => value > 0) 16 | 17 | // $ExpectType Record 18 | pipe(r, RR.map(( 19 | value, // $ExpectType number 20 | _key, // $ExpectType string 21 | ) => value > 0)) 22 | 23 | // $ExpectType Record<"a" | "b", boolean> 24 | RR.map(struct, ( 25 | value, // $ExpectType number 26 | _key, // $ExpectType "a" | "b" 27 | ) => value > 0) 28 | 29 | // $ExpectType Record<"a" | "b", boolean> 30 | pipe(struct, RR.map(( 31 | value, // $ExpectType number 32 | _key, // $ExpectType "a" | "b" 33 | ) => value > 0)) 34 | 35 | const constStruct = { a: 1, b: 2 } as const; 36 | 37 | function mapToBoolean(): { [K in keyof typeof constStruct]: boolean } { 38 | return RR.map(constStruct, () => true); 39 | } 40 | 41 | // $ExpectType { readonly a: boolean; readonly b: boolean; } 42 | mapToBoolean() 43 | 44 | // 45 | // get 46 | // 47 | 48 | // $ExpectType Option 49 | pipe(r, RR.get('a')) 50 | 51 | // 52 | // replaceOption 53 | // 54 | 55 | // $ExpectType Option> 56 | pipe(r, RR.replaceOption('a', 2)) 57 | 58 | // $ExpectType Option> 59 | pipe(r, RR.replaceOption('a', true)) 60 | 61 | // 62 | // modifyOption 63 | // 64 | 65 | // $ExpectType Option> 66 | pipe(r, RR.modifyOption('a', () => 2)) 67 | 68 | // $ExpectType Option> 69 | pipe(r, RR.modifyOption('a', () => true)) 70 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main Flow 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [16.17.1] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | fetch-depth: 0 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - uses: pnpm/action-setup@v2.0.1 27 | name: Install pnpm 28 | id: pnpm-install 29 | with: 30 | version: 7 31 | run_install: false 32 | - name: Get pnpm store directory 33 | id: pnpm-cache 34 | run: | 35 | echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" 36 | - uses: actions/cache@v3 37 | name: Setup pnpm cache 38 | with: 39 | path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} 40 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 41 | restore-keys: | 42 | ${{ runner.os }}-pnpm-store- 43 | - run: pnpm install 44 | - run: pnpm build 45 | - run: pnpm test 46 | - run: pnpm dtslint 47 | - run: pnpm lint 48 | - run: pnpm run docs 49 | - name: Create Release Pull Request or Publish 50 | id: changesets 51 | uses: changesets/action@v1 52 | with: 53 | version: pnpm run version 54 | publish: pnpm exec changeset publish 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 58 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `@fp-ts/core` 2 | 3 | We welcome all contributions to the `@fp-ts/core` library. Your help makes the library better for everyone! 4 | 5 | ## Creating an Issue 6 | 7 | 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. 8 | 9 | ## Development Workflow 10 | 11 | 1. Fork the repository on GitHub. 12 | 2. Clone your forked repository using the following command: `git clone git@github.com:{your_username}/core.git` 13 | 3. Install dependencies with `pnpm install`. 14 | 4. Make your contributions and commit your changes. 15 | 5. If you have made changes to the code, run `pnpm changeset` and select the appropriate level of change (`patch`, `minor`, `major`) 16 | 17 | ### Available Commands 18 | 19 | - `pnpm build`: Deletes the `dist` folder and recompiles the `src` code into `dist`. 20 | - `pnpm test`: Runs all vitest tests in watch mode. 21 | - `pnpm coverage`: Runs all vitest tests and collects coverage information. 22 | - `pnpm dtslint`: Runs type-level tests. 23 | 24 | ### Writing Tests 25 | 26 | `@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. 27 | 28 | ### Documentation 29 | 30 | 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. 31 | 32 | ## Licensing 33 | 34 | By contributing your code to the `@fp-ts/core` GitHub repository, you agree to license your contribution under the MIT license. 35 | -------------------------------------------------------------------------------- /test/typeclass/Covariant.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as RA from "@fp-ts/core/ReadonlyArray" 4 | import * as _ from "@fp-ts/core/typeclass/Covariant" 5 | import * as U from "../util" 6 | 7 | describe("Covariant", () => { 8 | it("mapComposition", () => { 9 | const map = _.mapComposition(RA.Covariant, RA.Covariant) 10 | const f = (a: string) => a + "!" 11 | U.deepStrictEqual(map([], f), []) 12 | U.deepStrictEqual(map([[]], f), [[]]) 13 | U.deepStrictEqual(map([["a"]], f), [["a!"]]) 14 | U.deepStrictEqual(map([["a"], ["b"]], f), [["a!"], ["b!"]]) 15 | U.deepStrictEqual(map([["a", "c"], ["b", "d", "e"]], f), [["a!", "c!"], [ 16 | "b!", 17 | "d!", 18 | "e!" 19 | ]]) 20 | }) 21 | 22 | it("flap", () => { 23 | const flap = _.flap(O.Covariant) 24 | U.deepStrictEqual(pipe(1, flap(O.none())), O.none()) 25 | U.deepStrictEqual(pipe(1, flap(O.some(U.double))), O.some(2)) 26 | }) 27 | 28 | it("as", () => { 29 | const as = _.as(O.Covariant) 30 | U.deepStrictEqual(pipe(O.none(), as(1)), O.none()) 31 | U.deepStrictEqual(pipe(O.some(1), as(2)), O.some(2)) 32 | }) 33 | 34 | it("asUnit", () => { 35 | const asUnit = _.asUnit(O.Covariant) 36 | U.deepStrictEqual(pipe(O.none(), asUnit), O.none()) 37 | U.deepStrictEqual(pipe(O.some(1), asUnit), O.some(undefined)) 38 | }) 39 | 40 | it("let", () => { 41 | const letOption = _.let(O.Covariant) 42 | U.deepStrictEqual( 43 | pipe(O.some({ a: 1, b: 2 }), letOption("c", ({ a, b }) => a + b)), 44 | O.some({ a: 1, b: 2, c: 3 }) 45 | ) 46 | }) 47 | 48 | it("imap", () => { 49 | const f = _.imap(O.map)((s: string) => [s], ([s]) => s) 50 | U.deepStrictEqual(pipe(O.none(), f), O.none()) 51 | U.deepStrictEqual(pipe(O.some("a"), f), O.some(["a"])) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /src/typeclass/Product.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Of } from "@fp-ts/core/typeclass/Of" 7 | import type { SemiProduct } from "@fp-ts/core/typeclass/SemiProduct" 8 | 9 | /** 10 | * @category type class 11 | * @since 1.0.0 12 | */ 13 | export interface Product extends SemiProduct, Of { 14 | readonly productAll: ( 15 | collection: Iterable> 16 | ) => Kind> 17 | } 18 | 19 | /** 20 | * @since 1.0.0 21 | */ 22 | export const tuple = (F: Product) => 23 | >>(...elements: T): Kind< 24 | F, 25 | ([T[number]] extends [Kind] ? R : never), 26 | ([T[number]] extends [Kind] ? O : never), 27 | ([T[number]] extends [Kind] ? E : never), 28 | { [I in keyof T]: [T[I]] extends [Kind] ? A : never } 29 | > => F.productAll(elements) as any 30 | 31 | /** 32 | * @since 1.0.0 33 | */ 34 | export const struct = (F: Product) => 35 | }>(fields: R): Kind< 36 | F, 37 | ([R[keyof R]] extends [Kind] ? R : never), 38 | ([R[keyof R]] extends [Kind] ? O : never), 39 | ([R[keyof R]] extends [Kind] ? E : never), 40 | { [K in keyof R]: [R[K]] extends [Kind] ? A : never } 41 | > => { 42 | const keys = Object.keys(fields) 43 | return F.imap(F.productAll(keys.map(k => fields[k])), values => { 44 | const out: any = {} 45 | for (let i = 0; i < values.length; i++) { 46 | out[keys[i]] = values[i] 47 | } 48 | return out 49 | }, (r) => keys.map(k => r[k])) 50 | } 51 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Product.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Product.ts 3 | nav_order: 37 4 | parent: Modules 5 | --- 6 | 7 | ## Product overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Product (interface)](#product-interface) 17 | - [utils](#utils) 18 | - [struct](#struct) 19 | - [tuple](#tuple) 20 | 21 | --- 22 | 23 | # type class 24 | 25 | ## Product (interface) 26 | 27 | **Signature** 28 | 29 | ```ts 30 | export interface Product extends SemiProduct, Of { 31 | readonly productAll: (collection: Iterable>) => Kind> 32 | } 33 | ``` 34 | 35 | Added in v1.0.0 36 | 37 | # utils 38 | 39 | ## struct 40 | 41 | **Signature** 42 | 43 | ```ts 44 | export declare const struct: ( 45 | F: Product 46 | ) => }>( 47 | fields: R 48 | ) => Kind< 49 | F, 50 | [R[keyof R]] extends [Kind] ? R : never, 51 | [R[keyof R]] extends [Kind] ? O : never, 52 | [R[keyof R]] extends [Kind] ? E : never, 53 | { [K in keyof R]: [R[K]] extends [Kind] ? A : never } 54 | > 55 | ``` 56 | 57 | Added in v1.0.0 58 | 59 | ## tuple 60 | 61 | **Signature** 62 | 63 | ```ts 64 | export declare const tuple: ( 65 | F: Product 66 | ) => []>( 67 | ...elements: T 68 | ) => Kind< 69 | F, 70 | [T[number]] extends [Kind] ? R : never, 71 | [T[number]] extends [Kind] ? O : never, 72 | [T[number]] extends [Kind] ? E : never, 73 | { [I in keyof T]: [T[I]] extends [Kind] ? A : never } 74 | > 75 | ``` 76 | 77 | Added in v1.0.0 78 | -------------------------------------------------------------------------------- /src/typeclass/Bounded.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { TypeLambda } from "@fp-ts/core/HKT" 5 | import type { Monoid } from "@fp-ts/core/typeclass/Monoid" 6 | import * as monoid from "@fp-ts/core/typeclass/Monoid" 7 | import * as order from "@fp-ts/core/typeclass/Order" 8 | import type { Order } from "@fp-ts/core/typeclass/Order" 9 | import * as semigroup from "@fp-ts/core/typeclass/Semigroup" 10 | 11 | /** 12 | * @category type class 13 | * @since 1.0.0 14 | */ 15 | export interface Bounded
extends Order { 16 | readonly maxBound: A 17 | readonly minBound: A 18 | } 19 | 20 | /** 21 | * @category type lambdas 22 | * @since 1.0.0 23 | */ 24 | export interface BoundedTypeLambda extends TypeLambda { 25 | readonly type: Bounded 26 | } 27 | 28 | /** 29 | * `Monoid` that returns last minimum of elements. 30 | * 31 | * @category constructors 32 | * @since 1.0.0 33 | */ 34 | export const min = (B: Bounded): Monoid => 35 | monoid.fromSemigroup(semigroup.min(B), B.maxBound) 36 | 37 | /** 38 | * `Monoid` that returns last maximum of elements. 39 | * 40 | * @category constructors 41 | * @since 1.0.0 42 | */ 43 | export const max = (B: Bounded): Monoid => 44 | monoid.fromSemigroup(semigroup.max(B), B.minBound) 45 | 46 | /** 47 | * @category instances 48 | * @since 1.0.0 49 | */ 50 | export const number: Bounded = { 51 | compare: order.number.compare, 52 | maxBound: Infinity, 53 | minBound: -Infinity 54 | } 55 | 56 | /** 57 | * Clamp a value between `minBound` and `maxBound` values. 58 | * 59 | * @since 1.0.0 60 | */ 61 | export const clamp = (B: Bounded): (a: A) => A => order.clamp(B)(B.minBound, B.maxBound) 62 | 63 | /** 64 | * Reverses the `Order` of a `Bounded` and flips `maxBound` and `minBound` values. 65 | * 66 | * @since 1.0.0 67 | */ 68 | export const reverse = (B: Bounded): Bounded => ({ 69 | ...order.reverse(B), 70 | minBound: B.maxBound, 71 | maxBound: B.minBound 72 | }) 73 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Contravariant.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Contravariant.ts 3 | nav_order: 24 4 | parent: Modules 5 | --- 6 | 7 | ## Contravariant overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Contravariant (interface)](#contravariant-interface) 17 | - [utils](#utils) 18 | - [contramapComposition](#contramapcomposition) 19 | - [imap](#imap) 20 | 21 | --- 22 | 23 | # type class 24 | 25 | ## Contravariant (interface) 26 | 27 | **Signature** 28 | 29 | ```ts 30 | export interface Contravariant extends Invariant { 31 | readonly contramap: { 32 | (f: (b: B) => A): (self: Kind) => Kind 33 | (self: Kind, f: (b: B) => A): Kind 34 | } 35 | } 36 | ``` 37 | 38 | Added in v1.0.0 39 | 40 | # utils 41 | 42 | ## contramapComposition 43 | 44 | Composing two contravariant functors yields a Covariant functor. 45 | 46 | Returns a default binary `map` composition. 47 | 48 | **Signature** 49 | 50 | ```ts 51 | export declare const contramapComposition: ( 52 | F: Contravariant, 53 | G: Contravariant 54 | ) => ( 55 | self: Kind>, 56 | f: (a: A) => B 57 | ) => Kind> 58 | ``` 59 | 60 | Added in v1.0.0 61 | 62 | ## imap 63 | 64 | Returns a default `imap` implementation. 65 | 66 | **Signature** 67 | 68 | ```ts 69 | export declare const imap: ( 70 | contramap: (self: Kind, f: (b: B) => A) => Kind 71 | ) => { 72 | (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind 73 | (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind 74 | } 75 | ``` 76 | 77 | Added in v1.0.0 78 | -------------------------------------------------------------------------------- /test/typeclass/Invariant.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as P from "@fp-ts/core/Predicate" 4 | import * as String from "@fp-ts/core/String" 5 | import * as _ from "@fp-ts/core/typeclass/Invariant" 6 | import * as semigroup from "@fp-ts/core/typeclass/Semigroup" 7 | import * as U from "../util" 8 | 9 | describe("Invariant", () => { 10 | it("imapComposition", () => { 11 | const imap = _.imapComposition(semigroup.Invariant, O.Invariant) 12 | const S = imap(O.getOptionalMonoid(String.Semigroup), s => [s], ([s]) => s) 13 | U.deepStrictEqual(S.combine(O.none(), O.none()), O.none()) 14 | U.deepStrictEqual(S.combine(O.none(), O.some(["b"])), O.some(["b"])) 15 | U.deepStrictEqual(S.combine(O.some(["a"]), O.none()), O.some(["a"])) 16 | U.deepStrictEqual( 17 | S.combine(O.some(["a"]), O.some(["b"])), 18 | O.some(["ab"]) 19 | ) 20 | }) 21 | 22 | describe("bindTo", () => { 23 | it("Covariant (Option)", () => { 24 | const bindTo = _.bindTo(O.Invariant) 25 | U.deepStrictEqual(pipe(O.none(), bindTo("a")), O.none()) 26 | U.deepStrictEqual(pipe(O.some(1), bindTo("a")), O.some({ a: 1 })) 27 | }) 28 | 29 | it("Contravariant (Predicate)", () => { 30 | const bindTo = _.bindTo(P.Invariant) 31 | const p = pipe(String.isString, bindTo("a")) 32 | U.deepStrictEqual(p({ a: "a" }), true) 33 | U.deepStrictEqual(p({ a: 1 }), false) 34 | }) 35 | }) 36 | 37 | describe("tupled", () => { 38 | it("Covariant (Option)", () => { 39 | const tupled = _.tupled(O.Invariant) 40 | U.deepStrictEqual(pipe(O.none(), tupled), O.none()) 41 | U.deepStrictEqual(pipe(O.some(1), tupled), O.some([1])) 42 | }) 43 | 44 | it("Contravariant (Predicate)", () => { 45 | const tupled = _.tupled(P.Invariant) 46 | const p = pipe(String.isString, tupled) 47 | U.deepStrictEqual(p(["a"]), true) 48 | U.deepStrictEqual(p([1]), false) 49 | }) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /test/typeclass/Monoid.ts: -------------------------------------------------------------------------------- 1 | import * as N from "@fp-ts/core/Number" 2 | import * as String from "@fp-ts/core/String" 3 | import * as _ from "@fp-ts/core/typeclass/Monoid" 4 | import * as U from "../util" 5 | 6 | describe("Monoid", () => { 7 | it("min", () => { 8 | const M = _.min(N.Bounded) 9 | U.deepStrictEqual(M.combineAll([]), +Infinity) 10 | U.deepStrictEqual(M.combineAll([1]), 1) 11 | U.deepStrictEqual(M.combineAll([1, -1]), -1) 12 | }) 13 | 14 | it("max", () => { 15 | const M = _.max(N.Bounded) 16 | U.deepStrictEqual(M.combineAll([]), -Infinity) 17 | U.deepStrictEqual(M.combineAll([1]), 1) 18 | U.deepStrictEqual(M.combineAll([1, -1]), 1) 19 | }) 20 | 21 | it("reverse", () => { 22 | const M = _.reverse(String.Monoid) 23 | U.deepStrictEqual(M.combine("a", "b"), "ba") 24 | U.deepStrictEqual(M.combine("a", M.empty), "a") 25 | U.deepStrictEqual(M.combine(M.empty, "a"), "a") 26 | U.deepStrictEqual(M.combineMany("a", []), "a") 27 | U.deepStrictEqual(M.combineMany("a", ["b", "c", "d"]), "dcba") 28 | U.deepStrictEqual(M.combineMany("a", [M.empty]), "a") 29 | U.deepStrictEqual(M.combineMany(M.empty, ["a"]), "a") 30 | }) 31 | 32 | describe("struct", () => { 33 | it("baseline", () => { 34 | const M = _.struct({ 35 | name: String.Monoid, 36 | age: N.MonoidSum 37 | }) 38 | U.deepStrictEqual(M.empty, { name: "", age: 0 }) 39 | U.deepStrictEqual(M.combine({ name: "a", age: 10 }, { name: "b", age: 20 }), { 40 | name: "ab", 41 | age: 30 42 | }) 43 | }) 44 | 45 | it("should ignore non own properties", () => { 46 | const monoids = Object.create({ a: 1 }) 47 | const M = _.struct(monoids) 48 | U.deepStrictEqual(M.empty, {}) 49 | }) 50 | }) 51 | 52 | it("tuple", () => { 53 | const M = _.tuple( 54 | String.Monoid, 55 | N.MonoidSum 56 | ) 57 | U.deepStrictEqual(M.empty, ["", 0]) 58 | U.deepStrictEqual(M.combine(["a", 10], ["b", 20]), ["ab", 30]) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /test/typeclass/SemiApplicative.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as String from "@fp-ts/core/String" 4 | import * as _ from "@fp-ts/core/typeclass/SemiApplicative" 5 | import * as U from "../util" 6 | 7 | describe("SemiApplicative", () => { 8 | it("ap", () => { 9 | const ap = _.ap(O.SemiApplicative) 10 | const double = (n: number) => n * 2 11 | U.deepStrictEqual(pipe(O.none(), ap(O.none())), O.none()) 12 | U.deepStrictEqual(pipe(O.none(), ap(O.some(1))), O.none()) 13 | U.deepStrictEqual(pipe(O.some(double), ap(O.none())), O.none()) 14 | U.deepStrictEqual(pipe(O.some(double), ap(O.some(1))), O.some(2)) 15 | }) 16 | 17 | it("andThenDiscard", () => { 18 | const andThenDiscard = _.andThenDiscard(O.SemiApplicative) 19 | U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.none())), O.none()) 20 | U.deepStrictEqual(pipe(O.none(), andThenDiscard(O.some(2))), O.none()) 21 | U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.none())), O.none()) 22 | U.deepStrictEqual(pipe(O.some(1), andThenDiscard(O.some(2))), O.some(1)) 23 | }) 24 | 25 | it("andThen", () => { 26 | const andThen = _.andThen(O.SemiApplicative) 27 | U.deepStrictEqual(pipe(O.none(), andThen(O.none())), O.none()) 28 | U.deepStrictEqual(pipe(O.none(), andThen(O.some(2))), O.none()) 29 | U.deepStrictEqual(pipe(O.some(1), andThen(O.none())), O.none()) 30 | U.deepStrictEqual(pipe(O.some(1), andThen(O.some(2))), O.some(2)) 31 | }) 32 | 33 | it("liftSemigroup", () => { 34 | const liftSemigroup = _.getSemigroup(O.SemiApplicative) 35 | const S = liftSemigroup(String.Semigroup) 36 | U.deepStrictEqual(S.combine(O.none(), O.none()), O.none()) 37 | U.deepStrictEqual(S.combine(O.none(), O.some("b")), O.none()) 38 | U.deepStrictEqual(S.combine(O.some("a"), O.none()), O.none()) 39 | U.deepStrictEqual(S.combine(O.some("a"), O.some("b")), O.some("ab")) 40 | 41 | U.deepStrictEqual(S.combineMany(O.some("a"), [O.some("b"), O.some("c")]), O.some("abc")) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /src/typeclass/Bicovariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual, identity } from "@fp-ts/core/Function" 5 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Bicovariant extends TypeClass { 13 | readonly bimap: { 14 | ( 15 | f: (e: E1) => E2, 16 | g: (a: A) => B 17 | ): (self: Kind) => Kind 18 | ( 19 | self: Kind, 20 | f: (e: E1) => E2, 21 | g: (a: A) => B 22 | ): Kind 23 | } 24 | } 25 | 26 | /** 27 | * Returns a default ternary `bimap` composition. 28 | * 29 | * @since 1.0.0 30 | */ 31 | export const bimapComposition = ( 32 | CovariantF: Covariant, 33 | BicovariantG: Bicovariant 34 | ) => 35 | ( 36 | self: Kind>, 37 | f: (e: E1) => E2, 38 | g: (a: A) => B 39 | ): Kind> => CovariantF.map(self, BicovariantG.bimap(f, g)) 40 | 41 | /** 42 | * Returns a default `mapLeft` implementation. 43 | * 44 | * @since 1.0.0 45 | */ 46 | export const mapLeft = ( 47 | F: Bicovariant 48 | ): { 49 | (f: (e: E) => G): (self: Kind) => Kind 50 | (self: Kind, f: (e: E) => G): Kind 51 | } => 52 | dual( 53 | 2, 54 | (self: Kind, f: (e: E) => G): Kind => 55 | F.bimap(self, f, identity) 56 | ) 57 | 58 | /** 59 | * Returns a default `map` implementation. 60 | * 61 | * @since 1.0.0 62 | */ 63 | export const map = ( 64 | F: Bicovariant 65 | ): Covariant["map"] => 66 | dual( 67 | 2, 68 | (self: Kind, f: (a: A) => B): Kind => 69 | F.bimap(self, identity, f) 70 | ) 71 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Bicovariant.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Bicovariant.ts 3 | nav_order: 21 4 | parent: Modules 5 | --- 6 | 7 | ## Bicovariant overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Bicovariant (interface)](#bicovariant-interface) 17 | - [utils](#utils) 18 | - [bimapComposition](#bimapcomposition) 19 | - [map](#map) 20 | - [mapLeft](#mapleft) 21 | 22 | --- 23 | 24 | # type class 25 | 26 | ## Bicovariant (interface) 27 | 28 | **Signature** 29 | 30 | ```ts 31 | export interface Bicovariant extends TypeClass { 32 | readonly bimap: { 33 | (f: (e: E1) => E2, g: (a: A) => B): (self: Kind) => Kind 34 | (self: Kind, f: (e: E1) => E2, g: (a: A) => B): Kind 35 | } 36 | } 37 | ``` 38 | 39 | Added in v1.0.0 40 | 41 | # utils 42 | 43 | ## bimapComposition 44 | 45 | Returns a default ternary `bimap` composition. 46 | 47 | **Signature** 48 | 49 | ```ts 50 | export declare const bimapComposition: ( 51 | CovariantF: Covariant, 52 | BicovariantG: Bicovariant 53 | ) => ( 54 | self: Kind>, 55 | f: (e: E1) => E2, 56 | g: (a: A) => B 57 | ) => Kind> 58 | ``` 59 | 60 | Added in v1.0.0 61 | 62 | ## map 63 | 64 | Returns a default `map` implementation. 65 | 66 | **Signature** 67 | 68 | ```ts 69 | export declare const map: ( 70 | F: Bicovariant 71 | ) => { 72 | (f: (a: A) => B): (self: Kind) => Kind 73 | (self: Kind, f: (a: A) => B): Kind 74 | } 75 | ``` 76 | 77 | Added in v1.0.0 78 | 79 | ## mapLeft 80 | 81 | Returns a default `mapLeft` implementation. 82 | 83 | **Signature** 84 | 85 | ```ts 86 | export declare const mapLeft: ( 87 | F: Bicovariant 88 | ) => { 89 | (f: (e: E) => G): (self: Kind) => Kind 90 | (self: Kind, f: (e: E) => G): Kind 91 | } 92 | ``` 93 | 94 | Added in v1.0.0 95 | -------------------------------------------------------------------------------- /test/typeclass/Foldable.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as N from "@fp-ts/core/Number" 3 | import * as O from "@fp-ts/core/Option" 4 | import * as RA from "@fp-ts/core/ReadonlyArray" 5 | import * as _ from "@fp-ts/core/typeclass/Foldable" 6 | import * as U from "../util" 7 | 8 | describe("Foldable", () => { 9 | it("reduceComposition", () => { 10 | const reduce = _.reduceComposition(RA.Foldable, RA.Foldable) 11 | const f = (b: string, a: string) => b + a 12 | U.deepStrictEqual(reduce([], "-", f), "-") 13 | U.deepStrictEqual(reduce([[]], "-", f), "-") 14 | U.deepStrictEqual(reduce([["a", "c"], ["b", "d"]], "-", f), "-acbd") 15 | }) 16 | 17 | it("toArray", () => { 18 | const toArray = _.toArray(O.Foldable) 19 | U.deepStrictEqual(toArray(O.none()), []) 20 | U.deepStrictEqual(toArray(O.some(2)), [2]) 21 | }) 22 | 23 | it("toArrayMap", () => { 24 | const toArrayMap = _.toArrayMap(O.Foldable) 25 | U.deepStrictEqual(toArrayMap(O.none(), U.double), []) 26 | U.deepStrictEqual(toArrayMap(O.some(2), U.double), [4]) 27 | }) 28 | 29 | it("combineMap", () => { 30 | const combineMap = _.combineMap(RA.Foldable) 31 | U.deepStrictEqual(combineMap(N.MonoidSum)([1, 2, 3], U.double), 12) 32 | }) 33 | 34 | it("reduceKind", () => { 35 | const reduceKind = _.reduceKind(RA.Foldable)(O.Monad) 36 | U.deepStrictEqual(reduceKind([], "-", () => O.none()), O.some("-")) 37 | U.deepStrictEqual(reduceKind(["a"], "-", () => O.none()), O.none()) 38 | U.deepStrictEqual( 39 | reduceKind(["a", "b", "c"], "-", (b, a) => O.some(b + a)), 40 | O.some("-abc") 41 | ) 42 | U.deepStrictEqual( 43 | reduceKind(["a", "b", "c"], "-", (b, a) => a === "b" ? O.none() : O.some(b + a)), 44 | O.none() 45 | ) 46 | }) 47 | 48 | it("coproductMapKind", () => { 49 | const coproductMapKind = _.coproductMapKind(RA.Foldable)(O.Alternative) 50 | U.deepStrictEqual(pipe([], coproductMapKind(() => O.none())), O.none()) 51 | U.deepStrictEqual(pipe(["a"], coproductMapKind(() => O.none())), O.none()) 52 | U.deepStrictEqual(pipe(["a", "b", "c"], coproductMapKind((a) => O.some(a))), O.some("a")) 53 | U.deepStrictEqual( 54 | pipe(["a", "b", "c"], coproductMapKind((a) => a === "b" ? O.none() : O.some(a))), 55 | O.some("a") 56 | ) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Bounded.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Bounded.ts 3 | nav_order: 22 4 | parent: Modules 5 | --- 6 | 7 | ## Bounded overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [constructors](#constructors) 16 | - [max](#max) 17 | - [min](#min) 18 | - [instances](#instances) 19 | - [number](#number) 20 | - [type class](#type-class) 21 | - [Bounded (interface)](#bounded-interface) 22 | - [type lambdas](#type-lambdas) 23 | - [BoundedTypeLambda (interface)](#boundedtypelambda-interface) 24 | - [utils](#utils) 25 | - [clamp](#clamp) 26 | - [reverse](#reverse) 27 | 28 | --- 29 | 30 | # constructors 31 | 32 | ## max 33 | 34 | `Monoid` that returns last maximum of elements. 35 | 36 | **Signature** 37 | 38 | ```ts 39 | export declare const max:
(B: Bounded) => Monoid 40 | ``` 41 | 42 | Added in v1.0.0 43 | 44 | ## min 45 | 46 | `Monoid` that returns last minimum of elements. 47 | 48 | **Signature** 49 | 50 | ```ts 51 | export declare const min: (B: Bounded) => Monoid 52 | ``` 53 | 54 | Added in v1.0.0 55 | 56 | # instances 57 | 58 | ## number 59 | 60 | **Signature** 61 | 62 | ```ts 63 | export declare const number: Bounded 64 | ``` 65 | 66 | Added in v1.0.0 67 | 68 | # type class 69 | 70 | ## Bounded (interface) 71 | 72 | **Signature** 73 | 74 | ```ts 75 | export interface Bounded extends Order { 76 | readonly maxBound: A 77 | readonly minBound: A 78 | } 79 | ``` 80 | 81 | Added in v1.0.0 82 | 83 | # type lambdas 84 | 85 | ## BoundedTypeLambda (interface) 86 | 87 | **Signature** 88 | 89 | ```ts 90 | export interface BoundedTypeLambda extends TypeLambda { 91 | readonly type: Bounded 92 | } 93 | ``` 94 | 95 | Added in v1.0.0 96 | 97 | # utils 98 | 99 | ## clamp 100 | 101 | Clamp a value between `minBound` and `maxBound` values. 102 | 103 | **Signature** 104 | 105 | ```ts 106 | export declare const clamp: (B: Bounded) => (a: A) => A 107 | ``` 108 | 109 | Added in v1.0.0 110 | 111 | ## reverse 112 | 113 | Reverses the `Order` of a `Bounded` and flips `maxBound` and `minBound` values. 114 | 115 | **Signature** 116 | 117 | ```ts 118 | export declare const reverse: (B: Bounded) => Bounded 119 | ``` 120 | 121 | Added in v1.0.0 122 | -------------------------------------------------------------------------------- /src/typeclass/FlatMap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual, identity } from "@fp-ts/core/Function" 5 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 6 | 7 | /** 8 | * @category type class 9 | * @since 1.0.0 10 | */ 11 | export interface FlatMap extends TypeClass { 12 | readonly flatMap: { 13 | ( 14 | f: (a: A) => Kind 15 | ): (self: Kind) => Kind 16 | ( 17 | self: Kind, 18 | f: (a: A) => Kind 19 | ): Kind 20 | } 21 | } 22 | 23 | /** 24 | * @since 1.0.0 25 | */ 26 | export const flatten = (F: FlatMap) => 27 | ( 28 | self: Kind> 29 | ): Kind => F.flatMap(self, identity) 30 | 31 | /** 32 | * A variant of `flatMap` that ignores the value produced by this effect. 33 | * 34 | * @since 1.0.0 35 | */ 36 | export const andThen = (F: FlatMap): { 37 | ( 38 | that: Kind 39 | ): (self: Kind) => Kind 40 | ( 41 | self: Kind, 42 | that: Kind 43 | ): Kind 44 | } => 45 | dual(2, ( 46 | self: Kind, 47 | that: Kind 48 | ): Kind => F.flatMap(self, () => that)) 49 | 50 | /** 51 | * @since 1.0.0 52 | */ 53 | export const composeKleisliArrow = ( 54 | F: FlatMap 55 | ): { 56 | ( 57 | bfc: (b: B) => Kind 58 | ): ( 59 | afb: (a: A) => Kind 60 | ) => (a: A) => Kind 61 | ( 62 | afb: (a: A) => Kind, 63 | bfc: (b: B) => Kind 64 | ): (a: A) => Kind 65 | } => 66 | dual( 67 | 2, 68 | ( 69 | afb: (a: A) => Kind, 70 | bfc: (b: B) => Kind 71 | ): ((a: A) => Kind) => a => F.flatMap(afb(a), bfc) 72 | ) 73 | -------------------------------------------------------------------------------- /test/Identity.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as _ from "@fp-ts/core/Identity" 3 | import * as O from "@fp-ts/core/Option" 4 | import * as String from "@fp-ts/core/String" 5 | import * as U from "./util" 6 | 7 | describe.concurrent("Identity", () => { 8 | it("instances and derived exports", () => { 9 | expect(_.Invariant).exist 10 | expect(_.Covariant).exist 11 | expect(_.Of).exist 12 | expect(_.Pointed).exist 13 | expect(_.FlatMap).exist 14 | expect(_.Chainable).exist 15 | expect(_.Monad).exist 16 | expect(_.SemiProduct).exist 17 | expect(_.Product).exist 18 | expect(_.SemiApplicative).exist 19 | expect(_.Applicative).exist 20 | expect(_.Foldable).exist 21 | expect(_.Traversable).exist 22 | 23 | expect(_.bindTo).exist 24 | expect(_.let).exist 25 | expect(_.Do).exist 26 | expect(_.bind).exist 27 | }) 28 | 29 | it("Of", () => { 30 | U.deepStrictEqual(_.Of.of("a"), "a") 31 | }) 32 | 33 | it("SemiProduct", () => { 34 | U.deepStrictEqual(_.SemiProduct.productMany("a", ["b", "c"]), ["a", "b", "c"]) 35 | }) 36 | 37 | it("Product", () => { 38 | U.deepStrictEqual(_.Product.productAll([]), []) 39 | U.deepStrictEqual(_.Product.productAll(["a", "b", "c"]), ["a", "b", "c"]) 40 | }) 41 | 42 | it("Covariant", () => { 43 | assert.deepStrictEqual(_.Covariant.map(1, n => n * 2), 2) 44 | }) 45 | 46 | it("FlatMap", () => { 47 | U.deepStrictEqual( 48 | pipe("a", _.FlatMap.flatMap((a) => a + "b")), 49 | "ab" 50 | ) 51 | }) 52 | 53 | it("SemiProduct", () => { 54 | const product = _.SemiProduct.product 55 | U.deepStrictEqual(product("a", "b"), ["a", "b"]) 56 | }) 57 | 58 | it("getSemiCoproduct", () => { 59 | const F = _.getSemiCoproduct(String.Semigroup) 60 | U.deepStrictEqual(F.coproduct("a", "b"), "ab") 61 | U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") 62 | }) 63 | 64 | it("getSemiAlternative", () => { 65 | const F = _.getSemiAlternative(String.Semigroup) 66 | U.deepStrictEqual(F.coproduct("a", "b"), "ab") 67 | U.deepStrictEqual(F.coproductMany("a", ["b", "c"]), "abc") 68 | }) 69 | 70 | it("Foldable", () => { 71 | U.deepStrictEqual(pipe("b", _.Foldable.reduce("a", (b, a) => b + a)), "ab") 72 | }) 73 | 74 | it("Traversable", () => { 75 | U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(O.some)), O.some(1)) 76 | U.deepStrictEqual(pipe(1, _.Traversable.traverse(O.Applicative)(() => O.none())), O.none()) 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /guides/FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## HKT implementation 4 | 5 | **What’s that second branch of conditional type in `Kind` type?** 6 | 7 | That's to enforce variance. 8 | 9 | For example let's say we define the following typeclass 10 | 11 | ```ts 12 | import { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT"; 13 | 14 | export interface Zippable extends TypeClass { 15 | readonly zip: ( 16 | first: Kind, 17 | second: Kind 18 | ) => Kind; 19 | } 20 | ``` 21 | 22 | and then derive a `pipe`-able version of `zip` 23 | 24 | ```ts 25 | export const zip = 26 | (Zippable: Zippable) => 27 | (that: Kind) => 28 | ( 29 | self: Kind 30 | ): Kind => 31 | Zippable.zip(self, that); 32 | ``` 33 | 34 | Now let's say we make a mistake while typing the return type of `zip` (`R1` instead of `R1 & R2`) 35 | 36 | ```diff 37 | - ): Kind => 38 | + ): Kind => 39 | ``` 40 | 41 | It doesn't type check with the following error 42 | 43 | ``` 44 | Type 'Kind' is not assignable to type 'Kind'. 45 | Type '(F & { readonly InOut1: S; readonly In1: R1 & R2; readonly Out3: O2 | O1; readonly Out2: E2 | E1; readonly Out1: readonly [A, B]; })["type"] | { readonly F: F; ... 4 more ...; readonly Out1: () => readonly [...]; }' is not assignable to type 'Kind'. 46 | Type 'F["type"]' is not assignable to type 'Kind'. 47 | Type '{ readonly F: F; readonly InOut1: (_: S) => S; readonly In1: (_: R1 & R2) => void; readonly Out3: () => O2 | O1; readonly Out2: () => E2 | E1; readonly Out1: () => readonly [A, B]; }' is not assignable to type '{ readonly F: F; readonly InOut1: (_: S) => S; readonly In1: (_: R1) => void; readonly Out3: () => O2 | O1; readonly Out2: () => E2 | E1; readonly Out1: () => readonly [A, B]; }'. 48 | Types of property 'In1' are incompatible. 49 | Type '(_: R1 & R2) => void' is not assignable to type '(_: R1) => void'. 50 | Types of parameters '_' and '_' are incompatible. 51 | Type 'R1' is not assignable to type 'R1 & R2'. 52 | ``` 53 | -------------------------------------------------------------------------------- /src/typeclass/Invariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The `Invariant` typeclass is a higher-order abstraction over types that allow mapping the contents of a type in both directions. 3 | * It is similar to the `Covariant` typeclass but provides an `imap` opration, which allows transforming a value in both directions. 4 | * This typeclass is useful when dealing with data types that can be converted to and from some other types. 5 | * The `imap` operation provides a way to convert such data types to other types that they can interact with while preserving their invariants. 6 | * 7 | * @since 1.0.0 8 | */ 9 | import { dual } from "@fp-ts/core/Function" 10 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 11 | 12 | /** 13 | * @category type class 14 | * @since 1.0.0 15 | */ 16 | export interface Invariant extends TypeClass { 17 | readonly imap: { 18 | ( 19 | to: (a: A) => B, 20 | from: (b: B) => A 21 | ): (self: Kind) => Kind 22 | ( 23 | self: Kind, 24 | to: (a: A) => B, 25 | from: (b: B) => A 26 | ): Kind 27 | } 28 | } 29 | 30 | /** 31 | * Returns a default ternary `imap` composition. 32 | * 33 | * @since 1.0.0 34 | */ 35 | export const imapComposition = ( 36 | F: Invariant, 37 | G: Invariant 38 | ) => 39 | ( 40 | self: Kind>, 41 | to: (a: A) => B, 42 | from: (b: B) => A 43 | ): Kind> => F.imap(self, G.imap(to, from), G.imap(from, to)) 44 | 45 | /** 46 | * @category do notation 47 | * @since 1.0.0 48 | */ 49 | export const bindTo = (F: Invariant): { 50 | ( 51 | name: N 52 | ): (self: Kind) => Kind 53 | ( 54 | self: Kind, 55 | name: N 56 | ): Kind 57 | } => 58 | dual(2, ( 59 | self: Kind, 60 | name: N 61 | ): Kind => 62 | F.imap(self, a => ({ [name]: a } as any), ({ [name]: a }) => a)) 63 | 64 | /** 65 | * Convert a value in a singleton array in a given effect. 66 | * 67 | * @since 1.0.0 68 | */ 69 | export const tupled = ( 70 | F: Invariant 71 | ): ((self: Kind) => Kind) => 72 | F.imap(a => [a], ([a]) => a) 73 | -------------------------------------------------------------------------------- /dtslint/ts4.8/Option.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from '@fp-ts/core/Function' 2 | import * as _ from '@fp-ts/core/Option' 3 | 4 | declare const n: number 5 | declare const sn: string | number 6 | declare const isString: (u: unknown) => u is string 7 | declare const predicate: (sn: string | number) => boolean 8 | declare const on: _.Option 9 | declare const osn: _.Option 10 | 11 | // ------------------------------------------------------------------------------------- 12 | // liftPredicate 13 | // ------------------------------------------------------------------------------------- 14 | 15 | // $ExpectType Option 16 | pipe(sn, _.liftPredicate(isString)) 17 | pipe( 18 | sn, 19 | _.liftPredicate( 20 | ( 21 | n // $ExpectType string | number 22 | ): n is number => typeof n === 'number' 23 | ) 24 | ) 25 | 26 | // $ExpectType Option 27 | pipe(sn, _.liftPredicate(predicate)) 28 | // $ExpectType Option 29 | pipe(n, _.liftPredicate(predicate)) 30 | // $ExpectType Option 31 | pipe( 32 | n, 33 | _.liftPredicate( 34 | ( 35 | _n // $ExpectType number 36 | ) => true 37 | ) 38 | ) 39 | 40 | // ------------------------------------------------------------------------------------- 41 | // getOrElse 42 | // ------------------------------------------------------------------------------------- 43 | 44 | // $ExpectType string | null 45 | pipe(_.some('a'), _.getOrElse(() => null)) 46 | 47 | // ------------------------------------------------------------------------------------- 48 | // do notation 49 | // ------------------------------------------------------------------------------------- 50 | 51 | // $ExpectType Option<{ a1: number; a2: string; }> 52 | pipe( 53 | _.Do, 54 | _.bind('a1', () => _.some(1)), 55 | _.bind('a2', () => _.some('b')) 56 | ) 57 | 58 | // ------------------------------------------------------------------------------------- 59 | // filter 60 | // ------------------------------------------------------------------------------------- 61 | 62 | // $ExpectType Option 63 | pipe(on, _.filter(predicate)) 64 | 65 | // $ExpectType Option 66 | _.filter(on, predicate) 67 | 68 | // $ExpectType Option 69 | pipe(osn, _.filter(isString)) 70 | 71 | // $ExpectType Option 72 | _.filter(osn, isString) 73 | 74 | // $ExpectType Option 75 | pipe( 76 | on, 77 | _.filter( 78 | ( 79 | x // $ExpectType number 80 | ): x is number => true 81 | ) 82 | ) 83 | 84 | // $ExpectType Option 85 | pipe( 86 | on, 87 | _.filter( 88 | ( 89 | _x // $ExpectType number 90 | ) => true 91 | ) 92 | ) 93 | -------------------------------------------------------------------------------- /test/typeclass/Filterable.ts: -------------------------------------------------------------------------------- 1 | import * as E from "@fp-ts/core/Either" 2 | import { pipe } from "@fp-ts/core/Function" 3 | import * as O from "@fp-ts/core/Option" 4 | import * as RA from "@fp-ts/core/ReadonlyArray" 5 | import * as _ from "@fp-ts/core/typeclass/Filterable" 6 | import * as U from "../util" 7 | 8 | describe("Filterable", () => { 9 | it("filterMapComposition", () => { 10 | const filterMap = _.filterMapComposition(RA.Covariant, O.Filterable) 11 | const f = (s: string) => s.length > 1 ? O.some(s.length) : O.none() 12 | U.deepStrictEqual(filterMap([], f), []) 13 | U.deepStrictEqual(filterMap([O.none()], f), [O.none()]) 14 | U.deepStrictEqual(filterMap([O.some("a")], f), [O.none()]) 15 | U.deepStrictEqual(filterMap([O.some("aa")], f), [O.some(2)]) 16 | }) 17 | 18 | it("partitionMapComposition", () => { 19 | const partitionMap = _.partitionMapComposition(RA.Covariant, O.Filterable) 20 | const f = (s: string) => s.length > 1 ? E.right(s.length) : E.left(s + "!") 21 | U.deepStrictEqual(partitionMap([], f), [[], []]) 22 | U.deepStrictEqual(partitionMap([O.none()], f), [[O.none()], [O.none()]]) 23 | U.deepStrictEqual(partitionMap([O.some("a")], f), [[O.some("a!")], [O.none()]]) 24 | U.deepStrictEqual(partitionMap([O.some("aa")], f), [[O.none()], [O.some(2)]]) 25 | }) 26 | 27 | it("filter", () => { 28 | const filter = _.filter(RA.Filterable) 29 | const f = filter((n: number) => n > 0) 30 | U.deepStrictEqual(pipe([], f), []) 31 | U.deepStrictEqual(pipe([1], f), [1]) 32 | U.deepStrictEqual(pipe([-1], f), []) 33 | U.deepStrictEqual(pipe([1, -1], f), [1]) 34 | }) 35 | 36 | it("partition", () => { 37 | const partition = _.partition(RA.Filterable) 38 | const f = partition((n: number) => n > 0) 39 | U.deepStrictEqual(pipe([], f), [[], []]) 40 | U.deepStrictEqual(pipe([1], f), [[], [1]]) 41 | U.deepStrictEqual(pipe([-1], f), [[-1], []]) 42 | U.deepStrictEqual(pipe([1, -1], f), [[-1], [1]]) 43 | }) 44 | 45 | it("compact", () => { 46 | const compact = _.compact(RA.Filterable) 47 | assert.deepStrictEqual(compact([]), []) 48 | assert.deepStrictEqual(compact([O.some(1), O.some(2), O.some(3)]), [ 49 | 1, 50 | 2, 51 | 3 52 | ]) 53 | assert.deepStrictEqual(compact([O.some(1), O.none(), O.some(3)]), [ 54 | 1, 55 | 3 56 | ]) 57 | }) 58 | 59 | it("separate", () => { 60 | const separate = _.separate(RA.Filterable) 61 | U.deepStrictEqual(pipe([], separate), [[], []]) 62 | U.deepStrictEqual(pipe([E.right(1), E.left("e"), E.right(2)], separate), [ 63 | ["e"], 64 | [1, 2] 65 | ]) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /patches/docs-ts@0.6.10.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/Core.js b/lib/Core.js 2 | index c0f282ca9e3ddd93e44a62e1d05c1cc45b9f5c6a..616f7f6223459e0621522279ea7fc1cf1868abe8 100644 3 | --- a/lib/Core.js 4 | +++ b/lib/Core.js 5 | @@ -167,7 +167,30 @@ var typeCheckExamples = function (modules) { 6 | return function_1.pipe(getExampleFiles(modules), RTE.chain(handleImports), RTE.chain(function (examples) { 7 | return examples.length === 0 8 | ? cleanExamples 9 | - : function_1.pipe(writeExamples(examples), RTE.chain(function () { return spawnTsNode; }), RTE.chain(function () { return cleanExamples; })); 10 | + : function_1.pipe(writeExamples(examples), RTE.chain(function () { return writeFile({"path": "./docs/examples/tsconfig.json", "content": JSON.stringify({ 11 | + "compilerOptions": { 12 | + "noEmit": true, 13 | + "strict": true, 14 | + "noImplicitAny": true, 15 | + "noImplicitThis": true, 16 | + "strictNullChecks": true, 17 | + "strictFunctionTypes": true, 18 | + "noImplicitReturns": false, 19 | + "noUnusedLocals": false, 20 | + "noUnusedParameters": false, 21 | + "noFallthroughCasesInSwitch": true, 22 | + "moduleResolution": "node", 23 | + "target": "ES2021", 24 | + "lib": ["ES2021"], 25 | + "paths": { 26 | + "@fp-ts/core": ["../../src/index.ts"], 27 | + "@fp-ts/core/test/*": ["../../test/*"], 28 | + "@fp-ts/core/examples/*": ["../../examples/*"], 29 | + "@fp-ts/core/*": ["../../src/*"] 30 | + } 31 | + } 32 | + } 33 | + )}); }), RTE.chain(function () { return spawnTsNode; }), RTE.chain(function () { return cleanExamples; })); 34 | })); 35 | }; 36 | // ------------------------------------------------------------------------------------- 37 | diff --git a/lib/index.js b/lib/index.js 38 | index 51bdb346c4c33e8835bbda0c16aea71bc0c3115e..7d084d363efd3f8aad76051f4253b0b59cd0c5cd 100644 39 | --- a/lib/index.js 40 | +++ b/lib/index.js 41 | @@ -33,7 +33,13 @@ exports.exit = TE.fold(onLeft, function () { return onRight; }); 42 | * @internal 43 | */ 44 | exports.compilerOptions = { 45 | - strict: true 46 | + strict: true, 47 | + paths: { 48 | + "@fp-ts/core": ["./src/index.ts"], 49 | + "@fp-ts/core/test/*": ["./test/*"], 50 | + "@fp-ts/core/examples/*": ["./examples/*"], 51 | + "@fp-ts/core/*": ["./src/*"] 52 | + } 53 | }; 54 | var capabilities = { 55 | example: Example_1.Example, 56 | -------------------------------------------------------------------------------- /docs/modules/typeclass/FlatMap.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/FlatMap.ts 3 | nav_order: 29 4 | parent: Modules 5 | --- 6 | 7 | ## FlatMap overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [FlatMap (interface)](#flatmap-interface) 17 | - [utils](#utils) 18 | - [andThen](#andthen) 19 | - [composeKleisliArrow](#composekleisliarrow) 20 | - [flatten](#flatten) 21 | 22 | --- 23 | 24 | # type class 25 | 26 | ## FlatMap (interface) 27 | 28 | **Signature** 29 | 30 | ```ts 31 | export interface FlatMap extends TypeClass { 32 | readonly flatMap: { 33 | (f: (a: A) => Kind): ( 34 | self: Kind 35 | ) => Kind 36 | (self: Kind, f: (a: A) => Kind): Kind< 37 | F, 38 | R1 & R2, 39 | O1 | O2, 40 | E1 | E2, 41 | B 42 | > 43 | } 44 | } 45 | ``` 46 | 47 | Added in v1.0.0 48 | 49 | # utils 50 | 51 | ## andThen 52 | 53 | A variant of `flatMap` that ignores the value produced by this effect. 54 | 55 | **Signature** 56 | 57 | ```ts 58 | export declare const andThen: ( 59 | F: FlatMap 60 | ) => { 61 | (that: Kind): ( 62 | self: Kind 63 | ) => Kind 64 | (self: Kind, that: Kind): Kind< 65 | F, 66 | R1 & R2, 67 | O1 | O2, 68 | E1 | E2, 69 | B 70 | > 71 | } 72 | ``` 73 | 74 | Added in v1.0.0 75 | 76 | ## composeKleisliArrow 77 | 78 | **Signature** 79 | 80 | ```ts 81 | export declare const composeKleisliArrow: ( 82 | F: FlatMap 83 | ) => { 84 | (bfc: (b: B) => Kind): ( 85 | afb: (a: A) => Kind 86 | ) => (a: A) => Kind 87 | (afb: (a: A) => Kind, bfc: (b: B) => Kind): ( 88 | a: A 89 | ) => Kind 90 | } 91 | ``` 92 | 93 | Added in v1.0.0 94 | 95 | ## flatten 96 | 97 | **Signature** 98 | 99 | ```ts 100 | export declare const flatten: ( 101 | F: FlatMap 102 | ) => ( 103 | self: Kind> 104 | ) => Kind 105 | ``` 106 | 107 | Added in v1.0.0 108 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Invariant.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Invariant.ts 3 | nav_order: 31 4 | parent: Modules 5 | --- 6 | 7 | ## Invariant overview 8 | 9 | The `Invariant` typeclass is a higher-order abstraction over types that allow mapping the contents of a type in both directions. 10 | It is similar to the `Covariant` typeclass but provides an `imap` opration, which allows transforming a value in both directions. 11 | This typeclass is useful when dealing with data types that can be converted to and from some other types. 12 | The `imap` operation provides a way to convert such data types to other types that they can interact with while preserving their invariants. 13 | 14 | Added in v1.0.0 15 | 16 | --- 17 | 18 |

Table of contents

19 | 20 | - [do notation](#do-notation) 21 | - [bindTo](#bindto) 22 | - [type class](#type-class) 23 | - [Invariant (interface)](#invariant-interface) 24 | - [utils](#utils) 25 | - [imapComposition](#imapcomposition) 26 | - [tupled](#tupled) 27 | 28 | --- 29 | 30 | # do notation 31 | 32 | ## bindTo 33 | 34 | **Signature** 35 | 36 | ```ts 37 | export declare const bindTo: ( 38 | F: Invariant 39 | ) => { 40 | (name: N): (self: Kind) => Kind 41 | (self: Kind, name: N): Kind 42 | } 43 | ``` 44 | 45 | Added in v1.0.0 46 | 47 | # type class 48 | 49 | ## Invariant (interface) 50 | 51 | **Signature** 52 | 53 | ```ts 54 | export interface Invariant extends TypeClass { 55 | readonly imap: { 56 | (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind 57 | (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind 58 | } 59 | } 60 | ``` 61 | 62 | Added in v1.0.0 63 | 64 | # utils 65 | 66 | ## imapComposition 67 | 68 | Returns a default ternary `imap` composition. 69 | 70 | **Signature** 71 | 72 | ```ts 73 | export declare const imapComposition: ( 74 | F: Invariant, 75 | G: Invariant 76 | ) => ( 77 | self: Kind>, 78 | to: (a: A) => B, 79 | from: (b: B) => A 80 | ) => Kind> 81 | ``` 82 | 83 | Added in v1.0.0 84 | 85 | ## tupled 86 | 87 | Convert a value in a singleton array in a given effect. 88 | 89 | **Signature** 90 | 91 | ```ts 92 | export declare const tupled: ( 93 | F: Invariant 94 | ) => (self: Kind) => Kind 95 | ``` 96 | 97 | Added in v1.0.0 98 | -------------------------------------------------------------------------------- /test/Bigint.ts: -------------------------------------------------------------------------------- 1 | import * as Bigint from "@fp-ts/core/Bigint" 2 | import { pipe } from "@fp-ts/core/Function" 3 | import { deepStrictEqual } from "@fp-ts/core/test/util" 4 | 5 | describe.concurrent("Bigint", () => { 6 | it("exports", () => { 7 | expect(Bigint.SemigroupMax).exists 8 | expect(Bigint.SemigroupMin).exists 9 | expect(Bigint.sumAll).exists 10 | expect(Bigint.multiplyAll).exists 11 | expect(Bigint.lessThan).exists 12 | expect(Bigint.lessThanOrEqualTo).exists 13 | expect(Bigint.greaterThan).exists 14 | expect(Bigint.greaterThanOrEqualTo).exists 15 | expect(Bigint.between).exists 16 | expect(Bigint.clamp).exists 17 | expect(Bigint.min).exists 18 | expect(Bigint.max).exists 19 | }) 20 | 21 | it("sign", () => { 22 | assert.deepStrictEqual(Bigint.sign(-5n), -1) 23 | assert.deepStrictEqual(Bigint.sign(0n), 0) 24 | assert.deepStrictEqual(Bigint.sign(5n), 1) 25 | }) 26 | 27 | it("isBigint", () => { 28 | expect(Bigint.isBigint(1n)).toEqual(true) 29 | expect(Bigint.isBigint(1)).toEqual(false) 30 | expect(Bigint.isBigint("a")).toEqual(false) 31 | expect(Bigint.isBigint(true)).toEqual(false) 32 | }) 33 | 34 | it("sum", () => { 35 | deepStrictEqual(pipe(1n, Bigint.sum(2n)), 3n) 36 | }) 37 | 38 | it("multiply", () => { 39 | deepStrictEqual(pipe(2n, Bigint.multiply(3n)), 6n) 40 | }) 41 | 42 | it("subtract", () => { 43 | deepStrictEqual(pipe(3n, Bigint.subtract(1n)), 2n) 44 | }) 45 | 46 | it("divide", () => { 47 | deepStrictEqual(pipe(6n, Bigint.divide(2n)), 3n) 48 | }) 49 | 50 | it("increment", () => { 51 | deepStrictEqual(Bigint.increment(2n), 3n) 52 | }) 53 | 54 | it("decrement", () => { 55 | deepStrictEqual(Bigint.decrement(2n), 1n) 56 | }) 57 | 58 | it("Equivalence", () => { 59 | expect(Bigint.Equivalence(1n, 1n)).toBe(true) 60 | expect(Bigint.Equivalence(1n, 2n)).toBe(false) 61 | }) 62 | 63 | it("Order", () => { 64 | deepStrictEqual(Bigint.Order.compare(1n, 2n), -1) 65 | deepStrictEqual(Bigint.Order.compare(2n, 1n), 1) 66 | deepStrictEqual(Bigint.Order.compare(2n, 2n), 0) 67 | }) 68 | 69 | it("SemigroupSum", () => { 70 | deepStrictEqual(Bigint.SemigroupSum.combine(2n, 3n), 5n) 71 | }) 72 | 73 | it("MonoidSum", () => { 74 | deepStrictEqual(Bigint.MonoidSum.combineAll([1n, 2n, 3n]), 6n) 75 | }) 76 | 77 | it("SemigroupMultiply", () => { 78 | deepStrictEqual(Bigint.SemigroupMultiply.combine(2n, 3n), 6n) 79 | deepStrictEqual(Bigint.SemigroupMultiply.combineMany(0n, [1n, 2n, 3n]), 0n) 80 | deepStrictEqual(Bigint.SemigroupMultiply.combineMany(2n, [1n, 0n, 3n]), 0n) 81 | }) 82 | 83 | it("MonoidMultiply", () => { 84 | deepStrictEqual(Bigint.MonoidMultiply.combineAll([2n, 3n, 4n]), 24n) 85 | }) 86 | }) 87 | -------------------------------------------------------------------------------- /src/typeclass/Traversable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual, identity } from "@fp-ts/core/Function" 5 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Applicative } from "@fp-ts/core/typeclass/Applicative" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Traversable extends TypeClass { 13 | readonly traverse: ( 14 | F: Applicative 15 | ) => { 16 | ( 17 | f: (a: A) => Kind 18 | ): (self: Kind) => Kind> 19 | ( 20 | self: Kind, 21 | f: (a: A) => Kind 22 | ): Kind> 23 | } 24 | } 25 | 26 | /** 27 | * Returns a default binary `traverse` composition. 28 | * 29 | * @since 1.0.0 30 | */ 31 | export const traverseComposition = ( 32 | T: Traversable, 33 | G: Traversable 34 | ) => 35 | (F: Applicative) => 36 | ( 37 | self: Kind>, 38 | f: (a: A) => Kind 39 | ): Kind>> => 40 | T.traverse(F)(self, G.traverse(F)(f)) 41 | 42 | /** 43 | * Returns a default `sequence` implementation. 44 | * 45 | * @since 1.0.0 46 | */ 47 | export const sequence = (T: Traversable) => 48 | (F: Applicative) => 49 | ( 50 | self: Kind> 51 | ): Kind> => T.traverse(F)(self, identity) 52 | 53 | /** 54 | * Given a function which returns a `F` effect, thread this effect 55 | * through the running of this function on all the values in `T`, 56 | * returning an `T
` in a `F` context, ignoring the values 57 | * returned by the provided function. 58 | * 59 | * @since 1.0.0 60 | */ 61 | export const traverseTap = (T: Traversable) => 62 | (F: Applicative): { 63 | ( 64 | f: (a: A) => Kind 65 | ): (self: Kind) => Kind> 66 | ( 67 | self: Kind, 68 | f: (a: A) => Kind 69 | ): Kind> 70 | } => 71 | dual(2, ( 72 | self: Kind, 73 | f: (a: A) => Kind 74 | ): Kind> => T.traverse(F)(self, a => F.map(f(a), () => a))) 75 | -------------------------------------------------------------------------------- /test/typeclass/Product.ts: -------------------------------------------------------------------------------- 1 | import * as Boolean from "@fp-ts/core/Boolean" 2 | import * as Number from "@fp-ts/core/Number" 3 | import * as O from "@fp-ts/core/Option" 4 | import * as P from "@fp-ts/core/Predicate" 5 | import * as String from "@fp-ts/core/String" 6 | import * as _ from "@fp-ts/core/typeclass/Product" 7 | import * as semigroup from "@fp-ts/core/typeclass/Semigroup" 8 | import * as U from "../util" 9 | 10 | describe("Product", () => { 11 | describe("tuple", () => { 12 | it("Covariant (Option)", () => { 13 | const tuple = _.tuple(O.Product) 14 | U.deepStrictEqual(tuple(), O.some([])) 15 | U.deepStrictEqual(tuple(O.some("a")), O.some(["a"])) 16 | U.deepStrictEqual( 17 | tuple(O.some("a"), O.some(1), O.some(true)), 18 | O.some(["a", 1, true]) 19 | ) 20 | U.deepStrictEqual(tuple(O.some("a"), O.some(1), O.none()), O.none()) 21 | }) 22 | 23 | it("Invariant (Semigroup)", () => { 24 | const tuple = _.tuple(semigroup.Product) 25 | U.deepStrictEqual(tuple().combine([], []), []) 26 | const S = tuple(String.Semigroup, Number.SemigroupSum) 27 | U.deepStrictEqual(S.combine(["a", 2], ["b", 3]), ["ab", 5]) 28 | }) 29 | 30 | it("Contravariant (Predicate)", () => { 31 | const tuple = _.tuple(P.Product) 32 | U.deepStrictEqual(tuple()([]), true) 33 | const p = tuple(String.isString, Number.isNumber, Boolean.isBoolean) 34 | U.deepStrictEqual(p(["a", 1, true]), true) 35 | U.deepStrictEqual(p(["a", 1, "b"]), false) 36 | }) 37 | }) 38 | 39 | describe("struct", () => { 40 | it("Covariant (Option)", () => { 41 | const struct = _.struct(O.Product) 42 | U.deepStrictEqual(struct({}), O.some({})) 43 | U.deepStrictEqual(struct({ a: O.some("a") }), O.some({ a: "a" })) 44 | U.deepStrictEqual( 45 | struct({ a: O.some("a"), b: O.some(1), c: O.some(true) }), 46 | O.some({ a: "a", b: 1, c: true }) 47 | ) 48 | U.deepStrictEqual( 49 | struct({ a: O.some("a"), b: O.some(1), c: O.none() }), 50 | O.none() 51 | ) 52 | }) 53 | 54 | it("Invariant (Semigroup)", () => { 55 | const struct = _.struct(semigroup.Product) 56 | U.deepStrictEqual(struct({}).combine({}, {}), {}) 57 | const S = struct({ x: String.Semigroup, y: Number.SemigroupSum }) 58 | U.deepStrictEqual(S.combine({ x: "a", y: 2 }, { x: "b", y: 3 }), { x: "ab", y: 5 }) 59 | }) 60 | 61 | it("Contravariant (Predicate)", () => { 62 | const struct = _.struct(P.Product) 63 | U.deepStrictEqual(struct({})({}), true) 64 | const p = struct({ x: String.isString, y: Number.isNumber, z: Boolean.isBoolean }) 65 | U.deepStrictEqual(p({ x: "a", y: 1, z: true }), true) 66 | U.deepStrictEqual(p({ x: "a", y: 1, z: "b" }), false) 67 | }) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /test/limbo/NonEmptyTraversable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * NonEmptyTraversable describes a parameterized type T that contains one or more values of type `A`. 3 | * 4 | * @since 1.0.0 5 | */ 6 | import { identity, pipe } from "@fp-ts/core/Function" 7 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 8 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 9 | import type { SemiApplicative } from "@fp-ts/core/typeclass/SemiApplicative" 10 | 11 | /** 12 | * @category type class 13 | * @since 1.0.0 14 | */ 15 | export interface NonEmptyTraversable extends TypeClass { 16 | readonly traverseNonEmpty: ( 17 | F: SemiApplicative 18 | ) => ( 19 | f: (a: A) => Kind 20 | ) => ( 21 | self: Kind 22 | ) => Kind> 23 | 24 | readonly sequenceNonEmpty: ( 25 | F: SemiApplicative 26 | ) => ( 27 | self: Kind> 28 | ) => Kind> 29 | } 30 | 31 | /** 32 | * Returns a default `traverseNonEmpty` composition. 33 | * 34 | * @since 1.0.0 35 | */ 36 | export const traverseNonEmptyComposition = ( 37 | T: NonEmptyTraversable, 38 | G: NonEmptyTraversable 39 | ) => 40 | (F: SemiApplicative) => 41 | ( 42 | f: (a: A) => Kind 43 | ): (( 44 | self: Kind> 45 | ) => Kind>>) => 46 | T.traverseNonEmpty(F)(G.traverseNonEmpty(F)(f)) 47 | 48 | /** 49 | * Returns a default `sequenceNonEmpty` composition. 50 | * 51 | * @since 1.0.0 52 | */ 53 | export const sequenceNonEmptyComposition = ( 54 | T: NonEmptyTraversable & Covariant, 55 | G: NonEmptyTraversable 56 | ) => 57 | (F: SemiApplicative) => 58 | ( 59 | self: Kind>> 60 | ): Kind>> => 61 | T.sequenceNonEmpty(F)(pipe(self, T.map(G.sequenceNonEmpty(F)))) 62 | 63 | /** 64 | * Returns a default `sequenceNonEmpty` implementation. 65 | * 66 | * @since 1.0.0 67 | */ 68 | export const sequenceNonEmpty = ( 69 | traverseNonEmpty: NonEmptyTraversable["traverseNonEmpty"] 70 | ): NonEmptyTraversable["sequenceNonEmpty"] => 71 | ( 72 | F: SemiApplicative 73 | ): (( 74 | self: Kind> 75 | ) => Kind>) => traverseNonEmpty(F)(identity) 76 | -------------------------------------------------------------------------------- /dtslint/ts4.8/ReadonlyArray.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from '@fp-ts/core/Function' 2 | import * as RA from '@fp-ts/core/ReadonlyArray' 3 | import * as O from '@fp-ts/core/Option' 4 | 5 | declare const neras: RA.NonEmptyReadonlyArray 6 | declare const neas: RA.NonEmptyArray 7 | declare const ras: ReadonlyArray 8 | declare const as: Array 9 | 10 | // ------------------------------------------------------------------------------------- 11 | // isEmpty 12 | // ------------------------------------------------------------------------------------- 13 | 14 | if (RA.isEmpty(ras)) { 15 | // $ExpectType readonly [] 16 | ras 17 | } 18 | 19 | // $ExpectType (c: readonly A[]) => Option 20 | O.liftPredicate(RA.isEmpty) 21 | 22 | // ------------------------------------------------------------------------------------- 23 | // isEmptyArray 24 | // ------------------------------------------------------------------------------------- 25 | 26 | if (RA.isEmptyArray(as)) { 27 | // $ExpectType [] 28 | as 29 | } 30 | 31 | // $ExpectType (c: A[]) => Option<[]> 32 | O.liftPredicate(RA.isEmptyArray) 33 | 34 | // ------------------------------------------------------------------------------------- 35 | // isNonEmpty 36 | // ------------------------------------------------------------------------------------- 37 | 38 | if (RA.isNonEmpty(ras)) { 39 | // $ExpectType readonly [number, ...number[]] 40 | ras 41 | } 42 | 43 | // $ExpectType (c: readonly A[]) => Option 44 | O.liftPredicate(RA.isNonEmpty) 45 | 46 | // ------------------------------------------------------------------------------------- 47 | // isNonEmptyArray 48 | // ------------------------------------------------------------------------------------- 49 | 50 | if (RA.isNonEmptyArray(as)) { 51 | // $ExpectType [number, ...number[]] 52 | as 53 | } 54 | 55 | // $ExpectType (c: A[]) => Option<[A, ...A[]]> 56 | O.liftPredicate(RA.isNonEmptyArray) 57 | 58 | // ------------------------------------------------------------------------------------- 59 | // map 60 | // ------------------------------------------------------------------------------------- 61 | 62 | // $ExpectType number[] 63 | RA.map(ras, n => n + 1) 64 | 65 | // $ExpectType number[] 66 | pipe(ras, RA.map(n => n + 1)) 67 | 68 | // $ExpectType number[] 69 | RA.map(as, n => n + 1) 70 | 71 | // $ExpectType number[] 72 | pipe(as, RA.map(n => n + 1)) 73 | 74 | // ------------------------------------------------------------------------------------- 75 | // mapNonEmpty 76 | // ------------------------------------------------------------------------------------- 77 | 78 | // $ExpectType [number, ...number[]] 79 | RA.mapNonEmpty(neras, n => n + 1) 80 | 81 | // $ExpectType [number, ...number[]] 82 | pipe(neras, RA.mapNonEmpty(n => n + 1)) 83 | 84 | // $ExpectType [number, ...number[]] 85 | RA.mapNonEmpty(neas, n => n + 1) 86 | 87 | // $ExpectType [number, ...number[]] 88 | pipe(neas, RA.mapNonEmpty(n => n + 1)) 89 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Chainable.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Chainable.ts 3 | nav_order: 23 4 | parent: Modules 5 | --- 6 | 7 | ## Chainable overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [combining](#combining) 16 | - [andThenDiscard](#andthendiscard) 17 | - [do notation](#do-notation) 18 | - [bind](#bind) 19 | - [type class](#type-class) 20 | - [Chainable (interface)](#chainable-interface) 21 | - [utils](#utils) 22 | - [tap](#tap) 23 | 24 | --- 25 | 26 | # combining 27 | 28 | ## andThenDiscard 29 | 30 | Sequences the specified effect after this effect, but ignores the value 31 | produced by the effect. 32 | 33 | **Signature** 34 | 35 | ```ts 36 | export declare const andThenDiscard: ( 37 | F: Chainable 38 | ) => { 39 | (that: Kind): ( 40 | self: Kind 41 | ) => Kind 42 | (self: Kind, that: Kind): Kind< 43 | F, 44 | R1 & R2, 45 | O1 | O2, 46 | E1 | E2, 47 | A 48 | > 49 | } 50 | ``` 51 | 52 | Added in v1.0.0 53 | 54 | # do notation 55 | 56 | ## bind 57 | 58 | **Signature** 59 | 60 | ```ts 61 | export declare const bind: ( 62 | F: Chainable 63 | ) => { 64 | (name: Exclude, f: (a: A) => Kind): < 65 | R1, 66 | O1, 67 | E1 68 | >( 69 | self: Kind 70 | ) => Kind 71 | ( 72 | self: Kind, 73 | name: Exclude, 74 | f: (a: A) => Kind 75 | ): Kind 76 | } 77 | ``` 78 | 79 | Added in v1.0.0 80 | 81 | # type class 82 | 83 | ## Chainable (interface) 84 | 85 | **Signature** 86 | 87 | ```ts 88 | export interface Chainable extends FlatMap, Covariant {} 89 | ``` 90 | 91 | Added in v1.0.0 92 | 93 | # utils 94 | 95 | ## tap 96 | 97 | Returns an effect that effectfully "peeks" at the success of this effect. 98 | 99 | **Signature** 100 | 101 | ```ts 102 | export declare const tap: ( 103 | F: Chainable 104 | ) => { 105 | (f: (a: A) => Kind): ( 106 | self: Kind 107 | ) => Kind 108 | (self: Kind, f: (a: A) => Kind): Kind< 109 | F, 110 | R1 & R2, 111 | O1 | O2, 112 | E1 | E2, 113 | A 114 | > 115 | } 116 | ``` 117 | 118 | Added in v1.0.0 119 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | module.exports = { 3 | ignorePatterns: ["build", "dist", "dtslint", "benchmark", "*.mjs", "docs", "*.md"], 4 | parser: "@typescript-eslint/parser", 5 | parserOptions: { 6 | ecmaVersion: 2018, 7 | sourceType: "module", 8 | project: "./tsconfig.eslint.json" 9 | }, 10 | settings: { 11 | "import/parsers": { 12 | "@typescript-eslint/parser": [".ts", ".tsx"] 13 | }, 14 | "import/resolver": { 15 | typescript: { 16 | alwaysTryTypes: true 17 | } 18 | } 19 | }, 20 | extends: [ 21 | "eslint:recommended", 22 | "plugin:@typescript-eslint/eslint-recommended", 23 | "plugin:@typescript-eslint/recommended", 24 | "plugin:@repo-tooling/dprint/recommended" 25 | ], 26 | plugins: ["deprecation", "import", "sort-destructure-keys", "simple-import-sort", "codegen"], 27 | rules: { 28 | "codegen/codegen": "error", 29 | "no-fallthrough": "off", 30 | "no-irregular-whitespace": "off", 31 | "object-shorthand": "error", 32 | "prefer-destructuring": "off", 33 | "sort-imports": "off", 34 | "no-unused-vars": "off", 35 | "prefer-rest-params": "off", 36 | "prefer-spread": "off", 37 | "import/first": "error", 38 | "import/no-cycle": "error", 39 | "import/newline-after-import": "error", 40 | "import/no-duplicates": "error", 41 | "import/no-unresolved": "off", 42 | "import/order": "off", 43 | "simple-import-sort/imports": "off", 44 | "sort-destructure-keys/sort-destructure-keys": "error", 45 | "deprecation/deprecation": "off", 46 | "@typescript-eslint/array-type": ["warn", { "default": "generic", "readonly": "generic" }], 47 | "@typescript-eslint/prefer-readonly": "warn", 48 | "@typescript-eslint/member-delimiter-style": 0, 49 | "@typescript-eslint/no-non-null-assertion": "off", 50 | "@typescript-eslint/ban-types": "off", 51 | "@typescript-eslint/no-explicit-any": "off", 52 | "@typescript-eslint/no-empty-interface": "off", 53 | "@typescript-eslint/consistent-type-imports": "warn", 54 | "@typescript-eslint/no-unused-vars": ["error", { 55 | "argsIgnorePattern": "^_", 56 | "varsIgnorePattern": "^_" 57 | }], 58 | "@typescript-eslint/ban-ts-comment": "off", 59 | "@typescript-eslint/camelcase": "off", 60 | "@typescript-eslint/explicit-function-return-type": "off", 61 | "@typescript-eslint/explicit-module-boundary-types": "off", 62 | "@typescript-eslint/interface-name-prefix": "off", 63 | "@typescript-eslint/no-array-constructor": "off", 64 | "@typescript-eslint/no-use-before-define": "off", 65 | "@typescript-eslint/no-namespace": "off", 66 | "@repo-tooling/dprint/dprint": [ 67 | "error", 68 | { 69 | config: { 70 | "indentWidth": 2, 71 | "lineWidth": 100, 72 | "semiColons": "asi", 73 | "quoteStyle": "alwaysDouble", 74 | "trailingCommas": "never", 75 | "operatorPosition": "maintain", 76 | "useParentheses": "preferNone" 77 | } 78 | } 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/typeclass/NonEmptyTraversable.ts: -------------------------------------------------------------------------------- 1 | import { identity, pipe } from "@fp-ts/core/Function" 2 | import type { TypeLambda } from "@fp-ts/core/HKT" 3 | import * as O from "@fp-ts/core/Option" 4 | import * as RA from "@fp-ts/core/ReadonlyArray" 5 | import * as _ from "@fp-ts/core/test/limbo/NonEmptyTraversable" 6 | import * as covariant from "@fp-ts/core/typeclass/Covariant" 7 | import * as U from "../util" 8 | 9 | const NonEmptyTraversable: _.NonEmptyTraversable = { 10 | // @ts-expect-error 11 | traverseNonEmpty: RA.traverseNonEmpty, 12 | // @ts-expect-error 13 | sequenceNonEmpty: F => self => pipe(self, RA.traverseNonEmpty(F)(identity)) 14 | } 15 | 16 | /** 17 | * @category type lambdas 18 | * @since 1.0.0 19 | */ 20 | export interface NonEmptyReadonlyArrayTypeLambda extends TypeLambda { 21 | readonly type: RA.NonEmptyReadonlyArray 22 | } 23 | 24 | const imap = covariant.imap(RA.mapNonEmpty) 25 | 26 | /** 27 | * @category instances 28 | * @since 1.0.0 29 | */ 30 | export const NonEmptyCovariant: covariant.Covariant = { 31 | imap, 32 | map: RA.mapNonEmpty 33 | } 34 | 35 | describe("NonEmptyTraversable", () => { 36 | it("traverseNonEmptyComposition", () => { 37 | const traverseNonEmpty = _.traverseNonEmptyComposition( 38 | NonEmptyTraversable, 39 | NonEmptyTraversable 40 | )(O.SemiApplicative)((n: number) => (n > 0 ? O.some(n) : O.none())) 41 | U.deepStrictEqual(traverseNonEmpty([[1]]), O.some([[1]] as const)) 42 | U.deepStrictEqual(traverseNonEmpty([[1, -1]]), O.none()) 43 | U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, 5]]), O.some([[1, 2, 3], [4, 5]] as const)) 44 | U.deepStrictEqual(traverseNonEmpty([[1, 2, 3], [4, -1]]), O.none()) 45 | }) 46 | 47 | it("traverseNonEmptyComposition", () => { 48 | const sequence = _.sequenceNonEmptyComposition( 49 | { ...NonEmptyTraversable, ...NonEmptyCovariant }, 50 | NonEmptyTraversable 51 | )(O.SemiApplicative) 52 | U.deepStrictEqual(sequence([[O.some(1)]]), O.some([[1]] as const)) 53 | U.deepStrictEqual(sequence([[O.some(1), O.none()]]), O.none()) 54 | U.deepStrictEqual( 55 | sequence([[O.some(1), O.some(2), O.some(3)], [O.some(4), O.some(5)]]), 56 | O.some([[1, 2, 3], [4, 5]] as const) 57 | ) 58 | U.deepStrictEqual( 59 | sequence([[O.some(1), O.some(2), O.some(3)], [O.some(4), O.none()]]), 60 | O.none() 61 | ) 62 | }) 63 | 64 | it("sequenceNonEmpty", () => { 65 | const sequenceNonEmpty = _.sequenceNonEmpty( 66 | NonEmptyTraversable.traverseNonEmpty 67 | )(O.SemiApplicative) 68 | U.deepStrictEqual(sequenceNonEmpty([O.none()]), O.none()) 69 | U.deepStrictEqual(sequenceNonEmpty([O.some(1)]), O.some([1] as const)) 70 | U.deepStrictEqual(sequenceNonEmpty([O.none()]), O.none()) 71 | U.deepStrictEqual(sequenceNonEmpty([O.some(1), O.none()]), O.none()) 72 | U.deepStrictEqual(sequenceNonEmpty([O.some(1), O.some(2)]), O.some([1, 2] as const)) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /src/typeclass/Chainable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual } from "@fp-ts/core/Function" 5 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Covariant } from "@fp-ts/core/typeclass/Covariant" 7 | import type { FlatMap } from "@fp-ts/core/typeclass/FlatMap" 8 | 9 | /** 10 | * @category type class 11 | * @since 1.0.0 12 | */ 13 | export interface Chainable extends FlatMap, Covariant {} 14 | 15 | /** 16 | * Sequences the specified effect after this effect, but ignores the value 17 | * produced by the effect. 18 | * 19 | * @category combining 20 | * @since 1.0.0 21 | */ 22 | export const andThenDiscard = (F: Chainable): { 23 | ( 24 | that: Kind 25 | ): (self: Kind) => Kind 26 | ( 27 | self: Kind, 28 | that: Kind 29 | ): Kind 30 | } => 31 | dual(2, ( 32 | self: Kind, 33 | that: Kind 34 | ): Kind => tap(F)(self, () => that)) 35 | 36 | /** 37 | * Returns an effect that effectfully "peeks" at the success of this effect. 38 | * 39 | * @since 1.0.0 40 | */ 41 | export const tap = (F: Chainable): { 42 | ( 43 | f: (a: A) => Kind 44 | ): (self: Kind) => Kind 45 | ( 46 | self: Kind, 47 | f: (a: A) => Kind 48 | ): Kind 49 | } => 50 | dual( 51 | 2, 52 | ( 53 | self: Kind, 54 | f: (a: A) => Kind 55 | ): Kind => F.flatMap(self, a => F.map(f(a), () => a)) 56 | ) 57 | 58 | /** 59 | * @category do notation 60 | * @since 1.0.0 61 | */ 62 | export const bind = (F: Chainable): { 63 | ( 64 | name: Exclude, 65 | f: (a: A) => Kind 66 | ): ( 67 | self: Kind 68 | ) => Kind 69 | ( 70 | self: Kind, 71 | name: Exclude, 72 | f: (a: A) => Kind 73 | ): Kind 74 | } => 75 | dual(3, ( 76 | self: Kind, 77 | name: Exclude, 78 | f: (a: A) => Kind 79 | ): Kind => 80 | F.flatMap(self, a => F.map(f(a), b => Object.assign({}, a, { [name]: b }) as any))) 81 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Traversable.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Traversable.ts 3 | nav_order: 43 4 | parent: Modules 5 | --- 6 | 7 | ## Traversable overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Traversable (interface)](#traversable-interface) 17 | - [utils](#utils) 18 | - [sequence](#sequence) 19 | - [traverseComposition](#traversecomposition) 20 | - [traverseTap](#traversetap) 21 | 22 | --- 23 | 24 | # type class 25 | 26 | ## Traversable (interface) 27 | 28 | **Signature** 29 | 30 | ```ts 31 | export interface Traversable extends TypeClass { 32 | readonly traverse: ( 33 | F: Applicative 34 | ) => { 35 | (f: (a: A) => Kind): ( 36 | self: Kind 37 | ) => Kind> 38 | (self: Kind, f: (a: A) => Kind): Kind< 39 | F, 40 | R, 41 | O, 42 | E, 43 | Kind 44 | > 45 | } 46 | } 47 | ``` 48 | 49 | Added in v1.0.0 50 | 51 | # utils 52 | 53 | ## sequence 54 | 55 | Returns a default `sequence` implementation. 56 | 57 | **Signature** 58 | 59 | ```ts 60 | export declare const sequence: ( 61 | T: Traversable 62 | ) => ( 63 | F: Applicative 64 | ) => ( 65 | self: Kind> 66 | ) => Kind> 67 | ``` 68 | 69 | Added in v1.0.0 70 | 71 | ## traverseComposition 72 | 73 | Returns a default binary `traverse` composition. 74 | 75 | **Signature** 76 | 77 | ```ts 78 | export declare const traverseComposition: ( 79 | T: Traversable, 80 | G: Traversable 81 | ) => ( 82 | F: Applicative 83 | ) => ( 84 | self: Kind>, 85 | f: (a: A) => Kind 86 | ) => Kind>> 87 | ``` 88 | 89 | Added in v1.0.0 90 | 91 | ## traverseTap 92 | 93 | Given a function which returns a `F` effect, thread this effect 94 | through the running of this function on all the values in `T`, 95 | returning an `T
` in a `F` context, ignoring the values 96 | returned by the provided function. 97 | 98 | **Signature** 99 | 100 | ```ts 101 | export declare const traverseTap: ( 102 | T: Traversable 103 | ) => ( 104 | F: Applicative 105 | ) => { 106 | (f: (a: A) => Kind): ( 107 | self: Kind 108 | ) => Kind> 109 | (self: Kind, f: (a: A) => Kind): Kind< 110 | F, 111 | R, 112 | O, 113 | E, 114 | Kind 115 | > 116 | } 117 | ``` 118 | 119 | Added in v1.0.0 120 | -------------------------------------------------------------------------------- /docs/modules/Ordering.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ordering.ts 3 | nav_order: 10 4 | parent: Modules 5 | --- 6 | 7 | ## Ordering overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [instances](#instances) 16 | - [Monoid](#monoid) 17 | - [Semigroup](#semigroup) 18 | - [model](#model) 19 | - [Ordering (type alias)](#ordering-type-alias) 20 | - [pattern matching](#pattern-matching) 21 | - [match](#match) 22 | - [utils](#utils) 23 | - [reverse](#reverse) 24 | 25 | --- 26 | 27 | # instances 28 | 29 | ## Monoid 30 | 31 | `Monoid` instance for `Ordering`, returns the left-most non-zero `Ordering`. 32 | 33 | The `empty` value is `0`. 34 | 35 | **Signature** 36 | 37 | ```ts 38 | export declare const Monoid: monoid.Monoid<0 | 1 | -1> 39 | ``` 40 | 41 | **Example** 42 | 43 | ```ts 44 | import { Monoid } from '@fp-ts/core/Ordering' 45 | 46 | assert.deepStrictEqual(Monoid.combine(Monoid.empty, -1), -1) 47 | assert.deepStrictEqual(Monoid.combine(Monoid.empty, 1), 1) 48 | assert.deepStrictEqual(Monoid.combine(1, -1), 1) 49 | ``` 50 | 51 | Added in v1.0.0 52 | 53 | ## Semigroup 54 | 55 | `Semigroup` instance for `Ordering`, returns the left-most non-zero `Ordering`. 56 | 57 | **Signature** 58 | 59 | ```ts 60 | export declare const Semigroup: semigroup.Semigroup<0 | 1 | -1> 61 | ``` 62 | 63 | **Example** 64 | 65 | ```ts 66 | import { Semigroup } from '@fp-ts/core/Ordering' 67 | 68 | assert.deepStrictEqual(Semigroup.combine(0, -1), -1) 69 | assert.deepStrictEqual(Semigroup.combine(0, 1), 1) 70 | assert.deepStrictEqual(Semigroup.combine(1, -1), 1) 71 | ``` 72 | 73 | Added in v1.0.0 74 | 75 | # model 76 | 77 | ## Ordering (type alias) 78 | 79 | **Signature** 80 | 81 | ```ts 82 | export type Ordering = -1 | 0 | 1 83 | ``` 84 | 85 | Added in v1.0.0 86 | 87 | # pattern matching 88 | 89 | ## match 90 | 91 | Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. 92 | 93 | **Signature** 94 | 95 | ```ts 96 | export declare const match: { 97 | (onLessThan: LazyArg
, onEqual: LazyArg, onGreaterThan: LazyArg): (self: Ordering) => A | B | C 98 | (o: Ordering, onLessThan: LazyArg, onEqual: LazyArg, onGreaterThan: LazyArg): A | B | C 99 | } 100 | ``` 101 | 102 | **Example** 103 | 104 | ```ts 105 | import { match } from '@fp-ts/core/Ordering' 106 | import { constant } from '@fp-ts/core/Function' 107 | 108 | const toMessage = match(constant('less than'), constant('equal'), constant('greater than')) 109 | 110 | assert.deepStrictEqual(toMessage(-1), 'less than') 111 | assert.deepStrictEqual(toMessage(0), 'equal') 112 | assert.deepStrictEqual(toMessage(1), 'greater than') 113 | ``` 114 | 115 | Added in v1.0.0 116 | 117 | # utils 118 | 119 | ## reverse 120 | 121 | Inverts the ordering of the input `Ordering`. 122 | 123 | **Signature** 124 | 125 | ```ts 126 | export declare const reverse: (o: Ordering) => Ordering 127 | ``` 128 | 129 | **Example** 130 | 131 | ```ts 132 | import { reverse } from '@fp-ts/core/Ordering' 133 | 134 | assert.deepStrictEqual(reverse(1), -1) 135 | assert.deepStrictEqual(reverse(-1), 1) 136 | assert.deepStrictEqual(reverse(0), 0) 137 | ``` 138 | 139 | Added in v1.0.0 140 | -------------------------------------------------------------------------------- /test/typeclass/TraversableFilterable.ts: -------------------------------------------------------------------------------- 1 | import * as E from "@fp-ts/core/Either" 2 | import * as O from "@fp-ts/core/Option" 3 | import * as RA from "@fp-ts/core/ReadonlyArray" 4 | import * as _ from "@fp-ts/core/typeclass/TraversableFilterable" 5 | import * as U from "../util" 6 | 7 | describe("TraversableFilterable", () => { 8 | it("traversePartitionMap", () => { 9 | const traversePartitionMap: ( 10 | self: ReadonlyArray, 11 | f: (a: A) => O.Option> 12 | ) => O.Option<[ReadonlyArray, ReadonlyArray]> = _.traversePartitionMap({ 13 | ...RA.Traversable, 14 | ...RA.Covariant, 15 | ...RA.Filterable 16 | })(O.Applicative) 17 | const f = (s: string) => 18 | s.length > 1 ? O.some(E.right(s)) : s.length > 0 ? O.some(E.left(s)) : O.none() 19 | assert.deepStrictEqual(traversePartitionMap([], f), O.some([[], []])) 20 | assert.deepStrictEqual(traversePartitionMap([""], f), O.none()) 21 | assert.deepStrictEqual(traversePartitionMap(["a"], f), O.some([["a"], []])) 22 | assert.deepStrictEqual(traversePartitionMap(["aa"], f), O.some([[], ["aa"]])) 23 | assert.deepStrictEqual(traversePartitionMap(["aa", "a", ""], f), O.none()) 24 | assert.deepStrictEqual( 25 | traversePartitionMap(["aa", "a", "aaa"], f), 26 | O.some([["a"], ["aa", "aaa"]]) 27 | ) 28 | }) 29 | 30 | it("traverseFilterMap", () => { 31 | const traverseFilterMap: ( 32 | self: ReadonlyArray, 33 | f: (a: A) => O.Option> 34 | ) => O.Option> = _.traverseFilterMap({ 35 | ...RA.Traversable, 36 | ...RA.Filterable 37 | })(O.Applicative) 38 | const f = (s: string) => 39 | s.length > 1 ? O.some(O.some(s)) : s.length > 0 ? O.some(O.none()) : O.none() 40 | assert.deepStrictEqual(traverseFilterMap([], f), O.some([])) 41 | assert.deepStrictEqual(traverseFilterMap([""], f), O.none()) 42 | assert.deepStrictEqual(traverseFilterMap(["a"], f), O.some([])) 43 | assert.deepStrictEqual(traverseFilterMap(["aa"], f), O.some(["aa"])) 44 | assert.deepStrictEqual(traverseFilterMap(["aa", "a", ""], f), O.none()) 45 | assert.deepStrictEqual( 46 | traverseFilterMap(["aa", "a", "aaa"], f), 47 | O.some(["aa", "aaa"]) 48 | ) 49 | }) 50 | 51 | it("traverseFilter", () => { 52 | const traverseFilter = _.traverseFilter( 53 | RA.TraversableFilterable 54 | )(O.Applicative) 55 | const f = traverseFilter((s: string) => 56 | s.length > 2 ? O.some(false) : s.length > 1 ? O.some(true) : O.none() 57 | ) 58 | U.deepStrictEqual(f([]), O.some([])) 59 | U.deepStrictEqual(f(["a"]), O.none()) 60 | U.deepStrictEqual(f(["a", "aa"]), O.none()) 61 | U.deepStrictEqual(f(["aa"]), O.some(["aa"])) 62 | U.deepStrictEqual(f(["aaa"]), O.some([])) 63 | U.deepStrictEqual(f(["aaa", "aa"]), O.some(["aa"])) 64 | }) 65 | 66 | it("traversePartition", () => { 67 | const traversePartition = _.traversePartition( 68 | RA.TraversableFilterable 69 | )(O.Applicative) 70 | const f = traversePartition((s: string) => 71 | s.length > 2 ? O.some(false) : s.length > 1 ? O.some(true) : O.none() 72 | ) 73 | expect(f([])).toEqual(O.some([[], []])) 74 | expect(f(["a"])).toEqual(O.none()) 75 | expect(f(["a", "aa"])).toEqual(O.none()) 76 | expect(f(["aa"])).toEqual(O.some([[], ["aa"]])) 77 | expect(f(["aaa"])).toEqual(O.some([["aaa"], []])) 78 | expect(f(["aaa", "aa"])).toEqual(O.some([["aaa"], ["aa"]])) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /src/typeclass/Covariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import { dual } from "@fp-ts/core/Function" 5 | import type { Kind, TypeLambda } from "@fp-ts/core/HKT" 6 | import type { Invariant } from "@fp-ts/core/typeclass/Invariant" 7 | 8 | /** 9 | * @category type class 10 | * @since 1.0.0 11 | */ 12 | export interface Covariant extends Invariant { 13 | readonly map: { 14 | (f: (a: A) => B): (self: Kind) => Kind 15 | (self: Kind, f: (a: A) => B): Kind 16 | } 17 | } 18 | 19 | /** 20 | * Returns a default `map` composition. 21 | * 22 | * @since 1.0.0 23 | */ 24 | export const mapComposition = ( 25 | F: Covariant, 26 | G: Covariant 27 | ): (( 28 | self: Kind>, 29 | f: (a: A) => B 30 | ) => Kind>) => (self, f) => F.map(self, G.map(f)) 31 | 32 | /** 33 | * Returns a default `imap` implementation. 34 | * 35 | * @since 1.0.0 36 | */ 37 | export const imap = ( 38 | map: (self: Kind, f: (a: A) => B) => Kind 39 | ): Invariant["imap"] => dual(3, (self, to, _) => map(self, to)) 40 | 41 | /** 42 | * @category mapping 43 | * @since 1.0.0 44 | */ 45 | export const flap = (F: Covariant): { 46 | (self: Kind B>): (a: A) => Kind 47 | (a: A, self: Kind B>): Kind 48 | } => 49 | dual( 50 | 2, 51 | (a: A, self: Kind B>): Kind => 52 | F.map(self, f => f(a)) 53 | ) 54 | 55 | /** 56 | * @category mapping 57 | * @since 1.0.0 58 | */ 59 | export const as = (F: Covariant): { 60 | (b: B): (self: Kind) => Kind 61 | (self: Kind, b: B): Kind 62 | } => 63 | dual( 64 | 2, 65 | (self: Kind, b: B): Kind => F.map(self, () => b) 66 | ) 67 | 68 | /** 69 | * @category mapping 70 | * @since 1.0.0 71 | */ 72 | export const asUnit = ( 73 | F: Covariant 74 | ): ((self: Kind) => Kind) => as(F)(undefined) 75 | 76 | const let_ = ( 77 | F: Covariant 78 | ): { 79 | ( 80 | name: Exclude, 81 | f: (a: A) => B 82 | ): ( 83 | self: Kind 84 | ) => Kind 85 | ( 86 | self: Kind, 87 | name: Exclude, 88 | f: (a: A) => B 89 | ): Kind 90 | } => 91 | dual(3, ( 92 | self: Kind, 93 | name: Exclude, 94 | f: (a: A) => B 95 | ): Kind => 96 | F.map(self, a => Object.assign({}, a, { [name]: f(a) }) as any)) 97 | 98 | export { 99 | /** 100 | * @category do notation 101 | * @since 1.0.0 102 | */ 103 | let_ as let 104 | } 105 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Foldable.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Foldable.ts 3 | nav_order: 30 4 | parent: Modules 5 | --- 6 | 7 | ## Foldable overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [type class](#type-class) 16 | - [Foldable (interface)](#foldable-interface) 17 | - [utils](#utils) 18 | - [combineMap](#combinemap) 19 | - [coproductMapKind](#coproductmapkind) 20 | - [reduceComposition](#reducecomposition) 21 | - [reduceKind](#reducekind) 22 | - [toArray](#toarray) 23 | - [toArrayMap](#toarraymap) 24 | 25 | --- 26 | 27 | # type class 28 | 29 | ## Foldable (interface) 30 | 31 | **Signature** 32 | 33 | ```ts 34 | export interface Foldable extends TypeClass { 35 | readonly reduce: { 36 | (b: B, f: (b: B, a: A) => B): (self: Kind) => B 37 | (self: Kind, b: B, f: (b: B, a: A) => B): B 38 | } 39 | } 40 | ``` 41 | 42 | Added in v1.0.0 43 | 44 | # utils 45 | 46 | ## combineMap 47 | 48 | **Signature** 49 | 50 | ```ts 51 | export declare const combineMap: ( 52 | F: Foldable 53 | ) => (M: Monoid) => { 54 |
(f: (a: A) => M): (self: Kind) => M 55 | (self: Kind, f: (a: A) => M): M 56 | } 57 | ``` 58 | 59 | Added in v1.0.0 60 | 61 | ## coproductMapKind 62 | 63 | **Signature** 64 | 65 | ```ts 66 | export declare const coproductMapKind: ( 67 | F: Foldable 68 | ) => ( 69 | G: Coproduct 70 | ) => { 71 | (f: (a: A) => Kind): (self: Kind) => Kind 72 | (self: Kind, f: (a: A) => Kind): Kind 73 | } 74 | ``` 75 | 76 | Added in v1.0.0 77 | 78 | ## reduceComposition 79 | 80 | Returns a default ternary `reduce` composition. 81 | 82 | **Signature** 83 | 84 | ```ts 85 | export declare const reduceComposition: ( 86 | F: Foldable, 87 | G: Foldable 88 | ) => (self: Kind>, b: B, f: (b: B, a: A) => B) => B 89 | ``` 90 | 91 | Added in v1.0.0 92 | 93 | ## reduceKind 94 | 95 | **Signature** 96 | 97 | ```ts 98 | export declare const reduceKind: ( 99 | F: Foldable 100 | ) => ( 101 | G: Monad 102 | ) => { 103 | (b: B, f: (b: B, a: A) => Kind): ( 104 | self: Kind 105 | ) => Kind 106 | (self: Kind, b: B, f: (b: B, a: A) => Kind): Kind< 107 | G, 108 | R, 109 | O, 110 | E, 111 | B 112 | > 113 | } 114 | ``` 115 | 116 | Added in v1.0.0 117 | 118 | ## toArray 119 | 120 | **Signature** 121 | 122 | ```ts 123 | export declare const toArray: (F: Foldable) => (self: Kind) => A[] 124 | ``` 125 | 126 | Added in v1.0.0 127 | 128 | ## toArrayMap 129 | 130 | **Signature** 131 | 132 | ```ts 133 | export declare const toArrayMap: ( 134 | F: Foldable 135 | ) => { 136 | (f: (a: A) => B): (self: Kind) => B[] 137 | (self: Kind, f: (a: A) => B): B[] 138 | } 139 | ``` 140 | 141 | Added in v1.0.0 142 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Covariant.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Covariant.ts 3 | nav_order: 26 4 | parent: Modules 5 | --- 6 | 7 | ## Covariant overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [do notation](#do-notation) 16 | - [let](#let) 17 | - [mapping](#mapping) 18 | - [as](#as) 19 | - [asUnit](#asunit) 20 | - [flap](#flap) 21 | - [type class](#type-class) 22 | - [Covariant (interface)](#covariant-interface) 23 | - [utils](#utils) 24 | - [imap](#imap) 25 | - [mapComposition](#mapcomposition) 26 | 27 | --- 28 | 29 | # do notation 30 | 31 | ## let 32 | 33 | **Signature** 34 | 35 | ```ts 36 | export declare const let: ( 37 | F: Covariant 38 | ) => { 39 | (name: Exclude, f: (a: A) => B): ( 40 | self: Kind 41 | ) => Kind 42 | ( 43 | self: Kind, 44 | name: Exclude, 45 | f: (a: A) => B 46 | ): Kind 47 | } 48 | ``` 49 | 50 | Added in v1.0.0 51 | 52 | # mapping 53 | 54 | ## as 55 | 56 | **Signature** 57 | 58 | ```ts 59 | export declare const as: ( 60 | F: Covariant 61 | ) => { 62 | (b: B): (self: Kind) => Kind 63 | (self: Kind, b: B): Kind 64 | } 65 | ``` 66 | 67 | Added in v1.0.0 68 | 69 | ## asUnit 70 | 71 | **Signature** 72 | 73 | ```ts 74 | export declare const asUnit: ( 75 | F: Covariant 76 | ) => (self: Kind) => Kind 77 | ``` 78 | 79 | Added in v1.0.0 80 | 81 | ## flap 82 | 83 | **Signature** 84 | 85 | ```ts 86 | export declare const flap: ( 87 | F: Covariant 88 | ) => { 89 | (self: Kind B>): (a: A) => Kind 90 | (a: A, self: Kind B>): Kind 91 | } 92 | ``` 93 | 94 | Added in v1.0.0 95 | 96 | # type class 97 | 98 | ## Covariant (interface) 99 | 100 | **Signature** 101 | 102 | ```ts 103 | export interface Covariant extends Invariant { 104 | readonly map: { 105 | (f: (a: A) => B): (self: Kind) => Kind 106 | (self: Kind, f: (a: A) => B): Kind 107 | } 108 | } 109 | ``` 110 | 111 | Added in v1.0.0 112 | 113 | # utils 114 | 115 | ## imap 116 | 117 | Returns a default `imap` implementation. 118 | 119 | **Signature** 120 | 121 | ```ts 122 | export declare const imap: ( 123 | map: (self: Kind, f: (a: A) => B) => Kind 124 | ) => { 125 | (to: (a: A) => B, from: (b: B) => A): (self: Kind) => Kind 126 | (self: Kind, to: (a: A) => B, from: (b: B) => A): Kind 127 | } 128 | ``` 129 | 130 | Added in v1.0.0 131 | 132 | ## mapComposition 133 | 134 | Returns a default `map` composition. 135 | 136 | **Signature** 137 | 138 | ```ts 139 | export declare const mapComposition: ( 140 | F: Covariant, 141 | G: Covariant 142 | ) => ( 143 | self: Kind>, 144 | f: (a: A) => B 145 | ) => Kind> 146 | ``` 147 | 148 | Added in v1.0.0 149 | -------------------------------------------------------------------------------- /src/Ordering.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | import type { LazyArg } from "@fp-ts/core/Function" 5 | import { dual } from "@fp-ts/core/Function" 6 | import * as monoid from "@fp-ts/core/typeclass/Monoid" 7 | import * as semigroup from "@fp-ts/core/typeclass/Semigroup" 8 | 9 | /** 10 | * @category model 11 | * @since 1.0.0 12 | */ 13 | export type Ordering = -1 | 0 | 1 14 | 15 | /** 16 | * Inverts the ordering of the input `Ordering`. 17 | * 18 | * @param o - The input `Ordering`. 19 | * 20 | * @example 21 | * import { reverse } from "@fp-ts/core/Ordering" 22 | * 23 | * assert.deepStrictEqual(reverse(1), -1) 24 | * assert.deepStrictEqual(reverse(-1), 1) 25 | * assert.deepStrictEqual(reverse(0), 0) 26 | * 27 | * @since 1.0.0 28 | */ 29 | export const reverse = (o: Ordering): Ordering => (o === -1 ? 1 : o === 1 ? -1 : 0) 30 | 31 | /** 32 | * Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. 33 | * 34 | * @param self - The `Ordering` parameter to match against. 35 | * @param onLessThan - A function that will be called if the `Ordering` parameter is `-1`. 36 | * @param onEqual - A function that will be called if the `Ordering` parameter is `0`. 37 | * @param onGreaterThan - A function that will be called if the `Ordering` parameter is `1`. 38 | * 39 | * @example 40 | * import { match } from "@fp-ts/core/Ordering" 41 | * import { constant } from "@fp-ts/core/Function" 42 | * 43 | * const toMessage = match( 44 | * constant('less than'), 45 | * constant('equal'), 46 | * constant('greater than') 47 | * ) 48 | * 49 | * assert.deepStrictEqual(toMessage(-1), "less than") 50 | * assert.deepStrictEqual(toMessage(0), "equal") 51 | * assert.deepStrictEqual(toMessage(1), "greater than") 52 | * 53 | * @category pattern matching 54 | * @since 1.0.0 55 | */ 56 | export const match: { 57 | ( 58 | onLessThan: LazyArg
, 59 | onEqual: LazyArg, 60 | onGreaterThan: LazyArg 61 | ): (self: Ordering) => A | B | C 62 | ( 63 | o: Ordering, 64 | onLessThan: LazyArg, 65 | onEqual: LazyArg, 66 | onGreaterThan: LazyArg 67 | ): A | B | C 68 | } = dual(4, ( 69 | self: Ordering, 70 | onLessThan: LazyArg, 71 | onEqual: LazyArg, 72 | onGreaterThan: LazyArg 73 | ): A | B | C => self === -1 ? onLessThan() : self === 0 ? onEqual() : onGreaterThan()) 74 | 75 | /** 76 | * `Semigroup` instance for `Ordering`, returns the left-most non-zero `Ordering`. 77 | * 78 | * @example 79 | * import { Semigroup } from "@fp-ts/core/Ordering" 80 | * 81 | * assert.deepStrictEqual(Semigroup.combine(0, -1), -1) 82 | * assert.deepStrictEqual(Semigroup.combine(0, 1), 1) 83 | * assert.deepStrictEqual(Semigroup.combine(1, -1), 1) 84 | * 85 | * @category instances 86 | * @since 1.0.0 87 | */ 88 | export const Semigroup: semigroup.Semigroup = semigroup.make( 89 | (self, that) => self !== 0 ? self : that, 90 | (self, collection) => { 91 | let ordering = self 92 | if (ordering !== 0) { 93 | return ordering 94 | } 95 | for (ordering of collection) { 96 | if (ordering !== 0) { 97 | return ordering 98 | } 99 | } 100 | return ordering 101 | } 102 | ) 103 | 104 | /** 105 | * `Monoid` instance for `Ordering`, returns the left-most non-zero `Ordering`. 106 | * 107 | * The `empty` value is `0`. 108 | * 109 | * @example 110 | * import { Monoid } from "@fp-ts/core/Ordering" 111 | * 112 | * assert.deepStrictEqual(Monoid.combine(Monoid.empty, -1), -1) 113 | * assert.deepStrictEqual(Monoid.combine(Monoid.empty, 1), 1) 114 | * assert.deepStrictEqual(Monoid.combine(1, -1), 1) 115 | * 116 | * @category instances 117 | * @since 1.0.0 118 | */ 119 | export const Monoid: monoid.Monoid = monoid.fromSemigroup(Semigroup, 0) 120 | -------------------------------------------------------------------------------- /src/typeclass/Foldable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @since 1.0.0 3 | */ 4 | 5 | import { dual, identity } from "@fp-ts/core/Function" 6 | import type { Kind, TypeClass, TypeLambda } from "@fp-ts/core/HKT" 7 | import type { Coproduct } from "@fp-ts/core/typeclass/Coproduct" 8 | import type { Monad } from "@fp-ts/core/typeclass/Monad" 9 | import type { Monoid } from "@fp-ts/core/typeclass/Monoid" 10 | 11 | /** 12 | * @category type class 13 | * @since 1.0.0 14 | */ 15 | export interface Foldable extends TypeClass { 16 | readonly reduce: { 17 | (b: B, f: (b: B, a: A) => B): (self: Kind) => B 18 | (self: Kind, b: B, f: (b: B, a: A) => B): B 19 | } 20 | } 21 | 22 | /** 23 | * Returns a default ternary `reduce` composition. 24 | * 25 | * @since 1.0.0 26 | */ 27 | export const reduceComposition = ( 28 | F: Foldable, 29 | G: Foldable 30 | ) => 31 | ( 32 | self: Kind>, 33 | b: B, 34 | f: (b: B, a: A) => B 35 | ): B => F.reduce(self, b, (b, ga) => G.reduce(ga, b, f)) 36 | 37 | /** 38 | * @since 1.0.0 39 | */ 40 | export const toArrayMap = ( 41 | F: Foldable 42 | ): { 43 | (f: (a: A) => B): (self: Kind) => Array 44 | (self: Kind, f: (a: A) => B): Array 45 | } => 46 | dual( 47 | 2, 48 | (self: Kind, f: (a: A) => B): Array => 49 | F.reduce(self, [], (out: Array, a) => [...out, f(a)]) 50 | ) 51 | 52 | /** 53 | * @since 1.0.0 54 | */ 55 | export const toArray = ( 56 | F: Foldable 57 | ): (self: Kind) => Array => toArrayMap(F)(identity) 58 | 59 | /** 60 | * @since 1.0.0 61 | */ 62 | export const combineMap = (F: Foldable) => 63 | (M: Monoid): { 64 | (f: (a: A) => M): (self: Kind) => M 65 | (self: Kind, f: (a: A) => M): M 66 | } => 67 | dual(2, (self: Kind, f: (a: A) => M): M => 68 | F.reduce(self, M.empty, (m, a) => 69 | M.combine(m, f(a)))) 70 | 71 | /** 72 | * @since 1.0.0 73 | */ 74 | export const reduceKind = (F: Foldable) => 75 | (G: Monad): { 76 | ( 77 | b: B, 78 | f: (b: B, a: A) => Kind 79 | ): (self: Kind) => Kind 80 | ( 81 | self: Kind, 82 | b: B, 83 | f: (b: B, a: A) => Kind 84 | ): Kind 85 | } => 86 | dual(3, ( 87 | self: Kind, 88 | b: B, 89 | f: (b: B, a: A) => Kind 90 | ): Kind => 91 | F.reduce( 92 | self, 93 | G.of(b), 94 | (gb: Kind, a) => G.flatMap(gb, b => f(b, a)) 95 | )) 96 | 97 | /** 98 | * @since 1.0.0 99 | */ 100 | export const coproductMapKind = (F: Foldable) => 101 | (G: Coproduct): { 102 | ( 103 | f: (a: A) => Kind 104 | ): (self: Kind) => Kind 105 | ( 106 | self: Kind, 107 | f: (a: A) => Kind 108 | ): Kind 109 | } => 110 | dual(2, ( 111 | self: Kind, 112 | f: (a: A) => Kind 113 | ): Kind => 114 | F.reduce(self, G.zero(), (gb: Kind, a) => G.coproduct(gb, f(a)))) 115 | -------------------------------------------------------------------------------- /test/Number.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as Number from "@fp-ts/core/Number" 3 | import { deepStrictEqual } from "@fp-ts/core/test/util" 4 | 5 | describe.concurrent("Number", () => { 6 | it("exports", () => { 7 | expect(Number.SemigroupMax).exists 8 | expect(Number.SemigroupMin).exists 9 | expect(Number.MonoidMax).exists 10 | expect(Number.MonoidMin).exists 11 | expect(Number.sumAll).exists 12 | expect(Number.multiplyAll).exists 13 | expect(Number.lessThan).exists 14 | expect(Number.lessThanOrEqualTo).exists 15 | expect(Number.greaterThan).exists 16 | expect(Number.greaterThanOrEqualTo).exists 17 | expect(Number.between).exists 18 | expect(Number.clamp).exists 19 | expect(Number.min).exists 20 | expect(Number.max).exists 21 | }) 22 | 23 | it("isNumber", () => { 24 | expect(Number.isNumber(1)).toEqual(true) 25 | expect(Number.isNumber("a")).toEqual(false) 26 | expect(Number.isNumber(true)).toEqual(false) 27 | }) 28 | 29 | it("sum", () => { 30 | deepStrictEqual(pipe(1, Number.sum(2)), 3) 31 | }) 32 | 33 | it("multiply", () => { 34 | deepStrictEqual(pipe(2, Number.multiply(3)), 6) 35 | }) 36 | 37 | it("subtract", () => { 38 | deepStrictEqual(pipe(3, Number.subtract(1)), 2) 39 | }) 40 | 41 | it("divide", () => { 42 | deepStrictEqual(pipe(6, Number.divide(2)), 3) 43 | }) 44 | 45 | it("increment", () => { 46 | deepStrictEqual(Number.increment(2), 3) 47 | }) 48 | 49 | it("decrement", () => { 50 | deepStrictEqual(Number.decrement(2), 1) 51 | }) 52 | 53 | it("Equivalence", () => { 54 | expect(Number.Equivalence(1, 1)).toBe(true) 55 | expect(Number.Equivalence(1, 2)).toBe(false) 56 | }) 57 | 58 | it("Order", () => { 59 | deepStrictEqual(Number.Order.compare(1, 2), -1) 60 | deepStrictEqual(Number.Order.compare(2, 1), 1) 61 | deepStrictEqual(Number.Order.compare(2, 2), 0) 62 | }) 63 | 64 | it("Bounded", () => { 65 | expect(Number.Bounded.maxBound).toEqual(Infinity) 66 | expect(Number.Bounded.minBound).toEqual(-Infinity) 67 | }) 68 | 69 | it("SemigroupSum", () => { 70 | deepStrictEqual(Number.SemigroupSum.combine(2, 3), 5) 71 | }) 72 | 73 | it("MonoidSum", () => { 74 | deepStrictEqual(Number.MonoidSum.combineAll([1, 2, 3]), 6) 75 | }) 76 | 77 | it("SemigroupMultiply", () => { 78 | deepStrictEqual(Number.SemigroupMultiply.combine(2, 3), 6) 79 | deepStrictEqual(Number.SemigroupMultiply.combineMany(0, [1, 2, 3]), 0) 80 | deepStrictEqual(Number.SemigroupMultiply.combineMany(2, [1, 0, 3]), 0) 81 | }) 82 | 83 | it("MonoidMultiply", () => { 84 | deepStrictEqual(Number.MonoidMultiply.combineAll([2, 3, 4]), 24) 85 | }) 86 | 87 | it("sign", () => { 88 | deepStrictEqual(Number.sign(0), 0) 89 | deepStrictEqual(Number.sign(0.0), 0) 90 | deepStrictEqual(Number.sign(-0.1), -1) 91 | deepStrictEqual(Number.sign(-10), -1) 92 | deepStrictEqual(Number.sign(10), 1) 93 | deepStrictEqual(Number.sign(0.1), 1) 94 | }) 95 | 96 | it("remainder", () => { 97 | assert.deepStrictEqual(Number.remainder(2, 2), 0) 98 | assert.deepStrictEqual(Number.remainder(3, 2), 1) 99 | assert.deepStrictEqual(Number.remainder(4, 2), 0) 100 | assert.deepStrictEqual(Number.remainder(2.5, 2), 0.5) 101 | assert.deepStrictEqual(Number.remainder(-2, 2), -0) 102 | assert.deepStrictEqual(Number.remainder(-3, 2), -1) 103 | assert.deepStrictEqual(Number.remainder(-4, 2), -0) 104 | assert.deepStrictEqual(Number.remainder(-2.8, -.2), -0) 105 | assert.deepStrictEqual(Number.remainder(-2, -.2), -0) 106 | assert.deepStrictEqual(Number.remainder(-1.5, -.2), -0.1) 107 | assert.deepStrictEqual(Number.remainder(0, -.2), 0) 108 | assert.deepStrictEqual(Number.remainder(1, -.2), 0) 109 | assert.deepStrictEqual(Number.remainder(2.6, -.2), 0) 110 | assert.deepStrictEqual(Number.remainder(3.1, -.2), 0.1) 111 | }) 112 | }) 113 | -------------------------------------------------------------------------------- /test/typeclass/Equivalence.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as _ from "@fp-ts/core/typeclass/Equivalence" 3 | 4 | describe("Equivalence", () => { 5 | it("exports", () => { 6 | expect(_.Invariant).exists 7 | expect(_.Contravariant).exists 8 | expect(_.SemiProduct).exists 9 | expect(_.Product).exists 10 | expect(_.tuple).exists 11 | expect(_.struct).exists 12 | }) 13 | 14 | test("strict returns an Equivalence that uses strict equality (===) to compare values", () => { 15 | const eq = _.strict<{ a: number }>() 16 | const a = { a: 1 } 17 | expect(eq(a, a)).toBe(true) 18 | expect(eq({ a: 1 }, { a: 1 })).toBe(false) 19 | }) 20 | 21 | it("contramap", () => { 22 | interface Person { 23 | readonly name: string 24 | readonly age: number 25 | } 26 | const eqPerson = pipe(_.string, _.contramap((p: Person) => p.name)) 27 | expect(eqPerson({ name: "a", age: 1 }, { name: "a", age: 2 })).toEqual(true) 28 | expect(eqPerson({ name: "a", age: 1 }, { name: "a", age: 1 })).toEqual(true) 29 | expect(eqPerson({ name: "a", age: 1 }, { name: "b", age: 1 })).toEqual(false) 30 | expect(eqPerson({ name: "a", age: 1 }, { name: "b", age: 2 })).toEqual(false) 31 | }) 32 | 33 | it("getSemigroup", () => { 34 | type T = readonly [string, number, boolean] 35 | const S = _.getSemigroup() 36 | const E0: _.Equivalence = _.contramap((x: T) => x[0])(_.string) 37 | const E1: _.Equivalence = _.contramap((x: T) => x[1])(_.number) 38 | const eqE0E1 = S.combine(E0, E1) 39 | expect(eqE0E1(["a", 1, true], ["a", 1, true])).toEqual(true) 40 | expect(eqE0E1(["a", 1, true], ["a", 1, false])).toEqual(true) 41 | expect(eqE0E1(["a", 1, true], ["b", 1, true])).toEqual(false) 42 | expect(eqE0E1(["a", 1, true], ["a", 2, false])).toEqual(false) 43 | const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) 44 | const eqE0E1E2 = S.combineMany(E0, [E1, E2]) 45 | expect(eqE0E1E2(["a", 1, true], ["a", 1, true])).toEqual(true) 46 | expect(eqE0E1E2(["a", 1, true], ["b", 1, true])).toEqual(false) 47 | expect(eqE0E1E2(["a", 1, true], ["a", 2, true])).toEqual(false) 48 | expect(eqE0E1E2(["a", 1, true], ["a", 1, false])).toEqual(false) 49 | }) 50 | 51 | it("getMonoid", () => { 52 | type T = readonly [string, number, boolean] 53 | const M = _.getMonoid() 54 | const E0: _.Equivalence = _.contramap((x: T) => x[0])(_.string) 55 | const E1: _.Equivalence = _.contramap((x: T) => x[1])(_.number) 56 | const E2: _.Equivalence = _.contramap((x: T) => x[2])(_.boolean) 57 | const eqE0E1E2 = M.combineAll([E0, E1, E2]) 58 | expect(eqE0E1E2(["a", 1, true], ["a", 1, true])).toEqual(true) 59 | expect(eqE0E1E2(["a", 1, true], ["b", 1, true])).toEqual(false) 60 | expect(eqE0E1E2(["a", 1, true], ["a", 2, true])).toEqual(false) 61 | expect(eqE0E1E2(["a", 1, true], ["a", 1, false])).toEqual(false) 62 | }) 63 | 64 | it("of", () => { 65 | const eq = _.Product.of([]) 66 | expect(eq([], [])).toEqual(true) 67 | }) 68 | 69 | it("product", () => { 70 | const eq = _.Product.product(_.string, _.string) 71 | expect(eq(["a", "b"], ["a", "b"])).toEqual(true) 72 | expect(eq(["a", "b"], ["c", "b"])).toEqual(false) 73 | expect(eq(["a", "b"], ["a", "c"])).toEqual(false) 74 | }) 75 | 76 | it("productMany", () => { 77 | const eq = _.Product.productMany(_.string, [_.string]) 78 | expect(eq(["a", "b"], ["a", "b"])).toEqual(true) 79 | expect(eq(["a", "b"], ["a", "b", "c"])).toEqual(true) 80 | expect(eq(["a", "b", "c"], ["a", "b"])).toEqual(true) 81 | expect(eq(["a", "b"], ["c", "b"])).toEqual(false) 82 | expect(eq(["a", "b"], ["a", "c"])).toEqual(false) 83 | }) 84 | 85 | it("productAll", () => { 86 | const eq = _.Product.productAll([_.string, _.string]) 87 | expect(eq(["a"], ["a"])).toEqual(true) 88 | expect(eq(["a"], ["b"])).toEqual(false) 89 | expect(eq(["a", "b"], ["a", "b"])).toEqual(true) 90 | expect(eq(["a", "b"], ["a", "c"])).toEqual(false) 91 | }) 92 | }) 93 | -------------------------------------------------------------------------------- /docs/modules/typeclass/SemiApplicative.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/SemiApplicative.ts 3 | nav_order: 39 4 | parent: Modules 5 | --- 6 | 7 | ## SemiApplicative overview 8 | 9 | Added in v1.0.0 10 | 11 | --- 12 | 13 |

Table of contents

14 | 15 | - [lifting](#lifting) 16 | - [getSemigroup](#getsemigroup) 17 | - [lift2](#lift2) 18 | - [type class](#type-class) 19 | - [SemiApplicative (interface)](#semiapplicative-interface) 20 | - [utils](#utils) 21 | - [andThen](#andthen) 22 | - [andThenDiscard](#andthendiscard) 23 | - [ap](#ap) 24 | - [zipWith](#zipwith) 25 | 26 | --- 27 | 28 | # lifting 29 | 30 | ## getSemigroup 31 | 32 | Lift a `Semigroup` into 'F', the inner values are combined using the provided `Semigroup`. 33 | 34 | **Signature** 35 | 36 | ```ts 37 | export declare const getSemigroup: ( 38 | F: SemiApplicative 39 | ) => (S: Semigroup
) => Semigroup> 40 | ``` 41 | 42 | Added in v1.0.0 43 | 44 | ## lift2 45 | 46 | Lifts a binary function into `F`. 47 | 48 | **Signature** 49 | 50 | ```ts 51 | export declare const lift2: ( 52 | F: SemiApplicative 53 | ) => ( 54 | f: (a: A, b: B) => C 55 | ) => { 56 | (that: Kind): ( 57 | self: Kind 58 | ) => Kind 59 | (self: Kind, that: Kind): Kind< 60 | F, 61 | R1 & R2, 62 | O1 | O2, 63 | E1 | E2, 64 | C 65 | > 66 | } 67 | ``` 68 | 69 | Added in v1.0.0 70 | 71 | # type class 72 | 73 | ## SemiApplicative (interface) 74 | 75 | **Signature** 76 | 77 | ```ts 78 | export interface SemiApplicative extends SemiProduct, Covariant {} 79 | ``` 80 | 81 | Added in v1.0.0 82 | 83 | # utils 84 | 85 | ## andThen 86 | 87 | **Signature** 88 | 89 | ```ts 90 | export declare const andThen: ( 91 | F: SemiApplicative 92 | ) => { 93 | (that: Kind): ( 94 | self: Kind 95 | ) => Kind 96 | (self: Kind, that: Kind): Kind< 97 | F, 98 | R1 & R2, 99 | O1 | O2, 100 | E1 | E2, 101 | B 102 | > 103 | } 104 | ``` 105 | 106 | Added in v1.0.0 107 | 108 | ## andThenDiscard 109 | 110 | **Signature** 111 | 112 | ```ts 113 | export declare const andThenDiscard: ( 114 | F: SemiApplicative 115 | ) => { 116 | (that: Kind): ( 117 | self: Kind 118 | ) => Kind 119 | (self: Kind, that: Kind): Kind< 120 | F, 121 | R1 & R2, 122 | O1 | O2, 123 | E1 | E2, 124 | A 125 | > 126 | } 127 | ``` 128 | 129 | Added in v1.0.0 130 | 131 | ## ap 132 | 133 | **Signature** 134 | 135 | ```ts 136 | export declare const ap: ( 137 | F: SemiApplicative 138 | ) => { 139 | (that: Kind): ( 140 | self: Kind B> 141 | ) => Kind 142 | (self: Kind B>, that: Kind): Kind< 143 | F, 144 | R1 & R2, 145 | O1 | O2, 146 | E1 | E2, 147 | B 148 | > 149 | } 150 | ``` 151 | 152 | Added in v1.0.0 153 | 154 | ## zipWith 155 | 156 | Zips two `F` values together using a provided function, returning a new `F` of the result. 157 | 158 | **Signature** 159 | 160 | ```ts 161 | export declare const zipWith: ( 162 | F: SemiApplicative 163 | ) => { 164 | (that: Kind, f: (a: A, b: B) => C): ( 165 | self: Kind 166 | ) => Kind 167 | ( 168 | self: Kind, 169 | that: Kind, 170 | f: (a: A, b: B) => C 171 | ): Kind 172 | } 173 | ``` 174 | 175 | Added in v1.0.0 176 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fp-ts/core", 3 | "version": "0.2.1", 4 | "publishConfig": { 5 | "access": "public", 6 | "directory": "dist" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/fp-ts/core.git" 11 | }, 12 | "author": "Giulio Canti ", 13 | "license": "MIT", 14 | "bugs": { 15 | "url": "https://github.com/fp-ts/core/issues" 16 | }, 17 | "homepage": "https://github.com/fp-ts/core", 18 | "description": "Functional programming in TypeScript", 19 | "tags": [ 20 | "typescript", 21 | "algebraic-data-types", 22 | "functional-programming" 23 | ], 24 | "keywords": [ 25 | "typescript", 26 | "algebraic-data-types", 27 | "functional-programming" 28 | ], 29 | "scripts": { 30 | "version": "changeset version && pnpm install --no-frozen-lockfile && pnpm run docs-update", 31 | "release": "pnpm run build && changeset publish", 32 | "clean": "rimraf build tsbuildinfo dist .ultra.cache.json", 33 | "build": "pnpm build-all && pnpm build-pack", 34 | "build-cjs": "babel build/esm --config-file ./.babel.cjs.json --out-dir build/cjs --out-file-extension .js --source-maps", 35 | "build-mjs": "babel build/esm --config-file ./.babel.mjs.json --out-dir build/mjs --out-file-extension .mjs --source-maps", 36 | "build-post": "build-utils pack-v3", 37 | "build-pack": "concurrently \"pnpm build-cjs\" \"pnpm build-mjs\" && pnpm build-post", 38 | "build-all": "tsc -b tsconfig.json", 39 | "build-watch": "tsc -b tsconfig.json --watch", 40 | "dtslint": "dtslint dtslint", 41 | "lint": "eslint . --ext .ts,.tsx", 42 | "autofix": "pnpm lint --fix", 43 | "tc": "tsc --noEmit", 44 | "docs": "docs-ts", 45 | "docs-update": "git add --force --all docs/modules || true", 46 | "circular": "madge --ts-config ./tsconfig.madge.json --circular --no-color --no-spinner --warning build/esm", 47 | "test": "vitest", 48 | "test-all": "npm run circular && npm run dtslint && vitest run", 49 | "coverage": "vitest run --coverage" 50 | }, 51 | "exports": { 52 | ".": { 53 | "require": "./build/cjs/index.js" 54 | }, 55 | "./*": { 56 | "require": "./build/cjs/*.js" 57 | } 58 | }, 59 | "config": { 60 | "side": [], 61 | "modules": [], 62 | "global": [] 63 | }, 64 | "devDependencies": { 65 | "@babel/cli": "^7.18.6", 66 | "@babel/core": "^7.20.5", 67 | "@babel/plugin-transform-modules-commonjs": "^7.19.6", 68 | "@changesets/changelog-github": "^0.4.7", 69 | "@changesets/cli": "^2.25.2", 70 | "@effect-ts/build-utils": "0.40.3", 71 | "@effect-ts/core": "^0.60.2", 72 | "@effect/language-service": "^0.0.17", 73 | "@repo-tooling/eslint-plugin-dprint": "^0.0.4", 74 | "@types/benchmark": "^2.1.2", 75 | "@types/glob": "^8.0.0", 76 | "@types/jest": "^29.2.3", 77 | "@types/node": "^18.11.9", 78 | "@types/prettier": "2.7.1", 79 | "@types/rimraf": "^3.0.2", 80 | "@typescript-eslint/eslint-plugin": "^5.44.0", 81 | "@typescript-eslint/parser": "^5.44.0", 82 | "@vitest/coverage-c8": "^0.27.1", 83 | "babel-plugin-annotate-pure-calls": "^0.4.0", 84 | "benchmark": "^2.1.4", 85 | "c8": "^7.11.3", 86 | "concurrently": "^7.6.0", 87 | "cpx": "^1.5.0", 88 | "docs-ts": "0.6.10", 89 | "dtslint": "github:gcanti/dtslint", 90 | "eslint": "^8.28.0", 91 | "eslint-import-resolver-typescript": "^3.5.2", 92 | "eslint-plugin-codegen": "0.16.1", 93 | "eslint-plugin-deprecation": "^1.3.3", 94 | "eslint-plugin-import": "^2.26.0", 95 | "eslint-plugin-simple-import-sort": "^8.0.0", 96 | "eslint-plugin-sort-destructure-keys": "^1.4.0", 97 | "fast-check": "^3.3.0", 98 | "glob": "^8.0.3", 99 | "madge": "^5.0.1", 100 | "picocolors": "^1.0.0", 101 | "prettier": "^2.8.0", 102 | "rimraf": "^3.0.2", 103 | "ts-node": "^10.9.1", 104 | "tslint": "^6.1.3", 105 | "typescript": "^4.9.3", 106 | "ultra-runner": "^3.10.5", 107 | "vite": "^3.2.4", 108 | "vitest": "0.25.3" 109 | }, 110 | "pnpm": { 111 | "patchedDependencies": { 112 | "@effect-ts/build-utils@0.40.3": "patches/@effect-ts__build-utils@0.40.3.patch", 113 | "docs-ts@0.6.10": "patches/docs-ts@0.6.10.patch" 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /docs/modules/typeclass/TraversableFilterable.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/TraversableFilterable.ts 3 | nav_order: 44 4 | parent: Modules 5 | --- 6 | 7 | ## TraversableFilterable overview 8 | 9 | `TraversableFilterable` represents data structures which can be _partitioned_ with effects in some `Applicative` functor. 10 | 11 | Added in v1.0.0 12 | 13 | --- 14 | 15 |

Table of contents

16 | 17 | - [models](#models) 18 | - [TraversableFilterable (interface)](#traversablefilterable-interface) 19 | - [utils](#utils) 20 | - [traverseFilter](#traversefilter) 21 | - [traverseFilterMap](#traversefiltermap) 22 | - [traversePartition](#traversepartition) 23 | - [traversePartitionMap](#traversepartitionmap) 24 | 25 | --- 26 | 27 | # models 28 | 29 | ## TraversableFilterable (interface) 30 | 31 | **Signature** 32 | 33 | ```ts 34 | export interface TraversableFilterable extends TypeClass { 35 | readonly traversePartitionMap: ( 36 | F: Applicative 37 | ) => { 38 | (f: (a: A) => Kind>): ( 39 | self: Kind 40 | ) => Kind, Kind]> 41 | (self: Kind, f: (a: A) => Kind>): Kind< 42 | F, 43 | R, 44 | O, 45 | E, 46 | [Kind, Kind] 47 | > 48 | } 49 | 50 | readonly traverseFilterMap: ( 51 | F: Applicative 52 | ) => { 53 | (f: (a: A) => Kind>): ( 54 | self: Kind 55 | ) => Kind> 56 | (self: Kind, f: (a: A) => Kind>): Kind< 57 | F, 58 | R, 59 | O, 60 | E, 61 | Kind 62 | > 63 | } 64 | } 65 | ``` 66 | 67 | Added in v1.0.0 68 | 69 | # utils 70 | 71 | ## traverseFilter 72 | 73 | **Signature** 74 | 75 | ```ts 76 | export declare const traverseFilter: ( 77 | T: TraversableFilterable 78 | ) => ( 79 | F: Applicative 80 | ) => { 81 | (predicate: (a: A) => Kind): ( 82 | self: Kind 83 | ) => Kind> 84 | ( 85 | self: Kind, 86 | predicate: (a: A) => Kind 87 | ): Kind> 88 | } 89 | ``` 90 | 91 | Added in v1.0.0 92 | 93 | ## traverseFilterMap 94 | 95 | Returns a default binary `traverseFilterMap` implementation. 96 | 97 | **Signature** 98 | 99 | ```ts 100 | export declare const traverseFilterMap: ( 101 | T: Traversable & filterable.Filterable 102 | ) => ( 103 | F: Applicative 104 | ) => ( 105 | self: Kind, 106 | f: (a: A) => Kind> 107 | ) => Kind> 108 | ``` 109 | 110 | Added in v1.0.0 111 | 112 | ## traversePartition 113 | 114 | **Signature** 115 | 116 | ```ts 117 | export declare const traversePartition: ( 118 | T: TraversableFilterable 119 | ) => ( 120 | F: Applicative 121 | ) => { 122 | (predicate: (a: A) => Kind): ( 123 | self: Kind 124 | ) => Kind, Kind]> 125 | ( 126 | self: Kind, 127 | predicate: (a: A) => Kind 128 | ): Kind, Kind]> 129 | } 130 | ``` 131 | 132 | Added in v1.0.0 133 | 134 | ## traversePartitionMap 135 | 136 | Returns a default binary `traversePartitionMap` implementation. 137 | 138 | **Signature** 139 | 140 | ```ts 141 | export declare const traversePartitionMap: ( 142 | T: Traversable & Covariant & filterable.Filterable 143 | ) => ( 144 | F: Applicative 145 | ) => ( 146 | self: Kind, 147 | f: (a: A) => Kind> 148 | ) => Kind, Kind]> 149 | ``` 150 | 151 | Added in v1.0.0 152 | -------------------------------------------------------------------------------- /test/typeclass/Semigroup.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "@fp-ts/core/Function" 2 | import * as Number from "@fp-ts/core/Number" 3 | import * as String from "@fp-ts/core/String" 4 | import * as order from "@fp-ts/core/typeclass/Order" 5 | import * as _ from "@fp-ts/core/typeclass/Semigroup" 6 | import * as U from "../util" 7 | 8 | describe("Semigroup", () => { 9 | it("exports", () => { 10 | expect(_.Invariant).exist 11 | }) 12 | 13 | it("reverse", () => { 14 | const S = _.reverse(String.Semigroup) 15 | U.deepStrictEqual(S.combine("a", "b"), "ba") 16 | U.deepStrictEqual(S.combineMany("a", []), "a") 17 | U.deepStrictEqual(S.combineMany("a", ["b"]), "ba") 18 | U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "dcba") 19 | }) 20 | 21 | it("constant", () => { 22 | const S = _.constant("-") 23 | U.deepStrictEqual(S.combine("a", "b"), "-") 24 | U.deepStrictEqual(S.combineMany("a", []), "-") 25 | U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "-") 26 | }) 27 | 28 | it("intercalate", () => { 29 | const S = pipe(String.Semigroup, _.intercalate("|")) 30 | U.deepStrictEqual(S.combine("a", "b"), "a|b") 31 | U.deepStrictEqual(S.combineMany("a", []), "a") 32 | U.deepStrictEqual(S.combineMany("a", ["b"]), "a|b") 33 | U.deepStrictEqual(S.combineMany("a", ["b", "c", "d"]), "a|b|c|d") 34 | }) 35 | 36 | describe("min", () => { 37 | it("should return the minimum", () => { 38 | const S = _.min(Number.Order) 39 | U.deepStrictEqual(S.combineMany(1, []), 1) 40 | U.deepStrictEqual(S.combineMany(1, [3, 2]), 1) 41 | }) 42 | 43 | it("should return the last minimum", () => { 44 | type Item = { a: number } 45 | const A = _.min(pipe(Number.Order, order.contramap((_: Item) => _.a))) 46 | const item: Item = { a: 1 } 47 | U.strictEqual(A.combineMany({ a: 2 }, [{ a: 1 }, item]), item) 48 | U.strictEqual(A.combineMany(item, []), item) 49 | }) 50 | }) 51 | 52 | describe("max", () => { 53 | it("should return the maximum", () => { 54 | const S = _.max(Number.Order) 55 | U.deepStrictEqual(S.combineMany(1, []), 1) 56 | U.deepStrictEqual(S.combineMany(1, [3, 2]), 3) 57 | }) 58 | 59 | it("should return the last minimum", () => { 60 | type Item = { a: number } 61 | const S = _.max(pipe(Number.Order, order.contramap((_: Item) => _.a))) 62 | const item: Item = { a: 2 } 63 | U.strictEqual(S.combineMany({ a: 1 }, [{ a: 2 }, item]), item) 64 | U.strictEqual(S.combineMany(item, []), item) 65 | }) 66 | }) 67 | 68 | it("first", () => { 69 | const S = _.first() 70 | U.deepStrictEqual(S.combine(1, 2), 1) 71 | U.deepStrictEqual(S.combineMany(1, []), 1) 72 | U.deepStrictEqual(S.combineMany(1, [2, 3, 4, 5, 6]), 1) 73 | }) 74 | 75 | it("last", () => { 76 | const S = _.last() 77 | U.deepStrictEqual(S.combine(1, 2), 2) 78 | U.deepStrictEqual(S.combineMany(1, []), 1) 79 | U.deepStrictEqual(S.combineMany(1, [2, 3, 4, 5, 6]), 6) 80 | }) 81 | 82 | it("imap", () => { 83 | const imap = _.imap 84 | const S1 = imap((s: string) => [s], ([s]) => s)(String.Semigroup) 85 | U.deepStrictEqual(S1.combine(["a"], ["b"]), ["ab"]) 86 | U.deepStrictEqual(S1.combineMany(["a"], []), ["a"]) 87 | U.deepStrictEqual(S1.combineMany(["a"], [["b"]]), ["ab"]) 88 | U.deepStrictEqual(S1.combineMany(["a"], [["b"], ["c"]]), ["abc"]) 89 | // should handle an Iterable 90 | U.deepStrictEqual(S1.combineMany(["a"], new Set([["b"], ["c"]])), ["abc"]) 91 | 92 | const S2 = pipe(String.Semigroup, _.Invariant.imap((s: string) => [s], ([s]) => s)) 93 | U.deepStrictEqual(S2.combineMany(["a"], [["b"], ["c"]]), ["abc"]) 94 | }) 95 | 96 | it("product", () => { 97 | const S = pipe( 98 | _.SemiProduct.product( 99 | _.SemiProduct.product(String.Semigroup, Number.SemigroupSum), 100 | Number.SemigroupMultiply 101 | ), 102 | _.imap( 103 | ([[a, b], c]): [string, number, number] => [a, b, c], 104 | ([a, b, c]): [[string, number], number] => [[a, b], c] 105 | ) 106 | ) 107 | U.deepStrictEqual(S.combine(["a", 2, 3], ["b", 3, 4]), ["ab", 5, 12]) 108 | }) 109 | 110 | it("productMany", () => { 111 | const S = _.SemiProduct.productMany(String.Semigroup, [String.Semigroup, String.Semigroup]) 112 | U.deepStrictEqual(S.combine(["a", "b", "c"], ["d", "e", "f"]), ["ad", "be", "cf"]) 113 | }) 114 | 115 | it("productAll", () => { 116 | const S = _.Product.productAll([String.Semigroup, String.Semigroup]) 117 | U.deepStrictEqual(S.combine(["a1", "b1"], ["a2", "b2"]), ["a1a2", "b1b2"]) 118 | }) 119 | }) 120 | -------------------------------------------------------------------------------- /docs/modules/typeclass/Filterable.ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typeclass/Filterable.ts 3 | nav_order: 28 4 | parent: Modules 5 | --- 6 | 7 | ## Filterable overview 8 | 9 | `Filterable` represents data structures which can be _partitioned_/_filtered_. 10 | 11 | Added in v1.0.0 12 | 13 | --- 14 | 15 |

Table of contents

16 | 17 | - [models](#models) 18 | - [Filterable (interface)](#filterable-interface) 19 | - [utils](#utils) 20 | - [compact](#compact) 21 | - [filter](#filter) 22 | - [filterMapComposition](#filtermapcomposition) 23 | - [partition](#partition) 24 | - [partitionMapComposition](#partitionmapcomposition) 25 | - [separate](#separate) 26 | 27 | --- 28 | 29 | # models 30 | 31 | ## Filterable (interface) 32 | 33 | **Signature** 34 | 35 | ```ts 36 | export interface Filterable extends TypeClass { 37 | readonly partitionMap: { 38 | (f: (a: A) => Either): ( 39 | self: Kind 40 | ) => [Kind, Kind] 41 | (self: Kind, f: (a: A) => Either): [Kind, Kind] 42 | } 43 | 44 | readonly filterMap: { 45 | (f: (a: A) => Option): (self: Kind) => Kind 46 | (self: Kind, f: (a: A) => Option): Kind 47 | } 48 | } 49 | ``` 50 | 51 | Added in v1.0.0 52 | 53 | # utils 54 | 55 | ## compact 56 | 57 | **Signature** 58 | 59 | ```ts 60 | export declare const compact: ( 61 | F: Filterable 62 | ) => (self: Kind>) => Kind 63 | ``` 64 | 65 | Added in v1.0.0 66 | 67 | ## filter 68 | 69 | **Signature** 70 | 71 | ```ts 72 | export declare const filter: ( 73 | F: Filterable 74 | ) => { 75 | (refinement: (a: A) => a is B): ( 76 | self: Kind 77 | ) => Kind 78 | (predicate: (a: A) => boolean): (self: Kind) => Kind 79 | (self: Kind, refinement: (a: A) => a is B): Kind< 80 | F, 81 | R, 82 | O, 83 | E, 84 | B 85 | > 86 | (self: Kind, predicate: (a: A) => boolean): Kind 87 | } 88 | ``` 89 | 90 | Added in v1.0.0 91 | 92 | ## filterMapComposition 93 | 94 | Returns a default binary `filterMap` composition. 95 | 96 | **Signature** 97 | 98 | ```ts 99 | export declare const filterMapComposition: ( 100 | F: Covariant, 101 | G: Filterable 102 | ) => ( 103 | self: Kind>, 104 | f: (a: A) => Option 105 | ) => Kind> 106 | ``` 107 | 108 | Added in v1.0.0 109 | 110 | ## partition 111 | 112 | **Signature** 113 | 114 | ```ts 115 | export declare const partition: ( 116 | F: Filterable 117 | ) => { 118 | (refinement: (a: A) => a is B): ( 119 | self: Kind 120 | ) => [Kind, Kind] 121 | (predicate: (a: A) => boolean): ( 122 | self: Kind 123 | ) => [Kind, Kind] 124 | (self: Kind, refinement: (a: A) => a is B): [ 125 | Kind, 126 | Kind 127 | ] 128 | (self: Kind, predicate: (a: A) => boolean): [ 129 | Kind, 130 | Kind 131 | ] 132 | } 133 | ``` 134 | 135 | Added in v1.0.0 136 | 137 | ## partitionMapComposition 138 | 139 | Returns a default binary `partitionMap` composition. 140 | 141 | **Signature** 142 | 143 | ```ts 144 | export declare const partitionMapComposition: ( 145 | F: Covariant, 146 | G: Filterable 147 | ) => ( 148 | self: Kind>, 149 | f: (a: A) => Either 150 | ) => [Kind>, Kind>] 151 | ``` 152 | 153 | Added in v1.0.0 154 | 155 | ## separate 156 | 157 | **Signature** 158 | 159 | ```ts 160 | export declare const separate: ( 161 | F: Filterable 162 | ) => (self: Kind>) => [Kind, Kind] 163 | ``` 164 | 165 | Added in v1.0.0 166 | -------------------------------------------------------------------------------- /test/Function.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "@fp-ts/core/Function" 2 | import * as String from "@fp-ts/core/String" 3 | import { deepStrictEqual, double } from "@fp-ts/core/test/util" 4 | import * as assert from "assert" 5 | 6 | const f = (n: number): number => n + 1 7 | const g = double 8 | 9 | describe.concurrent("Function", () => { 10 | it("apply", () => { 11 | deepStrictEqual(_.pipe(String.length, _.apply("a")), 1) 12 | }) 13 | 14 | it("compose", () => { 15 | deepStrictEqual(_.pipe(String.length, _.compose(double))("aaa"), 6) 16 | deepStrictEqual(_.compose(String.length, double)("aaa"), 6) 17 | }) 18 | 19 | it("flip", () => { 20 | const f = (a: number) => (b: string) => a - b.length 21 | const g = (a: number, i = 0) => (b: number) => a ** b + i 22 | 23 | deepStrictEqual(_.flip(f)("aaa")(2), -1) 24 | deepStrictEqual(_.flip(g)(2)(2, 1), 5) 25 | }) 26 | 27 | it("unsafeCoerce", () => { 28 | deepStrictEqual(_.unsafeCoerce, _.identity) 29 | }) 30 | 31 | it("constant", () => { 32 | deepStrictEqual(_.constant("a")(), "a") 33 | }) 34 | 35 | it("constTrue", () => { 36 | deepStrictEqual(_.constTrue(), true) 37 | }) 38 | 39 | it("constFalse", () => { 40 | deepStrictEqual(_.constFalse(), false) 41 | }) 42 | 43 | it("constNull", () => { 44 | deepStrictEqual(_.constNull(), null) 45 | }) 46 | 47 | it("constUndefined", () => { 48 | deepStrictEqual(_.constUndefined(), undefined) 49 | }) 50 | 51 | it("constVoid", () => { 52 | deepStrictEqual(_.constVoid(), undefined) 53 | }) 54 | 55 | it("absurd", () => { 56 | assert.throws(() => _.absurd(null as any as never)) 57 | }) 58 | 59 | it("hole", () => { 60 | assert.throws(() => _.hole()) 61 | }) 62 | 63 | it("SK", () => { 64 | expect(_.SK(1, 2)).toEqual(2) 65 | }) 66 | 67 | it("flow", () => { 68 | deepStrictEqual(_.flow(f)(2), 3) 69 | deepStrictEqual(_.flow(f, g)(2), 6) 70 | deepStrictEqual(_.flow(f, g, f)(2), 7) 71 | deepStrictEqual(_.flow(f, g, f, g)(2), 14) 72 | deepStrictEqual(_.flow(f, g, f, g, f)(2), 15) 73 | deepStrictEqual(_.flow(f, g, f, g, f, g)(2), 30) 74 | deepStrictEqual(_.flow(f, g, f, g, f, g, f)(2), 31) 75 | deepStrictEqual(_.flow(f, g, f, g, f, g, f, g)(2), 62) 76 | deepStrictEqual(_.flow(f, g, f, g, f, g, f, g, f)(2), 63) 77 | // this is just to satisfy noImplicitReturns and 100% coverage 78 | deepStrictEqual((_.flow as any)(...[f, g, f, g, f, g, f, g, f, g]), undefined) 79 | }) 80 | 81 | it("tupled", () => { 82 | const f1 = (a: number): number => a * 2 83 | const f2 = (a: number, b: number): number => a + b 84 | const u1 = _.tupled(f1) 85 | const u2 = _.tupled(f2) 86 | deepStrictEqual(u1([1]), 2) 87 | deepStrictEqual(u2([1, 2]), 3) 88 | }) 89 | 90 | it("untupled", () => { 91 | const f1 = (a: readonly [number]): number => a[0] * 2 92 | const f2 = (a: readonly [number, number]): number => a[0] + a[1] 93 | const u1 = _.untupled(f1) 94 | const u2 = _.untupled(f2) 95 | deepStrictEqual(u1(1), 2) 96 | deepStrictEqual(u2(1, 2), 3) 97 | }) 98 | 99 | it("pipe", () => { 100 | deepStrictEqual(_.pipe(2), 2) 101 | deepStrictEqual(_.pipe(2, f), 3) 102 | deepStrictEqual(_.pipe(2, f, g), 6) 103 | deepStrictEqual(_.pipe(2, f, g, f), 7) 104 | deepStrictEqual(_.pipe(2, f, g, f, g), 14) 105 | deepStrictEqual(_.pipe(2, f, g, f, g, f), 15) 106 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g), 30) 107 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f), 31) 108 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g), 62) 109 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f), 63) 110 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g), 126) 111 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f), 127) 112 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g), 254) 113 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f), 255) 114 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 510) 115 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 511) 116 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 1022) 117 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 1023) 118 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g), 2046) 119 | deepStrictEqual(_.pipe(2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f), 2047) 120 | deepStrictEqual( 121 | (_.pipe as any)(...[2, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g, f, g]), 122 | 4094 123 | ) 124 | }) 125 | 126 | it("dual", () => { 127 | const f = _.dual< 128 | (that: number) => (self: number) => number, 129 | (self: number, that: number) => number 130 | >(2, (a: number, b: number): number => a - b) 131 | deepStrictEqual(f(3, 2), 1) 132 | deepStrictEqual(_.pipe(3, f(2)), 1) 133 | }) 134 | }) 135 | --------------------------------------------------------------------------------