├── dtslint
├── ts3.5
│ ├── index.d.ts
│ ├── tslint.json
│ ├── tsconfig.json
│ ├── Iso.ts
│ ├── Traversal.ts
│ ├── Lens.ts
│ ├── Optional.ts
│ ├── Prism.ts
│ └── index.ts
└── index.d.ts
├── .gitignore
├── .vscode
└── settings.json
├── .prettierrc
├── docs
├── modules
│ ├── index.md
│ ├── At
│ │ ├── Set.ts.md
│ │ ├── Record.ts.md
│ │ ├── ReadonlySet.ts.md
│ │ └── ReadonlyRecord.ts.md
│ ├── internal.ts.md
│ ├── Index
│ │ ├── Array.ts.md
│ │ ├── Record.ts.md
│ │ ├── NonEmptyArray.ts.md
│ │ ├── ReadonlyArray.ts.md
│ │ ├── ReadonlyRecord.ts.md
│ │ └── ReadonlyNonEmptyArray.ts.md
│ ├── Either.ts.md
│ ├── At.ts.md
│ ├── Ix.ts.md
│ ├── Lens.ts.md
│ ├── Iso.ts.md
│ └── Prism.ts.md
├── _config.yml
└── index.md
├── tsconfig.build.json
├── tsconfig.build-es6.json
├── test
├── util.ts
├── 2.2
│ ├── Getter.ts
│ ├── Either.ts
│ ├── Setter.ts
│ ├── Iso.ts
│ ├── Fold.ts
│ ├── conversions.ts
│ ├── Traversal.ts
│ ├── At.ts
│ ├── Prism.ts
│ ├── Lens.ts
│ ├── Optional.ts
│ └── Index.ts
├── Ix.ts
├── At.ts
├── Traversal.ts
├── Lens.ts
├── Optional.ts
├── Iso.ts
└── Prism.ts
├── .github
├── ISSUE_TEMPLATE
│ ├── Documentation.md
│ ├── Bug_report.md
│ └── Feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── main.yml
├── scripts
├── pre-publish.ts
├── run.ts
├── release.ts
├── release-as-next.ts
├── FileSystem.ts
└── build.ts
├── src
├── Index
│ ├── ReadonlyArray.ts
│ ├── Record.ts
│ ├── ReadonlyRecord.ts
│ ├── ReadonlyNonEmptyArray.ts
│ ├── Array.ts
│ └── NonEmptyArray.ts
├── At
│ ├── ReadonlySet.ts
│ ├── ReadonlyRecord.ts
│ ├── Set.ts
│ └── Record.ts
├── Either.ts
├── At.ts
└── Ix.ts
├── examples
├── tsconfig.json
├── Iso.ts
├── Traversal.ts
├── Fold.ts
├── Optional.ts
├── Prism.ts
├── introduction.ts
└── Lens.ts
├── tslint.json
├── jest.config.js
├── tsconfig.json
├── LICENSE
├── package.json
├── perf
└── 119.ts
├── README.md
└── CHANGELOG.md
/dtslint/ts3.5/index.d.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/dtslint/index.d.ts:
--------------------------------------------------------------------------------
1 | // TypeScript Version: 3.5
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | node_modules
3 | lib
4 | es6
5 | dev
6 | coverage
7 | dist
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "./node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 120,
5 | "trailingComma": "none"
6 | }
7 |
--------------------------------------------------------------------------------
/docs/modules/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Modules
3 | has_children: true
4 | permalink: /docs/modules
5 | nav_order: 2
6 | ---
7 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false
5 | },
6 | "include": ["./src"]
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.build-es6.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.build.json",
3 | "compilerOptions": {
4 | "outDir": "./dist/es6",
5 | "module": "es6"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/util.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 |
3 | export const deepStrictEqual = (actual: A, expected: A) => {
4 | assert.deepStrictEqual(actual, expected)
5 | }
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Documentation.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Documentation"
3 | about: Imrovements or suggestions of monocle-ts documentation
4 | ---
5 |
6 | ## 📖 Documentation
7 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dtslint.json",
3 | "rules": {
4 | "semicolon": false,
5 | "array-type": false,
6 | "no-unnecessary-generics": false
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/scripts/pre-publish.ts:
--------------------------------------------------------------------------------
1 | import { left } from 'fp-ts/lib/TaskEither'
2 | import { run } from './run'
3 |
4 | const main = left(new Error('"npm publish" can not be run from root, run "npm run release" instead'))
5 |
6 | run(main)
7 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | remote_theme: pmarsceill/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 | 'monocle-ts on GitHub':
9 | - 'https://github.com/gcanti/monocle-ts'
10 |
--------------------------------------------------------------------------------
/src/Index/ReadonlyArray.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.2.0
3 | */
4 | import { Index } from '..'
5 | import * as A from './Array'
6 |
7 | /**
8 | * @category constructor
9 | * @since 2.2.0
10 | */
11 | export const indexReadonlyArray: () => Index, number, A> = A.indexArray as any
12 |
--------------------------------------------------------------------------------
/src/At/ReadonlySet.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.2.0
3 | */
4 | import { Eq } from 'fp-ts/lib/Eq'
5 | import { At } from '..'
6 | import * as S from './Set'
7 |
8 | /**
9 | * @category constructor
10 | * @since 2.2.0
11 | */
12 | export const atReadonlySet: (E: Eq) => At, A, boolean> = S.atSet as any
13 |
--------------------------------------------------------------------------------
/src/Index/Record.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.7.0
3 | */
4 | import { Index } from '..'
5 | import { atRecord } from '../At/Record'
6 |
7 | /**
8 | * @category constructor
9 | * @since 1.7.0
10 | */
11 | export function indexRecord(): Index, string, A> {
12 | return Index.fromAt(atRecord())
13 | }
14 |
--------------------------------------------------------------------------------
/src/Index/ReadonlyRecord.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.2.0
3 | */
4 | import { ReadonlyRecord } from 'fp-ts/lib/ReadonlyRecord'
5 | import { Index } from '..'
6 | import * as R from './Record'
7 |
8 | /**
9 | * @category constructor
10 | * @since 2.2.0
11 | */
12 | export const indexReadonlyRecord: () => Index, string, A> = R.indexRecord
13 |
--------------------------------------------------------------------------------
/src/At/ReadonlyRecord.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.2.0
3 | */
4 | import { Option } from 'fp-ts/lib/Option'
5 | import { ReadonlyRecord } from 'fp-ts/lib/ReadonlyRecord'
6 | import { At } from '..'
7 | import * as R from './Record'
8 |
9 | /**
10 | * @category constructor
11 | * @since 2.2.0
12 | */
13 | export const atReadonlyRecord: () => At, string, Option> = R.atRecord
14 |
--------------------------------------------------------------------------------
/test/2.2/Getter.ts:
--------------------------------------------------------------------------------
1 | import { Getter } from '../../src'
2 | import * as assert from 'assert'
3 |
4 | type Point = {
5 | readonly x: number
6 | readonly y: number
7 | }
8 |
9 | const _x = new Getter((p: Point): number => p.x)
10 |
11 | describe('Getter', () => {
12 | const eg0 = { x: 42, y: -1 }
13 |
14 | it('get', () => {
15 | assert.strictEqual(_x.get(eg0), 42)
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "strict": true,
5 | "noImplicitAny": true,
6 | "noImplicitThis": true,
7 | "strictNullChecks": true,
8 | "strictFunctionTypes": true,
9 | "noImplicitReturns": false,
10 | "noUnusedLocals": false,
11 | "noUnusedParameters": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "target": "es5",
14 | "lib": ["es2015"]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitAny": true,
5 | "noImplicitReturns": true,
6 | "noImplicitThis": true,
7 | "noUnusedLocals": false,
8 | "noUnusedParameters": false,
9 | "noFallthroughCasesInSwitch": true,
10 | "noEmitOnError": true,
11 | "strictNullChecks": true,
12 | "target": "es5",
13 | "forceConsistentCasingInFileNames": true,
14 | "lib": [
15 | "es6"
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/modules/At/Set.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: At/Set.ts
3 | nav_order: 5
4 | parent: Modules
5 | ---
6 |
7 | ## Set overview
8 |
9 | Added in v1.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [atSet](#atset)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## atSet
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare function atSet(E: Eq): At, A, boolean>
28 | ```
29 |
30 | Added in v1.2.0
31 |
--------------------------------------------------------------------------------
/docs/modules/internal.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: internal.ts
3 | nav_order: 14
4 | parent: Modules
5 | ---
6 |
7 | ## internal overview
8 |
9 | **This module is experimental**
10 |
11 | Experimental features are published in order to get early feedback from the community.
12 |
13 | A feature tagged as _Experimental_ is in a high state of flux, you're at risk of it changing without notice.
14 |
15 | Added in v2.3.0
16 |
17 | ---
18 |
19 | Table of contents
20 |
21 | ---
22 |
--------------------------------------------------------------------------------
/examples/Iso.ts:
--------------------------------------------------------------------------------
1 | import { Iso } from '../src'
2 |
3 | const mTokm = new Iso(
4 | (m) => m / 1000,
5 | (km) => km * 1000
6 | )
7 |
8 | console.log(mTokm.get(100)) // => 0.1
9 | console.log(mTokm.reverseGet(1.2)) // => 1200
10 |
11 | const kmToMile = new Iso(
12 | (km) => km / 1.60934,
13 | (miles) => miles * 1.60934
14 | )
15 |
16 | // composition
17 | const mToMile = mTokm.compose(kmToMile)
18 |
19 | console.log(mToMile.get(100)) // => 0.06213727366498068
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Before submitting a pull request,** please make sure the following is done:
2 |
3 | - Fork [the repository](https://github.com/gcanti/monocle-ts) and create your branch from `master`.
4 | - Run `npm install` in the repository root.
5 | - If you've fixed a bug or added code that should be tested, add tests!
6 | - Ensure the test suite passes (`npm test`).
7 |
8 | **Note**. If you find a typo in the **documentation**, make sure to modify the corresponding source (docs are generated).
9 |
--------------------------------------------------------------------------------
/scripts/run.ts:
--------------------------------------------------------------------------------
1 | import { fold } from 'fp-ts/lib/Either'
2 | import { TaskEither } from 'fp-ts/lib/TaskEither'
3 |
4 | export function run(eff: TaskEither): void {
5 | eff()
6 | .then(
7 | fold(
8 | (e) => {
9 | throw e
10 | },
11 | (_) => {
12 | process.exitCode = 0
13 | }
14 | )
15 | )
16 | .catch((e) => {
17 | console.error(e) // tslint:disable-line no-console
18 |
19 | process.exitCode = 1
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-config-standard", "tslint-immutable"],
3 | "rules": {
4 | "space-before-function-paren": false,
5 | "no-use-before-declare": false,
6 | "variable-name": false,
7 | "quotemark": [true, "single", "jsx-double"],
8 | "ter-indent": false,
9 | "strict-boolean-expressions": true,
10 | "forin": true,
11 | "no-console": true,
12 | "array-type": [true, "generic"],
13 | "readonly-keyword": true,
14 | "readonly-array": false
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/modules/Index/Array.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/Array.ts
3 | nav_order: 8
4 | parent: Modules
5 | ---
6 |
7 | ## Array overview
8 |
9 | Added in v1.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexArray](#indexarray)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## indexArray
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare function indexArray(): Index, number, A>
28 | ```
29 |
30 | Added in v1.2.0
31 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'node',
4 | collectCoverage: true,
5 | collectCoverageFrom: ['src/**/*.ts', '!src/index.ts'],
6 | transform: {
7 | '^.+\\.tsx?$': 'ts-jest'
8 | },
9 | testRegex: 'test',
10 | moduleFileExtensions: ['ts', 'js'],
11 | coverageThreshold: {
12 | global: {
13 | branches: 100,
14 | functions: 100,
15 | lines: 100,
16 | statements: 100
17 | }
18 | },
19 | modulePathIgnorePatterns: ['util']
20 | }
21 |
--------------------------------------------------------------------------------
/docs/modules/At/Record.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: At/Record.ts
3 | nav_order: 4
4 | parent: Modules
5 | ---
6 |
7 | ## Record overview
8 |
9 | Added in v1.7.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [atRecord](#atrecord)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## atRecord
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare function atRecord(): At, string, Option>
28 | ```
29 |
30 | Added in v1.7.0
31 |
--------------------------------------------------------------------------------
/docs/modules/Index/Record.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/Record.ts
3 | nav_order: 13
4 | parent: Modules
5 | ---
6 |
7 | ## Record overview
8 |
9 | Added in v1.7.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexRecord](#indexrecord)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## indexRecord
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare function indexRecord(): Index, string, A>
28 | ```
29 |
30 | Added in v1.7.0
31 |
--------------------------------------------------------------------------------
/src/Index/ReadonlyNonEmptyArray.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.2.0
3 | */
4 | import { Index } from '..'
5 | import * as NEA from './NonEmptyArray'
6 |
7 | /**
8 | * @category model
9 | * @since 2.2.0
10 | */
11 | export interface ReadonlyNonEmptyArray extends ReadonlyArray {
12 | readonly 0: A
13 | }
14 |
15 | /**
16 | * @category constructor
17 | * @since 2.2.0
18 | */
19 | export const indexReadonlyNonEmptyArray: () => Index<
20 | ReadonlyNonEmptyArray,
21 | number,
22 | A
23 | > = NEA.indexNonEmptyArray as any
24 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/Iso.ts:
--------------------------------------------------------------------------------
1 | import * as _ from '../../src/Iso'
2 | import { pipe } from 'fp-ts/lib/pipeable'
3 |
4 | interface A {
5 | a: string
6 | b: number
7 | c: string | boolean
8 | }
9 |
10 | declare const isoC: _.Iso
11 |
12 | //
13 | // modify
14 | //
15 |
16 | // $ExpectType (s: A) => A
17 | pipe(isoC, _.modify((
18 | a // $ExpectType string | boolean
19 | ) => a))
20 |
21 | // $ExpectType (s: A) => A
22 | pipe(isoC, _.modify(() => 'foo'))
23 |
24 | // $ExpectType (s: A) => A
25 | pipe(isoC, _.modify(() => 'foo'))
26 |
--------------------------------------------------------------------------------
/examples/Traversal.ts:
--------------------------------------------------------------------------------
1 | import { fromTraversable } from '../src'
2 | import { array } from 'fp-ts/lib/Array'
3 |
4 | const eachL = fromTraversable(array)()
5 |
6 | const xs = [1, 2, 3, 4]
7 |
8 | console.log(eachL.set(0)(xs)) // => [ 0, 0, 0, 0 ]
9 |
10 | console.log(eachL.modify((n: number) => n + 1)(xs)) // => [ 2, 3, 4, 5 ]
11 |
12 | const fold = eachL.asFold()
13 |
14 | console.log(fold.getAll(xs))
15 | console.log(fold.headOption(xs))
16 | console.log(fold.find((n) => n > 2)(xs))
17 | console.log(fold.all((n: number) => n % 2 === 0)(xs))
18 |
--------------------------------------------------------------------------------
/docs/modules/At/ReadonlySet.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: At/ReadonlySet.ts
3 | nav_order: 3
4 | parent: Modules
5 | ---
6 |
7 | ## ReadonlySet overview
8 |
9 | Added in v2.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [atReadonlySet](#atreadonlyset)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## atReadonlySet
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare const atReadonlySet: (E: Eq) => At, A, boolean>
28 | ```
29 |
30 | Added in v2.2.0
31 |
--------------------------------------------------------------------------------
/examples/Fold.ts:
--------------------------------------------------------------------------------
1 | import { Fold, fromFoldable } from '../src'
2 | import { array } from 'fp-ts/lib/Array'
3 | import { monoidProduct, monoidSum } from 'fp-ts/lib/Monoid'
4 | import { identity } from 'fp-ts/lib/function'
5 |
6 | const xs = ['a', 'bb']
7 | const fold = fromFoldable(array)()
8 | const len = (s: string) => s.length
9 |
10 | console.log(fold.foldMap(monoidSum)(len)(xs))
11 | console.log(fold.foldMap(monoidProduct)(len)(xs))
12 |
13 | import * as either from 'fp-ts/lib/Either'
14 |
15 | const fold2 = fromFoldable(either)()
16 |
--------------------------------------------------------------------------------
/docs/modules/Index/NonEmptyArray.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/NonEmptyArray.ts
3 | nav_order: 9
4 | parent: Modules
5 | ---
6 |
7 | ## NonEmptyArray overview
8 |
9 | Added in v1.5.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexNonEmptyArray](#indexnonemptyarray)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## indexNonEmptyArray
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare function indexNonEmptyArray(): Index, number, A>
28 | ```
29 |
30 | Added in v1.5.0
31 |
--------------------------------------------------------------------------------
/docs/modules/Index/ReadonlyArray.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/ReadonlyArray.ts
3 | nav_order: 10
4 | parent: Modules
5 | ---
6 |
7 | ## ReadonlyArray overview
8 |
9 | Added in v2.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexReadonlyArray](#indexreadonlyarray)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## indexReadonlyArray
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare const indexReadonlyArray: () => Index
28 | ```
29 |
30 | Added in v2.2.0
31 |
--------------------------------------------------------------------------------
/docs/modules/At/ReadonlyRecord.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: At/ReadonlyRecord.ts
3 | nav_order: 2
4 | parent: Modules
5 | ---
6 |
7 | ## ReadonlyRecord overview
8 |
9 | Added in v2.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [atReadonlyRecord](#atreadonlyrecord)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## atReadonlyRecord
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare const atReadonlyRecord: () => At>, string, Option>
28 | ```
29 |
30 | Added in v2.2.0
31 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "outDir": "./dist/lib",
5 | "target": "es5",
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "lib": ["es6", "dom"],
9 | "sourceMap": false,
10 | "declaration": true,
11 | "strict": true,
12 | "noImplicitReturns": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "forceConsistentCasingInFileNames": true,
17 | "stripInternal": true
18 | },
19 | "include": ["./src", "./test", "./scripts"]
20 | }
21 |
--------------------------------------------------------------------------------
/docs/modules/Index/ReadonlyRecord.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/ReadonlyRecord.ts
3 | nav_order: 12
4 | parent: Modules
5 | ---
6 |
7 | ## ReadonlyRecord overview
8 |
9 | Added in v2.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexReadonlyRecord](#indexreadonlyrecord)
17 |
18 | ---
19 |
20 | # constructor
21 |
22 | ## indexReadonlyRecord
23 |
24 | **Signature**
25 |
26 | ```ts
27 | export declare const indexReadonlyRecord: () => Index>, string, A>
28 | ```
29 |
30 | Added in v2.2.0
31 |
--------------------------------------------------------------------------------
/src/At/Set.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.2.0
3 | */
4 | import { At, Lens } from '..'
5 | import { Eq } from 'fp-ts/lib/Eq'
6 | import * as S from 'fp-ts/lib/Set'
7 |
8 | /**
9 | * @category constructor
10 | * @since 1.2.0
11 | */
12 | export function atSet(E: Eq): At, A, boolean> {
13 | const elemE = S.elem(E)
14 | const insertE = S.insert(E)
15 | const removeE = S.remove(E)
16 | return new At((at) => {
17 | const insertEAt = insertE(at)
18 | const removeEAt = removeE(at)
19 | return new Lens(
20 | (s) => elemE(at, s),
21 | (a) => (s) => (a ? insertEAt(s) : removeEAt(s))
22 | )
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/Either.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.6.0
3 | */
4 | import { Either, right, left, fold } from 'fp-ts/lib/Either'
5 | import { fromEither, none, some } from 'fp-ts/lib/Option'
6 | import { Prism } from '.'
7 |
8 | const r = new Prism, any>(fromEither, right)
9 |
10 | /**
11 | * @category constructor
12 | * @since 1.6.0
13 | */
14 | export const _right = (): Prism, A> => r
15 |
16 | const l = new Prism, any>(
17 | fold(some, () => none),
18 | left
19 | )
20 |
21 | /**
22 | * @category constructor
23 | * @since 1.6.0
24 | */
25 | export const _left = (): Prism, E> => l
26 |
--------------------------------------------------------------------------------
/scripts/release.ts:
--------------------------------------------------------------------------------
1 | import { run } from './run'
2 | import * as child_process from 'child_process'
3 | import { left, right } from 'fp-ts/lib/Either'
4 | import * as TE from 'fp-ts/lib/TaskEither'
5 |
6 | const DIST = 'dist'
7 |
8 | const exec = (cmd: string, args?: child_process.ExecOptions): TE.TaskEither => () =>
9 | new Promise((resolve) => {
10 | child_process.exec(cmd, args, (err) => {
11 | if (err !== null) {
12 | return resolve(left(err))
13 | }
14 |
15 | return resolve(right(undefined))
16 | })
17 | })
18 |
19 | export const main = exec('npm publish', {
20 | cwd: DIST
21 | })
22 |
23 | run(main)
24 |
--------------------------------------------------------------------------------
/test/2.2/Either.ts:
--------------------------------------------------------------------------------
1 | import { _right, _left } from '../../src/Either'
2 | import { right, left } from 'fp-ts/lib/Either'
3 | import { some, none } from 'fp-ts/lib/Option'
4 | import * as U from '../util'
5 |
6 | describe('Either', () => {
7 | it('_right', () => {
8 | const prism = _right()
9 | U.deepStrictEqual(prism.getOption(right(1)), some(1))
10 | U.deepStrictEqual(prism.getOption(left('a')), none)
11 | })
12 |
13 | it('_left', () => {
14 | const prism = _left()
15 | U.deepStrictEqual(prism.getOption(right(1)), none)
16 | U.deepStrictEqual(prism.getOption(left('a')), some('a'))
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/2.2/Setter.ts:
--------------------------------------------------------------------------------
1 | import { Setter } from '../../src'
2 | import * as assert from 'assert'
3 |
4 | type Point = {
5 | readonly x: number
6 | readonly y: number
7 | }
8 |
9 | const _x = new Setter((f) => (s) => ({
10 | x: f(s.x),
11 | y: s.y
12 | }))
13 |
14 | describe('Setter', () => {
15 | const double = (n: number) => n * 2
16 | const eg0 = { x: 42, y: -1 }
17 |
18 | it('modify', () => {
19 | const eg1 = _x.modify(double)(eg0)
20 | assert.strictEqual(eg1.x, double(eg0.x))
21 | assert.strictEqual(eg1.y, eg0.y)
22 | })
23 |
24 | it('set', () => {
25 | assert.strictEqual(_x.set(0)(eg0).x, 0)
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/src/At/Record.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.7.0
3 | */
4 | import { At, Lens } from '..'
5 | import { Option, isNone } from 'fp-ts/lib/Option'
6 | import * as R from 'fp-ts/lib/Record'
7 |
8 | /**
9 | * @category constructor
10 | * @since 1.7.0
11 | */
12 | export function atRecord(): At, string, Option> {
13 | return new At(
14 | (k) =>
15 | new Lens(
16 | (r) => R.lookup(k, r),
17 | (oa) => (r) => {
18 | if (isNone(oa)) {
19 | return R.deleteAt(k)(r)
20 | } else {
21 | return R.insertAt(k, oa.value)(r)
22 | }
23 | }
24 | )
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/scripts/release-as-next.ts:
--------------------------------------------------------------------------------
1 | import { run } from './run'
2 | import * as child_process from 'child_process'
3 | import { left, right } from 'fp-ts/lib/Either'
4 | import * as TE from 'fp-ts/lib/TaskEither'
5 |
6 | const DIST = 'dist'
7 |
8 | const exec = (cmd: string, args?: child_process.ExecOptions): TE.TaskEither => () =>
9 | new Promise((resolve) => {
10 | child_process.exec(cmd, args, (err) => {
11 | if (err !== null) {
12 | return resolve(left(err))
13 | }
14 |
15 | return resolve(right(undefined))
16 | })
17 | })
18 |
19 | export const main = exec('npm publish --tag=next', {
20 | cwd: DIST
21 | })
22 |
23 | run(main)
24 |
--------------------------------------------------------------------------------
/docs/modules/Either.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Either.ts
3 | nav_order: 6
4 | parent: Modules
5 | ---
6 |
7 | ## Either overview
8 |
9 | Added in v1.6.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [\_left](#_left)
17 | - [\_right](#_right)
18 |
19 | ---
20 |
21 | # constructor
22 |
23 | ## \_left
24 |
25 | **Signature**
26 |
27 | ```ts
28 | export declare const _left: () => Prism, E>
29 | ```
30 |
31 | Added in v1.6.0
32 |
33 | ## \_right
34 |
35 | **Signature**
36 |
37 | ```ts
38 | export declare const _right: () => Prism, A>
39 | ```
40 |
41 | Added in v1.6.0
42 |
--------------------------------------------------------------------------------
/src/Index/Array.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.2.0
3 | */
4 | import { Index, Optional } from '..'
5 | import { lookup, updateAt } from 'fp-ts/lib/Array'
6 | import { isNone } from 'fp-ts/lib/Option'
7 |
8 | /**
9 | * @category constructor
10 | * @since 1.2.0
11 | */
12 | export function indexArray(): Index, number, A> {
13 | return new Index(
14 | (i) =>
15 | new Optional(
16 | (as) => lookup(i, as),
17 | (a) => (as) => {
18 | const oas = updateAt(i, a)(as)
19 | if (isNone(oas)) {
20 | return as
21 | } else {
22 | return oas.value
23 | }
24 | }
25 | )
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | # Controls when the action will run.
4 | on:
5 | push:
6 | branches: [ master ]
7 | pull_request:
8 | branches: [ master ]
9 |
10 | # Allows you to run this workflow manually from the Actions tab
11 | workflow_dispatch:
12 |
13 | jobs:
14 | build:
15 |
16 | runs-on: ubuntu-latest
17 |
18 | strategy:
19 | matrix:
20 | node-version: [12.x]
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v1
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | - run: npm install
29 | - run: npm run build --if-present
30 | - run: npm test
31 |
--------------------------------------------------------------------------------
/src/Index/NonEmptyArray.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 1.5.0
3 | */
4 | import { Index, Optional } from '..'
5 | import { NonEmptyArray, updateAt } from 'fp-ts/lib/NonEmptyArray'
6 | import { lookup } from 'fp-ts/lib/Array'
7 | import { isNone } from 'fp-ts/lib/Option'
8 |
9 | /**
10 | * @category constructor
11 | * @since 1.5.0
12 | */
13 | export function indexNonEmptyArray(): Index, number, A> {
14 | return new Index(
15 | (i) =>
16 | new Optional(
17 | (s) => lookup(i, s),
18 | (a) => (nea) => {
19 | const onea = updateAt(i, a)(nea)
20 | if (isNone(onea)) {
21 | return nea
22 | } else {
23 | return onea.value
24 | }
25 | }
26 | )
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/Traversal.ts:
--------------------------------------------------------------------------------
1 | import * as T from '../../src/Traversal'
2 | import { pipe } from 'fp-ts/lib/pipeable'
3 |
4 | interface A {
5 | a: string
6 | b: number
7 | c: string | boolean
8 | }
9 |
10 | declare const traversalC: T.Traversal
11 |
12 | //
13 | // modify
14 | //
15 |
16 | // $ExpectType (s: A) => A
17 | pipe(traversalC, T.modify((
18 | a // $ExpectType string | boolean
19 | ) => a))
20 |
21 | // $ExpectType (s: A) => A
22 | pipe(traversalC, T.modify(() => 'foo'))
23 |
24 | // $ExpectType (s: A) => A
25 | pipe(traversalC, T.modify(() => 'foo'))
26 |
27 | // $ExpectError
28 | pipe(T.id(), T.props())
29 | // $ExpectError
30 | pipe(T.id(), T.props('a'))
31 |
32 | pipe(T.id(), T.props('a', 'b')) // $ExpectType Traversal
33 |
--------------------------------------------------------------------------------
/docs/modules/Index/ReadonlyNonEmptyArray.ts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Index/ReadonlyNonEmptyArray.ts
3 | nav_order: 11
4 | parent: Modules
5 | ---
6 |
7 | ## ReadonlyNonEmptyArray overview
8 |
9 | Added in v2.2.0
10 |
11 | ---
12 |
13 | Table of contents
14 |
15 | - [constructor](#constructor)
16 | - [indexReadonlyNonEmptyArray](#indexreadonlynonemptyarray)
17 | - [model](#model)
18 | - [ReadonlyNonEmptyArray (interface)](#readonlynonemptyarray-interface)
19 |
20 | ---
21 |
22 | # constructor
23 |
24 | ## indexReadonlyNonEmptyArray
25 |
26 | **Signature**
27 |
28 | ```ts
29 | export declare const indexReadonlyNonEmptyArray: () => Index, number, A>
30 | ```
31 |
32 | Added in v2.2.0
33 |
34 | # model
35 |
36 | ## ReadonlyNonEmptyArray (interface)
37 |
38 | **Signature**
39 |
40 | ```ts
41 | export interface ReadonlyNonEmptyArray extends ReadonlyArray {
42 | readonly 0: A
43 | }
44 | ```
45 |
46 | Added in v2.2.0
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Create a report to help make monocle-ts better
4 | ---
5 |
6 | ## 🐛 Bug report
7 |
8 | ### Current Behavior
9 |
10 |
11 |
12 | ### Expected behavior
13 |
14 |
15 |
16 | ### Reproducible example
17 |
18 | ### Suggested solution(s)
19 |
20 |
21 |
22 | ### Additional context
23 |
24 |
25 |
26 | ### Your environment
27 |
28 | Which versions of monocle-ts are affected by this issue? Did this work in previous versions of monocle-ts?
29 |
30 |
31 |
32 | | Software | Version(s) |
33 | | ---------- | ---------- |
34 | | monocle-ts | |
35 | | fp-ts | |
36 | | TypeScript | |
37 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/Lens.ts:
--------------------------------------------------------------------------------
1 | import * as L from '../../src/Lens'
2 | import { pipe } from 'fp-ts/lib/pipeable'
3 |
4 | interface A {
5 | a: string
6 | b: number
7 | c: string | boolean
8 | }
9 |
10 | declare const lensC: L.Lens
11 |
12 | //
13 | // modify
14 | //
15 |
16 | // $ExpectType (s: A) => A
17 | pipe(lensC, L.modify((
18 | a // $ExpectType string | boolean
19 | ) => a))
20 |
21 | // $ExpectType (s: A) => A
22 | pipe(lensC, L.modify(() => 'foo'))
23 |
24 | // $ExpectType (s: A) => A
25 | pipe(lensC, L.modify(() => 'foo'))
26 |
27 | //
28 | // prop
29 | //
30 |
31 | // $ExpectError
32 | pipe(L.id(), L.prop('d'))
33 |
34 | //
35 | // props
36 | //
37 |
38 | // $ExpectError
39 | pipe(L.id(), L.props())
40 | // $ExpectError
41 | pipe(L.id(), L.props('a'))
42 |
43 | pipe(L.id(), L.props('a', 'b')) // $ExpectType Lens
44 |
45 | //
46 | // component
47 | //
48 |
49 | // $ExpectError
50 | pipe(L.id<{ 1: number }>(), L.component(1))
51 |
--------------------------------------------------------------------------------
/examples/Optional.ts:
--------------------------------------------------------------------------------
1 | import { Lens, Optional, Prism } from '../src'
2 | import { Option, some, none } from 'fp-ts/lib/Option'
3 | import { nameLens, employee } from './Lens'
4 |
5 | const head = new Optional(
6 | (s) => (s.length > 0 ? some(s[0]) : none),
7 | (a) => (s) => (s.length > 0 ? a + s.substring(1) : '')
8 | )
9 |
10 | console.log(head.getOption('')) // => None
11 | console.log(head.getOption('hello')) // => Some('h')
12 | console.log(head.set('H')('')) // => ''
13 | console.log(head.set('H')('hello')) // => 'Hello'
14 |
15 | const optional = nameLens.asOptional().compose(head)
16 |
17 | console.log(JSON.stringify(optional.modify((s) => s.toUpperCase())(employee), null, 2))
18 |
19 | interface Person {
20 | name: string
21 | surname: Option
22 | }
23 |
24 | const surname = Lens.fromProp()('surname').composePrism(Prism.some())
25 |
26 | const p: Person = { name: 'Giulio', surname: none }
27 |
28 | console.log(surname.getOption(p)) // => none
29 | console.log(surname.set('Canti')(p)) // => { name: 'Giulio', surname: none }
30 |
--------------------------------------------------------------------------------
/test/Ix.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 | import * as _ from '../src/Ix'
3 | import * as O from 'fp-ts/lib/Option'
4 | import { eqString } from 'fp-ts/lib/Eq'
5 | import * as U from './util'
6 |
7 | describe('Ix', () => {
8 | it('indexReadonlyMap', () => {
9 | const index = _.indexReadonlyMap(eqString)().index('a')
10 | U.deepStrictEqual(index.getOption(new Map([])), O.none)
11 | U.deepStrictEqual(
12 | index.getOption(
13 | new Map([
14 | ['a', 1],
15 | ['b', 2]
16 | ])
17 | ),
18 | O.some(1)
19 | )
20 | U.deepStrictEqual(index.set(3)(new Map([['b', 2]])), new Map([['b', 2]]))
21 | U.deepStrictEqual(
22 | index.set(3)(
23 | new Map([
24 | ['a', 1],
25 | ['b', 2]
26 | ])
27 | ),
28 | new Map([
29 | ['a', 3],
30 | ['b', 2]
31 | ])
32 | )
33 | // should return the same reference if nothing changed
34 | const x = new Map([
35 | ['a', 1],
36 | ['b', 2]
37 | ])
38 | assert.strictEqual(index.set(1)(x), x)
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 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 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680Feature request"
3 | about: Suggest an idea for monocle-ts
4 | ---
5 |
6 | ## 🚀 Feature request
7 |
8 | ### Current Behavior
9 |
10 |
11 |
12 | ### Desired Behavior
13 |
14 |
15 |
16 | ### Suggested Solution
17 |
18 |
19 |
20 |
21 |
22 | ### Who does this impact? Who is this for?
23 |
24 |
25 |
26 | ### Describe alternatives you've considered
27 |
28 |
29 |
30 | ### Additional context
31 |
32 |
33 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/Optional.ts:
--------------------------------------------------------------------------------
1 | import * as O from '../../src/Optional'
2 | import { pipe } from 'fp-ts/lib/pipeable'
3 |
4 | interface A {
5 | a: string
6 | b: number
7 | c: string | boolean
8 | }
9 |
10 | declare const optionalC: O.Optional
11 |
12 | //
13 | // modifyOption
14 | //
15 |
16 | // $ExpectType (s: A) => Option
17 | pipe(optionalC, O.modifyOption((
18 | a // $ExpectType string | boolean
19 | ) => a))
20 |
21 | // $ExpectType (s: A) => Option
22 | pipe(optionalC, O.modifyOption(() => 'foo'))
23 |
24 | // $ExpectType (s: A) => Option
25 | pipe(optionalC, O.modifyOption(() => 'foo'))
26 |
27 | //
28 | // modify
29 | //
30 |
31 | // $ExpectType (s: A) => A
32 | pipe(optionalC, O.modify((
33 | a // $ExpectType string | boolean
34 | ) => a))
35 |
36 | // $ExpectType (s: A) => A
37 | pipe(optionalC, O.modify(() => 'foo'))
38 |
39 | // $ExpectType (s: A) => A
40 | pipe(optionalC, O.modify(() => 'foo'))
41 |
42 | // $ExpectError
43 | pipe(O.id(), O.props())
44 | // $ExpectError
45 | pipe(O.id(), O.props('a'))
46 |
47 | pipe(O.id(), O.props('a', 'b')) // $ExpectType Optional
48 |
--------------------------------------------------------------------------------
/dtslint/ts3.5/Prism.ts:
--------------------------------------------------------------------------------
1 | import * as P from '../../src/Prism'
2 | import { pipe } from 'fp-ts/lib/pipeable'
3 |
4 | interface A {
5 | a: string
6 | b: number
7 | c: string | boolean
8 | }
9 |
10 | declare const prismC: P.Prism
11 |
12 | //
13 | // modifyOption
14 | //
15 |
16 | // $ExpectType (s: A) => Option
17 | pipe(prismC, P.modifyOption((
18 | a // $ExpectType string | boolean
19 | ) => a))
20 |
21 | // $ExpectType (s: A) => Option
22 | pipe(prismC, P.modifyOption(() => 'foo'))
23 |
24 | // $ExpectType (s: A) => Option
25 | pipe(prismC, P.modifyOption(() => 'foo'))
26 |
27 | //
28 | // modify
29 | //
30 |
31 | // $ExpectType (s: A) => A
32 | pipe(prismC, P.modify((
33 | a // $ExpectType string | boolean
34 | ) => a))
35 |
36 | // $ExpectType (s: A) => A
37 | pipe(prismC, P.modify(() => 'foo'))
38 |
39 | // $ExpectType (s: A) => A
40 | pipe(prismC, P.modify(() => 'foo'))
41 |
42 | //
43 | // props
44 | //
45 |
46 | // $ExpectError
47 | pipe(P.id(), P.props())
48 | // $ExpectError
49 | pipe(P.id(), P.props('a'))
50 |
51 | pipe(P.id(), P.props('a', 'b')) // $ExpectType Optional
52 |
--------------------------------------------------------------------------------
/scripts/FileSystem.ts:
--------------------------------------------------------------------------------
1 | import * as TE from 'fp-ts/lib/TaskEither'
2 | import { flow } from 'fp-ts/lib/function'
3 | import * as fs from 'fs'
4 | import * as G from 'glob'
5 |
6 | export interface FileSystem {
7 | readonly readFile: (path: string) => TE.TaskEither
8 | readonly writeFile: (path: string, content: string) => TE.TaskEither
9 | readonly copyFile: (from: string, to: string) => TE.TaskEither
10 | readonly glob: (pattern: string) => TE.TaskEither>
11 | readonly mkdir: (path: string) => TE.TaskEither
12 | }
13 |
14 | const readFile = TE.taskify(fs.readFile)
15 | const writeFile = TE.taskify(fs.writeFile)
16 | const copyFile = TE.taskify(fs.copyFile)
17 | const glob = TE.taskify>(G)
18 | const mkdirTE = TE.taskify(fs.mkdir)
19 |
20 | export const fileSystem: FileSystem = {
21 | readFile: (path) => readFile(path, 'utf8'),
22 | writeFile,
23 | copyFile,
24 | glob,
25 | mkdir: flow(
26 | mkdirTE,
27 | TE.map(() => undefined),
28 | TE.orElse((e) => (e.code === 'EEXIST' ? TE.right(undefined) : TE.left(e)))
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/examples/Prism.ts:
--------------------------------------------------------------------------------
1 | import { Prism } from '../src'
2 | import { Option, some, none } from 'fp-ts/lib/Option'
3 |
4 | class JStr {
5 | constructor(public readonly value: string) {}
6 | }
7 | class JNum {
8 | constructor(public readonly value: number) {}
9 | }
10 | class JObj {
11 | constructor(public readonly value: { [key: string]: Json }) {}
12 | }
13 |
14 | type Json = null | JStr | JNum | JObj
15 |
16 | const jStr = new Prism(
17 | (s) => (s instanceof JStr ? some(s.value) : none),
18 | (a) => new JStr(a)
19 | )
20 |
21 | console.log(jStr.getOption(new JStr('hello')))
22 | console.log(jStr.getOption(new JNum(1)))
23 |
24 | // a function is applied only if there is a match
25 | const reverse = (s: string): string => s.split('').reverse().join('')
26 | console.log(jStr.modify(reverse)(new JStr('hello')))
27 | console.log(jStr.modify(reverse)(new JNum(1)))
28 | console.log(jStr.modifyOption(reverse)(new JStr('hello')))
29 | console.log(jStr.modifyOption(reverse)(new JNum(1)))
30 |
31 | // composizione
32 | const jNum = new Prism(
33 | (s) => (s instanceof JNum ? some(s.value) : none),
34 | (a) => new JNum(a)
35 | )
36 | const numberToInt = new Prism(
37 | (s) => (s % 1 === 0 ? some(s) : none),
38 | (a) => a
39 | )
40 |
41 | const jInt = jNum.compose(numberToInt)
42 |
43 | console.log(jInt.getOption(new JNum(5.0)))
44 | console.log(jInt.getOption(new JNum(5.2)))
45 |
--------------------------------------------------------------------------------
/test/2.2/Iso.ts:
--------------------------------------------------------------------------------
1 | import { Iso } from '../../src'
2 | import * as assert from 'assert'
3 |
4 | const mTokm = new Iso(
5 | (m) => m / 1000,
6 | (km) => km * 1000
7 | )
8 | const kmToMile = new Iso(
9 | (km) => km * 0.621371,
10 | (mile) => mile / 0.621371
11 | )
12 |
13 | describe('Iso', () => {
14 | it('get', () => {
15 | assert.strictEqual(mTokm.get(100), 0.1)
16 | assert.strictEqual(mTokm.unwrap(100), 0.1)
17 | assert.strictEqual(mTokm.to(100), 0.1)
18 | })
19 |
20 | it('reverseGet', () => {
21 | assert.strictEqual(mTokm.reverseGet(1.2), 1200)
22 | assert.strictEqual(mTokm.wrap(1.2), 1200)
23 | assert.strictEqual(mTokm.from(1.2), 1200)
24 | })
25 |
26 | it('modify', () => {
27 | const double = (x: number) => x * 2
28 | assert.strictEqual(mTokm.modify(double)(1000), 2000)
29 | })
30 |
31 | it('reverse', () => {
32 | const double = (x: number) => x * 2
33 | assert.strictEqual(mTokm.reverse().modify(double)(2000), 4000)
34 | })
35 |
36 | it('compose', () => {
37 | const composition1 = mTokm.compose(kmToMile)
38 | const composition2 = mTokm.composeIso(kmToMile)
39 | assert.strictEqual(composition1.get(1500).toFixed(2), '0.93')
40 | assert.strictEqual(composition1.reverseGet(1).toFixed(2), '1609.34')
41 |
42 | assert.strictEqual(composition2.get(1500), composition1.get(1500))
43 | assert.strictEqual(composition2.reverseGet(1), composition1.reverseGet(1))
44 | })
45 | })
46 |
--------------------------------------------------------------------------------
/test/2.2/Fold.ts:
--------------------------------------------------------------------------------
1 | import { Getter, fromFoldable } from '../../src'
2 | import { some, none } from 'fp-ts/lib/Option'
3 | import { array } from 'fp-ts/lib/Array'
4 | import * as U from '../util'
5 |
6 | type Point = {
7 | readonly x: number
8 | readonly y: number
9 | }
10 |
11 | const _x = new Getter((p: Point): number => p.x)
12 |
13 | describe('Fold', () => {
14 | const eg0 = { x: 42, y: -1 }
15 |
16 | it('getAll', () => {
17 | U.deepStrictEqual(_x.asFold().getAll(eg0), [42])
18 | })
19 |
20 | it('find', () => {
21 | U.deepStrictEqual(_x.asFold().find((n) => n >= 42)(eg0), some(42))
22 | U.deepStrictEqual(_x.asFold().find((n) => n < 42)(eg0), none)
23 | })
24 |
25 | it('exist', () => {
26 | U.deepStrictEqual(_x.asFold().exist((n) => n >= 42)(eg0), true)
27 | U.deepStrictEqual(_x.asFold().exist((n) => n < 42)(eg0), false)
28 | })
29 |
30 | it('all', () => {
31 | U.deepStrictEqual(_x.asFold().all((n) => n >= 42)(eg0), true)
32 | U.deepStrictEqual(_x.asFold().all((n) => n < 42)(eg0), false)
33 | })
34 |
35 | it('fromFoldable', () => {
36 | const fold = fromFoldable(array)()
37 | U.deepStrictEqual(fold.all((n) => n >= 2)([1, 2, 3]), false)
38 | U.deepStrictEqual(fold.all((n) => n >= 1)([1, 2, 3]), true)
39 | })
40 |
41 | it('headOption', () => {
42 | const fold = fromFoldable(array)()
43 | U.deepStrictEqual(fold.headOption([]), none)
44 | U.deepStrictEqual(fold.headOption([1, 2, 3]), some(1))
45 | })
46 | })
47 |
--------------------------------------------------------------------------------
/test/At.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 | import * as _ from '../src/At'
3 | import * as O from 'fp-ts/lib/Option'
4 | import { eqString } from 'fp-ts/lib/Eq'
5 | import * as U from './util'
6 |
7 | describe('At', () => {
8 | it('atReadonlyMap', () => {
9 | const at = _.atReadonlyMap(eqString)().at('a')
10 | U.deepStrictEqual(at.get(new Map([])), O.none)
11 | U.deepStrictEqual(
12 | at.get(
13 | new Map([
14 | ['a', 1],
15 | ['b', 2]
16 | ])
17 | ),
18 | O.some(1)
19 | )
20 | U.deepStrictEqual(at.set(O.none)(new Map()), new Map())
21 | U.deepStrictEqual(
22 | at.set(O.none)(
23 | new Map([
24 | ['a', 1],
25 | ['b', 2]
26 | ])
27 | ),
28 | new Map([['b', 2]])
29 | )
30 | U.deepStrictEqual(
31 | at.set(O.some(3))(
32 | new Map([
33 | ['a', 1],
34 | ['b', 2]
35 | ])
36 | ),
37 | new Map([
38 | ['a', 3],
39 | ['b', 2]
40 | ])
41 | )
42 | // should return the same reference if nothing changed
43 | const x = new Map([
44 | ['a', 1],
45 | ['b', 2]
46 | ])
47 | assert.strictEqual(at.set(O.some(1))(x), x)
48 | })
49 |
50 | it('atReadonlySet', () => {
51 | const at = _.atReadonlySet(eqString).at('a')
52 | U.deepStrictEqual(at.get(new Set()), false)
53 | U.deepStrictEqual(at.get(new Set(['a'])), true)
54 | U.deepStrictEqual(at.set(true)(new Set()), new Set(['a']))
55 | U.deepStrictEqual(at.set(true)(new Set(['a', 'b'])), new Set(['a', 'b']))
56 | U.deepStrictEqual(at.set(false)(new Set(['a', 'b'])), new Set(['b']))
57 | })
58 | })
59 |
--------------------------------------------------------------------------------
/examples/introduction.ts:
--------------------------------------------------------------------------------
1 | interface Street {
2 | num: number
3 | name: string
4 | }
5 | interface Address {
6 | city: string
7 | street: Street
8 | }
9 | interface Company {
10 | name: string
11 | address: Address
12 | }
13 | interface Employee {
14 | name: string
15 | company: Company
16 | }
17 |
18 | const employee: Employee = {
19 | name: 'john',
20 | company: {
21 | name: 'awesome inc',
22 | address: {
23 | city: 'london',
24 | street: {
25 | num: 23,
26 | name: 'high street'
27 | }
28 | }
29 | }
30 | }
31 |
32 | function capitalize(s: string): string {
33 | return s.substring(0, 1).toUpperCase() + s.substring(1)
34 | }
35 |
36 | const employee2 = {
37 | ...employee,
38 | company: {
39 | ...employee.company,
40 | address: {
41 | ...employee.company.address,
42 | street: {
43 | ...employee.company.address.street,
44 | name: capitalize(employee.company.address.street.name)
45 | }
46 | }
47 | }
48 | }
49 |
50 | console.log(JSON.stringify(employee2, null, 2))
51 |
52 | import { Lens, Optional } from '../src'
53 |
54 | const company = Lens.fromProp()('company')
55 | const address = Lens.fromProp()('address')
56 | const street = Lens.fromProp()('street')
57 | const name = Lens.fromProp()('name')
58 |
59 | import { some, none } from 'fp-ts/lib/Option'
60 |
61 | const firstLetter = new Optional(
62 | (s) => (s.length > 0 ? some(s[0]) : none),
63 | (a) => (s) => a + s.substring(1)
64 | )
65 |
66 | console.log(
67 | JSON.stringify(
68 | company
69 | .compose(address)
70 | .compose(street)
71 | .compose(name)
72 | .asOptional()
73 | .compose(firstLetter)
74 | .modify((s) => s.toUpperCase())(employee),
75 | null,
76 | 2
77 | )
78 | )
79 |
--------------------------------------------------------------------------------
/test/2.2/conversions.ts:
--------------------------------------------------------------------------------
1 | import { Iso, Lens, Prism, Optional, Traversal, Getter, Fold, Setter } from '../../src'
2 | import * as assert from 'assert'
3 |
4 | type U0 = true
5 | type U1 = false
6 |
7 | // This test exists to type check the optic conversion functions
8 |
9 | const anIso = new Iso(
10 | () => false,
11 | () => true
12 | )
13 |
14 | const hasType = (_: T): void => assert(true)
15 |
16 | describe('Conversions', () => {
17 | it('type check', () => {
18 | // Iso conversions
19 | const isoLens: Lens = anIso.asLens()
20 | const isoPrism: Prism = anIso.asPrism()
21 | const isoOptional: Optional = anIso.asOptional()
22 | const isoTraversal: Traversal = anIso.asTraversal()
23 | const isoGetter: Getter = anIso.asGetter()
24 | hasType>(anIso.asFold())
25 | hasType>(anIso.asSetter())
26 |
27 | // Lens conversions
28 | hasType>(isoLens.asOptional())
29 | hasType>(isoLens.asTraversal())
30 | hasType>(isoLens.asGetter())
31 | hasType>(isoLens.asFold())
32 | hasType>(isoLens.asSetter())
33 |
34 | // Prism conversions
35 | hasType>(isoPrism.asOptional())
36 | hasType>(isoPrism.asTraversal())
37 | hasType>(isoPrism.asFold())
38 | hasType>(isoPrism.asSetter())
39 |
40 | // Optional conversions
41 | hasType>(isoOptional.asTraversal())
42 | hasType>(isoOptional.asFold())
43 | hasType>(isoOptional.asSetter())
44 |
45 | // Traversal conversions
46 | hasType>(isoTraversal.asFold())
47 | hasType>(isoTraversal.asSetter())
48 |
49 | // Getter conversions
50 | hasType>(isoGetter.asFold())
51 | })
52 | })
53 |
--------------------------------------------------------------------------------
/test/2.2/Traversal.ts:
--------------------------------------------------------------------------------
1 | import { Lens, fromTraversable } from '../../src'
2 | import { array } from 'fp-ts/lib/Array'
3 | import { Option, isSome, some, Some } from 'fp-ts/lib/Option'
4 | import * as U from '../util'
5 |
6 | describe('Traversal', () => {
7 | it('fromTraversable', () => {
8 | interface Tweet {
9 | readonly text: string
10 | }
11 |
12 | interface Tweets {
13 | readonly tweets: Array
14 | }
15 |
16 | const tweetsLens = Lens.fromProp()('tweets')
17 | const tweetTextLens = Lens.fromProp()('text')
18 | const tweetTraversal = fromTraversable(array)()
19 | const composedTraversal = tweetsLens.composeTraversal(tweetTraversal).composeLens(tweetTextLens)
20 |
21 | const tweet1: Tweet = { text: 'hello world' }
22 | const tweet2: Tweet = { text: 'foobar' }
23 | const model: Tweets = { tweets: [tweet1, tweet2] }
24 |
25 | const newModel = composedTraversal.modify((text) => text.split('').reverse().join(''))(model)
26 | U.deepStrictEqual(newModel, { tweets: [{ text: 'dlrow olleh' }, { text: 'raboof' }] })
27 | })
28 |
29 | it('set', () => {
30 | const traversal = fromTraversable(array)()
31 | U.deepStrictEqual(traversal.set('a')([]), [])
32 | U.deepStrictEqual(traversal.set('a')(['b', 'c']), ['a', 'a'])
33 | })
34 |
35 | it('filter', () => {
36 | const traversal1 = fromTraversable(array)().filter((s) => s.length > 2)
37 | U.deepStrictEqual(traversal1.set('a')([]), [])
38 | U.deepStrictEqual(traversal1.set('a')(['b', 'c']), ['b', 'c'])
39 | U.deepStrictEqual(traversal1.set('a')(['b', 'foo', 'c']), ['b', 'a', 'c'])
40 |
41 | const traversal2 = fromTraversable(array)