Socket
Socket
Sign inDemoInstall

@fp-ts/optic

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fp-ts/optic - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

_mjs/data/Boolean.mjs

6

experimental.d.ts
/**
* @since 1.0.0
*/
import type { Kind, TypeLambda } from "@fp-ts/core/HKT";
import type { Applicative } from "@fp-ts/core/typeclass/Applicative";
import type { Either } from "@fp-ts/data/Either";

@@ -30,2 +32,6 @@ import type { Getter, Lens, PolyOptional } from "@fp-ts/optic";

/**
* @since 1.0.0
*/
export declare const modifyApplicative: <F extends TypeLambda>(F: Applicative<F>) => <S, T, A, B>(optic: PolyOptional<S, T, A, B>) => <R, E, O>(f: (a: A) => Kind<F, R, E, O, B>) => (s: S) => Kind<F, R, E, O, T>;
/**
* An optic that accesses a nested field of a struct.

@@ -32,0 +38,0 @@ *

11

experimental.js

@@ -6,3 +6,3 @@ "use strict";

});
exports.ZoomerTypeId = void 0;
exports.modifyApplicative = exports.ZoomerTypeId = void 0;
exports.path = path;

@@ -24,7 +24,12 @@ exports.zoom = exports.traversal = exports.polyTraversal = exports.pick = void 0;

const traversal = (decode, replace) => polyTraversal(s => (0, _Function.pipe)(decode(s), E.mapLeft(e => [e, s])), as => s => (0, _Function.pipe)(replace(as)(s), E.mapLeft(e => [e, s])));
/**
* @since 1.0.0
*/
exports.traversal = traversal;
const modifyApplicative = F => optic => f => s => (0, _Function.pipe)(optic.getOptic(s), E.match(([_, t]) => F.of(t), a => (0, _Function.pipe)(f(a), F.map(b => (0, _Function.pipe)(optic.setOptic(b)(s), E.match(([_, t]) => t, _Function.identity))))));
exports.modifyApplicative = modifyApplicative;
function path(...path) {
let out = Optic.id();
for (const k of path) {
out = out.compose(Optic.key(k));
out = out.compose(Optic.at(k));
}

@@ -57,3 +62,3 @@ return out;

for (const k of x[ZoomerTypeId]) {
out = out.compose(Optic.key(k));
out = out.compose(Optic.at(k));
}

@@ -60,0 +65,0 @@ return out;

@@ -6,2 +6,3 @@ /**

import type { Option } from "@fp-ts/data/Option";
import type { Predicate, Refinement } from "@fp-ts/data/Predicate";
/**

@@ -86,7 +87,7 @@ * @since 1.0.0

/**
* An optic that accesses a key of a struct or a tuple.
* An optic that accesses the specified key of a struct or a tuple.
*
* @since 1.0.0
*/
export declare const key: {
export declare const at: {
<S, Key extends keyof S & (string | symbol)>(key: Key): Lens<S, S[Key]>;

@@ -135,6 +136,15 @@ <S, Key extends keyof S & (string | symbol), B>(key: Key): PolyLens<S, {

*/
export declare const nullable: <S>() => Prism<S, NonNullable<S>>;
export declare const nonNullable: <S>() => Prism<S, NonNullable<S>>;
/**
* An optic that accesses the case specified by a predicate.
*
* @since 1.0.0
*/
export declare const filter: {
<S extends A, B extends A, A = S>(refinement: Refinement<A, B>): Prism<S, B>;
<S extends A, A = S>(predicate: Predicate<A>): Prism<S, S>;
};
/**
* @since 1.0.0
*/
export interface PolyOptional<in S, out T, out A, in B> extends Optic<S, S, B, Error, Error, A, T> {

@@ -166,5 +176,7 @@ }

/**
* An optic that accesses the specified index of a `ReadonlyArray`.
*
* @since 1.0.0
*/
export declare const at: <A>(n: number) => Optional<readonly A[], A>;
export declare const index: <A>(n: number) => Optional<readonly A[], A>;
/**

@@ -179,4 +191,13 @@ * @since 1.0.0

/**
* An optic that accesses the first case specified by a predicate.
*
* @since 1.0.0
*/
export declare const findFirst: {
<C extends A, B extends A, A = C>(refinement: Refinement<A, B>): Optional<ReadonlyArray<C>, B>;
<B extends A, A = B>(predicate: Predicate<A>): Optional<ReadonlyArray<B>, B>;
};
/**
* @since 1.0.0
*/
export interface PolySetter<in S, out T, in A> extends Optic<never, S, A, unknown, Error, unknown, T> {

@@ -183,0 +204,0 @@ }

@@ -6,3 +6,3 @@ "use strict";

});
exports.tail = exports.set = exports.replaceOption = exports.replace = exports.prism = exports.polyPrism = exports.polyOptional = exports.optional = exports.nullable = exports.modify = exports.lens = exports.key = exports.iso = exports.id = exports.head = exports.getOrModify = exports.getOption = exports.get = exports.encode = exports.cons = exports.at = exports.OpticImpl = void 0;
exports.tail = exports.set = exports.replaceOption = exports.replace = exports.prism = exports.polyPrism = exports.polyOptional = exports.optional = exports.nonNullable = exports.modify = exports.lens = exports.iso = exports.index = exports.id = exports.head = exports.getOrModify = exports.getOption = exports.get = exports.findFirst = exports.filter = exports.encode = exports.cons = exports.at = exports.OpticImpl = void 0;
var E = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@fp-ts/data/Either"));

@@ -72,3 +72,3 @@ var _Function = /*#__PURE__*/require("@fp-ts/data/Function");

/**
* An optic that accesses a key of a struct or a tuple.
* An optic that accesses the specified key of a struct or a tuple.
*

@@ -78,3 +78,3 @@ * @since 1.0.0

exports.set = set;
const key = key => lens(s => s[key], b => s => {
const at = key => lens(s => s[key], b => s => {
if (Array.isArray(s)) {

@@ -94,3 +94,3 @@ const out = s.slice();

*/
exports.key = key;
exports.at = at;
const polyPrism = (polyDecode, encode) => new OpticImpl("prism", polyDecode, b => _ => E.right(encode(b)));

@@ -121,8 +121,15 @@ /**

exports.cons = cons;
const nullable = () => prism(s => s == null ? E.left(new Error(`${s} did not satisfy isNonNullable`)) : E.right(s), _Function.identity);
const nonNullable = () => prism(s => s == null ? E.left(new Error(`${s} did not satisfy isNonNullable`)) : E.right(s), _Function.identity);
/**
* An optic that accesses the case specified by a predicate.
*
* @since 1.0.0
*/
exports.nonNullable = nonNullable;
const filter = predicate => prism(s => predicate(s) ? E.right(s) : E.left(new Error(`${s} did not satisfy the specified predicate`)), _Function.identity);
/**
* @category constructors
* @since 1.0.0
*/
exports.nullable = nullable;
exports.filter = filter;
const polyOptional = (polyDecode, polyReplaceEither) => new OpticImpl("lens", polyDecode, polyReplaceEither);

@@ -146,6 +153,8 @@ /**

/**
* An optic that accesses the specified index of a `ReadonlyArray`.
*
* @since 1.0.0
*/
exports.optional = optional;
const at = n => optional(as => n >= 0 && n < as.length ? E.right(as[n]) : E.left(new Error(`[${as}] did not satisfy hasAt(${n})`)), a => as => {
const index = n => optional(as => n >= 0 && n < as.length ? E.right(as[n]) : E.left(new Error(`[${as}] did not satisfy hasAt(${n})`)), a => as => {
if (n >= 0 && n < as.length) {

@@ -161,4 +170,4 @@ const out = as.slice();

*/
exports.at = at;
const head = () => cons().compose(key("0"));
exports.index = index;
const head = () => cons().compose(at("0"));
/**

@@ -168,7 +177,18 @@ * @since 1.0.0

exports.head = head;
const tail = () => cons().compose(key("1"));
const tail = () => cons().compose(at("1"));
/**
* An optic that accesses the first case specified by a predicate.
*
* @since 1.0.0
*/
exports.tail = tail;
const findFirst = predicate => optional(as => (0, _Function.pipe)(as, RA.findFirst(predicate), E.fromOption(() => new Error(`[${as}] did not satisfy the specified predicate`))), a => as => (0, _Function.pipe)(as, RA.findFirstIndex(predicate), E.fromOption(() => new Error(`[${as}] did not satisfy the specified predicate`)), E.map(index => {
const out = as.slice();
out[index] = a;
return out;
})));
/**
* @since 1.0.0
*/
exports.findFirst = findFirst;
const replace = optic => SetPiece => SetWholeBefore => (0, _Function.pipe)(optic.setOptic(SetPiece)(SetWholeBefore), E.match(([_, t]) => t, _Function.identity));

@@ -175,0 +195,0 @@ /**

{
"name": "@fp-ts/optic",
"version": "0.0.2",
"version": "0.0.3",
"license": "MIT",

@@ -11,3 +11,3 @@ "repository": {

"@fp-ts/core": "^0.0.8",
"@fp-ts/data": "^0.0.15"
"@fp-ts/data": "^0.0.16"
},

@@ -14,0 +14,0 @@ "main": "./index.js",

@@ -17,2 +17,11 @@ <h3 align="center">

`@fp-ts/optic` is a library that makes it easy to modify parts of larger data structures based on a single representation of an optic as a combination of a getter and setter.
`@fp-ts/optic` features a unified representation of optics, deep `@fp-ts/data` integration, helpful error messages,
# Features
- **Unified Representation Of Optics**. All optics compose the same way because they are all instances of the same data type (`Optic`)
- **Integration**. Built-in optics for `@fp-ts/data` data structures, like `List` and `Chunk`
# Optics

@@ -22,3 +31,2 @@

flowchart TD
Optic --> Iso
Iso --> Lens

@@ -30,10 +38,16 @@ Iso --> Prism

Optional --> Setter
Getter --> Optic
Setter --> Optic
```
# Example
# Summary
Let's say we have an employee and we need to upper case the first character of his company street name.
```ts
import * as O from "@fp-ts/data/Option";
interface Street {
num: number;
name: string;
name: O.Option<string>;
}

@@ -52,8 +66,4 @@ interface Address {

}
```
Let's say we have an employee and we need to upper case the first character of his company street name. Here is how we could write it in vanilla TypeScript
```ts
const employee: Employee = {
const from: Employee = {
name: "john",

@@ -66,3 +76,3 @@ company: {

num: 23,
name: "high street",
name: O.some("high street"),
},

@@ -73,14 +83,11 @@ },

const capitalize = (s: string): string =>
s.substring(0, 1).toUpperCase() + s.substring(1);
const employeeCapitalized = {
...employee,
const to: Employee = {
name: "john",
company: {
...employee.company,
name: "awesome inc",
address: {
...employee.company.address,
city: "london",
street: {
...employee.company.address.street,
name: capitalize(employee.company.address.street.name),
num: 23,
name: O.some("High street"),
},

@@ -92,18 +99,122 @@ },

As we can see copy is not convenient to update nested objects because we need to repeat ourselves. Let's see what could we do with `@fp-ts/optic`
Let's see what could we do with `@fp-ts/optic`
```ts
import * as Optic from "@fp-ts/optic";
import * as OptionOptic from "@fp-ts/optic/data/Option";
import * as StringOptic from "@fp-ts/optic/data/string";
const _name = Optic.id<Employee>()
.compose(Optic.key("company"))
.compose(Optic.key("address"))
.compose(Optic.key("street"))
.compose(Optic.key("name"));
const _name: Optic.Optional<Employee, string> = Optic.id<Employee>()
.compose(Optic.at("company")) // Lens<Employee, Company>
.compose(Optic.at("address")) // Lens<Employee, Company>
.compose(Optic.at("street")) // Lens<<Employee, Company>
.compose(Optic.at("name")) // Lens<Street, O.Option<string>>
.compose(OptionOptic.some()) // Prism<O.Option<string>, string>
.compose(StringOptic.index(0)); // Optional<string, string>
const capitalize = (s: string): string => s.toUpperCase();
const capitalizeName = Optic.modify(_name)(capitalize);
expect(capitalizeName(employee)).toEqual(employeeCapitalized);
expect(capitalizeName(from)).toEqual(to);
```
# Understanding Optics
`@fp-ts/optic` is based on a single representation of an optic as a combination of a getter and a setter.
```ts
export interface Optic<
in GetWhole,
in SetWholeBefore,
in SetPiece,
out GetError,
out SetError,
out GetPiece,
out SetWholeAfter
> {
readonly getOptic: (
GetWhole: GetWhole
) => Either<readonly [GetError, SetWholeAfter], GetPiece>;
readonly setOptic: (
SetPiece: SetPiece
) => (
SetWholeBefore: SetWholeBefore
) => Either<readonly [SetError, SetWholeAfter], SetWholeAfter>;
}
```
The getter can take some larger structure of type `GetWhole` and get a part of it of type `GetPiece`. It can potentially fail with an error of type `GetError` because the part we are trying to get might not exist in the larger structure.
The setter has the ability, given some piece of type `SetPiece` and an original structure of type `SetWholeBefore`, to return a new structure of type `SetWholeAfter`. Setting can fail with an error of type `SetError` because the piece we are trying to set might not exist in the structure.
## Lens
A `Lens` is an optic that accesses a field of a product type, such as a tuple or a struct.
The `GetError` type of a `Lens` is `never` because we can always get a field of a product type. The `SetError` type is also `never` because we can always set the field of a product type to a new value.
In this case the `GetWhole`, `SetWholeBefore`, and `SetWholeAfter` types are the same and represent the product type. The `GetPiece` and `SetPiece` types are also the same and represent the field.
Thus, we have:
```ts
export interface Lens<in out S, in out A>
extends Optic<S, S, A, never, never, A, S> {}
```
The simplified signature is:
```ts
export interface Lens<in out S, in out A> {
readonly getOptic: (s: S) => Either<never, A>;
readonly setOptic: (a: A) => (s: S) => Either<never, S>;
}
```
This conforms exactly to our description above. A lens is an optic where we can always get part of the larger structure and given an original structure we can always set a new value in that structure.
## Prism
A `Prism` is an optic that accesses a case of a sum type, such as the `Left` or `Right` cases of an `Either`.
Getting part of a larger data structure with a prism can fail because the case we are trying to access might not exist. For example, we might be trying to access the right side of an `Either` but the either is actually a `Left`.
We use the data type `Error` to model the different ways that getting or setting with an optic can fail. So the `GetError` type of a prism will be `Error`.
The `SetError` type of a prism will be `never` because given one of the cases of a product type we can always return a new value of the product type since each case of the product type is an instance of the product type.
A prism also differs from a lens in that we do not need any original structure to set. A product type consists of nothing but its cases so if we have a new value of the case we want to set we can just use that value and don't need the original structure.
We represent this by using `unknown` for the `SetWholeBefore` type, indicating that we do not need any original structure to set a new value.
Thus, the definition of a prism is:
```ts
export interface Prism<in out S, in out A>
extends Optic<S, unknown, A, Error, never, A, S> {}
```
And the simplified signature is:
```ts
export interface Prism<in out S, in out A> {
readonly getOptic: (s: S) => Either<Error, A>;
readonly setOptic: (a: A) => (s: unknown) => Either<never, S>;
}
```
Again this conforms exactly to our description. A prism is an optic where we might not be able to get a value but can always set a value and in fact do not require any original structure to set.
## Other
`@fp-ts/optic` supports a wide variety of other optics:
- **Optional**. An `Optional` is an optic that accesses part of a larger structure where the part being accessed may not exist and the structure contains more than just that part. Both the `GetError` and `SetError` types are `Error` because the part may not exist in the structure and setting does require the original structure since it consists of more than just this one part.
- **Iso**. An `Iso` is an optic that accesses a part of a structure where the structure consists of nothing but the part. Both the `GetError` and `SetError` types are `never` and the `SetWholeBefore` type is `unknown`.
- **Getter**. A `Getter` is an optic that only allows getting a value. The `SetWholeBefore` and `SetPiece` types are `never` because it is impossible to ever set.
- **Setter**. A `Setter` is an optic that only allows setting a value. The `GetWhole` type is `never` because it is impossible to ever get.
There are also more polymorphic versions of each optic that allow the types of the data structure and part before and after to differ. For example, a `PolyPrism` could allow us to access the right case of an `Either<A, B>` and set a `C` value to return an `Either<A, C>`.
# Installation

@@ -110,0 +221,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc