diff --git a/CHANGELOG.md b/CHANGELOG.md index aab2fdc4c..59dc29d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,24 @@ **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice. +# 2.5.0 + +- **New Feature** + - add `ReadonlyArray` module (@gcanti) + - add `ReadonlyNonEmptyArray` module (@gcanti) + - add `ReadonlySet` module (@gcanti) + - add `ReadonlyMap` module (@gcanti) + - add `ReadonlyRecord` module (@gcanti) + - add `ReadonlyTuple` module (@gcanti) + - `NonEmptyArray` + - add `fold` (@vicrac) + - `Semigroup` + - add `getIntercalateSemigroup` (@gcanti) + - `Set` + - add `toggle` (@ryota-ka) + - `TaskEither` + - add `tryCatchK` (@DenisFrezzato) + # 2.4.4 - **Polish** diff --git a/docs/getting-started/Functor.md b/docs/getting-started/Functor.md new file mode 100644 index 000000000..bfecbf109 --- /dev/null +++ b/docs/getting-started/Functor.md @@ -0,0 +1,219 @@ +--- +title: Functor +parent: Getting started +nav_order: 6 +--- + +# Getting started with fp-ts: Functor + +In the [last post](./Category.md) about categories I presented the _TS_ category (the TypeScript category) and the central problem with function composition + +> How can we compose two generic functions `f: (a: A) => B` and `g: (c: C) => D`? + +Why finding solutions to this problem is so important? + +Because if categories can be used to model programmming languages, morphisms (i.e. functions in _TS_) can be used to model **programs**. + +Therefore solving the problem also means to find **how to compose programs in a general way**. And _this_ is pretty interesting for a developer, isn't it? + +## Functions as programs + +We call **pure program** a function with the following signature + +```scala +(a: A) => B +``` + +Such a signature models a program which accepts an input of type `A` and yields a result of type `B`, without any effect. + +We call **effectful program** a function with the following signature + +```scala +(a: A) => F +``` + +Such a signature models a program which accepts an input of type `A` and yields a result of type `B`, along with an **effect** `F`, where `F` is some type constructor. + +Recall that a [type constructor](https://en.wikipedia.org/wiki/Type_constructor) is an `n`-ary type operator taking as argument zero or more types, and returning another type. + +**Example** + +Given the concrete type `string`, the `Array` type constructor returns the concrete type `Array` + +Here we are interested in `n`-ary type constructors with `n >= 1`, for example + +| Type constructor | Effect (interpretation) | +| ---------------- | ------------------------------- | +| `Array` | a non deterministic computation | +| `Option` | a computation that may fail | +| `Task` | an asynchronous computation | + +Now back to our main problem + +> How can we compose two generic functions `f: (a: A) => B` and `g: (c: C) => D`? + +Since the general problem is intractable, we need to put some _constraint_ on `B` and `C`. + +We already know that if `B = C` then the solution is the usual function composition + +```ts +function compose(g: (b: B) => C, f: (a: A) => B): (a: A) => C { + return a => g(f(a)) +} +``` + +What about the other cases? + +## In which the constraint `B = F` leads to functors + +Let's consider the following constraint: `B = F` for some type constructor `F`, or in other words (and after some renaming) + +- `f: (a: A) => F` is an effectful program +- `g: (b: B) => C` is a pure program + +In order to compose `f` with `g` we could find a way to **lift** `g` from a function `(b: B) => C` to a function `(fb: F) => F` so that we can use the usual function composition (the output type of `f` would be the same as the input type of the lifted function) + +So we turned the original problem into another one: can we find such a `lift` function? + +Let's see some examples + +**Example** (`F = Array`) + +```ts +function lift(g: (b: B) => C): (fb: Array) => Array { + return fb => fb.map(g) +} +``` + +**Example** (`F = Option`) + +```ts +import { Option, isNone, none, some } from 'fp-ts/lib/Option' + +function lift(g: (b: B) => C): (fb: Option) => Option { + return fb => (isNone(fb) ? none : some(g(fb.value))) +} +``` + +**Example** (`F = Task`) + +```ts +import { Task } from 'fp-ts/lib/Task' + +function lift(g: (b: B) => C): (fb: Task) => Task { + return fb => () => fb().then(g) +} +``` + +All those `lift` functions almost look the same. It's not a coincidence, there's a functional pattern under the hood. + +Indeed all those type constructors (and many others) admit a **functor instance**. + +## Functors + +Functors are **mappings between categories** that preserve the categorical structure, i.e. that preserve identity morphisms and composition. + +Since categories are constituted of two things (objects and morphisms) a functor is constituted of two things as well: + +- a **mapping between objects** that associates to each object `X` in _C_ an object in _D_ +- a **mapping between morphisms** that associates to each morphism in _C_ a morphism in _D_ + +where _C_ and _D_ are two categories (aka two programming languages). + +functor +
(source: [functor on ncatlab.org](https://ncatlab.org/nlab/show/functor))
+ +Even if a mapping between two different programming languages is an intriguing idea, we are more interested in a mapping where _C_ and _D_ coincide (with _TS_). In this case we talk about **endofunctors** ("endo" means "within", "inside"). + +From now on when I write "functor" I actually mean an endofunctor in _TS_. + +### Definition + +A functor is a pair `(F, lift)` where + +- `F` is a `n`-ary type constructor (`n >= 1`) which maps each type `X` to the type `F` (**mapping between objects**) +- `lift` is a function with the following signature + +```ts +lift: (f: (a: A) => B) => ((fa: F
) => F) +``` + +which maps each function `f: (a: A) => B` to a function `lift(f): (fa: F) => F` (**mapping between morphisms**). + +The following properties must hold + +- `lift(identity`X`)` = `identity`F(X) (**identities map to identities**) +- `lift(g ∘ f) = lift(g) ∘ lift(f)` (**mapping a composition is the composition of the mappings**) + +The `lift` function is also known through a variant called `map`, which is basically `lift` with the arguments rearranged + +```ts +lift: (f: (a: A) => B) => ((fa: F) => F) +map: (fa: F, f: (a: A) => B) => F +``` + +Note that `map` can be derived from `lift` (and viceversa). + +## Functors in `fp-ts` + +How can we define a functor instance in `fp-ts`? Let's see a practical example. + +The following declaration defines a model for the response of an API call + +```ts +interface Response { + url: string + status: number + headers: Record + body: A +} +``` + +Note that the `body` field is parametrized, this makes `Response` a good candidate for a functor instance since `Response` is a `n`-ary type constructors with `n >= 1` (a necessary precondition). + +In order to define a functor instance for `Response` we must define a `map` function (along with some [technicalities](../recipes/HKT.md) required by `fp-ts`) + +```ts +// `Response.ts` module + +import { Functor1 } from 'fp-ts/lib/Functor' + +export const URI = 'Response' + +export type URI = typeof URI + +declare module 'fp-ts/lib/HKT' { + interface URItoKind { + readonly Response: Response + } +} + +export interface Response { + url: string + status: number + headers: Record + body: A +} + +function map(fa: Response, f: (a: A) => B): Response { + return { ...fa, body: f(fa.body) } +} + +// functor instance for `Response` +export const functorResponse: Functor1 = { + URI, + map +} +``` + +## Is the general problem solved? + +Not at all. Functors allow us to compose an effectful program `f` with a pure program `g`, but `g` must be **unary**, that is it must accept only one argument as input. What if `g` accepts two arguments? Or three? + +| Program f | Program g | Composition | +| --------- | ----------------------- | ------------- | +| pure | pure | `g ∘ f` | +| effectful | pure (unary) | `lift(g) ∘ f` | +| effectful | pure (`n`-ary, `n > 1`) | ? | + +In order to handle such circumstances we need something more: in the next [post](./Applicative.md) I'll talk about another remarkable abstraction of functional programming: **applicative functors**. diff --git a/docs/guides/HKT.md b/docs/guides/HKT.md index 5ea36bdd0..1fa47ea5f 100644 --- a/docs/guides/HKT.md +++ b/docs/guides/HKT.md @@ -29,7 +29,7 @@ export type URI = typeof URI declare module 'fp-ts/lib/HKT' { interface URItoKind { - Identity: Identity + readonly Identity: Identity } } @@ -68,7 +68,7 @@ export interface URItoKind {} declare module 'fp-ts/lib/HKT' { interface URItoKind { - Identity: Identity // maps the key "Identity" to the type `Identity` + readonly Identity: Identity // maps the key "Identity" to the type `Identity` } } ``` @@ -95,7 +95,7 @@ export type URI = typeof URI declare module 'fp-ts/lib/HKT' { interface URItoKind2 { - Either: Either + readonly Either: Either } } diff --git a/docs/guides/purescript.md b/docs/guides/purescript.md index aea1281f4..154c691f8 100644 --- a/docs/guides/purescript.md +++ b/docs/guides/purescript.md @@ -54,12 +54,21 @@ TypeScript ```ts interface Bar { +<<<<<<< HEAD:docs/guides/purescript.md type: 'Bar' value: string } interface Baz { type: 'Baz' value: boolean +======= + readonly type: 'Bar' + readonly value: string +} +interface Baz { + readonly type: 'Baz' + readonly value: boolean +>>>>>>> add readonly modules:docs/recipes/purescript.md } // type type Foo = Bar | Baz @@ -81,7 +90,7 @@ TypeScript ```ts declare module 'fp-ts/lib/HKT' { interface URItoKind { - Option: Option + readonly Option: Option } } diff --git a/docs/modules/Apply.ts.md b/docs/modules/Apply.ts.md index 4a6aa4ff8..e505de6d9 100644 --- a/docs/modules/Apply.ts.md +++ b/docs/modules/Apply.ts.md @@ -199,37 +199,37 @@ Tuple sequencing, i.e., take a tuple of monadic actions and does them from left- export function sequenceT( F: Apply4 ): >>( - ...t: T & { 0: Kind4 } + ...t: T & { readonly 0: Kind4 } ) => Kind4] ? A : never }> export function sequenceT( F: Apply3 ): >>( - ...t: T & { 0: Kind3 } + ...t: T & { readonly 0: Kind3 } ) => Kind3] ? A : never }> export function sequenceT( F: Apply3C ): >>( - ...t: T & { 0: Kind3 } + ...t: T & { readonly 0: Kind3 } ) => Kind3] ? A : never }> export function sequenceT( F: Apply2 ): >>( - ...t: T & { 0: Kind2 } + ...t: T & { readonly 0: Kind2 } ) => Kind2] ? A : never }> export function sequenceT( F: Apply2C ): >>( - ...t: T & { 0: Kind2 } + ...t: T & { readonly 0: Kind2 } ) => Kind2] ? A : never }> export function sequenceT( F: Apply1 ): >>( - ...t: T & { 0: Kind } + ...t: T & { readonly 0: Kind } ) => Kind] ? A : never }> export function sequenceT( F: Apply ): >>( - ...t: T & { 0: HKT } + ...t: T & { readonly 0: HKT } ) => HKT] ? A : never }> { ... } ``` diff --git a/docs/modules/Array.ts.md b/docs/modules/Array.ts.md index c86c19b51..1c66ed6c1 100644 --- a/docs/modules/Array.ts.md +++ b/docs/modules/Array.ts.md @@ -216,7 +216,7 @@ value and the rest of the array. **Signature** ```ts -export function chop(f: (as: NonEmptyArray) => [B, Array]): (as: Array) => Array { ... } +export const chop: (f: (as: NonEmptyArray) => [B, Array]) => (as: Array) => Array = ... ``` **Example** @@ -251,7 +251,7 @@ whenever `n` evenly divides the length of `xs`. **Signature** ```ts -export function chunksOf(n: number): (as: Array) => Array> { ... } +export const chunksOf: (n: number) => (as: Array) => Array> = ... ``` **Example** @@ -337,7 +337,7 @@ Attaches an element to the front of an array, creating a new non empty array **Signature** ```ts -export function cons(head: A, tail: Array): NonEmptyArray { ... } +export const cons: (head: A, tail: Array) => NonEmptyArray = ... ``` **Example** @@ -355,7 +355,7 @@ Added in v2.0.0 **Signature** ```ts -export function copy(as: Array): Array { ... } +export const copy: (as: Array) => Array = ... ``` Added in v2.0.0 @@ -367,7 +367,7 @@ Delete the element at the specified index, creating a new array, or returning `N **Signature** ```ts -export function deleteAt(i: number): (as: Array) => Option> { ... } +export const deleteAt: (i: number) => (as: Array) => Option> = ... ``` **Example** @@ -390,7 +390,7 @@ comparisons. The order and references of result values are determined by the fir **Signature** ```ts -export function difference(E: Eq): (xs: Array, ys: Array) => Array { ... } +export const difference: (E: Eq) => (xs: Array, ys: Array) => Array = ... ``` **Example** @@ -411,7 +411,7 @@ Drop a number of elements from the start of an array, creating a new array **Signature** ```ts -export function dropLeft(n: number): (as: Array) => Array { ... } +export const dropLeft: (n: number) => (as: Array) => Array = ... ``` **Example** @@ -431,7 +431,7 @@ Remove the longest initial subarray for which all element satisfy the specified **Signature** ```ts -export function dropLeftWhile(predicate: Predicate): (as: Array) => Array { ... } +export const dropLeftWhile: (predicate: Predicate) => (as: Array) => Array = ... ``` **Example** @@ -451,7 +451,7 @@ Drop a number of elements from the end of an array, creating a new array **Signature** ```ts -export function dropRight(n: number): (as: Array) => Array { ... } +export const dropRight: (n: number) => (as: Array) => Array = ... ``` **Example** @@ -483,7 +483,7 @@ an array of type `Array`. **Signature** ```ts -export function elem(E: Eq): (a: A, as: Array) => boolean { ... } +export const elem: (E: Eq) => (a: A, as: Array) => boolean = ... ``` **Example** @@ -595,7 +595,7 @@ Find the first element returned by an option based selector function **Signature** ```ts -export function findFirstMap(f: (a: A) => Option): (as: Array) => Option { ... } +export const findFirstMap: (f: (a: A) => Option) => (as: Array) => Option = ... ``` **Example** @@ -624,7 +624,7 @@ Find the first index for which a predicate holds **Signature** ```ts -export function findIndex(predicate: Predicate): (as: Array) => Option { ... } +export const findIndex: (predicate: Predicate) => (as: Array) => Option = ... ``` **Example** @@ -674,7 +674,7 @@ Returns the index of the last element of the list which matches the predicate **Signature** ```ts -export function findLastIndex(predicate: Predicate): (as: Array) => Option { ... } +export const findLastIndex: (predicate: Predicate) => (as: Array) => Option = ... ``` **Example** @@ -704,7 +704,7 @@ Find the last element returned by an option based selector function **Signature** ```ts -export function findLastMap(f: (a: A) => Option): (as: Array) => Option { ... } +export const findLastMap: (f: (a: A) => Option) => (as: Array) => Option = ... ``` **Example** @@ -733,7 +733,7 @@ Removes one level of nesting **Signature** ```ts -export function flatten(mma: Array>): Array { ... } +export const flatten: (mma: Array>) => Array = ... ``` **Example** @@ -753,7 +753,10 @@ Break an array into its first element and remaining elements **Signature** ```ts -export function foldLeft(onNil: () => B, onCons: (head: A, tail: Array) => B): (as: Array) => B { ... } +export const foldLeft: ( + onNil: () => B, + onCons: (head: A, tail: Array) => B +) => (as: Array) => B = ... ``` **Example** @@ -797,7 +800,10 @@ Break an array into its initial elements and the last element **Signature** ```ts -export function foldRight(onNil: () => B, onCons: (init: Array, last: A) => B): (as: Array) => B { ... } +export const foldRight: ( + onNil: () => B, + onCons: (init: Array, last: A) => B +) => (as: Array) => B = ... ``` Added in v2.0.0 @@ -811,7 +817,7 @@ different lengths, the result is non equality. **Signature** ```ts -export function getEq(E: Eq): Eq> { ... } +export const getEq: (E: Eq) => Eq> = ... ``` **Example** @@ -834,7 +840,7 @@ Returns a `Monoid` for `Array` **Signature** ```ts -export function getMonoid(): Monoid> { ... } +export const getMonoid: () => Monoid> = ... ``` **Example** @@ -858,7 +864,7 @@ the same length, the result is equality. **Signature** ```ts -export function getOrd(O: Ord): Ord> { ... } +export const getOrd: (O: Ord) => Ord> = ... ``` **Example** @@ -880,7 +886,7 @@ Added in v2.0.0 **Signature** ```ts -export function getShow(S: Show): Show> { ... } +export const getShow: (S: Show) => Show> = ... ``` Added in v2.0.0 @@ -892,7 +898,7 @@ Get the first element in an array, or `None` if the array is empty **Signature** ```ts -export function head(as: Array): Option { ... } +export const head: (as: Array) => Option = ... ``` **Example** @@ -914,7 +920,7 @@ Get all but the last element of an array, creating a new array, or `None` if the **Signature** ```ts -export function init(as: Array): Option> { ... } +export const init: (as: Array) => Option> = ... ``` **Example** @@ -936,7 +942,7 @@ Insert an element at the specified index, creating a new array, or returning `No **Signature** ```ts -export function insertAt(i: number, a: A): (as: Array) => Option> { ... } +export const insertAt: (i: number, a: A) => (as: Array) => Option> = ... ``` **Example** @@ -958,7 +964,7 @@ comparisons. The order and references of result values are determined by the fir **Signature** ```ts -export function intersection(E: Eq): (xs: Array, ys: Array) => Array { ... } +export const intersection: (E: Eq) => (xs: Array, ys: Array) => Array = ... ``` **Example** @@ -979,7 +985,7 @@ Test whether an array is empty **Signature** ```ts -export function isEmpty(as: Array): boolean { ... } +export const isEmpty: (as: Array) => boolean = ... ``` **Example** @@ -999,7 +1005,7 @@ Test whether an array is non empty narrowing down the type to `NonEmptyArray` **Signature** ```ts -export function isNonEmpty(as: Array): as is NonEmptyArray { ... } +export const isNonEmpty: (as: Array) => as is NonEmptyArray = ... ``` Added in v2.0.0 @@ -1011,7 +1017,7 @@ Test whether an array contains a particular index **Signature** ```ts -export function isOutOfBound(i: number, as: Array): boolean { ... } +export const isOutOfBound: (i: number, as: Array) => boolean = ... ``` Added in v2.0.0 @@ -1023,7 +1029,7 @@ Get the last element in an array, or `None` if the array is empty **Signature** ```ts -export function last(as: Array): Option { ... } +export const last: (as: Array) => Option = ... ``` **Example** @@ -1045,7 +1051,7 @@ Extracts from an array of `Either` all the `Left` elements. All the `Left` eleme **Signature** ```ts -export function lefts(as: Array>): Array { ... } +export const lefts: (as: Array>) => Array = ... ``` **Example** @@ -1066,7 +1072,7 @@ This function provides a safe way to read a value at a particular index from an **Signature** ```ts -export function lookup(i: number, as: Array): Option { ... } +export const lookup: (i: number, as: Array) => Option = ... ``` **Example** @@ -1088,7 +1094,7 @@ Return a list of length `n` with element `i` initialized with `f(i)` **Signature** ```ts -export function makeBy(n: number, f: (i: number) => A): Array { ... } +export const makeBy: (n: number, f: (i: number) => A) => Array = ... ``` **Example** @@ -1130,7 +1136,7 @@ of bounds **Signature** ```ts -export function modifyAt(i: number, f: (a: A) => A): (as: Array) => Option> { ... } +export const modifyAt: (i: number, f: (a: A) => A) => (as: Array) => Option> = ... ``` **Example** @@ -1151,7 +1157,7 @@ Added in v2.0.0 **Signature** ```ts -export const of = (a: A): Array => ... +export const of: (a: A) => Array = ... ``` Added in v2.0.0 @@ -1203,7 +1209,7 @@ Create an array containing a range of integers, including both endpoints **Signature** ```ts -export function range(start: number, end: number): Array { ... } +export const range: (start: number, end: number) => Array = ... ``` **Example** @@ -1263,7 +1269,7 @@ Create an array containing a value repeated the specified number of times **Signature** ```ts -export function replicate(n: number, a: A): Array { ... } +export const replicate: (n: number, a: A) => Array = ... ``` **Example** @@ -1283,7 +1289,7 @@ Reverse an array, creating a new array **Signature** ```ts -export function reverse(as: Array): Array { ... } +export const reverse: (as: Array) => Array = ... ``` **Example** @@ -1303,7 +1309,7 @@ Extracts from an array of `Either` all the `Right` elements. All the `Right` ele **Signature** ```ts -export function rights(as: Array>): Array { ... } +export const rights: (as: Array>) => Array = ... ``` **Example** @@ -1324,7 +1330,7 @@ Rotate an array to the right by `n` steps **Signature** ```ts -export function rotate(n: number): (as: Array) => Array { ... } +export const rotate: (n: number) => (as: Array) => Array = ... ``` **Example** @@ -1350,7 +1356,7 @@ assert.deepStrictEqual(scanLeft(10, (b, a: number) => b - a)([1, 2, 3]), [10, 9, **Signature** ```ts -export function scanLeft(b: B, f: (b: B, a: A) => B): (as: Array) => Array { ... } +export const scanLeft: (b: B, f: (b: B, a: A) => B) => (as: Array) => Array = ... ``` Added in v2.0.0 @@ -1362,7 +1368,7 @@ Fold an array from the right, keeping all intermediate results instead of only t **Signature** ```ts -export function scanRight(b: B, f: (a: A, b: B) => B): (as: Array) => Array { ... } +export const scanRight: (b: B, f: (a: A, b: B) => B) => (as: Array) => Array = ... ``` **Example** @@ -1392,7 +1398,7 @@ Append an element to the end of an array, creating a new non empty array **Signature** ```ts -export function snoc(init: Array, end: A): NonEmptyArray { ... } +export const snoc: (init: Array, end: A) => NonEmptyArray = ... ``` **Example** @@ -1412,7 +1418,7 @@ Sort the elements of an array in increasing order, creating a new array **Signature** ```ts -export function sort(O: Ord): (as: Array) => Array { ... } +export const sort: (O: Ord) => (as: Array) => Array = ... ``` **Example** @@ -1434,7 +1440,7 @@ etc... **Signature** ```ts -export function sortBy(ords: Array>): (as: Array) => Array { ... } +export const sortBy: (ords: Array>) => (as: Array) => Array = ... ``` **Example** @@ -1501,7 +1507,7 @@ Splits an array into two pieces, the first piece has `n` elements. **Signature** ```ts -export function splitAt(n: number): (as: Array) => [Array, Array] { ... } +export const splitAt: (n: number) => (as: Array) => [Array, Array] = ... ``` **Example** @@ -1524,7 +1530,7 @@ Get all but the first element of an array, creating a new array, or `None` if th **Signature** ```ts -export function tail(as: Array): Option> { ... } +export const tail: (as: Array) => Option> = ... ``` **Example** @@ -1547,7 +1553,7 @@ Keep only a number of elements from the start of an array, creating a new array. **Signature** ```ts -export function takeLeft(n: number): (as: Array) => Array { ... } +export const takeLeft: (n: number) => (as: Array) => Array = ... ``` **Example** @@ -1589,7 +1595,7 @@ Keep only a number of elements from the end of an array, creating a new array. **Signature** ```ts -export function takeRight(n: number): (as: Array) => Array { ... } +export const takeRight: (n: number) => (as: Array) => Array = ... ``` **Example** @@ -1609,7 +1615,7 @@ Creates an array of unique values, in order, from all given arrays using a `Eq` **Signature** ```ts -export function union(E: Eq): (xs: Array, ys: Array) => Array { ... } +export const union: (E: Eq) => (xs: Array, ys: Array) => Array = ... ``` **Example** @@ -1630,7 +1636,7 @@ Remove duplicates from an array, keeping the first occurrence of an element. **Signature** ```ts -export function uniq(E: Eq): (as: Array) => Array { ... } +export const uniq: (E: Eq) => (as: Array) => Array = ... ``` **Example** @@ -1649,7 +1655,7 @@ Added in v2.0.0 **Signature** ```ts -export function unsafeDeleteAt(i: number, as: Array): Array { ... } +export const unsafeDeleteAt: (i: number, as: Array) => Array = ... ``` Added in v2.0.0 @@ -1659,7 +1665,7 @@ Added in v2.0.0 **Signature** ```ts -export function unsafeInsertAt(i: number, a: A, as: Array): Array { ... } +export const unsafeInsertAt: (i: number, a: A, as: Array) => Array = ... ``` Added in v2.0.0 @@ -1669,7 +1675,7 @@ Added in v2.0.0 **Signature** ```ts -export function unsafeUpdateAt(i: number, a: A, as: Array): Array { ... } +export const unsafeUpdateAt: (i: number, a: A, as: Array) => Array = ... ``` Added in v2.0.0 @@ -1681,7 +1687,7 @@ The function is reverse of `zip`. Takes an array of pairs and return two corresp **Signature** ```ts -export function unzip(as: Array<[A, B]>): [Array, Array] { ... } +export const unzip: (as: Array<[A, B]>) => [Array, Array] = ... ``` **Example** @@ -1711,7 +1717,7 @@ Change the element at the specified index, creating a new array, or returning `N **Signature** ```ts -export function updateAt(i: number, a: A): (as: Array) => Option> { ... } +export const updateAt: (i: number, a: A) => (as: Array) => Option> = ... ``` **Example** @@ -1734,7 +1740,7 @@ longer array are discarded **Signature** ```ts -export function zip(fa: Array, fb: Array): Array<[A, B]> { ... } +export const zip: (fa: Array, fb: Array) => Array<[A, B]> = ... ``` **Example** @@ -1759,7 +1765,7 @@ input array is short, excess elements of the longer array are discarded. **Signature** ```ts -export function zipWith(fa: Array, fb: Array, f: (a: A, b: B) => C): Array { ... } +export const zipWith: (fa: Array, fb: Array, f: (a: A, b: B) => C) => Array = ... ``` **Example** diff --git a/docs/modules/Eq.ts.md b/docs/modules/Eq.ts.md index 221b727f3..f28ae2db4 100644 --- a/docs/modules/Eq.ts.md +++ b/docs/modules/Eq.ts.md @@ -143,7 +143,7 @@ Added in v2.0.0 **Signature** ```ts -export function getStructEq(eqs: { [K in keyof O]: Eq }): Eq { ... } +export function getStructEq>(eqs: { [K in keyof O]: Eq }): Eq { ... } ``` Added in v2.0.0 @@ -155,7 +155,7 @@ Given a tuple of `Eq`s returns a `Eq` for the tuple **Signature** ```ts -export function getTupleEq>>( +export function getTupleEq>>( ...eqs: T ): Eq<{ [K in keyof T]: T[K] extends Eq ? A : never }> { ... } ``` diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md index e11423156..6a6f88001 100644 --- a/docs/modules/IOEither.ts.md +++ b/docs/modules/IOEither.ts.md @@ -227,7 +227,9 @@ Added in v2.0.0 **Signature** ```ts -export function fromEitherK, B>(f: (...a: A) => Either): (...a: A) => IOEither { ... } +export function fromEitherK, B>( + f: (...a: A) => Either +): (...a: A) => IOEither { ... } ``` Added in v2.4.0 diff --git a/docs/modules/Map.ts.md b/docs/modules/Map.ts.md index 32071f644..a9a7332a7 100644 --- a/docs/modules/Map.ts.md +++ b/docs/modules/Map.ts.md @@ -75,7 +75,7 @@ Added in v2.0.0 **Signature** ```ts -export function collect(O: Ord): (f: (k: K, a: A) => B) => (m: Map) => Array { ... } +export const collect: (O: Ord) => (f: (k: K, a: A) => B) => (m: Map) => Array = ... ``` Added in v2.0.0 @@ -97,7 +97,7 @@ Delete a key and value from a map **Signature** ```ts -export function deleteAt(E: Eq): (k: K) => (m: Map) => Map { ... } +export const deleteAt: (E: Eq) => (k: K) => (m: Map) => Map = ... ``` Added in v2.0.0 @@ -109,7 +109,7 @@ Test whether or not a value is a member of a map **Signature** ```ts -export function elem(E: Eq): (a: A, m: Map) => boolean { ... } +export const elem: (E: Eq) => (a: A, m: Map) => boolean = ... ``` Added in v2.0.0 @@ -177,7 +177,7 @@ Added in v2.0.0 **Signature** ```ts -export function getEq(SK: Eq, SA: Eq): Eq> { ... } +export const getEq: (SK: Eq, SA: Eq) => Eq> = ... ``` Added in v2.0.0 @@ -187,7 +187,11 @@ Added in v2.0.0 **Signature** ```ts -export function getFilterableWithIndex(): FilterableWithIndex2C { ... } +export const getFilterableWithIndex: () => FilterableWithIndex2C< + URI, + K, + K +> = ... ``` Added in v2.0.0 @@ -199,7 +203,7 @@ Gets `Monoid` instance for Maps given `Semigroup` instance for their values **Signature** ```ts -export function getMonoid(SK: Eq, SA: Semigroup): Monoid> { ... } +export const getMonoid: (SK: Eq, SA: Semigroup) => Monoid> = ... ``` Added in v2.0.0 @@ -209,7 +213,7 @@ Added in v2.0.0 **Signature** ```ts -export function getShow(SK: Show, SA: Show): Show> { ... } +export const getShow: (SK: Show, SA: Show) => Show> = ... ``` Added in v2.0.0 @@ -219,7 +223,9 @@ Added in v2.0.0 **Signature** ```ts -export function getWitherable(O: Ord): Witherable2C & TraversableWithIndex2C { ... } +export const getWitherable: ( + O: Ord +) => Witherable2C & TraversableWithIndex2C = ... ``` Added in v2.0.0 @@ -231,7 +237,7 @@ Insert or replace a key/value pair in a map **Signature** ```ts -export function insertAt(E: Eq): (k: K, a: A) => (m: Map) => Map { ... } +export const insertAt: (E: Eq) => (k: K, a: A) => (m: Map) => Map = ... ``` Added in v2.0.0 @@ -243,7 +249,7 @@ Test whether or not a map is empty **Signature** ```ts -export function isEmpty(d: Map): boolean { ... } +export const isEmpty: (d: Map) => boolean = ... ``` Added in v2.0.0 @@ -255,7 +261,7 @@ Test whether or not one Map contains all of the keys and values contained in ano **Signature** ```ts -export function isSubmap(SK: Eq, SA: Eq): (d1: Map, d2: Map) => boolean { ... } +export const isSubmap: (SK: Eq, SA: Eq) => (d1: Map, d2: Map) => boolean = ... ``` Added in v2.0.0 @@ -267,7 +273,7 @@ Get a sorted array of the keys contained in a map **Signature** ```ts -export function keys(O: Ord): (m: Map) => Array { ... } +export const keys: (O: Ord) => (m: Map) => Array = ... ``` Added in v2.0.0 @@ -279,7 +285,7 @@ Lookup the value for a key in a `Map`. **Signature** ```ts -export function lookup(E: Eq): (k: K, m: Map) => Option { ... } +export const lookup: (E: Eq) => (k: K, m: Map) => Option = ... ``` Added in v2.0.0 @@ -292,7 +298,7 @@ If the result is a `Some`, the existing key is also returned. **Signature** ```ts -export function lookupWithKey(E: Eq): (k: K, m: Map) => Option<[K, A]> { ... } +export const lookupWithKey: (E: Eq) => (k: K, m: Map) => Option<[K, A]> = ... ``` Added in v2.0.0 @@ -324,7 +330,7 @@ Test whether or not a key exists in a map **Signature** ```ts -export function member(E: Eq): (k: K, m: Map) => boolean { ... } +export const member: (E: Eq) => (k: K, m: Map) => boolean = ... ``` Added in v2.0.0 @@ -334,7 +340,9 @@ Added in v2.0.0 **Signature** ```ts -export function modifyAt(E: Eq): (k: K, f: (a: A) => A) => (m: Map) => Option> { ... } +export const modifyAt: ( + E: Eq +) => (k: K, f: (a: A) => A) => (m: Map) => Option> = ... ``` Added in v2.0.0 @@ -366,7 +374,7 @@ Delete a key and value from a map, returning the value as well as the subsequent **Signature** ```ts -export function pop(E: Eq): (k: K) => (m: Map) => Option<[A, Map]> { ... } +export const pop: (E: Eq) => (k: K) => (m: Map) => Option<[A, Map]> = ... ``` Added in v2.0.0 @@ -388,7 +396,7 @@ Create a map with one key/value pair **Signature** ```ts -export function singleton(k: K, a: A): Map { ... } +export const singleton: (k: K, a: A) => Map = ... ``` Added in v2.0.0 @@ -400,7 +408,7 @@ Calculate the number of key/value pairs in a map **Signature** ```ts -export function size(d: Map): number { ... } +export const size: (d: Map) => number = ... ``` Added in v2.0.0 @@ -412,7 +420,7 @@ Get a sorted of the key/value pairs contained in a map **Signature** ```ts -export function toArray(O: Ord): (m: Map) => Array<[K, A]> { ... } +export const toArray: (O: Ord) => (m: Map) => Array<[K, A]> = ... ``` Added in v2.0.0 @@ -435,7 +443,7 @@ Added in v2.0.0 **Signature** ```ts -export function updateAt(E: Eq): (k: K, a: A) => (m: Map) => Option> { ... } +export const updateAt: (E: Eq) => (k: K, a: A) => (m: Map) => Option> = ... ``` Added in v2.0.0 @@ -447,7 +455,7 @@ Get a sorted array of the values contained in a map **Signature** ```ts -export function values(O: Ord): (m: Map) => Array { ... } +export const values: (O: Ord) => (m: Map) => Array = ... ``` Added in v2.0.0 diff --git a/docs/modules/Monoid.ts.md b/docs/modules/Monoid.ts.md index 74837884a..954e8e7e7 100644 --- a/docs/modules/Monoid.ts.md +++ b/docs/modules/Monoid.ts.md @@ -47,7 +47,7 @@ Added in v2.0.0 **Signature** ```ts -export function fold(M: Monoid): (as: Array) => A { ... } +export function fold(M: Monoid): (as: ReadonlyArray) => A { ... } ``` Added in v2.0.0 @@ -107,7 +107,7 @@ Added in v2.0.0 **Signature** ```ts -export function getStructMonoid( +export function getStructMonoid>( monoids: { [K in keyof O]: Monoid } ): Monoid { ... } ``` @@ -121,7 +121,7 @@ Given a tuple of monoids returns a monoid for the tuple **Signature** ```ts -export function getTupleMonoid>>( +export function getTupleMonoid>>( ...monoids: T ): Monoid<{ [K in keyof T]: T[K] extends Semigroup ? A : never }> { ... } ``` diff --git a/docs/modules/NonEmptyArray.ts.md b/docs/modules/NonEmptyArray.ts.md index 420a0d17d..7be43b438 100644 --- a/docs/modules/NonEmptyArray.ts.md +++ b/docs/modules/NonEmptyArray.ts.md @@ -181,7 +181,7 @@ Added in v2.0.0 **Signature** ```ts -export const copy: (nea: NonEmptyArray) => NonEmptyArray = ... +export function copy(nea: NonEmptyArray): NonEmptyArray { ... } ``` Added in v2.0.0 @@ -224,9 +224,9 @@ Added in v2.0.0 **Signature** ```ts -export function filterWithIndex( +export const filterWithIndex: ( predicate: (i: number, a: A) => boolean -): (nea: NonEmptyArray) => Option> { ... } +) => (nea: NonEmptyArray) => Option> = ... ``` Added in v2.0.0 @@ -246,7 +246,7 @@ Added in v2.0.0 **Signature** ```ts -;(S: Semigroup) => (fa: NonEmptyArray) => A +export const fold: (S: Semigroup) => (fa: NonEmptyArray) => A = ... ``` Added in v2.5.0 @@ -256,7 +256,7 @@ Added in v2.5.0 **Signature** ```ts -;(S: Semigroup) => (f: (a: A) => S) => (fa: NonEmptyArray) => S +;(S: Semigroup) => (f: (a: A) => S) => (fa: RNEA.ReadonlyNonEmptyArray) => S ``` Added in v2.0.0 @@ -266,7 +266,7 @@ Added in v2.0.0 **Signature** ```ts -;(S: Semigroup) => (f: (i: number, a: A) => S) => (fa: NonEmptyArray) => S +;(S: Semigroup) => (f: (i: number, a: A) => S) => (fa: RNEA.ReadonlyNonEmptyArray) => S ``` Added in v2.0.0 @@ -278,7 +278,7 @@ Builds a `NonEmptyArray` from an `Array` returning `none` if `as` is an empty ar **Signature** ```ts -export function fromArray(as: Array): Option> { ... } +export const fromArray: (as: Array) => Option> = ... ``` Added in v2.0.0 @@ -311,7 +311,7 @@ Builds a `Semigroup` instance for `NonEmptyArray` **Signature** ```ts -export function getSemigroup(): Semigroup> { ... } +export const getSemigroup: () => Semigroup> = ... ``` Added in v2.0.0 @@ -360,7 +360,9 @@ function on each element, and grouping the results according to values returned **Signature** ```ts -export function groupBy(f: (a: A) => string): (as: Array) => Record> { ... } +export const groupBy: ( + f: (a: A) => string +) => (as: Array) => Record> = ... ``` **Example** @@ -383,7 +385,7 @@ Sort and then group the elements of an array into non empty arrays. **Signature** ```ts -export function groupSort(O: Ord): (as: Array) => Array> { ... } +export const groupSort: (O: Ord) => (as: Array) => Array> = ... ``` **Example** @@ -402,7 +404,7 @@ Added in v2.0.0 **Signature** ```ts -export function head(nea: NonEmptyArray): A { ... } +export const head: (nea: NonEmptyArray) => A = ... ``` Added in v2.0.0 @@ -414,7 +416,7 @@ Get all but the last element of a non empty array, creating a new array. **Signature** ```ts -export function init(nea: NonEmptyArray): Array { ... } +export const init: (nea: NonEmptyArray) => Array = ... ``` **Example** @@ -433,7 +435,10 @@ Added in v2.2.0 **Signature** ```ts -export function insertAt(i: number, a: A): (nea: NonEmptyArray) => Option> { ... } +export const insertAt: ( + i: number, + a: A +) => (nea: NonEmptyArray) => Option> = ... ``` Added in v2.0.0 @@ -443,7 +448,7 @@ Added in v2.0.0 **Signature** ```ts -export function last(nea: NonEmptyArray): A { ... } +export const last: (nea: NonEmptyArray) => A = ... ``` Added in v2.0.0 @@ -473,7 +478,7 @@ Added in v2.0.0 **Signature** ```ts -export function max(ord: Ord): (nea: NonEmptyArray) => A { ... } +export const max: (ord: Ord) => (nea: NonEmptyArray) => A = ... ``` Added in v2.0.0 @@ -483,7 +488,7 @@ Added in v2.0.0 **Signature** ```ts -export function min(ord: Ord): (nea: NonEmptyArray) => A { ... } +export const min: (ord: Ord) => (nea: NonEmptyArray) => A = ... ``` Added in v2.0.0 @@ -493,7 +498,10 @@ Added in v2.0.0 **Signature** ```ts -export function modifyAt(i: number, f: (a: A) => A): (nea: NonEmptyArray) => Option> { ... } +export const modifyAt: ( + i: number, + f: (a: A) => A +) => (nea: NonEmptyArray) => Option> = ... ``` Added in v2.0.0 @@ -598,7 +606,7 @@ Added in v2.0.0 **Signature** ```ts -export function sort(O: Ord): (nea: NonEmptyArray) => NonEmptyArray { ... } +export const sort: (O: Ord) => (nea: NonEmptyArray) => NonEmptyArray = ... ``` Added in v2.0.0 @@ -608,7 +616,7 @@ Added in v2.0.0 **Signature** ```ts -export function tail(nea: NonEmptyArray): Array { ... } +export const tail: (nea: NonEmptyArray) => Array = ... ``` Added in v2.0.0 @@ -618,7 +626,10 @@ Added in v2.0.0 **Signature** ```ts -export function updateAt(i: number, a: A): (nea: NonEmptyArray) => Option> { ... } +export const updateAt: ( + i: number, + a: A +) => (nea: NonEmptyArray) => Option> = ... ``` Added in v2.0.0 diff --git a/docs/modules/Ord.ts.md b/docs/modules/Ord.ts.md index 3c9d34f63..92f264ca6 100644 --- a/docs/modules/Ord.ts.md +++ b/docs/modules/Ord.ts.md @@ -223,7 +223,7 @@ Given a tuple of `Ord`s returns an `Ord` for the tuple **Signature** ```ts -export function getTupleOrd>>( +export function getTupleOrd>>( ...ords: T ): Ord<{ [K in keyof T]: T[K] extends Ord ? A : never }> { ... } ``` diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md index 4510551a4..e57f6c895 100644 --- a/docs/modules/ReaderEither.ts.md +++ b/docs/modules/ReaderEither.ts.md @@ -230,7 +230,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => ReaderEither { ... } ``` diff --git a/docs/modules/ReaderTask.ts.md b/docs/modules/ReaderTask.ts.md index c4f6331a8..6e06870d6 100644 --- a/docs/modules/ReaderTask.ts.md +++ b/docs/modules/ReaderTask.ts.md @@ -188,7 +188,7 @@ Added in v2.3.0 **Signature** ```ts -export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => ReaderTask { ... } +export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => ReaderTask { ... } ``` Added in v2.4.0 @@ -218,7 +218,9 @@ Added in v2.3.0 **Signature** ```ts -export function fromTaskK, B>(f: (...a: A) => Task): (...a: A) => ReaderTask { ... } +export function fromTaskK, B>( + f: (...a: A) => Task +): (...a: A) => ReaderTask { ... } ``` Added in v2.4.0 diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md index 772ce35d9..66c7a2f02 100644 --- a/docs/modules/ReaderTaskEither.ts.md +++ b/docs/modules/ReaderTaskEither.ts.md @@ -289,7 +289,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => ReaderTaskEither { ... } ``` @@ -311,7 +311,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => ReaderTaskEither { ... } ``` @@ -363,7 +363,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromTaskEitherK, B>( +export function fromTaskEitherK, B>( f: (...a: A) => TaskEither ): (...a: A) => ReaderTaskEither { ... } ``` diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md new file mode 100644 index 000000000..4725bcc12 --- /dev/null +++ b/docs/modules/ReadonlyArray.ts.md @@ -0,0 +1,1815 @@ +--- +title: ReadonlyArray.ts +nav_order: 69 +parent: Modules +--- + +# ReadonlyArray overview + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [Spanned (interface)](#spanned-interface) +- [URI (type alias)](#uri-type-alias) +- [URI](#uri) +- [alt](#alt) +- [ap](#ap) +- [apFirst](#apfirst) +- [apSecond](#apsecond) +- [chain](#chain) +- [chainFirst](#chainfirst) +- [chop](#chop) +- [chunksOf](#chunksof) +- [compact](#compact) +- [comprehension](#comprehension) +- [cons](#cons) +- [deleteAt](#deleteat) +- [difference](#difference) +- [dropLeft](#dropleft) +- [dropLeftWhile](#dropleftwhile) +- [dropRight](#dropright) +- [duplicate](#duplicate) +- [elem](#elem) +- [empty](#empty) +- [extend](#extend) +- [filter](#filter) +- [filterMap](#filtermap) +- [filterMapWithIndex](#filtermapwithindex) +- [filterWithIndex](#filterwithindex) +- [findFirst](#findfirst) +- [findFirstMap](#findfirstmap) +- [findIndex](#findindex) +- [findLast](#findlast) +- [findLastIndex](#findlastindex) +- [findLastMap](#findlastmap) +- [flatten](#flatten) +- [foldLeft](#foldleft) +- [foldMap](#foldmap) +- [foldMapWithIndex](#foldmapwithindex) +- [foldRight](#foldright) +- [fromArray](#fromarray) +- [getEq](#geteq) +- [getMonoid](#getmonoid) +- [getOrd](#getord) +- [getShow](#getshow) +- [head](#head) +- [init](#init) +- [insertAt](#insertat) +- [intersection](#intersection) +- [isEmpty](#isempty) +- [isNonEmpty](#isnonempty) +- [isOutOfBound](#isoutofbound) +- [last](#last) +- [lefts](#lefts) +- [lookup](#lookup) +- [makeBy](#makeby) +- [map](#map) +- [mapWithIndex](#mapwithindex) +- [modifyAt](#modifyat) +- [of](#of) +- [partition](#partition) +- [partitionMap](#partitionmap) +- [partitionMapWithIndex](#partitionmapwithindex) +- [partitionWithIndex](#partitionwithindex) +- [range](#range) +- [readonlyArray](#readonlyarray) +- [reduce](#reduce) +- [reduceRight](#reduceright) +- [reduceRightWithIndex](#reducerightwithindex) +- [reduceWithIndex](#reducewithindex) +- [replicate](#replicate) +- [reverse](#reverse) +- [rights](#rights) +- [rotate](#rotate) +- [scanLeft](#scanleft) +- [scanRight](#scanright) +- [separate](#separate) +- [snoc](#snoc) +- [sort](#sort) +- [sortBy](#sortby) +- [spanLeft](#spanleft) +- [splitAt](#splitat) +- [tail](#tail) +- [takeLeft](#takeleft) +- [takeLeftWhile](#takeleftwhile) +- [takeRight](#takeright) +- [toArray](#toarray) +- [union](#union) +- [uniq](#uniq) +- [unsafeDeleteAt](#unsafedeleteat) +- [unsafeInsertAt](#unsafeinsertat) +- [unsafeUpdateAt](#unsafeupdateat) +- [unzip](#unzip) +- [updateAt](#updateat) +- [zip](#zip) +- [zipWith](#zipwith) + +--- + +# Spanned (interface) + +**Signature** + +```ts +export interface Spanned { + readonly init: ReadonlyArray + readonly rest: ReadonlyArray +} +``` + +Added in v2.5.0 + +# URI (type alias) + +**Signature** + +```ts +export type URI = typeof URI +``` + +Added in v2.5.0 + +# URI + +**Signature** + +```ts +export const URI: "ReadonlyArray" = ... +``` + +Added in v2.5.0 + +# alt + +**Signature** + +```ts +
(that: () => readonly A[]) => (fa: readonly A[]) => readonly A[] +``` + +Added in v2.5.0 + +# ap + +**Signature** + +```ts +(fa: readonly A[]) => (fab: readonly ((a: A) => B)[]) => readonly B[] +``` + +Added in v2.5.0 + +# apFirst + +**Signature** + +```ts +(fb: readonly B[]) => (fa: readonly A[]) => readonly A[] +``` + +Added in v2.5.0 + +# apSecond + +**Signature** + +```ts +(fb: readonly B[]) => (fa: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# chain + +**Signature** + +```ts +(f: (a: A) => readonly B[]) => (ma: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# chainFirst + +**Signature** + +```ts +(f: (a: A) => readonly B[]) => (ma: readonly A[]) => readonly A[] +``` + +Added in v2.5.0 + +# chop + +A useful recursion pattern for processing an array to produce a new array, often used for "chopping" up the input +array. Typically chop is called with some function that will consume an initial prefix of the array and produce a +value and the rest of the array. + +**Signature** + +```ts +export function chop( + f: (as: ReadonlyNonEmptyArray) => readonly [B, ReadonlyArray] +): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { Eq, eqNumber } from 'fp-ts/lib/Eq' +import { chop, spanLeft } from 'fp-ts/lib/ReadonlyArray' + +const group = (S: Eq): ((as: ReadonlyArray) => ReadonlyArray>) => { + return chop(as => { + const { init, rest } = spanLeft((a: A) => S.equals(a, as[0]))(as) + return [init, rest] + }) +} +assert.deepStrictEqual(group(eqNumber)([1, 1, 2, 3, 3, 4]), [[1, 1], [2], [3, 3], [4]]) +``` + +Added in v2.5.0 + +# chunksOf + +Splits an array into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of +the array. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive +definition of `chunksOf`; it satisfies the property that + +```ts +chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys))) +``` + +whenever `n` evenly divides the length of `xs`. + +**Signature** + +```ts +export function chunksOf(n: number): (as: ReadonlyArray) => ReadonlyArray> { ... } +``` + +**Example** + +```ts +import { chunksOf } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]]) +``` + +Added in v2.5.0 + +# compact + +**Signature** + +```ts +(fa: readonly Option[]) => readonly A[] +``` + +Added in v2.5.0 + +# comprehension + +Array comprehension + +``` +[ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ] +``` + +**Signature** + +```ts +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray, ReadonlyArray], + f: (a: A, b: B, c: C, d: D) => R, + g?: (a: A, b: B, c: C, d: D) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray], + f: (a: A, b: B, c: C) => R, + g?: (a: A, b: B, c: C) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray], + f: (a: A) => R, + g?: (a: A) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray], + f: (a: A, b: B) => R, + g?: (a: A, b: B) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray], + f: (a: A) => boolean, + g?: (a: A) => R +): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { comprehension } from 'fp-ts/lib/ReadonlyArray' +import { tuple } from 'fp-ts/lib/function' + +assert.deepStrictEqual( + comprehension( + [ + [1, 2, 3], + ['a', 'b'] + ], + tuple, + (a, b) => (a + b.length) % 2 === 0 + ), + [ + [1, 'a'], + [1, 'b'], + [3, 'a'], + [3, 'b'] + ] +) +``` + +Added in v2.5.0 + +# cons + +Attaches an element to the front of an array, creating a new non empty array + +**Signature** + +```ts +export function cons(head: A, tail: ReadonlyArray): ReadonlyNonEmptyArray { ... } +``` + +**Example** + +```ts +import { cons } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(cons(0, [1, 2, 3]), [0, 1, 2, 3]) +``` + +Added in v2.5.0 + +# deleteAt + +Delete the element at the specified index, creating a new array, or returning `None` if the index is out of bounds + +**Signature** + +```ts +export function deleteAt(i: number): (as: ReadonlyArray) => Option> { ... } +``` + +**Example** + +```ts +import { deleteAt } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(deleteAt(0)([1, 2, 3]), some([2, 3])) +assert.deepStrictEqual(deleteAt(1)([]), none) +``` + +Added in v2.5.0 + +# difference + +Creates an array of array values not included in the other given array using a `Eq` for equality +comparisons. The order and references of result values are determined by the first array. + +**Signature** + +```ts +export function difference(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { difference } from 'fp-ts/lib/ReadonlyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.deepStrictEqual(difference(eqNumber)([1, 2], [2, 3]), [1]) +``` + +Added in v2.5.0 + +# dropLeft + +Drop a number of elements from the start of an array, creating a new array + +**Signature** + +```ts +export function dropLeft(n: number): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { dropLeft } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(dropLeft(2)([1, 2, 3]), [3]) +``` + +Added in v2.5.0 + +# dropLeftWhile + +Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new array + +**Signature** + +```ts +export function dropLeftWhile(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { dropLeftWhile } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(dropLeftWhile((n: number) => n % 2 === 1)([1, 3, 2, 4, 5]), [2, 4, 5]) +``` + +Added in v2.5.0 + +# dropRight + +Drop a number of elements from the end of an array, creating a new array + +**Signature** + +```ts +export function dropRight(n: number): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { dropRight } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(dropRight(2)([1, 2, 3, 4, 5]), [1, 2, 3]) +``` + +Added in v2.5.0 + +# duplicate + +**Signature** + +```ts +(ma: readonly A[]) => readonly (readonly A[])[] +``` + +Added in v2.5.0 + +# elem + +Test if a value is a member of an array. Takes a `Eq` as a single +argument which returns the function to use to search for a value of type `A` in +an array of type `ReadonlyArray`. + +**Signature** + +```ts +export function elem(E: Eq): (a: A, as: ReadonlyArray) => boolean { ... } +``` + +**Example** + +```ts +import { elem } from 'fp-ts/lib/ReadonlyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.strictEqual(elem(eqNumber)(1, [1, 2, 3]), true) +assert.strictEqual(elem(eqNumber)(4, [1, 2, 3]), false) +``` + +Added in v2.5.0 + +# empty + +An empty array + +**Signature** + +```ts +export const empty: ReadonlyArray = ... +``` + +Added in v2.5.0 + +# extend + +**Signature** + +```ts +(f: (fa: readonly A[]) => B) => (ma: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# filter + +**Signature** + +```ts +{ (refinement: Refinement): (fa: readonly A[]) => readonly B[]; (predicate: Predicate): (fa: readonly A[]) => readonly A[]; } +``` + +Added in v2.5.0 + +# filterMap + +**Signature** + +```ts +(f: (a: A) => Option) => (fa: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# filterMapWithIndex + +**Signature** + +```ts +(f: (i: number, a: A) => Option) => (fa: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# filterWithIndex + +**Signature** + +```ts +{ (refinementWithIndex: RefinementWithIndex): (fa: readonly A[]) => readonly B[]; (predicateWithIndex: PredicateWithIndex): (fa: readonly A[]) => readonly A[]; } +``` + +Added in v2.5.0 + +# findFirst + +Find the first element which satisfies a predicate (or a refinement) function + +**Signature** + +```ts +export function findFirst(refinement: Refinement): (as: ReadonlyArray) => Option +export function findFirst(predicate: Predicate): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findFirst } from 'fp-ts/lib/ReadonlyArray' +import { some } from 'fp-ts/lib/Option' + +assert.deepStrictEqual( + findFirst((x: { a: number; b: number }) => x.a === 1)([ + { a: 1, b: 1 }, + { a: 1, b: 2 } + ]), + some({ a: 1, b: 1 }) +) +``` + +Added in v2.5.0 + +# findFirstMap + +Find the first element returned by an option based selector function + +**Signature** + +```ts +export function findFirstMap(f: (a: A) => Option): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findFirstMap } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +interface Person { + name: string + age?: number +} + +const persons: ReadonlyArray = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }] + +// returns the name of the first person that has an age +assert.deepStrictEqual(findFirstMap((p: Person) => (p.age === undefined ? none : some(p.name)))(persons), some('Mary')) +``` + +Added in v2.5.0 + +# findIndex + +Find the first index for which a predicate holds + +**Signature** + +```ts +export function findIndex(predicate: Predicate): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findIndex } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(findIndex((n: number) => n === 2)([1, 2, 3]), some(1)) +assert.deepStrictEqual(findIndex((n: number) => n === 2)([]), none) +``` + +Added in v2.5.0 + +# findLast + +Find the last element which satisfies a predicate function + +**Signature** + +```ts +export function findLast(refinement: Refinement): (as: ReadonlyArray) => Option +export function findLast(predicate: Predicate): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findLast } from 'fp-ts/lib/ReadonlyArray' +import { some } from 'fp-ts/lib/Option' + +assert.deepStrictEqual( + findLast((x: { a: number; b: number }) => x.a === 1)([ + { a: 1, b: 1 }, + { a: 1, b: 2 } + ]), + some({ a: 1, b: 2 }) +) +``` + +Added in v2.5.0 + +# findLastIndex + +Returns the index of the last element of the list which matches the predicate + +**Signature** + +```ts +export function findLastIndex(predicate: Predicate): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findLastIndex } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +interface X { + a: number + b: number +} +const xs: ReadonlyArray = [ + { a: 1, b: 0 }, + { a: 1, b: 1 } +] +assert.deepStrictEqual(findLastIndex((x: { a: number }) => x.a === 1)(xs), some(1)) +assert.deepStrictEqual(findLastIndex((x: { a: number }) => x.a === 4)(xs), none) +``` + +Added in v2.5.0 + +# findLastMap + +Find the last element returned by an option based selector function + +**Signature** + +```ts +export function findLastMap(f: (a: A) => Option): (as: ReadonlyArray) => Option { ... } +``` + +**Example** + +```ts +import { findLastMap } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +interface Person { + name: string + age?: number +} + +const persons: ReadonlyArray = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }] + +// returns the name of the last person that has an age +assert.deepStrictEqual(findLastMap((p: Person) => (p.age === undefined ? none : some(p.name)))(persons), some('Joey')) +``` + +Added in v2.5.0 + +# flatten + +Removes one level of nesting + +**Signature** + +```ts +export function flatten(mma: ReadonlyArray>): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { flatten } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(flatten([[1], [2], [3]]), [1, 2, 3]) +``` + +Added in v2.5.0 + +# foldLeft + +Break an array into its first element and remaining elements + +**Signature** + +```ts +export function foldLeft( + onNil: () => B, + onCons: (head: A, tail: ReadonlyArray) => B +): (as: ReadonlyArray) => B { ... } +``` + +**Example** + +```ts +import { foldLeft } from 'fp-ts/lib/ReadonlyArray' + +const len: (as: ReadonlyArray) => number = foldLeft( + () => 0, + (_, tail) => 1 + len(tail) +) +assert.strictEqual(len([1, 2, 3]), 3) +``` + +Added in v2.5.0 + +# foldMap + +**Signature** + +```ts +;(M: Monoid) => (f: (a: A) => M) => (fa: readonly A[]) => M +``` + +Added in v2.5.0 + +# foldMapWithIndex + +**Signature** + +```ts +;(M: Monoid) => (f: (i: number, a: A) => M) => (fa: readonly A[]) => M +``` + +Added in v2.5.0 + +# foldRight + +Break an array into its initial elements and the last element + +**Signature** + +```ts +export function foldRight( + onNil: () => B, + onCons: (init: ReadonlyArray, last: A) => B +): (as: ReadonlyArray) => B { ... } +``` + +Added in v2.5.0 + +# fromArray + +**Signature** + +```ts +export function fromArray(as: Array): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# getEq + +Derives an `Eq` over the `ReadonlyArray` of a given element type from the `Eq` of that type. The derived `Eq` defines two +arrays as equal if all elements of both arrays are compared equal pairwise with the given `E`. In case of arrays of +different lengths, the result is non equality. + +**Signature** + +```ts +export function getEq(E: Eq): Eq> { ... } +``` + +**Example** + +```ts +import { eqString } from 'fp-ts/lib/Eq' +import { getEq } from 'fp-ts/lib/ReadonlyArray' + +const E = getEq(eqString) +assert.strictEqual(E.equals(['a', 'b'], ['a', 'b']), true) +assert.strictEqual(E.equals(['a'], []), false) +``` + +Added in v2.5.0 + +# getMonoid + +Returns a `Monoid` for `ReadonlyArray` + +**Signature** + +```ts +export function getMonoid(): Monoid> { ... } +``` + +**Example** + +```ts +import { getMonoid } from 'fp-ts/lib/ReadonlyArray' + +const M = getMonoid() +assert.deepStrictEqual(M.concat([1, 2], [3, 4]), [1, 2, 3, 4]) +``` + +Added in v2.5.0 + +# getOrd + +Derives an `Ord` over the `ReadonlyArray` of a given element type from the `Ord` of that type. The ordering between two such +arrays is equal to: the first non equal comparison of each arrays elements taken pairwise in increasing order, in +case of equality over all the pairwise elements; the longest array is considered the greatest, if both arrays have +the same length, the result is equality. + +**Signature** + +```ts +export function getOrd(O: Ord): Ord> { ... } +``` + +**Example** + +```ts +import { getOrd } from 'fp-ts/lib/ReadonlyArray' +import { ordString } from 'fp-ts/lib/Ord' + +const O = getOrd(ordString) +assert.strictEqual(O.compare(['b'], ['a']), 1) +assert.strictEqual(O.compare(['a'], ['a']), 0) +assert.strictEqual(O.compare(['a'], ['b']), -1) +``` + +Added in v2.5.0 + +# getShow + +**Signature** + +```ts +export function getShow(S: Show): Show> { ... } +``` + +Added in v2.5.0 + +# head + +Get the first element in an array, or `None` if the array is empty + +**Signature** + +```ts +export function head(as: ReadonlyArray): Option { ... } +``` + +**Example** + +```ts +import { head } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(head([1, 2, 3]), some(1)) +assert.deepStrictEqual(head([]), none) +``` + +Added in v2.5.0 + +# init + +Get all but the last element of an array, creating a new array, or `None` if the array is empty + +**Signature** + +```ts +export function init(as: ReadonlyArray): Option> { ... } +``` + +**Example** + +```ts +import { init } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(init([1, 2, 3]), some([1, 2])) +assert.deepStrictEqual(init([]), none) +``` + +Added in v2.5.0 + +# insertAt + +Insert an element at the specified index, creating a new array, or returning `None` if the index is out of bounds + +**Signature** + +```ts +export function insertAt(i: number, a: A): (as: ReadonlyArray) => Option> { ... } +``` + +**Example** + +```ts +import { insertAt } from 'fp-ts/lib/ReadonlyArray' +import { some } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(insertAt(2, 5)([1, 2, 3, 4]), some([1, 2, 5, 3, 4])) +``` + +Added in v2.5.0 + +# intersection + +Creates an array of unique values that are included in all given arrays using a `Eq` for equality +comparisons. The order and references of result values are determined by the first array. + +**Signature** + +```ts +export function intersection(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { intersection } from 'fp-ts/lib/ReadonlyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.deepStrictEqual(intersection(eqNumber)([1, 2], [2, 3]), [2]) +``` + +Added in v2.5.0 + +# isEmpty + +Test whether an array is empty + +**Signature** + +```ts +export function isEmpty(as: ReadonlyArray): boolean { ... } +``` + +**Example** + +```ts +import { isEmpty } from 'fp-ts/lib/ReadonlyArray' + +assert.strictEqual(isEmpty([]), true) +``` + +Added in v2.5.0 + +# isNonEmpty + +Test whether an array is non empty narrowing down the type to `NonEmptyReadonlyArray` + +**Signature** + +```ts +export function isNonEmpty(as: ReadonlyArray): as is ReadonlyNonEmptyArray { ... } +``` + +Added in v2.5.0 + +# isOutOfBound + +Test whether an array contains a particular index + +**Signature** + +```ts +export function isOutOfBound(i: number, as: ReadonlyArray): boolean { ... } +``` + +Added in v2.5.0 + +# last + +Get the last element in an array, or `None` if the array is empty + +**Signature** + +```ts +export function last(as: ReadonlyArray): Option { ... } +``` + +**Example** + +```ts +import { last } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(last([1, 2, 3]), some(3)) +assert.deepStrictEqual(last([]), none) +``` + +Added in v2.5.0 + +# lefts + +Extracts from an array of `Either` all the `Left` elements. All the `Left` elements are extracted in order + +**Signature** + +```ts +export function lefts(as: ReadonlyArray>): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { lefts } from 'fp-ts/lib/ReadonlyArray' +import { left, right } from 'fp-ts/lib/Either' + +assert.deepStrictEqual(lefts([right(1), left('foo'), right(2)]), ['foo']) +``` + +Added in v2.5.0 + +# lookup + +This function provides a safe way to read a value at a particular index from an array + +**Signature** + +```ts +export function lookup(i: number, as: ReadonlyArray): Option { ... } +``` + +**Example** + +```ts +import { lookup } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(lookup(1, [1, 2, 3]), some(2)) +assert.deepStrictEqual(lookup(3, [1, 2, 3]), none) +``` + +Added in v2.5.0 + +# makeBy + +Return a list of length `n` with element `i` initialized with `f(i)` + +**Signature** + +```ts +export function makeBy(n: number, f: (i: number) => A): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { makeBy } from 'fp-ts/lib/ReadonlyArray' + +const double = (n: number): number => n * 2 +assert.deepStrictEqual(makeBy(5, double), [0, 2, 4, 6, 8]) +``` + +Added in v2.5.0 + +# map + +**Signature** + +```ts +(f: (a: A) => B) => (fa: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# mapWithIndex + +**Signature** + +```ts +(f: (i: number, a: A) => B) => (fa: readonly A[]) => readonly B[] +``` + +Added in v2.5.0 + +# modifyAt + +Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out +of bounds + +**Signature** + +```ts +export function modifyAt(i: number, f: (a: A) => A): (as: ReadonlyArray) => Option> { ... } +``` + +**Example** + +```ts +import { modifyAt } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +const double = (x: number): number => x * 2 +assert.deepStrictEqual(modifyAt(1, double)([1, 2, 3]), some([1, 4, 3])) +assert.deepStrictEqual(modifyAt(1, double)([]), none) +``` + +Added in v2.5.0 + +# of + +**Signature** + +```ts +export const of = (a: A): ReadonlyArray => ... +``` + +Added in v2.5.0 + +# partition + +**Signature** + +```ts +{ (refinement: Refinement): (fa: readonly A[]) => Separated; (predicate: Predicate): (fa: readonly A[]) => Separated; } +``` + +Added in v2.5.0 + +# partitionMap + +**Signature** + +```ts +(f: (a: A) => Either) => (fa: readonly A[]) => Separated +``` + +Added in v2.5.0 + +# partitionMapWithIndex + +**Signature** + +```ts +(f: (i: number, a: A) => Either) => (fa: readonly A[]) => Separated +``` + +Added in v2.5.0 + +# partitionWithIndex + +**Signature** + +```ts +{ (refinementWithIndex: RefinementWithIndex): (fa: readonly A[]) => Separated; (predicateWithIndex: PredicateWithIndex): (fa: readonly A[]) => Separated; } +``` + +Added in v2.5.0 + +# range + +Create an array containing a range of integers, including both endpoints + +**Signature** + +```ts +export function range(start: number, end: number): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { range } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(range(1, 5), [1, 2, 3, 4, 5]) +``` + +Added in v2.5.0 + +# readonlyArray + +**Signature** + +```ts +export const readonlyArray: Monad1 & + Foldable1 & + Unfoldable1 & + TraversableWithIndex1 & + Alternative1 & + Extend1 & + Compactable1 & + FilterableWithIndex1 & + Witherable1 & + FunctorWithIndex1 & + FoldableWithIndex1 = ... +``` + +Added in v2.5.0 + +# reduce + +**Signature** + +```ts +;(b: B, f: (b: B, a: A) => B) => (fa: readonly A[]) => B +``` + +Added in v2.5.0 + +# reduceRight + +**Signature** + +```ts +;(b: B, f: (a: A, b: B) => B) => (fa: readonly A[]) => B +``` + +Added in v2.5.0 + +# reduceRightWithIndex + +**Signature** + +```ts +;(b: B, f: (i: number, a: A, b: B) => B) => (fa: readonly A[]) => B +``` + +Added in v2.5.0 + +# reduceWithIndex + +**Signature** + +```ts +;(b: B, f: (i: number, b: B, a: A) => B) => (fa: readonly A[]) => B +``` + +Added in v2.5.0 + +# replicate + +Create an array containing a value repeated the specified number of times + +**Signature** + +```ts +export function replicate(n: number, a: A): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { replicate } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(replicate(3, 'a'), ['a', 'a', 'a']) +``` + +Added in v2.5.0 + +# reverse + +Reverse an array, creating a new array + +**Signature** + +```ts +export function reverse(as: ReadonlyArray): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { reverse } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(reverse([1, 2, 3]), [3, 2, 1]) +``` + +Added in v2.5.0 + +# rights + +Extracts from an array of `Either` all the `Right` elements. All the `Right` elements are extracted in order + +**Signature** + +```ts +export function rights(as: ReadonlyArray>): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { rights } from 'fp-ts/lib/ReadonlyArray' +import { right, left } from 'fp-ts/lib/Either' + +assert.deepStrictEqual(rights([right(1), left('foo'), right(2)]), [1, 2]) +``` + +Added in v2.5.0 + +# rotate + +Rotate an array to the right by `n` steps + +**Signature** + +```ts +export function rotate(n: number): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { rotate } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(rotate(2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) +``` + +Added in v2.5.0 + +# scanLeft + +Same as `reduce` but it carries over the intermediate steps + +```ts +import { scanLeft } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(scanLeft(10, (b, a: number) => b - a)([1, 2, 3]), [10, 9, 7, 4]) +``` + +**Signature** + +```ts +export function scanLeft(b: B, f: (b: B, a: A) => B): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# scanRight + +Fold an array from the right, keeping all intermediate results instead of only the final result + +**Signature** + +```ts +export function scanRight(b: B, f: (a: A, b: B) => B): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { scanRight } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(scanRight(10, (a: number, b) => b - a)([1, 2, 3]), [4, 5, 7, 10]) +``` + +Added in v2.5.0 + +# separate + +**Signature** + +```ts +(fa: readonly Either[]) => Separated +``` + +Added in v2.5.0 + +# snoc + +Append an element to the end of an array, creating a new non empty array + +**Signature** + +```ts +export function snoc(init: ReadonlyArray, end: A): ReadonlyNonEmptyArray { ... } +``` + +**Example** + +```ts +import { snoc } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4]) +``` + +Added in v2.5.0 + +# sort + +Sort the elements of an array in increasing order, creating a new array + +**Signature** + +```ts +export function sort(O: Ord): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { sort } from 'fp-ts/lib/ReadonlyArray' +import { ordNumber } from 'fp-ts/lib/Ord' + +assert.deepStrictEqual(sort(ordNumber)([3, 2, 1]), [1, 2, 3]) +``` + +Added in v2.5.0 + +# sortBy + +Sort the elements of an array in increasing order, where elements are compared using first `ords[0]`, then `ords[1]`, +etc... + +**Signature** + +```ts +export function sortBy(ords: ReadonlyArray>): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { sortBy } from 'fp-ts/lib/ReadonlyArray' +import { ord, ordString, ordNumber } from 'fp-ts/lib/Ord' + +interface Person { + name: string + age: number +} +const byName = ord.contramap(ordString, (p: Person) => p.name) +const byAge = ord.contramap(ordNumber, (p: Person) => p.age) + +const sortByNameByAge = sortBy([byName, byAge]) + +const persons = [ + { name: 'a', age: 1 }, + { name: 'b', age: 3 }, + { name: 'c', age: 2 }, + { name: 'b', age: 2 } +] +assert.deepStrictEqual(sortByNameByAge(persons), [ + { name: 'a', age: 1 }, + { name: 'b', age: 2 }, + { name: 'b', age: 3 }, + { name: 'c', age: 2 } +]) +``` + +Added in v2.5.0 + +# spanLeft + +Split an array into two parts: + +1. the longest initial subarray for which all elements satisfy the specified predicate +2. the remaining elements + +**Signature** + +```ts +export function spanLeft(refinement: Refinement): (as: ReadonlyArray) => Spanned +export function spanLeft(predicate: Predicate): (as: ReadonlyArray) => Spanned { ... } +``` + +**Example** + +```ts +import { spanLeft } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(spanLeft((n: number) => n % 2 === 1)([1, 3, 2, 4, 5]), { init: [1, 3], rest: [2, 4, 5] }) +``` + +Added in v2.5.0 + +# splitAt + +Splits an array into two pieces, the first piece has `n` elements. + +**Signature** + +```ts +export function splitAt(n: number): (as: ReadonlyArray) => readonly [ReadonlyArray, ReadonlyArray] { ... } +``` + +**Example** + +```ts +import { splitAt } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(splitAt(2)([1, 2, 3, 4, 5]), [ + [1, 2], + [3, 4, 5] +]) +``` + +Added in v2.5.0 + +# tail + +Get all but the first element of an array, creating a new array, or `None` if the array is empty + +**Signature** + +```ts +export function tail(as: ReadonlyArray): Option> { ... } +``` + +**Example** + +```ts +import { tail } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(tail([1, 2, 3]), some([2, 3])) +assert.deepStrictEqual(tail([]), none) +``` + +Added in v2.5.0 + +# takeLeft + +Keep only a number of elements from the start of an array, creating a new array. +`n` must be a natural number + +**Signature** + +```ts +export function takeLeft(n: number): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { takeLeft } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(takeLeft(2)([1, 2, 3]), [1, 2]) +``` + +Added in v2.5.0 + +# takeLeftWhile + +Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new array + +**Signature** + +```ts +export function takeLeftWhile(refinement: Refinement): (as: ReadonlyArray) => ReadonlyArray +export function takeLeftWhile(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { takeLeftWhile } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(takeLeftWhile((n: number) => n % 2 === 0)([2, 4, 3, 6]), [2, 4]) +``` + +Added in v2.5.0 + +# takeRight + +Keep only a number of elements from the end of an array, creating a new array. +`n` must be a natural number + +**Signature** + +```ts +export function takeRight(n: number): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { takeRight } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(takeRight(2)([1, 2, 3, 4, 5]), [4, 5]) +``` + +Added in v2.5.0 + +# toArray + +**Signature** + +```ts +export function toArray(ras: ReadonlyArray): Array { ... } +``` + +Added in v2.5.0 + +# union + +Creates an array of unique values, in order, from all given arrays using a `Eq` for equality comparisons + +**Signature** + +```ts +export function union(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { union } from 'fp-ts/lib/ReadonlyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.deepStrictEqual(union(eqNumber)([1, 2], [2, 3]), [1, 2, 3]) +``` + +Added in v2.5.0 + +# uniq + +Remove duplicates from an array, keeping the first occurrence of an element. + +**Signature** + +```ts +export function uniq(E: Eq): (as: ReadonlyArray) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { uniq } from 'fp-ts/lib/ReadonlyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.deepStrictEqual(uniq(eqNumber)([1, 2, 1]), [1, 2]) +``` + +Added in v2.5.0 + +# unsafeDeleteAt + +**Signature** + +```ts +export function unsafeDeleteAt(i: number, as: ReadonlyArray): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# unsafeInsertAt + +**Signature** + +```ts +export function unsafeInsertAt(i: number, a: A, as: ReadonlyArray): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# unsafeUpdateAt + +**Signature** + +```ts +export function unsafeUpdateAt(i: number, a: A, as: ReadonlyArray): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# unzip + +The function is reverse of `zip`. Takes an array of pairs and return two corresponding arrays + +**Signature** + +```ts +export function unzip(as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] { ... } +``` + +**Example** + +```ts +import { unzip } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual( + unzip([ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]), + [ + [1, 2, 3], + ['a', 'b', 'c'] + ] +) +``` + +Added in v2.5.0 + +# updateAt + +Change the element at the specified index, creating a new array, or returning `None` if the index is out of bounds + +**Signature** + +```ts +export function updateAt(i: number, a: A): (as: ReadonlyArray) => Option> { ... } +``` + +**Example** + +```ts +import { updateAt } from 'fp-ts/lib/ReadonlyArray' +import { some, none } from 'fp-ts/lib/Option' + +assert.deepStrictEqual(updateAt(1, 1)([1, 2, 3]), some([1, 1, 3])) +assert.deepStrictEqual(updateAt(1, 1)([]), none) +``` + +Added in v2.5.0 + +# zip + +Takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the +longer array are discarded + +**Signature** + +```ts +export function zip(fa: ReadonlyArray, fb: ReadonlyArray): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { zip } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual(zip([1, 2, 3], ['a', 'b', 'c', 'd']), [ + [1, 'a'], + [2, 'b'], + [3, 'c'] +]) +``` + +Added in v2.5.0 + +# zipWith + +Apply a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one +input array is short, excess elements of the longer array are discarded. + +**Signature** + +```ts +export function zipWith(fa: ReadonlyArray, fb: ReadonlyArray, f: (a: A, b: B) => C): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { zipWith } from 'fp-ts/lib/ReadonlyArray' + +assert.deepStrictEqual( + zipWith([1, 2, 3], ['a', 'b', 'c', 'd'], (n, s) => s + n), + ['a1', 'b2', 'c3'] +) +``` + +Added in v2.5.0 diff --git a/docs/modules/ReadonlyMap.ts.md b/docs/modules/ReadonlyMap.ts.md new file mode 100644 index 000000000..1e69b2d70 --- /dev/null +++ b/docs/modules/ReadonlyMap.ts.md @@ -0,0 +1,484 @@ +--- +title: ReadonlyMap.ts +nav_order: 70 +parent: Modules +--- + +# ReadonlyMap overview + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [URI (type alias)](#uri-type-alias) +- [URI](#uri) +- [collect](#collect) +- [compact](#compact) +- [deleteAt](#deleteat) +- [elem](#elem) +- [empty](#empty) +- [filter](#filter) +- [filterMap](#filtermap) +- [fromFoldable](#fromfoldable) +- [fromMap](#frommap) +- [getEq](#geteq) +- [getFilterableWithIndex](#getfilterablewithindex) +- [getMonoid](#getmonoid) +- [getShow](#getshow) +- [getWitherable](#getwitherable) +- [insertAt](#insertat) +- [isEmpty](#isempty) +- [isSubmap](#issubmap) +- [keys](#keys) +- [lookup](#lookup) +- [lookupWithKey](#lookupwithkey) +- [map](#map) +- [member](#member) +- [modifyAt](#modifyat) +- [partition](#partition) +- [partitionMap](#partitionmap) +- [pop](#pop) +- [readonlyMap](#readonlymap) +- [separate](#separate) +- [singleton](#singleton) +- [size](#size) +- [toMap](#tomap) +- [toReadonlyArray](#toreadonlyarray) +- [toUnfoldable](#tounfoldable) +- [updateAt](#updateat) +- [values](#values) + +--- + +# URI (type alias) + +**Signature** + +```ts +export type URI = typeof URI +``` + +Added in v2.5.0 + +# URI + +**Signature** + +```ts +export const URI: "ReadonlyMap" = ... +``` + +Added in v2.5.0 + +# collect + +**Signature** + +```ts +export function collect(O: Ord): (f: (k: K, a: A) => B) => (m: ReadonlyMap) => ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# compact + +**Signature** + +```ts +(fa: ReadonlyMap>) => ReadonlyMap +``` + +Added in v2.5.0 + +# deleteAt + +Delete a key and value from a map + +**Signature** + +```ts +export function deleteAt(E: Eq): (k: K) =>
(m: ReadonlyMap) => ReadonlyMap { ... } +``` + +Added in v2.5.0 + +# elem + +Test whether or not a value is a member of a map + +**Signature** + +```ts +export function elem(E: Eq): (a: A, m: ReadonlyMap) => boolean { ... } +``` + +Added in v2.5.0 + +# empty + +**Signature** + +```ts +export const empty: ReadonlyMap = ... +``` + +Added in v2.5.0 + +# filter + +**Signature** + +```ts +{ (refinement: Refinement): (fa: ReadonlyMap) => ReadonlyMap; (predicate: Predicate): (fa: ReadonlyMap) => ReadonlyMap; } +``` + +Added in v2.5.0 + +# filterMap + +**Signature** + +```ts +(f: (a: A) => Option) => (fa: ReadonlyMap) => ReadonlyMap +``` + +Added in v2.5.0 + +# fromFoldable + +Create a map from a foldable collection of key/value pairs, using the +specified `Magma` to combine values for duplicate keys. + +**Signature** + +```ts +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable3 +): (fka: Kind3) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable2 +): (fka: Kind2) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable1 +): (fka: Kind) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyMap { ... } +``` + +Added in v2.5.0 + +# fromMap + +**Signature** + +```ts +export function fromMap(m: Map): ReadonlyMap { ... } +``` + +Added in v2.5.0 + +# getEq + +**Signature** + +```ts +export function getEq(SK: Eq, SA: Eq): Eq> { ... } +``` + +Added in v2.5.0 + +# getFilterableWithIndex + +**Signature** + +```ts +export function getFilterableWithIndex(): FilterableWithIndex2C { ... } +``` + +Added in v2.5.0 + +# getMonoid + +Gets `Monoid` instance for Maps given `Semigroup` instance for their values + +**Signature** + +```ts +export function getMonoid(SK: Eq, SA: Semigroup): Monoid> { ... } +``` + +Added in v2.5.0 + +# getShow + +**Signature** + +```ts +export function getShow(SK: Show, SA: Show): Show> { ... } +``` + +Added in v2.5.0 + +# getWitherable + +**Signature** + +```ts +export function getWitherable(O: Ord): Witherable2C & TraversableWithIndex2C { ... } +``` + +Added in v2.5.0 + +# insertAt + +Insert or replace a key/value pair in a map + +**Signature** + +```ts +export function insertAt(E: Eq): (k: K, a: A) => (m: ReadonlyMap) => ReadonlyMap { ... } +``` + +Added in v2.5.0 + +# isEmpty + +Test whether or not a map is empty + +**Signature** + +```ts +export function isEmpty(d: ReadonlyMap): boolean { ... } +``` + +Added in v2.5.0 + +# isSubmap + +Test whether or not one Map contains all of the keys and values contained in another Map + +**Signature** + +```ts +export function isSubmap(SK: Eq, SA: Eq): (d1: ReadonlyMap, d2: ReadonlyMap) => boolean { ... } +``` + +Added in v2.5.0 + +# keys + +Get a sorted array of the keys contained in a map + +**Signature** + +```ts +export function keys(O: Ord): (m: ReadonlyMap) => ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# lookup + +Lookup the value for a key in a `Map`. + +**Signature** + +```ts +export function lookup(E: Eq): (k: K, m: ReadonlyMap) => Option { ... } +``` + +Added in v2.5.0 + +# lookupWithKey + +Lookup the value for a key in a `Map`. +If the result is a `Some`, the existing key is also returned. + +**Signature** + +```ts +export function lookupWithKey(E: Eq): (k: K, m: ReadonlyMap) => Option { ... } +``` + +Added in v2.5.0 + +# map + +**Signature** + +```ts +(f: (a: A) => B) => (fa: ReadonlyMap) => ReadonlyMap +``` + +Added in v2.5.0 + +# member + +Test whether or not a key exists in a map + +**Signature** + +```ts +export function member(E: Eq): (k: K, m: ReadonlyMap) => boolean { ... } +``` + +Added in v2.5.0 + +# modifyAt + +**Signature** + +```ts +export function modifyAt( + E: Eq +): (k: K, f: (a: A) => A) => (m: ReadonlyMap) => Option> { ... } +``` + +Added in v2.5.0 + +# partition + +**Signature** + +```ts +{ (refinement: Refinement): (fa: ReadonlyMap) => Separated, ReadonlyMap>; (predicate: Predicate): (fa: ReadonlyMap) => Separated, ReadonlyMap>; } +``` + +Added in v2.5.0 + +# partitionMap + +**Signature** + +```ts +(f: (a: A) => Either) => (fa: ReadonlyMap) => Separated, ReadonlyMap> +``` + +Added in v2.5.0 + +# pop + +Delete a key and value from a map, returning the value as well as the subsequent map + +**Signature** + +```ts +export function pop(E: Eq): (k: K) => (m: ReadonlyMap) => Option]> { ... } +``` + +Added in v2.5.0 + +# readonlyMap + +**Signature** + +```ts +export const readonlyMap: Filterable2 = ... +``` + +Added in v2.5.0 + +# separate + +**Signature** + +```ts +(fa: ReadonlyMap>) => Separated, ReadonlyMap> +``` + +Added in v2.5.0 + +# singleton + +Create a map with one key/value pair + +**Signature** + +```ts +export function singleton(k: K, a: A): ReadonlyMap { ... } +``` + +Added in v2.5.0 + +# size + +Calculate the number of key/value pairs in a map + +**Signature** + +```ts +export function size(d: ReadonlyMap): number { ... } +``` + +Added in v2.5.0 + +# toMap + +**Signature** + +```ts +export function toMap(m: ReadonlyMap): Map { ... } +``` + +Added in v2.5.0 + +# toReadonlyArray + +Get a sorted of the key/value pairs contained in a map + +**Signature** + +```ts +export function toReadonlyArray(O: Ord): (m: ReadonlyMap) => ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# toUnfoldable + +Unfolds a map into a list of key/value pairs + +**Signature** + +```ts +export function toUnfoldable( + O: Ord, + U: Unfoldable1 +): (d: ReadonlyMap) => Kind +export function toUnfoldable(O: Ord, U: Unfoldable): (d: ReadonlyMap) => HKT { ... } +``` + +Added in v2.5.0 + +# updateAt + +**Signature** + +```ts +export function updateAt(E: Eq): (k: K, a: A) => (m: ReadonlyMap) => Option> { ... } +``` + +Added in v2.5.0 + +# values + +Get a sorted array of the values contained in a map + +**Signature** + +```ts +export function values(O: Ord): (m: ReadonlyMap) => ReadonlyArray { ... } +``` + +Added in v2.5.0 diff --git a/docs/modules/ReadonlyNonEmptyArray.ts.md b/docs/modules/ReadonlyNonEmptyArray.ts.md new file mode 100644 index 000000000..613c9ab46 --- /dev/null +++ b/docs/modules/ReadonlyNonEmptyArray.ts.md @@ -0,0 +1,629 @@ +--- +title: ReadonlyNonEmptyArray.ts +nav_order: 71 +parent: Modules +--- + +# ReadonlyNonEmptyArray overview + +Data structure which represents non-empty arrays + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [ReadonlyNonEmptyArray (interface)](#readonlynonemptyarray-interface) +- [URI (type alias)](#uri-type-alias) +- [URI](#uri) +- [ap](#ap) +- [apFirst](#apfirst) +- [apSecond](#apsecond) +- [chain](#chain) +- [chainFirst](#chainfirst) +- [concat](#concat) +- [cons](#cons) +- [duplicate](#duplicate) +- [extend](#extend) +- [filter](#filter) +- [filterWithIndex](#filterwithindex) +- [flatten](#flatten) +- [fold](#fold) +- [foldMap](#foldmap) +- [foldMapWithIndex](#foldmapwithindex) +- [fromArray](#fromarray) +- [fromReadonlyArray](#fromreadonlyarray) +- [getEq](#geteq) +- [getSemigroup](#getsemigroup) +- [getShow](#getshow) +- [group](#group) +- [groupBy](#groupby) +- [groupSort](#groupsort) +- [head](#head) +- [init](#init) +- [insertAt](#insertat) +- [last](#last) +- [map](#map) +- [mapWithIndex](#mapwithindex) +- [max](#max) +- [min](#min) +- [modifyAt](#modifyat) +- [of](#of) +- [readonlyNonEmptyArray](#readonlynonemptyarray) +- [reduce](#reduce) +- [reduceRight](#reduceright) +- [reduceRightWithIndex](#reducerightwithindex) +- [reduceWithIndex](#reducewithindex) +- [reverse](#reverse) +- [snoc](#snoc) +- [sort](#sort) +- [tail](#tail) +- [updateAt](#updateat) + +--- + +# ReadonlyNonEmptyArray (interface) + +**Signature** + +```ts +export interface ReadonlyNonEmptyArray
extends ReadonlyArray { + readonly 0: A +} +``` + +Added in v2.5.0 + +# URI (type alias) + +**Signature** + +```ts +export type URI = typeof URI +``` + +Added in v2.5.0 + +# URI + +**Signature** + +```ts +export const URI: "ReadonlyNonEmptyArray" = ... +``` + +Added in v2.5.0 + +# ap + +**Signature** + +```ts +(fa: ReadonlyNonEmptyArray) => (fab: ReadonlyNonEmptyArray<(a: A) => B>) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# apFirst + +**Signature** + +```ts +(fb: ReadonlyNonEmptyArray) => (fa: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# apSecond + +**Signature** + +```ts +(fb: ReadonlyNonEmptyArray) => (fa: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# chain + +**Signature** + +```ts +(f: (a: A) => ReadonlyNonEmptyArray) => (ma: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# chainFirst + +**Signature** + +```ts +(f: (a: A) => ReadonlyNonEmptyArray) => (ma: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# concat + +**Signature** + +```ts +export function concat(fx: ReadonlyArray, fy: ReadonlyNonEmptyArray): ReadonlyNonEmptyArray +export function concat(fx: ReadonlyNonEmptyArray, fy: ReadonlyArray): ReadonlyNonEmptyArray { ... } +``` + +Added in v2.5.0 + +# cons + +Append an element to the front of an array, creating a new non empty array + +**Signature** + +```ts +export const cons: (head: A, tail: ReadonlyArray) => ReadonlyNonEmptyArray = ... +``` + +**Example** + +```ts +import { cons } from 'fp-ts/lib/ReadonlyNonEmptyArray' + +assert.deepStrictEqual(cons(1, [2, 3, 4]), [1, 2, 3, 4]) +``` + +Added in v2.5.0 + +# duplicate + +**Signature** + +```ts +(ma: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray> +``` + +Added in v2.5.0 + +# extend + +**Signature** + +```ts +(f: (fa: ReadonlyNonEmptyArray) => B) => (ma: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# filter + +**Signature** + +```ts +export function filter( + refinement: Refinement +): (nea: ReadonlyNonEmptyArray) => Option> +export function filter(predicate: Predicate): (nea: ReadonlyNonEmptyArray) => Option> { ... } +``` + +Added in v2.5.0 + +# filterWithIndex + +**Signature** + +```ts +export function filterWithIndex( + predicate: (i: number, a: A) => boolean +): (nea: ReadonlyNonEmptyArray) => Option> { ... } +``` + +Added in v2.5.0 + +# flatten + +**Signature** + +```ts +(mma: ReadonlyNonEmptyArray>) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# fold + +**Signature** + +```ts +export function fold(S: Semigroup): (fa: ReadonlyNonEmptyArray) => A { ... } +``` + +Added in v2.5.0 + +# foldMap + +**Signature** + +```ts +;(S: Semigroup) => (f: (a: A) => S) => (fa: ReadonlyNonEmptyArray) => S +``` + +Added in v2.5.0 + +# foldMapWithIndex + +**Signature** + +```ts +;(S: Semigroup) => (f: (i: number, a: A) => S) => (fa: ReadonlyNonEmptyArray) => S +``` + +Added in v2.5.0 + +# fromArray + +**Signature** + +```ts +export function fromArray(as: Array): Option> { ... } +``` + +Added in v2.5.0 + +# fromReadonlyArray + +Builds a `ReadonlyNonEmptyArray` from an array returning `none` if `as` is an empty array + +**Signature** + +```ts +export function fromReadonlyArray(as: ReadonlyArray): Option> { ... } +``` + +Added in v2.5.0 + +# getEq + +**Signature** + +```ts +export const getEq: (E: Eq) => Eq> = ... +``` + +**Example** + +```ts +import { getEq, cons } from 'fp-ts/lib/ReadonlyNonEmptyArray' +import { eqNumber } from 'fp-ts/lib/Eq' + +const E = getEq(eqNumber) +assert.strictEqual(E.equals(cons(1, [2]), [1, 2]), true) +assert.strictEqual(E.equals(cons(1, [2]), [1, 3]), false) +``` + +Added in v2.5.0 + +# getSemigroup + +Builds a `Semigroup` instance for `ReadonlyNonEmptyArray` + +**Signature** + +```ts +export function getSemigroup(): Semigroup> { ... } +``` + +Added in v2.5.0 + +# getShow + +**Signature** + +```ts +export const getShow: (S: Show) => Show> = ... +``` + +Added in v2.5.0 + +# group + +Group equal, consecutive elements of an array into non empty arrays. + +**Signature** + +```ts +export function group( + E: Eq +): { + (as: ReadonlyNonEmptyArray): ReadonlyNonEmptyArray> + (as: ReadonlyArray): ReadonlyArray> +} { ... } +``` + +**Example** + +```ts +import { cons, group } from 'fp-ts/lib/ReadonlyNonEmptyArray' +import { ordNumber } from 'fp-ts/lib/Ord' + +assert.deepStrictEqual(group(ordNumber)([1, 2, 1, 1]), [cons(1, []), cons(2, []), cons(1, [1])]) +``` + +Added in v2.5.0 + +# groupBy + +Splits an array into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning +function on each element, and grouping the results according to values returned + +**Signature** + +```ts +export function groupBy( + f: (a: A) => string +): (as: ReadonlyArray) => ReadonlyRecord> { ... } +``` + +**Example** + +```ts +import { cons, groupBy } from 'fp-ts/lib/ReadonlyNonEmptyArray' + +assert.deepStrictEqual(groupBy((s: string) => String(s.length))(['foo', 'bar', 'foobar']), { + '3': cons('foo', ['bar']), + '6': cons('foobar', []) +}) +``` + +Added in v2.5.0 + +# groupSort + +Sort and then group the elements of an array into non empty arrays. + +**Signature** + +```ts +export function groupSort(O: Ord): (as: ReadonlyArray) => ReadonlyArray> { ... } +``` + +**Example** + +```ts +import { cons, groupSort } from 'fp-ts/lib/ReadonlyNonEmptyArray' +import { ordNumber } from 'fp-ts/lib/Ord' + +assert.deepStrictEqual(groupSort(ordNumber)([1, 2, 1, 1]), [cons(1, [1, 1]), cons(2, [])]) +``` + +Added in v2.5.0 + +# head + +**Signature** + +```ts +export function head(nea: ReadonlyNonEmptyArray): A { ... } +``` + +Added in v2.5.0 + +# init + +Get all but the last element of a non empty array, creating a new array. + +**Signature** + +```ts +export function init(nea: ReadonlyNonEmptyArray): ReadonlyArray { ... } +``` + +**Example** + +```ts +import { init } from 'fp-ts/lib/ReadonlyNonEmptyArray' + +assert.deepStrictEqual(init([1, 2, 3]), [1, 2]) +assert.deepStrictEqual(init([1]), []) +``` + +Added in v2.5.0 + +# insertAt + +**Signature** + +```ts +export function insertAt(i: number, a: A): (nea: ReadonlyNonEmptyArray) => Option> { ... } +``` + +Added in v2.5.0 + +# last + +**Signature** + +```ts +export function last(nea: ReadonlyNonEmptyArray): A { ... } +``` + +Added in v2.5.0 + +# map + +**Signature** + +```ts +(f: (a: A) => B) => (fa: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# mapWithIndex + +**Signature** + +```ts +(f: (i: number, a: A) => B) => (fa: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray +``` + +Added in v2.5.0 + +# max + +**Signature** + +```ts +export function max(ord: Ord): (nea: ReadonlyNonEmptyArray) => A { ... } +``` + +Added in v2.5.0 + +# min + +**Signature** + +```ts +export function min(ord: Ord): (nea: ReadonlyNonEmptyArray) => A { ... } +``` + +Added in v2.5.0 + +# modifyAt + +**Signature** + +```ts +export function modifyAt( + i: number, + f: (a: A) => A +): (nea: ReadonlyNonEmptyArray) => Option> { ... } +``` + +Added in v2.5.0 + +# of + +**Signature** + +```ts +export const of: (a: A) => ReadonlyNonEmptyArray = ... +``` + +Added in v2.5.0 + +# readonlyNonEmptyArray + +**Signature** + +```ts +export const readonlyNonEmptyArray: Monad1 & + Comonad1 & + TraversableWithIndex1 & + FunctorWithIndex1 & + FoldableWithIndex1 & + Alt1 = ... +``` + +Added in v2.5.0 + +# reduce + +**Signature** + +```ts +;(b: B, f: (b: B, a: A) => B) => (fa: ReadonlyNonEmptyArray) => B +``` + +Added in v2.5.0 + +# reduceRight + +**Signature** + +```ts +;(b: B, f: (a: A, b: B) => B) => (fa: ReadonlyNonEmptyArray) => B +``` + +Added in v2.5.0 + +# reduceRightWithIndex + +**Signature** + +```ts +;(b: B, f: (i: number, a: A, b: B) => B) => (fa: ReadonlyNonEmptyArray) => B +``` + +Added in v2.5.0 + +# reduceWithIndex + +**Signature** + +```ts +;(b: B, f: (i: number, b: B, a: A) => B) => (fa: ReadonlyNonEmptyArray) => B +``` + +Added in v2.5.0 + +# reverse + +**Signature** + +```ts +export const reverse: (nea: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray = ... +``` + +Added in v2.5.0 + +# snoc + +Append an element to the end of an array, creating a new non empty array + +**Signature** + +```ts +export const snoc: (init: ReadonlyArray, end: A) => ReadonlyNonEmptyArray = ... +``` + +**Example** + +```ts +import { snoc } from 'fp-ts/lib/ReadonlyNonEmptyArray' + +assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4]) +``` + +Added in v2.5.0 + +# sort + +**Signature** + +```ts +export function sort(O: Ord): (nea: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray { ... } +``` + +Added in v2.5.0 + +# tail + +**Signature** + +```ts +export function tail(nea: ReadonlyNonEmptyArray): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# updateAt + +**Signature** + +```ts +export function updateAt(i: number, a: A): (nea: ReadonlyNonEmptyArray) => Option> { ... } +``` + +Added in v2.5.0 diff --git a/docs/modules/ReadonlyRecord.ts.md b/docs/modules/ReadonlyRecord.ts.md new file mode 100644 index 000000000..e8c1f541c --- /dev/null +++ b/docs/modules/ReadonlyRecord.ts.md @@ -0,0 +1,821 @@ +--- +title: ReadonlyRecord.ts +nav_order: 72 +parent: Modules +--- + +# ReadonlyRecord overview + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [ReadonlyRecord (type alias)](#readonlyrecord-type-alias) +- [URI (type alias)](#uri-type-alias) +- [URI](#uri) +- [collect](#collect) +- [compact](#compact) +- [deleteAt](#deleteat) +- [elem](#elem) +- [empty](#empty) +- [every](#every) +- [filter](#filter) +- [filterMap](#filtermap) +- [filterMapWithIndex](#filtermapwithindex) +- [filterWithIndex](#filterwithindex) +- [foldMap](#foldmap) +- [foldMapWithIndex](#foldmapwithindex) +- [fromFoldable](#fromfoldable) +- [fromFoldableMap](#fromfoldablemap) +- [fromRecord](#fromrecord) +- [getEq](#geteq) +- [getMonoid](#getmonoid) +- [getShow](#getshow) +- [hasOwnProperty (function)](#hasownproperty-function) +- [insertAt](#insertat) +- [isEmpty](#isempty) +- [isSubrecord](#issubrecord) +- [keys](#keys) +- [lookup](#lookup) +- [map](#map) +- [mapWithIndex](#mapwithindex) +- [modifyAt](#modifyat) +- [partition](#partition) +- [partitionMap](#partitionmap) +- [partitionMapWithIndex](#partitionmapwithindex) +- [partitionWithIndex](#partitionwithindex) +- [pop](#pop) +- [readonlyRecord](#readonlyrecord) +- [reduce](#reduce) +- [reduceRight](#reduceright) +- [reduceRightWithIndex](#reducerightwithindex) +- [reduceWithIndex](#reducewithindex) +- [separate](#separate) +- [sequence](#sequence) +- [singleton](#singleton) +- [size](#size) +- [some](#some) +- [toReadonlyArray](#toreadonlyarray) +- [toRecord](#torecord) +- [toUnfoldable](#tounfoldable) +- [traverse](#traverse) +- [traverseWithIndex](#traversewithindex) +- [updateAt](#updateat) + +--- + +# ReadonlyRecord (type alias) + +**Signature** + +```ts +export type ReadonlyRecord = Readonly> +``` + +Added in v2.5.0 + +# URI (type alias) + +**Signature** + +```ts +export type URI = typeof URI +``` + +Added in v2.5.0 + +# URI + +**Signature** + +```ts +export const URI: "ReadonlyRecord" = ... +``` + +Added in v2.5.0 + +# collect + +Map a record into an array + +**Signature** + +```ts +export function collect(f: (k: K, a: A) => B): (r: ReadonlyRecord) => ReadonlyArray { ... } +``` + +**Example** + +```ts +import { collect } from 'fp-ts/lib/ReadonlyRecord' + +const x: { a: string; b: boolean } = { a: 'foo', b: false } +assert.deepStrictEqual(collect((key, val) => ({ key: key, value: val }))(x), [ + { key: 'a', value: 'foo' }, + { key: 'b', value: false } +]) +``` + +Added in v2.5.0 + +# compact + +**Signature** + +```ts +
(fa: Readonly>>) => Readonly> +``` + +Added in v2.5.0 + +# deleteAt + +Delete a key and value from a map + +**Signature** + +```ts +export function deleteAt( + k: K +): (r: ReadonlyRecord) => ReadonlyRecord, A> { ... } +``` + +Added in v2.5.0 + +# elem + +**Signature** + +```ts +export function elem(E: Eq): (a: A, fa: ReadonlyRecord) => boolean { ... } +``` + +Added in v2.5.0 + +# empty + +**Signature** + +```ts +export const empty: ReadonlyRecord = ... +``` + +Added in v2.5.0 + +# every + +**Signature** + +```ts +export function every(predicate: Predicate): (r: ReadonlyRecord) => boolean { ... } +``` + +Added in v2.5.0 + +# filter + +**Signature** + +```ts +{ (refinement: Refinement): (fa: Readonly>) => Readonly>; (predicate: Predicate): (fa: Readonly>) => Readonly>; } +``` + +Added in v2.5.0 + +# filterMap + +**Signature** + +```ts +(f: (a: A) => Option) => (fa: Readonly>) => Readonly> +``` + +Added in v2.5.0 + +# filterMapWithIndex + +**Signature** + +```ts +export function filterMapWithIndex( + f: (key: K, a: A) => Option +): (fa: ReadonlyRecord) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# filterWithIndex + +**Signature** + +```ts +export function filterWithIndex( + refinementWithIndex: RefinementWithIndex +): (fa: ReadonlyRecord) => ReadonlyRecord +export function filterWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# foldMap + +**Signature** + +```ts +;(M: Monoid) => (f: (a: A) => M) => (fa: Readonly>) => M +``` + +Added in v2.5.0 + +# foldMapWithIndex + +**Signature** + +```ts +export function foldMapWithIndex( + M: Monoid +): (f: (k: K, a: A) => M) => (fa: ReadonlyRecord) => M { ... } +``` + +Added in v2.5.0 + +# fromFoldable + +Create a record from a foldable collection of key/value pairs, using the +specified `Magma` to combine values for duplicate keys. + +**Signature** + +```ts +export function fromFoldable( + M: Magma, + F: Foldable3 +): (fka: Kind3) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable2 +): (fka: Kind2) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable1 +): (fka: Kind) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# fromFoldableMap + +Create a record from a foldable collection using the specified functions to + +- map to key/value pairs +- combine values for duplicate keys. + +**Signature** + +```ts +export function fromFoldableMap( + M: Magma, + F: Foldable3 +): (fa: Kind3, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable2 +): (fa: Kind2, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable1 +): (fa: Kind, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable +): (fa: HKT, f: (a: A) => readonly [K, B]) => ReadonlyRecord { ... } +``` + +**Example** + +```ts +import { getLastSemigroup } from 'fp-ts/lib/Semigroup' +import { readonlyArray, zip } from 'fp-ts/lib/ReadonlyArray' +import { identity } from 'fp-ts/lib/function' +import { ReadonlyRecord, fromFoldableMap } from 'fp-ts/lib/ReadonlyRecord' + +// like lodash `zipObject` or ramda `zipObj` +export const zipObject = ( + keys: ReadonlyArray, + values: ReadonlyArray +): ReadonlyRecord => fromFoldableMap(getLastSemigroup(), readonlyArray)(zip(keys, values), identity) + +assert.deepStrictEqual(zipObject(['a', 'b'], [1, 2, 3]), { a: 1, b: 2 }) + +// build a record from a field +interface User { + id: string + name: string +} + +const users: ReadonlyArray = [ + { id: 'id1', name: 'name1' }, + { id: 'id2', name: 'name2' }, + { id: 'id1', name: 'name3' } +] + +assert.deepStrictEqual( + fromFoldableMap(getLastSemigroup(), readonlyArray)(users, user => [user.id, user]), + { + id1: { id: 'id1', name: 'name3' }, + id2: { id: 'id2', name: 'name2' } + } +) +``` + +Added in v2.5.0 + +# fromRecord + +**Signature** + +```ts +export function fromRecord(r: Record): ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# getEq + +**Signature** + +```ts +export function getEq(E: Eq): Eq> { ... } +``` + +Added in v2.5.0 + +# getMonoid + +Returns a `Semigroup` instance for records given a `Semigroup` instance for their values + +**Signature** + +```ts +export function getMonoid(S: Semigroup): Monoid> { ... } +``` + +**Example** + +```ts +import { semigroupSum } from 'fp-ts/lib/Semigroup' +import { getMonoid } from 'fp-ts/lib/ReadonlyRecord' + +const M = getMonoid(semigroupSum) +assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) +``` + +Added in v2.5.0 + +# getShow + +**Signature** + +```ts +export function getShow(S: Show): Show> { ... } +``` + +Added in v2.5.0 + +# hasOwnProperty (function) + +**Signature** + +```ts +export function hasOwnProperty(k: string, r: ReadonlyRecord): k is K { ... } +``` + +Added in v2.5.0 + +# insertAt + +Insert or replace a key/value pair in a record + +**Signature** + +```ts +export function insertAt( + k: K, + a: A +): (r: ReadonlyRecord) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# isEmpty + +Test whether a record is empty + +**Signature** + +```ts +export function isEmpty(r: ReadonlyRecord): boolean { ... } +``` + +Added in v2.5.0 + +# isSubrecord + +Test whether one record contains all of the keys and values contained in another record + +**Signature** + +```ts +export function isSubrecord(E: Eq): (x: ReadonlyRecord, y: ReadonlyRecord) => boolean { ... } +``` + +Added in v2.5.0 + +# keys + +**Signature** + +```ts +export function keys(r: ReadonlyRecord): ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# lookup + +Lookup the value for a key in a record + +**Signature** + +```ts +export function lookup(k: string, r: ReadonlyRecord): Option { ... } +``` + +Added in v2.5.0 + +# map + +Map a record passing the values to the iterating function + +**Signature** + +```ts +export function map(f: (a: A) => B): (fa: ReadonlyRecord) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# mapWithIndex + +Map a record passing the keys to the iterating function + +**Signature** + +```ts +export function mapWithIndex( + f: (k: K, a: A) => B +): (fa: ReadonlyRecord) => ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# modifyAt + +**Signature** + +```ts +export function modifyAt( + k: string, + f: (a: A) => A +): (r: ReadonlyRecord) => Option> { ... } +``` + +Added in v2.5.0 + +# partition + +**Signature** + +```ts +{ (refinement: Refinement): (fa: Readonly>) => Separated>, Readonly>>; (predicate: Predicate): (fa: Readonly>) => Separated>, Readonly>>; } +``` + +Added in v2.5.0 + +# partitionMap + +**Signature** + +```ts +(f: (a: A) => Either) => (fa: Readonly>) => Separated>, Readonly>> +``` + +Added in v2.5.0 + +# partitionMapWithIndex + +**Signature** + +```ts +export function partitionMapWithIndex( + f: (key: K, a: A) => Either +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> { ... } +``` + +Added in v2.5.0 + +# partitionWithIndex + +**Signature** + +```ts +export function partitionWithIndex( + refinementWithIndex: RefinementWithIndex +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> +export function partitionWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> { ... } +``` + +Added in v2.5.0 + +# pop + +Delete a key and value from a map, returning the value as well as the subsequent map + +**Signature** + +```ts +export function pop( + k: K +): ( + r: ReadonlyRecord +) => Option, A>]> { ... } +``` + +Added in v2.5.0 + +# readonlyRecord + +**Signature** + +```ts +export const readonlyRecord: FunctorWithIndex1 & + Foldable1 & + TraversableWithIndex1 & + Compactable1 & + FilterableWithIndex1 & + Witherable1 & + FoldableWithIndex1 = ... +``` + +Added in v2.5.0 + +# reduce + +**Signature** + +```ts +;(b: B, f: (b: B, a: A) => B) => (fa: Readonly>) => B +``` + +Added in v2.5.0 + +# reduceRight + +**Signature** + +```ts +;(b: B, f: (a: A, b: B) => B) => (fa: Readonly>) => B +``` + +Added in v2.5.0 + +# reduceRightWithIndex + +**Signature** + +```ts +export function reduceRightWithIndex( + b: B, + f: (k: K, a: A, b: B) => B +): (fa: ReadonlyRecord) => B { ... } +``` + +Added in v2.5.0 + +# reduceWithIndex + +**Signature** + +```ts +export function reduceWithIndex( + b: B, + f: (k: K, b: B, a: A) => B +): (fa: ReadonlyRecord) => B { ... } +``` + +Added in v2.5.0 + +# separate + +**Signature** + +```ts +(fa: Readonly>>) => Separated>, Readonly>> +``` + +Added in v2.5.0 + +# sequence + +**Signature** + +```ts +export function sequence( + F: Applicative3 +): (ta: ReadonlyRecord>) => Kind3> +export function sequence( + F: Applicative3C +): (ta: ReadonlyRecord>) => Kind3> +export function sequence( + F: Applicative2 +): (ta: ReadonlyRecord>) => Kind2> +export function sequence( + F: Applicative2C +): (ta: ReadonlyRecord>) => Kind2> +export function sequence( + F: Applicative1 +): (ta: ReadonlyRecord>) => Kind> +export function sequence( + F: Applicative +): (ta: ReadonlyRecord>) => HKT> { ... } +``` + +Added in v2.5.0 + +# singleton + +Create a record with one key/value pair + +**Signature** + +```ts +export function singleton(k: K, a: A): ReadonlyRecord { ... } +``` + +Added in v2.5.0 + +# size + +Calculate the number of key/value pairs in a record + +**Signature** + +```ts +export function size(r: ReadonlyRecord): number { ... } +``` + +Added in v2.5.0 + +# some + +**Signature** + +```ts +export function some(predicate: (a: A) => boolean): (r: ReadonlyRecord) => boolean { ... } +``` + +Added in v2.5.0 + +# toReadonlyArray + +**Signature** + +```ts +export const toReadonlyArray: ( + r: ReadonlyRecord +) => ReadonlyArray = ... +``` + +Added in v2.5.0 + +# toRecord + +**Signature** + +```ts +export function toRecord(r: ReadonlyRecord): Record { ... } +``` + +Added in v2.5.0 + +# toUnfoldable + +Unfolds a record into a list of key/value pairs + +**Signature** + +```ts +export function toUnfoldable( + U: Unfoldable1 +): (r: ReadonlyRecord) => Kind +export function toUnfoldable( + U: Unfoldable +): (r: ReadonlyRecord) => HKT { ... } +``` + +Added in v2.5.0 + +# traverse + +**Signature** + +```ts +export function traverse( + F: Applicative3 +): ( + f: (a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverse( + F: Applicative3C +): ( + f: (a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverse( + F: Applicative2 +): ( + f: (a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverse( + F: Applicative2C +): ( + f: (a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverse( + F: Applicative1 +): (f: (a: A) => Kind) => (ta: ReadonlyRecord) => Kind> +export function traverse( + F: Applicative +): (f: (a: A) => HKT) => (ta: ReadonlyRecord) => HKT> { ... } +``` + +Added in v2.5.0 + +# traverseWithIndex + +**Signature** + +```ts +export function traverseWithIndex( + F: Applicative3 +): ( + f: (k: K, a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverseWithIndex( + F: Applicative3C +): ( + f: (k: K, a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverseWithIndex( + F: Applicative2 +): ( + f: (k: K, a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverseWithIndex( + F: Applicative2C +): ( + f: (k: K, a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverseWithIndex( + F: Applicative1 +): ( + f: (k: K, a: A) => Kind +) => (ta: ReadonlyRecord) => Kind> +export function traverseWithIndex( + F: Applicative +): (f: (k: K, a: A) => HKT) => (ta: ReadonlyRecord) => HKT> { ... } +``` + +Added in v2.5.0 + +# updateAt + +**Signature** + +```ts +export function updateAt( + k: string, + a: A +): (r: ReadonlyRecord) => Option> { ... } +``` + +Added in v2.5.0 diff --git a/docs/modules/ReadonlySet.ts.md b/docs/modules/ReadonlySet.ts.md new file mode 100644 index 000000000..d6969db55 --- /dev/null +++ b/docs/modules/ReadonlySet.ts.md @@ -0,0 +1,376 @@ +--- +title: ReadonlySet.ts +nav_order: 73 +parent: Modules +--- + +# ReadonlySet overview + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [chain](#chain) +- [compact](#compact) +- [difference](#difference) +- [elem](#elem) +- [empty](#empty) +- [every](#every) +- [filter](#filter) +- [filterMap](#filtermap) +- [foldMap](#foldmap) +- [fromArray](#fromarray) +- [fromSet](#fromset) +- [getEq](#geteq) +- [getIntersectionSemigroup](#getintersectionsemigroup) +- [getShow](#getshow) +- [getUnionMonoid](#getunionmonoid) +- [insert](#insert) +- [intersection](#intersection) +- [isSubset](#issubset) +- [map](#map) +- [partition](#partition) +- [partitionMap](#partitionmap) +- [reduce](#reduce) +- [remove](#remove) +- [separate](#separate) +- [singleton](#singleton) +- [some](#some) +- [toReadonlyArray](#toreadonlyarray) +- [toSet](#toset) +- [union](#union) + +--- + +# chain + +**Signature** + +```ts +export function chain(E: Eq):
(f: (x: A) => ReadonlySet) => (set: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# compact + +**Signature** + +```ts +export function compact(E: Eq): (fa: ReadonlySet>) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# difference + +Form the set difference (`x` - `y`) + +**Signature** + +```ts +export function difference(E: Eq): (x: ReadonlySet, y: ReadonlySet) => ReadonlySet { ... } +``` + +**Example** + +```ts +import { difference } from 'fp-ts/lib/ReadonlySet' +import { eqNumber } from 'fp-ts/lib/Eq' + +assert.deepStrictEqual(difference(eqNumber)(new Set([1, 2]), new Set([1, 3])), new Set([2])) +``` + +Added in v2.5.0 + +# elem + +Test if a value is a member of a set + +**Signature** + +```ts +export function elem(E: Eq): (a: A, set: ReadonlySet) => boolean { ... } +``` + +Added in v2.5.0 + +# empty + +**Signature** + +```ts +export const empty: ReadonlySet = ... +``` + +Added in v2.5.0 + +# every + +**Signature** + +```ts +export function every(predicate: Predicate): (set: ReadonlySet) => boolean { ... } +``` + +Added in v2.5.0 + +# filter + +**Signature** + +```ts +export function filter(refinement: Refinement): (set: ReadonlySet) => ReadonlySet +export function filter(predicate: Predicate): (set: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# filterMap + +**Signature** + +```ts +export function filterMap(E: Eq): (f: (a: A) => Option) => (fa: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# foldMap + +**Signature** + +```ts +export function foldMap(O: Ord, M: Monoid): (f: (a: A) => M) => (fa: ReadonlySet) => M { ... } +``` + +Added in v2.5.0 + +# fromArray + +Create a set from an array + +**Signature** + +```ts +export function fromArray(E: Eq): (as: ReadonlyArray) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# fromSet + +**Signature** + +```ts +export function fromSet(s: Set): ReadonlySet { ... } +``` + +Added in v2.5.0 + +# getEq + +**Signature** + +```ts +export function getEq(E: Eq): Eq> { ... } +``` + +Added in v2.5.0 + +# getIntersectionSemigroup + +**Signature** + +```ts +export function getIntersectionSemigroup(E: Eq): Semigroup> { ... } +``` + +Added in v2.5.0 + +# getShow + +**Signature** + +```ts +export function getShow(S: Show): Show> { ... } +``` + +Added in v2.5.0 + +# getUnionMonoid + +**Signature** + +```ts +export function getUnionMonoid(E: Eq): Monoid> { ... } +``` + +Added in v2.5.0 + +# insert + +Insert a value into a set + +**Signature** + +```ts +export function insert(E: Eq): (a: A) => (set: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# intersection + +The set of elements which are in both the first and second set + +**Signature** + +```ts +export function intersection(E: Eq): (set: ReadonlySet, y: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# isSubset + +`true` if and only if every element in the first set is an element of the second set + +**Signature** + +```ts +export function isSubset(E: Eq): (x: ReadonlySet, y: ReadonlySet) => boolean { ... } +``` + +Added in v2.5.0 + +# map + +Projects a Set through a function + +**Signature** + +```ts +export function map(E: Eq): (f: (x: A) => B) => (set: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# partition + +**Signature** + +```ts +export function partition( + refinement: Refinement +): (set: ReadonlySet) => Separated, ReadonlySet> +export function partition( + predicate: Predicate +): (set: ReadonlySet) => Separated, ReadonlySet> { ... } +``` + +Added in v2.5.0 + +# partitionMap + +**Signature** + +```ts +export function partitionMap( + EB: Eq, + EC: Eq +): (f: (a: A) => Either) => (set: ReadonlySet) => Separated, ReadonlySet> { ... } +``` + +Added in v2.5.0 + +# reduce + +**Signature** + +```ts +export function reduce(O: Ord): (b: B, f: (b: B, a: A) => B) => (fa: ReadonlySet) => B { ... } +``` + +Added in v2.5.0 + +# remove + +Delete a value from a set + +**Signature** + +```ts +export function remove(E: Eq): (a: A) => (set: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 + +# separate + +**Signature** + +```ts +export function separate( + EE: Eq, + EA: Eq +): (fa: ReadonlySet>) => Separated, ReadonlySet> { ... } +``` + +Added in v2.5.0 + +# singleton + +Create a set with one element + +**Signature** + +```ts +export function singleton(a: A): ReadonlySet { ... } +``` + +Added in v2.5.0 + +# some + +**Signature** + +```ts +export function some(predicate: Predicate): (set: ReadonlySet) => boolean { ... } +``` + +Added in v2.5.0 + +# toReadonlyArray + +**Signature** + +```ts +export function toReadonlyArray(O: Ord): (set: ReadonlySet) => ReadonlyArray { ... } +``` + +Added in v2.5.0 + +# toSet + +**Signature** + +```ts +export function toSet(s: ReadonlySet): Set { ... } +``` + +Added in v2.5.0 + +# union + +Form the union of two sets + +**Signature** + +```ts +export function union(E: Eq): (set: ReadonlySet, y: ReadonlySet) => ReadonlySet { ... } +``` + +Added in v2.5.0 diff --git a/docs/modules/ReadonlyTuple.ts.md b/docs/modules/ReadonlyTuple.ts.md new file mode 100644 index 000000000..acb910b9d --- /dev/null +++ b/docs/modules/ReadonlyTuple.ts.md @@ -0,0 +1,240 @@ +--- +title: ReadonlyTuple.ts +nav_order: 74 +parent: Modules +--- + +# ReadonlyTuple overview + +Added in v2.5.0 + +--- + +

Table of contents

+ +- [URI (type alias)](#uri-type-alias) +- [URI](#uri) +- [bimap](#bimap) +- [compose](#compose) +- [duplicate](#duplicate) +- [extend](#extend) +- [foldMap](#foldmap) +- [fst](#fst) +- [getApplicative](#getapplicative) +- [getApply](#getapply) +- [getChain](#getchain) +- [getChainRec](#getchainrec) +- [getMonad](#getmonad) +- [map](#map) +- [mapLeft](#mapleft) +- [readonlyTuple](#readonlytuple) +- [reduce](#reduce) +- [reduceRight](#reduceright) +- [snd](#snd) +- [swap](#swap) + +--- + +# URI (type alias) + +**Signature** + +```ts +export type URI = typeof URI +``` + +Added in v2.5.0 + +# URI + +**Signature** + +```ts +export const URI: "ReadonlyTuple" = ... +``` + +Added in v2.5.0 + +# bimap + +**Signature** + +```ts +;(f: (e: E) => G, g: (a: A) => B) => (fa: readonly [A, E]) => readonly[(B, G)] +``` + +Added in v2.5.0 + +# compose + +**Signature** + +```ts +;(la: readonly [A, E]) => (ab: readonly [B, A]) => readonly[(B, E)] +``` + +Added in v2.5.0 + +# duplicate + +**Signature** + +```ts +;(ma: readonly [A, E]) => readonly[(readonly[(A, E)], E)] +``` + +Added in v2.5.0 + +# extend + +**Signature** + +```ts +;(f: (fa: readonly [A, E]) => B) => (ma: readonly [A, E]) => readonly[(B, E)] +``` + +Added in v2.5.0 + +# foldMap + +**Signature** + +```ts +;(M: Monoid) =>
(f: (a: A) => M) => (fa: readonly [A, E]) => M +``` + +Added in v2.5.0 + +# fst + +**Signature** + +```ts +export function fst(sa: readonly [A, S]): A { ... } +``` + +Added in v2.5.0 + +# getApplicative + +**Signature** + +```ts +export function getApplicative(M: Monoid): Applicative2C { ... } +``` + +Added in v2.5.0 + +# getApply + +**Signature** + +```ts +export function getApply(S: Semigroup): Apply2C { ... } +``` + +Added in v2.5.0 + +# getChain + +**Signature** + +```ts +export function getChain(S: Semigroup): Chain2C { ... } +``` + +Added in v2.5.0 + +# getChainRec + +**Signature** + +```ts +export function getChainRec(M: Monoid): ChainRec2C { ... } +``` + +Added in v2.5.0 + +# getMonad + +**Signature** + +```ts +export function getMonad(M: Monoid): Monad2C { ... } +``` + +Added in v2.5.0 + +# map + +**Signature** + +```ts +;(f: (a: A) => B) => (fa: readonly [A, E]) => readonly[(B, E)] +``` + +Added in v2.5.0 + +# mapLeft + +**Signature** + +```ts +;(f: (e: E) => G) => (fa: readonly [A, E]) => readonly[(A, G)] +``` + +Added in v2.5.0 + +# readonlyTuple + +**Signature** + +```ts +export const readonlyTuple: Semigroupoid2 & + Bifunctor2 & + Comonad2 & + Foldable2 & + Traversable2 = ... +``` + +Added in v2.5.0 + +# reduce + +**Signature** + +```ts +;(b: B, f: (b: B, a: A) => B) => (fa: readonly [A, E]) => B +``` + +Added in v2.5.0 + +# reduceRight + +**Signature** + +```ts +;(b: B, f: (a: A, b: B) => B) => (fa: readonly [A, E]) => B +``` + +Added in v2.5.0 + +# snd + +**Signature** + +```ts +export function snd(sa: readonly [A, S]): S { ... } +``` + +Added in v2.5.0 + +# swap + +**Signature** + +```ts +export function swap(sa: readonly [A, S]): readonly [S, A] { ... } +``` + +Added in v2.5.0 diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md index ad8fc5383..a26671e00 100644 --- a/docs/modules/Record.ts.md +++ b/docs/modules/Record.ts.md @@ -1,6 +1,6 @@ --- title: Record.ts -nav_order: 69 +nav_order: 75 parent: Modules --- @@ -90,7 +90,9 @@ Map a record into an array **Signature** ```ts -export function collect(f: (k: K, a: A) => B): (r: Record) => Array { ... } +export const collect: ( + f: (k: K, a: A) => B +) => (r: Record) => Array = ... ``` **Example** @@ -136,7 +138,7 @@ Added in v2.0.0 **Signature** ```ts -export function elem(E: Eq): (a: A, fa: Record) => boolean { ... } +export const elem: (E: Eq) => (a: A, fa: Record) => boolean = ... ``` Added in v2.0.0 @@ -156,7 +158,7 @@ Added in v2.0.0 **Signature** ```ts -export function every(predicate: Predicate): (r: Record) => boolean { ... } +export const every: (predicate: Predicate) => (r: Record) => boolean = ... ``` Added in v2.0.0 @@ -357,7 +359,7 @@ Added in v2.0.0 **Signature** ```ts -export function getShow(S: Show): Show> { ... } +export const getShow: (S: Show) => Show> = ... ``` Added in v2.0.0 @@ -367,7 +369,7 @@ Added in v2.0.0 **Signature** ```ts -export function hasOwnProperty(k: string, r: Record): k is K { ... } +export const hasOwnProperty: (k: string, r: Record) => k is K = ... ``` Added in v2.0.0 @@ -391,7 +393,7 @@ Test whether a record is empty **Signature** ```ts -export function isEmpty(r: Record): boolean { ... } +export const isEmpty: (r: Record) => boolean = ... ``` Added in v2.0.0 @@ -403,7 +405,7 @@ Test whether one record contains all of the keys and values contained in another **Signature** ```ts -export function isSubrecord(E: Eq): (x: Record, y: Record) => boolean { ... } +export const isSubrecord: (E: Eq) => (x: Record, y: Record) => boolean = ... ``` Added in v2.0.0 @@ -413,7 +415,7 @@ Added in v2.0.0 **Signature** ```ts -export function keys(r: Record): Array { ... } +export const keys: (r: Record) => Array = ... ``` Added in v2.0.0 @@ -425,7 +427,7 @@ Lookup the value for a key in a record **Signature** ```ts -export function lookup(k: string, r: Record): Option { ... } +export const lookup: (k: string, r: Record) => Option = ... ``` Added in v2.0.0 @@ -459,7 +461,7 @@ Added in v2.0.0 **Signature** ```ts -export function modifyAt(k: string, f: (a: A) => A): (r: Record) => Option> { ... } +export const : (k: string, f: (a: A) => A) => (r: Record) => Option> = ... ``` Added in v2.0.0 @@ -623,7 +625,7 @@ Create a record with one key/value pair **Signature** ```ts -export function singleton(k: K, a: A): Record { ... } +export const singleton: (k: K, a: A) => Record = ... ``` Added in v2.0.0 @@ -635,7 +637,7 @@ Calculate the number of key/value pairs in a record **Signature** ```ts -export function size(r: Record): number { ... } +export const size: (r: Record) => number = ... ``` Added in v2.0.0 @@ -645,7 +647,7 @@ Added in v2.0.0 **Signature** ```ts -export function some(predicate: (a: A) => boolean): (r: Record) => boolean { ... } +export const some: (predicate: (a: A) => boolean) => (r: Record) => boolean = ... ``` Added in v2.0.0 @@ -668,9 +670,9 @@ Unfolds a record into a list of key/value pairs ```ts export function toUnfoldable( - unfoldable: Unfoldable1 + U: Unfoldable1 ): (r: Record) => Kind -export function toUnfoldable(unfoldable: Unfoldable): (r: Record) => HKT { ... } +export function toUnfoldable(U: Unfoldable): (r: Record) => HKT { ... } ``` Added in v2.0.0 @@ -738,7 +740,7 @@ Added in v2.0.0 **Signature** ```ts -export function updateAt(k: string, a: A): (r: Record) => Option> { ... } +export const updateAt: (k: string, a: A) => (r: Record) => Option> = ... ``` Added in v2.0.0 diff --git a/docs/modules/Ring.ts.md b/docs/modules/Ring.ts.md index 5295bdb28..ee9b9f2ca 100644 --- a/docs/modules/Ring.ts.md +++ b/docs/modules/Ring.ts.md @@ -1,6 +1,6 @@ --- title: Ring.ts -nav_order: 70 +nav_order: 76 parent: Modules --- @@ -56,7 +56,7 @@ Given a tuple of `Ring`s returns a `Ring` for the tuple **Signature** ```ts -export function getTupleRing>>( +export function getTupleRing>>( ...rings: T ): Ring<{ [K in keyof T]: T[K] extends Ring ? A : never }> { ... } ``` diff --git a/docs/modules/Semigroup.ts.md b/docs/modules/Semigroup.ts.md index afc6cd2f1..d4d247a1e 100644 --- a/docs/modules/Semigroup.ts.md +++ b/docs/modules/Semigroup.ts.md @@ -1,6 +1,6 @@ --- title: Semigroup.ts -nav_order: 71 +nav_order: 77 parent: Modules --- @@ -17,6 +17,7 @@ Added in v2.0.0 - [getDualSemigroup](#getdualsemigroup) - [getFirstSemigroup](#getfirstsemigroup) - [getFunctionSemigroup](#getfunctionsemigroup) +- [getIntercalateSemigroup](#getintercalatesemigroup) - [getJoinSemigroup](#getjoinsemigroup) - [getLastSemigroup](#getlastsemigroup) - [getMeetSemigroup](#getmeetsemigroup) @@ -51,7 +52,7 @@ Added in v2.0.0 **Signature** ```ts -export function fold(S: Semigroup): (a: A, as: Array) => A { ... } +export function fold(S: Semigroup): (a: A, as: ReadonlyArray) => A { ... } ``` Added in v2.0.0 @@ -86,6 +87,29 @@ export function getFunctionSemigroup(S: Semigroup): () => Semig Added in v2.0.0 +# getIntercalateSemigroup + +You can glue items between and stay associative + +**Signature** + +```ts +export function getIntercalateSemigroup(a: A): (S: Semigroup) => Semigroup { ... } +``` + +**Example** + +```ts +import { getIntercalateSemigroup, semigroupString } from 'fp-ts/lib/Semigroup' + +const S = getIntercalateSemigroup(' ')(semigroupString) + +assert.strictEqual(S.concat('a', 'b'), 'a b') +assert.strictEqual(S.concat(S.concat('a', 'b'), 'c'), S.concat('a', S.concat('b', 'c'))) +``` + +Added in v2.5.0 + # getJoinSemigroup **Signature** @@ -147,7 +171,7 @@ Added in v2.0.0 **Signature** ```ts -export function getStructSemigroup( +export function getStructSemigroup>( semigroups: { [K in keyof O]: Semigroup } ): Semigroup { ... } ``` @@ -161,7 +185,7 @@ Given a tuple of semigroups returns a semigroup for the tuple **Signature** ```ts -export function getTupleSemigroup>>( +export function getTupleSemigroup>>( ...semigroups: T ): Semigroup<{ [K in keyof T]: T[K] extends Semigroup ? A : never }> { ... } ``` diff --git a/docs/modules/Semigroupoid.ts.md b/docs/modules/Semigroupoid.ts.md index 4c6a9f24d..6d3bdaf40 100644 --- a/docs/modules/Semigroupoid.ts.md +++ b/docs/modules/Semigroupoid.ts.md @@ -1,6 +1,6 @@ --- title: Semigroupoid.ts -nav_order: 72 +nav_order: 78 parent: Modules --- diff --git a/docs/modules/Semiring.ts.md b/docs/modules/Semiring.ts.md index 999254642..ce981a47b 100644 --- a/docs/modules/Semiring.ts.md +++ b/docs/modules/Semiring.ts.md @@ -1,6 +1,6 @@ --- title: Semiring.ts -nav_order: 73 +nav_order: 79 parent: Modules --- diff --git a/docs/modules/Set.ts.md b/docs/modules/Set.ts.md index 9f518117e..ee166e614 100644 --- a/docs/modules/Set.ts.md +++ b/docs/modules/Set.ts.md @@ -1,6 +1,6 @@ --- title: Set.ts -nav_order: 74 +nav_order: 80 parent: Modules --- @@ -48,7 +48,7 @@ Added in v2.0.0 **Signature** ```ts -export function chain(E: Eq): (f: (x: A) => Set) => (set: Set) => Set { ... } +export const chain: (E: Eq) => (f: (x: A) => Set) => (set: Set) => Set = ... ``` Added in v2.0.0 @@ -58,7 +58,7 @@ Added in v2.0.0 **Signature** ```ts -export function compact(E: Eq): (fa: Set>) => Set { ... } +export const compact: (E: Eq) => (fa: Set>) => Set = ... ``` Added in v2.0.0 @@ -70,7 +70,7 @@ Form the set difference (`x` - `y`) **Signature** ```ts -export function difference(E: Eq): (x: Set, y: Set) => Set { ... } +export const difference: (E: Eq) => (x: Set, y: Set) => Set = ... ``` **Example** @@ -91,7 +91,7 @@ Test if a value is a member of a set **Signature** ```ts -export function elem(E: Eq): (a: A, set: Set) => boolean { ... } +export const elem: (E: Eq) => (a: A, set: Set) => boolean = ... ``` Added in v2.0.0 @@ -111,7 +111,7 @@ Added in v2.0.0 **Signature** ```ts -export function every(predicate: Predicate): (set: Set) => boolean { ... } +export const every: (predicate: Predicate) => (set: Set) => boolean = ... ``` Added in v2.0.0 @@ -132,7 +132,7 @@ Added in v2.0.0 **Signature** ```ts -export function filterMap(E: Eq): (f: (a: A) => Option) => (fa: Set) => Set { ... } +export const filterMap: (E: Eq) => (f: (a: A) => Option) => (fa: Set) => Set = ... ``` Added in v2.0.0 @@ -142,7 +142,7 @@ Added in v2.0.0 **Signature** ```ts -export function foldMap(O: Ord, M: Monoid): (f: (a: A) => M) => (fa: Set) => M { ... } +export const foldMap: (O: Ord, M: Monoid) => (f: (a: A) => M) => (fa: Set) => M = ... ``` Added in v2.0.0 @@ -154,7 +154,7 @@ Create a set from an array **Signature** ```ts -export function fromArray(E: Eq): (as: Array) => Set { ... } +export const fromArray: (E: Eq) => (as: Array) => Set = ... ``` Added in v2.0.0 @@ -164,7 +164,7 @@ Added in v2.0.0 **Signature** ```ts -export function getEq(E: Eq): Eq> { ... } +export const getEq: (E: Eq) => Eq> = ... ``` Added in v2.0.0 @@ -174,7 +174,7 @@ Added in v2.0.0 **Signature** ```ts -export function getIntersectionSemigroup(E: Eq): Semigroup> { ... } +export const getIntersectionSemigroup: (E: Eq) => Semigroup> = ... ``` Added in v2.0.0 @@ -184,7 +184,7 @@ Added in v2.0.0 **Signature** ```ts -export function getShow(S: Show): Show> { ... } +export const getShow: (S: Show) => Show> = ... ``` Added in v2.0.0 @@ -194,7 +194,7 @@ Added in v2.0.0 **Signature** ```ts -export function getUnionMonoid(E: Eq): Monoid> { ... } +export const getUnionMonoid: (E: Eq) => Monoid> = ... ``` Added in v2.0.0 @@ -206,7 +206,7 @@ Insert a value into a set **Signature** ```ts -export function insert(E: Eq): (a: A) => (set: Set) => Set { ... } +export const insert: (E: Eq) => (a: A) => (set: Set) => Set = ... ``` Added in v2.0.0 @@ -218,7 +218,7 @@ The set of elements which are in both the first and second set **Signature** ```ts -export function intersection(E: Eq): (set: Set, y: Set) => Set { ... } +export const intersection: (E: Eq) => (set: Set, y: Set) => Set = ... ``` Added in v2.0.0 @@ -230,7 +230,7 @@ Projects a Set through a function **Signature** ```ts -export function map(E: Eq): (f: (x: A) => B) => (set: Set) => Set { ... } +export const map: (E: Eq) => (f: (x: A) => B) => (set: Set) => Set = ... ``` Added in v2.0.0 @@ -251,10 +251,10 @@ Added in v2.0.0 **Signature** ```ts -export function partitionMap( +export const partitionMap: ( EB: Eq, EC: Eq -): (f: (a: A) => Either) => (set: Set) => Separated, Set> { ... } +) => (f: (a: A) => Either) => (set: Set) => Separated, Set> = ... ``` Added in v2.0.0 @@ -264,7 +264,7 @@ Added in v2.0.0 **Signature** ```ts -export function reduce(O: Ord): (b: B, f: (b: B, a: A) => B) => (fa: Set) => B { ... } +export const reduce: (O: Ord) => (b: B, f: (b: B, a: A) => B) => (fa: Set) => B = ... ``` Added in v2.0.0 @@ -276,7 +276,7 @@ Delete a value from a set **Signature** ```ts -export function remove(E: Eq): (a: A) => (set: Set) => Set { ... } +export const remove: (E: Eq) => (a: A) => (set: Set) => Set = ... ``` Added in v2.0.0 @@ -286,7 +286,10 @@ Added in v2.0.0 **Signature** ```ts -export function separate(EE: Eq, EA: Eq): (fa: Set>) => Separated, Set> { ... } +export const separate: ( + EE: Eq, + EA: Eq +) => (fa: Set>) => Separated, Set> = ... ``` Added in v2.0.0 @@ -298,7 +301,7 @@ Create a set with one element **Signature** ```ts -export function singleton(a: A): Set { ... } +export const singleton: (a: A) => Set = ... ``` Added in v2.0.0 @@ -308,7 +311,7 @@ Added in v2.0.0 **Signature** ```ts -export function some(predicate: Predicate): (set: Set) => boolean { ... } +export const some: (predicate: Predicate) => (set: Set) => boolean = ... ``` Added in v2.0.0 @@ -320,7 +323,7 @@ Added in v2.0.0 **Signature** ```ts -export function subset(E: Eq): (x: Set, y: Set) => boolean { ... } +export const subset: (E: Eq) => (x: Set, y: Set) => boolean = ... ``` Added in v2.0.0 @@ -330,7 +333,7 @@ Added in v2.0.0 **Signature** ```ts -export function toArray(O: Ord): (set: Set) => Array { ... } +export const toArray: (O: Ord) => (set: Set) => Array = ... ``` Added in v2.0.0 @@ -356,7 +359,7 @@ Form the union of two sets **Signature** ```ts -export function union(E: Eq): (set: Set, y: Set) => Set { ... } +export const union: (E: Eq) => (set: Set, y: Set) => Set = ... ``` Added in v2.0.0 diff --git a/docs/modules/Show.ts.md b/docs/modules/Show.ts.md index 2f0ec6266..ea209bf20 100644 --- a/docs/modules/Show.ts.md +++ b/docs/modules/Show.ts.md @@ -1,18 +1,11 @@ --- title: Show.ts -nav_order: 75 +nav_order: 81 parent: Modules --- # Show overview -The `Show` type class represents those types which can be converted into -a human-readable `string` representation. - -While not required, it is recommended that for any expression `x`, the -string `show x` be executable TypeScript code which evaluates to the same -value as the expression `x`. - Added in v2.0.0 --- @@ -34,14 +27,14 @@ The `Show` type class represents those types which can be converted into a human-readable `string` representation. While not required, it is recommended that for any expression `x`, the -string `show x` be executable TypeScript code which evaluates to the same +string `show(x)` be executable TypeScript code which evaluates to the same value as the expression `x`. **Signature** ```ts export interface Show { - show: (a: A) => string + readonly show: (a: A) => string } ``` @@ -52,7 +45,7 @@ Added in v2.0.0 **Signature** ```ts -export function getStructShow(shows: { [K in keyof O]: Show }): Show { ... } +export function getStructShow>(shows: { [K in keyof O]: Show }): Show { ... } ``` Added in v2.0.0 @@ -62,7 +55,7 @@ Added in v2.0.0 **Signature** ```ts -export function getTupleShow>>( +export function getTupleShow>>( ...shows: T ): Show<{ [K in keyof T]: T[K] extends Show ? A : never }> { ... } ``` diff --git a/docs/modules/State.ts.md b/docs/modules/State.ts.md index 9c50e32ae..5f1226cfe 100644 --- a/docs/modules/State.ts.md +++ b/docs/modules/State.ts.md @@ -1,6 +1,6 @@ --- title: State.ts -nav_order: 76 +nav_order: 82 parent: Modules --- diff --git a/docs/modules/StateReaderTaskEither.ts.md b/docs/modules/StateReaderTaskEither.ts.md index 5e1c3ca8b..2620b4f06 100644 --- a/docs/modules/StateReaderTaskEither.ts.md +++ b/docs/modules/StateReaderTaskEither.ts.md @@ -1,6 +1,6 @@ --- title: StateReaderTaskEither.ts -nav_order: 77 +nav_order: 83 parent: Modules --- @@ -249,7 +249,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => StateReaderTaskEither { ... } ``` @@ -271,7 +271,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => StateReaderTaskEither { ... } ``` @@ -323,7 +323,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromReaderTaskEitherK, B>( +export function fromReaderTaskEitherK, B>( f: (...a: A) => ReaderTaskEither ): (...a: A) => StateReaderTaskEither { ... } ``` @@ -345,7 +345,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromTaskEitherK, B>( +export function fromTaskEitherK, B>( f: (...a: A) => TaskEither ): (...a: A) => StateReaderTaskEither { ... } ``` diff --git a/docs/modules/StateT.ts.md b/docs/modules/StateT.ts.md index 175d60647..6fdc62363 100644 --- a/docs/modules/StateT.ts.md +++ b/docs/modules/StateT.ts.md @@ -1,6 +1,6 @@ --- title: StateT.ts -nav_order: 78 +nav_order: 84 parent: Modules --- diff --git a/docs/modules/Store.ts.md b/docs/modules/Store.ts.md index c9a9e36f8..77e211c4f 100644 --- a/docs/modules/Store.ts.md +++ b/docs/modules/Store.ts.md @@ -1,6 +1,6 @@ --- title: Store.ts -nav_order: 79 +nav_order: 85 parent: Modules --- diff --git a/docs/modules/Strong.ts.md b/docs/modules/Strong.ts.md index d0c61e9f9..9ac3088b9 100644 --- a/docs/modules/Strong.ts.md +++ b/docs/modules/Strong.ts.md @@ -1,6 +1,6 @@ --- title: Strong.ts -nav_order: 80 +nav_order: 86 parent: Modules --- diff --git a/docs/modules/Task.ts.md b/docs/modules/Task.ts.md index 16808a728..812311303 100644 --- a/docs/modules/Task.ts.md +++ b/docs/modules/Task.ts.md @@ -1,6 +1,6 @@ --- title: Task.ts -nav_order: 81 +nav_order: 87 parent: Modules --- @@ -166,7 +166,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => Task { ... } +export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => Task { ... } ``` Added in v2.4.0 diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md index a095dbafa..7e9081741 100644 --- a/docs/modules/TaskEither.ts.md +++ b/docs/modules/TaskEither.ts.md @@ -1,6 +1,6 @@ --- title: TaskEither.ts -nav_order: 82 +nav_order: 88 parent: Modules --- @@ -248,7 +248,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => TaskEither { ... } ``` @@ -270,7 +270,7 @@ Added in v2.0.0 **Signature** ```ts -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => TaskEither { ... } ``` @@ -568,7 +568,7 @@ Converts a function returning a `Promise` to one returning a `TaskEither`. **Signature** ```ts -export function tryCatchK, B>( +export function tryCatchK, B>( f: (...a: A) => Promise, onRejected: (reason: unknown) => E ): (...a: A) => TaskEither { ... } diff --git a/docs/modules/TaskThese.ts.md b/docs/modules/TaskThese.ts.md index 10a12fcaf..6b0599796 100644 --- a/docs/modules/TaskThese.ts.md +++ b/docs/modules/TaskThese.ts.md @@ -1,6 +1,6 @@ --- title: TaskThese.ts -nav_order: 83 +nav_order: 89 parent: Modules --- diff --git a/docs/modules/These.ts.md b/docs/modules/These.ts.md index db335f0a3..50762d02b 100644 --- a/docs/modules/These.ts.md +++ b/docs/modules/These.ts.md @@ -1,6 +1,6 @@ --- title: These.ts -nav_order: 84 +nav_order: 90 parent: Modules --- diff --git a/docs/modules/TheseT.ts.md b/docs/modules/TheseT.ts.md index 7453e333e..4b8e9e990 100644 --- a/docs/modules/TheseT.ts.md +++ b/docs/modules/TheseT.ts.md @@ -1,6 +1,6 @@ --- title: TheseT.ts -nav_order: 85 +nav_order: 91 parent: Modules --- @@ -43,6 +43,7 @@ export interface TheseM { readonly left: (e: E) => TheseT readonly right: (a: A) => TheseT readonly both: (e: E, a: A) => TheseT + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT, e: E, a: A) => HKT readonly getMonad: ( S: Semigroup @@ -79,6 +80,7 @@ export interface TheseM1 { readonly left: (e: E) => TheseT1 readonly right: (a: A) => TheseT1 readonly both: (e: E, a: A) => TheseT1 + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT1, e: E, a: A) => Kind readonly getMonad: ( S: Semigroup @@ -115,6 +117,7 @@ export interface TheseM2 { readonly left: (e: E) => TheseT2 readonly right: (a: A) => TheseT2 readonly both: (e: E, a: A) => TheseT2 + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT2, e: E, a: A) => Kind2 readonly getMonad: ( S: Semigroup diff --git a/docs/modules/Traced.ts.md b/docs/modules/Traced.ts.md index 8b04bed4d..97a842051 100644 --- a/docs/modules/Traced.ts.md +++ b/docs/modules/Traced.ts.md @@ -1,6 +1,6 @@ --- title: Traced.ts -nav_order: 86 +nav_order: 92 parent: Modules --- diff --git a/docs/modules/Traversable.ts.md b/docs/modules/Traversable.ts.md index c48342710..96f44d028 100644 --- a/docs/modules/Traversable.ts.md +++ b/docs/modules/Traversable.ts.md @@ -1,6 +1,6 @@ --- title: Traversable.ts -nav_order: 87 +nav_order: 93 parent: Modules --- diff --git a/docs/modules/TraversableWithIndex.ts.md b/docs/modules/TraversableWithIndex.ts.md index fc1670bc0..e444211e6 100644 --- a/docs/modules/TraversableWithIndex.ts.md +++ b/docs/modules/TraversableWithIndex.ts.md @@ -1,6 +1,6 @@ --- title: TraversableWithIndex.ts -nav_order: 88 +nav_order: 94 parent: Modules --- diff --git a/docs/modules/Tree.ts.md b/docs/modules/Tree.ts.md index 31df36ea6..8e15ec145 100644 --- a/docs/modules/Tree.ts.md +++ b/docs/modules/Tree.ts.md @@ -1,6 +1,6 @@ --- title: Tree.ts -nav_order: 89 +nav_order: 95 parent: Modules --- diff --git a/docs/modules/Tuple.ts.md b/docs/modules/Tuple.ts.md index 4b32382cc..b7a370833 100644 --- a/docs/modules/Tuple.ts.md +++ b/docs/modules/Tuple.ts.md @@ -1,6 +1,6 @@ --- title: Tuple.ts -nav_order: 90 +nav_order: 96 parent: Modules --- @@ -110,7 +110,7 @@ Added in v2.0.0 **Signature** ```ts -export function fst(sa: [A, S]): A { ... } +export const fst: (sa: [A, S]) => A = ... ``` Added in v2.0.0 @@ -120,7 +120,7 @@ Added in v2.0.0 **Signature** ```ts -export function getApplicative(M: Monoid): Applicative2C { ... } +export const getApplicative: (M: Monoid) => Applicative2C = ... ``` Added in v2.0.0 @@ -130,7 +130,7 @@ Added in v2.0.0 **Signature** ```ts -export function getApply(S: Semigroup): Apply2C { ... } +export const getApply: (S: Semigroup) => Apply2C = ... ``` Added in v2.0.0 @@ -140,7 +140,7 @@ Added in v2.0.0 **Signature** ```ts -export function getChain(S: Semigroup): Chain2C { ... } +export const getChain: (S: Semigroup) => Chain2C = ... ``` Added in v2.0.0 @@ -150,7 +150,7 @@ Added in v2.0.0 **Signature** ```ts -export function getChainRec(M: Monoid): ChainRec2C { ... } +export const getChainRec: (M: Monoid) => ChainRec2C = ... ``` Added in v2.0.0 @@ -160,7 +160,7 @@ Added in v2.0.0 **Signature** ```ts -export function getMonad(M: Monoid): Monad2C { ... } +export const getMonad: (M: Monoid) => Monad2C = ... ``` Added in v2.0.0 @@ -210,7 +210,7 @@ Added in v2.0.0 **Signature** ```ts -export function snd(sa: [A, S]): S { ... } +export const snd: (sa: [A, S]) => S = ... ``` Added in v2.0.0 @@ -220,7 +220,7 @@ Added in v2.0.0 **Signature** ```ts -export function swap(sa: [A, S]): [S, A] { ... } +export const swap: (sa: [A, S]) => [S, A] = ... ``` Added in v2.0.0 diff --git a/docs/modules/Unfoldable.ts.md b/docs/modules/Unfoldable.ts.md index f85585434..579aa0001 100644 --- a/docs/modules/Unfoldable.ts.md +++ b/docs/modules/Unfoldable.ts.md @@ -1,6 +1,6 @@ --- title: Unfoldable.ts -nav_order: 91 +nav_order: 97 parent: Modules --- diff --git a/docs/modules/ValidationT.ts.md b/docs/modules/ValidationT.ts.md index d0f527041..f3041e855 100644 --- a/docs/modules/ValidationT.ts.md +++ b/docs/modules/ValidationT.ts.md @@ -1,6 +1,6 @@ --- title: ValidationT.ts -nav_order: 92 +nav_order: 98 parent: Modules --- diff --git a/docs/modules/Witherable.ts.md b/docs/modules/Witherable.ts.md index b925040c7..5f5e8c3cd 100644 --- a/docs/modules/Witherable.ts.md +++ b/docs/modules/Witherable.ts.md @@ -1,6 +1,6 @@ --- title: Witherable.ts -nav_order: 93 +nav_order: 99 parent: Modules --- @@ -363,12 +363,12 @@ export interface Witherable extends Traversable, Filterable { /** * Partition a structure with effects */ - wilt: Wilt + readonly wilt: Wilt /** * Filter a structure with effects */ - wither: Wither + readonly wither: Wither } ``` @@ -380,8 +380,8 @@ Added in v2.0.0 ```ts export interface Witherable1 extends Traversable1, Filterable1 { - wilt: Wilt1 - wither: Wither1 + readonly wilt: Wilt1 + readonly wither: Wither1 } ``` @@ -393,8 +393,8 @@ Added in v2.0.0 ```ts export interface Witherable2 extends Traversable2, Filterable2 { - wilt: Wilt2 - wither: Wither2 + readonly wilt: Wilt2 + readonly wither: Wither2 } ``` @@ -406,8 +406,8 @@ Added in v2.0.0 ```ts export interface Witherable2C extends Traversable2C, Filterable2C { - wilt: Wilt2C - wither: Wither2C + readonly wilt: Wilt2C + readonly wither: Wither2C } ``` @@ -419,8 +419,8 @@ Added in v2.0.0 ```ts export interface Witherable3 extends Traversable3, Filterable3 { - wilt: Wilt3 - wither: Wither3 + readonly wilt: Wilt3 + readonly wither: Wither3 } ``` diff --git a/docs/modules/Writer.ts.md b/docs/modules/Writer.ts.md index 2d2c4bdc2..def649af4 100644 --- a/docs/modules/Writer.ts.md +++ b/docs/modules/Writer.ts.md @@ -1,6 +1,6 @@ --- title: Writer.ts -nav_order: 94 +nav_order: 100 parent: Modules --- diff --git a/docs/modules/WriterT.ts.md b/docs/modules/WriterT.ts.md index 28159a025..0b0448723 100644 --- a/docs/modules/WriterT.ts.md +++ b/docs/modules/WriterT.ts.md @@ -1,6 +1,6 @@ --- title: WriterT.ts -nav_order: 95 +nav_order: 101 parent: Modules --- diff --git a/docs/modules/function.ts.md b/docs/modules/function.ts.md index 7372e1d95..3aa180337 100644 --- a/docs/modules/function.ts.md +++ b/docs/modules/function.ts.md @@ -54,7 +54,7 @@ Added in v2.0.0 **Signature** ```ts -export interface FunctionN, B> { +export interface FunctionN, B> { (...args: A): B } ``` @@ -216,27 +216,27 @@ Function composition (from left to right). **Signature** ```ts -export function flow, B>(ab: (...a: A) => B): (...a: A) => B -export function flow, B, C>(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C -export function flow, B, C, D>( +export function flow, B>(ab: (...a: A) => B): (...a: A) => B +export function flow, B, C>(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C +export function flow, B, C, D>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D ): (...a: A) => D -export function flow, B, C, D, E>( +export function flow, B, C, D, E>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E ): (...a: A) => E -export function flow, B, C, D, E, F>( +export function flow, B, C, D, E, F>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F ): (...a: A) => F -export function flow, B, C, D, E, F, G>( +export function flow, B, C, D, E, F, G>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -244,7 +244,7 @@ export function flow, B, C, D, E, F, G>( ef: (e: E) => F, fg: (f: F) => G ): (...a: A) => G -export function flow, B, C, D, E, F, G, H>( +export function flow, B, C, D, E, F, G, H>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -253,7 +253,7 @@ export function flow, B, C, D, E, F, G, H>( fg: (f: F) => G, gh: (g: G) => H ): (...a: A) => H -export function flow, B, C, D, E, F, G, H, I>( +export function flow, B, C, D, E, F, G, H, I>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -263,7 +263,7 @@ export function flow, B, C, D, E, F, G, H, I>( gh: (g: G) => H, hi: (h: H) => I ): (...a: A) => I -export function flow, B, C, D, E, F, G, H, I, J>( +export function flow, B, C, D, E, F, G, H, I, J>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -326,7 +326,7 @@ Added in v2.0.0 **Signature** ```ts -export function tuple>(...t: T): T { ... } +export function tuple>(...t: T): T { ... } ``` Added in v2.0.0 @@ -338,7 +338,7 @@ Creates a tupled version of this function: instead of `n` arguments, it accepts **Signature** ```ts -export function tupled, B>(f: (...a: A) => B): (a: A) => B { ... } +export function tupled, B>(f: (...a: A) => B): (a: A) => B { ... } ``` **Example** @@ -370,7 +370,7 @@ Inverse function of `tupled` **Signature** ```ts -export function untupled, B>(f: (a: A) => B): (...a: A) => B { ... } +export function untupled, B>(f: (a: A) => B): (...a: A) => B { ... } ``` Added in v2.4.0 diff --git a/docs/modules/index.ts.md b/docs/modules/index.ts.md index 035d61525..4215dd341 100644 --- a/docs/modules/index.ts.md +++ b/docs/modules/index.ts.md @@ -79,6 +79,12 @@ Added in v2.0.0 - [readerT](#readert) - [readerTask](#readertask) - [readerTaskEither](#readertaskeither) +- [readonlyArray](#readonlyarray) +- [readonlyMap](#readonlymap) +- [readonlyNonEmptyArray](#readonlynonemptyarray) +- [readonlyRecord](#readonlyrecord) +- [readonlySet](#readonlyset) +- [readonlyTuple](#readonlytuple) - [record](#record) - [ring](#ring) - [semigroup](#semigroup) @@ -779,6 +785,66 @@ typeof readerTaskEither Added in v2.0.0 +# readonlyArray + +**Signature** + +```ts +typeof readonlyArray +``` + +Added in v2.5.0 + +# readonlyMap + +**Signature** + +```ts +typeof readonlyMap +``` + +Added in v2.5.0 + +# readonlyNonEmptyArray + +**Signature** + +```ts +typeof readonlyNonEmptyArray +``` + +Added in v2.5.0 + +# readonlyRecord + +**Signature** + +```ts +typeof readonlyRecord +``` + +Added in v2.5.0 + +# readonlySet + +**Signature** + +```ts +typeof readonlySet +``` + +Added in v2.5.0 + +# readonlyTuple + +**Signature** + +```ts +typeof readonlyTuple +``` + +Added in v2.5.0 + # record **Signature** diff --git a/docs/modules/pipeable.ts.md b/docs/modules/pipeable.ts.md index c68a292a7..c94b8b0e8 100644 --- a/docs/modules/pipeable.ts.md +++ b/docs/modules/pipeable.ts.md @@ -1839,7 +1839,7 @@ Added in v2.0.0 ```ts export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain4 ? PipeableChain4 : I extends Apply4 @@ -1868,7 +1868,7 @@ export function pipeable( (I extends Semigroupoid4 ? PipeableSemigroupoid4 : {}) & (I extends MonadThrow4 ? PipeableMonadThrow4 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain3 ? PipeableChain3 : I extends Apply3 @@ -1897,7 +1897,7 @@ export function pipeable( (I extends Semigroupoid3 ? PipeableSemigroupoid3 : {}) & (I extends MonadThrow3 ? PipeableMonadThrow3 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain3C ? PipeableChain3C : I extends Apply3C @@ -1926,7 +1926,7 @@ export function pipeable( (I extends Semigroupoid3C ? PipeableSemigroupoid3C : {}) & (I extends MonadThrow3C ? PipeableMonadThrow3C : {}) export function pipeable( - I: { URI: F; _E: E } & I + I: { readonly URI: F; readonly _E: E } & I ): (I extends Chain2C ? PipeableChain2C : I extends Apply2C @@ -1954,7 +1954,7 @@ export function pipeable( (I extends Semigroupoid2C ? PipeableSemigroupoid2C : {}) & (I extends MonadThrow2C ? PipeableMonadThrow2C : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain2 ? PipeableChain2 : I extends Apply2 @@ -1983,7 +1983,7 @@ export function pipeable( (I extends Semigroupoid2 ? PipeableSemigroupoid2 : {}) & (I extends MonadThrow2 ? PipeableMonadThrow2 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain1 ? PipeableChain1 : I extends Apply1 @@ -2009,7 +2009,7 @@ export function pipeable( : {}) & (I extends MonadThrow1 ? PipeableMonadThrow1 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain ? PipeableChain : I extends Apply diff --git a/package-lock.json b/package-lock.json index c37826c8d..ff1071033 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fp-ts", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6626,6 +6626,15 @@ } } }, + "tslint-immutable": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tslint-immutable/-/tslint-immutable-6.0.1.tgz", + "integrity": "sha512-3GQ6HffN64gLmT/N1YzyVMqyf6uBjMvhNaevK8B0K01/QC0OU5AQZrH4TjMHo1IdG3JpqsZvuRy9IW1LA3zjwA==", + "dev": true, + "requires": { + "tsutils": "^2.28.0 || ^3.0.0" + } + }, "tsutils": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.5.0.tgz", diff --git a/package.json b/package.json index fa7dae4b2..eb593f1e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fp-ts", - "version": "2.4.4", + "version": "2.5.0", "description": "Functional programming in TypeScript", "files": [ "lib", @@ -55,6 +55,7 @@ "ts-node": "^8.0.2", "tslint": "5.11.0", "tslint-config-standard": "8.0.1", + "tslint-immutable": "^6.0.1", "typescript": "^3.7.2" }, "tags": [ diff --git a/src/Apply.ts b/src/Apply.ts index 71e93cbf6..069285a3b 100644 --- a/src/Apply.ts +++ b/src/Apply.ts @@ -65,7 +65,7 @@ export interface Apply4 extends Functor4 { readonly ap: (fab: Kind4 B>, fa: Kind4) => Kind4 } -function curried(f: Function, n: number, acc: Array) { +function curried(f: Function, n: number, acc: ReadonlyArray) { return function(x: unknown) { const combined = acc.concat([x]) return n === 0 ? f.apply(null, combined) : curried(f, n - 1, combined) @@ -81,6 +81,7 @@ function getTupleConstructor(len: number): (a: unknown) => any { return tupleConstructors[len] } +/* tslint:disable:readonly-array */ /** * Tuple sequencing, i.e., take a tuple of monadic actions and does them from left-to-right, returning the resulting tuple. * @@ -98,38 +99,37 @@ function getTupleConstructor(len: number): (a: unknown) => any { export function sequenceT( F: Apply4 ): >>( - ...t: T & { 0: Kind4 } + ...t: T & { readonly 0: Kind4 } ) => Kind4] ? A : never }> export function sequenceT( F: Apply3 ): >>( - ...t: T & { 0: Kind3 } + ...t: T & { readonly 0: Kind3 } ) => Kind3] ? A : never }> export function sequenceT( F: Apply3C ): >>( - ...t: T & { 0: Kind3 } + ...t: T & { readonly 0: Kind3 } ) => Kind3] ? A : never }> export function sequenceT( F: Apply2 ): >>( - ...t: T & { 0: Kind2 } + ...t: T & { readonly 0: Kind2 } ) => Kind2] ? A : never }> export function sequenceT( F: Apply2C ): >>( - ...t: T & { 0: Kind2 } + ...t: T & { readonly 0: Kind2 } ) => Kind2] ? A : never }> - export function sequenceT( F: Apply1 ): >>( - ...t: T & { 0: Kind } + ...t: T & { readonly 0: Kind } ) => Kind] ? A : never }> export function sequenceT( F: Apply ): >>( - ...t: T & { 0: HKT } + ...t: T & { readonly 0: HKT } ) => HKT] ? A : never }> export function sequenceT(F: Apply): any { return (...args: Array>) => { @@ -142,13 +142,14 @@ export function sequenceT(F: Apply): any { return fas } } +/* tslint:enable:readonly-array */ type EnforceNonEmptyRecord = keyof R extends never ? never : R -function getRecordConstructor(keys: Array) { +function getRecordConstructor(keys: ReadonlyArray) { const len = keys.length return curried( - (...args: Array) => { + (...args: ReadonlyArray) => { const r: Record = {} for (let i = 0; i < len; i++) { r[keys[i]] = args[i] @@ -160,6 +161,7 @@ function getRecordConstructor(keys: Array) { ) } +/* tslint:disable:readonly-array */ /** * Like `Apply.sequenceT` but works with structs instead of tuples. * @@ -233,3 +235,4 @@ export function sequenceS(F: Apply): (r: Record>) => H return fr } } +/* tslint:enable:readonly-array */ diff --git a/src/Array.ts b/src/Array.ts index bdc417ba2..17db05856 100644 --- a/src/Array.ts +++ b/src/Array.ts @@ -2,31 +2,32 @@ * @since 2.0.0 */ import { Alternative1 } from './Alternative' -import { Applicative } from './Applicative' -import { Compactable1, Separated } from './Compactable' +import { Compactable1 } from './Compactable' import { Either } from './Either' import { Eq } from './Eq' import { Extend1 } from './Extend' import { FilterableWithIndex1 } from './FilterableWithIndex' import { Foldable1 } from './Foldable' import { FoldableWithIndex1 } from './FoldableWithIndex' -import { Predicate, Refinement, identity } from './function' +import { Predicate, Refinement } from './function' import { FunctorWithIndex1 } from './FunctorWithIndex' -import { HKT } from './HKT' import { Monad1 } from './Monad' import { Monoid } from './Monoid' import { NonEmptyArray } from './NonEmptyArray' -import { isSome, none, Option, some } from './Option' -import { fromCompare, getMonoid as getOrdMonoid, Ord, ordNumber } from './Ord' +import { Option } from './Option' +import { Ord } from './Ord' import { pipeable } from './pipeable' +import * as RA from './ReadonlyArray' import { Show } from './Show' import { TraversableWithIndex1 } from './TraversableWithIndex' import { Unfoldable1 } from './Unfoldable' import { Witherable1 } from './Witherable' +/* tslint:disable:readonly-array */ + declare module './HKT' { interface URItoKind { - Array: Array + readonly Array: Array } } @@ -43,30 +44,7 @@ export type URI = typeof URI /** * @since 2.0.0 */ -export function getShow(S: Show): Show> { - return { - show: as => `[${as.map(S.show).join(', ')}]` - } -} - -const concat = (x: Array, y: Array): Array => { - const lenx = x.length - if (lenx === 0) { - return y - } - const leny = y.length - if (leny === 0) { - return x - } - const r = Array(lenx + leny) - for (let i = 0; i < lenx; i++) { - r[i] = x[i] - } - for (let i = 0; i < leny; i++) { - r[i + lenx] = y[i] - } - return r -} +export const getShow: (S: Show) => Show> = RA.getShow /** * Returns a `Monoid` for `Array` @@ -79,12 +57,7 @@ const concat = (x: Array, y: Array): Array => { * * @since 2.0.0 */ -export function getMonoid(): Monoid> { - return { - concat, - empty - } -} +export const getMonoid: () => Monoid> = RA.getMonoid as any /** * Derives an `Eq` over the `Array` of a given element type from the `Eq` of that type. The derived `Eq` defines two @@ -101,11 +74,7 @@ export function getMonoid(): Monoid> { * * @since 2.0.0 */ -export function getEq(E: Eq): Eq> { - return { - equals: (xs, ys) => xs === ys || (xs.length === ys.length && xs.every((x, i) => E.equals(x, ys[i]))) - } -} +export const getEq: (E: Eq) => Eq> = RA.getEq /** * Derives an `Ord` over the `Array` of a given element type from the `Ord` of that type. The ordering between two such @@ -125,20 +94,7 @@ export function getEq(E: Eq): Eq> { * * @since 2.0.0 */ -export function getOrd(O: Ord): Ord> { - return fromCompare((a, b) => { - const aLen = a.length - const bLen = b.length - const len = Math.min(aLen, bLen) - for (let i = 0; i < len; i++) { - const ordering = O.compare(a[i], b[i]) - if (ordering !== 0) { - return ordering - } - } - return ordNumber.compare(aLen, bLen) - }) -} +export const getOrd: (O: Ord) => Ord> = RA.getOrd /** * An empty array @@ -158,13 +114,7 @@ export const empty: Array = [] * * @since 2.0.0 */ -export function makeBy(n: number, f: (i: number) => A): Array { - const r: Array = [] - for (let i = 0; i < n; i++) { - r.push(f(i)) - } - return r -} +export const makeBy: (n: number, f: (i: number) => A) => Array = RA.makeBy as any /** * Create an array containing a range of integers, including both endpoints @@ -176,9 +126,7 @@ export function makeBy(n: number, f: (i: number) => A): Array { * * @since 2.0.0 */ -export function range(start: number, end: number): Array { - return makeBy(end - start + 1, i => start + i) -} +export const range: (start: number, end: number) => Array = RA.range as any /** * Create an array containing a value repeated the specified number of times @@ -190,9 +138,7 @@ export function range(start: number, end: number): Array { * * @since 2.0.0 */ -export function replicate(n: number, a: A): Array { - return makeBy(n, () => a) -} +export const replicate: (n: number, a: A) => Array = RA.replicate as any /** * Removes one level of nesting @@ -204,24 +150,7 @@ export function replicate(n: number, a: A): Array { * * @since 2.0.0 */ -export function flatten(mma: Array>): Array { - let rLen = 0 - const len = mma.length - for (let i = 0; i < len; i++) { - rLen += mma[i].length - } - const r = Array(rLen) - let start = 0 - for (let i = 0; i < len; i++) { - const arr = mma[i] - const l = arr.length - for (let j = 0; j < l; j++) { - r[j + start] = arr[j] - } - start += l - } - return r -} +export const flatten: (mma: Array>) => Array = RA.flatten as any /** * Break an array into its first element and remaining elements @@ -234,18 +163,20 @@ export function flatten(mma: Array>): Array { * * @since 2.0.0 */ -export function foldLeft(onNil: () => B, onCons: (head: A, tail: Array) => B): (as: Array) => B { - return as => (isEmpty(as) ? onNil() : onCons(as[0], as.slice(1))) -} +export const foldLeft: ( + onNil: () => B, + onCons: (head: A, tail: Array) => B +) => (as: Array) => B = RA.foldLeft as any /** * Break an array into its initial elements and the last element * * @since 2.0.0 */ -export function foldRight(onNil: () => B, onCons: (init: Array, last: A) => B): (as: Array) => B { - return as => (isEmpty(as) ? onNil() : onCons(as.slice(0, as.length - 1), as[as.length - 1])) -} +export const foldRight: ( + onNil: () => B, + onCons: (init: Array, last: A) => B +) => (as: Array) => B = RA.foldRight as any /** * Same as `reduce` but it carries over the intermediate steps @@ -258,17 +189,7 @@ export function foldRight(onNil: () => B, onCons: (init: Array, last: A * * @since 2.0.0 */ -export function scanLeft(b: B, f: (b: B, a: A) => B): (as: Array) => Array { - return as => { - const l = as.length - const r: Array = new Array(l + 1) - r[0] = b - for (let i = 0; i < l; i++) { - r[i + 1] = f(r[i], as[i]) - } - return r - } -} +export const scanLeft: (b: B, f: (b: B, a: A) => B) => (as: Array) => Array = RA.scanLeft as any /** * Fold an array from the right, keeping all intermediate results instead of only the final result @@ -280,17 +201,7 @@ export function scanLeft(b: B, f: (b: B, a: A) => B): (as: Array) => Ar * * @since 2.0.0 */ -export function scanRight(b: B, f: (a: A, b: B) => B): (as: Array) => Array { - return as => { - const l = as.length - const r: Array = new Array(l + 1) - r[l] = b - for (let i = l - 1; i >= 0; i--) { - r[i] = f(as[i], r[i + 1]) - } - return r - } -} +export const scanRight: (b: B, f: (a: A, b: B) => B) => (as: Array) => Array = RA.scanRight as any /** * Test whether an array is empty @@ -302,27 +213,21 @@ export function scanRight(b: B, f: (a: A, b: B) => B): (as: Array) => A * * @since 2.0.0 */ -export function isEmpty(as: Array): boolean { - return as.length === 0 -} +export const isEmpty: (as: Array) => boolean = RA.isEmpty /** * Test whether an array is non empty narrowing down the type to `NonEmptyArray` * * @since 2.0.0 */ -export function isNonEmpty(as: Array): as is NonEmptyArray { - return as.length > 0 -} +export const isNonEmpty: (as: Array) => as is NonEmptyArray = RA.isNonEmpty as any /** * Test whether an array contains a particular index * * @since 2.0.0 */ -export function isOutOfBound(i: number, as: Array): boolean { - return i < 0 || i >= as.length -} +export const isOutOfBound: (i: number, as: Array) => boolean = RA.isOutOfBound /** * This function provides a safe way to read a value at a particular index from an array @@ -336,9 +241,7 @@ export function isOutOfBound(i: number, as: Array): boolean { * * @since 2.0.0 */ -export function lookup(i: number, as: Array): Option { - return isOutOfBound(i, as) ? none : some(as[i]) -} +export const lookup: (i: number, as: Array) => Option = RA.lookup /** * Attaches an element to the front of an array, creating a new non empty array @@ -350,15 +253,7 @@ export function lookup(i: number, as: Array): Option { * * @since 2.0.0 */ -export function cons(head: A, tail: Array): NonEmptyArray { - const len = tail.length - const r = Array(len + 1) - for (let i = 0; i < len; i++) { - r[i + 1] = tail[i] - } - r[0] = head - return r as NonEmptyArray -} +export const cons: (head: A, tail: Array) => NonEmptyArray = RA.cons as any /** * Append an element to the end of an array, creating a new non empty array @@ -370,15 +265,7 @@ export function cons(head: A, tail: Array): NonEmptyArray { * * @since 2.0.0 */ -export function snoc(init: Array, end: A): NonEmptyArray { - const len = init.length - const r = Array(len + 1) - for (let i = 0; i < len; i++) { - r[i] = init[i] - } - r[len] = end - return r as NonEmptyArray -} +export const snoc: (init: Array, end: A) => NonEmptyArray = RA.snoc as any /** * Get the first element in an array, or `None` if the array is empty @@ -392,10 +279,7 @@ export function snoc(init: Array, end: A): NonEmptyArray { * * @since 2.0.0 */ -export function head(as: Array): Option { - return isEmpty(as) ? none : some(as[0]) -} - +export const head: (as: Array) => Option = RA.head /** * Get the last element in an array, or `None` if the array is empty * @@ -408,9 +292,7 @@ export function head(as: Array): Option { * * @since 2.0.0 */ -export function last(as: Array): Option { - return lookup(as.length - 1, as) -} +export const last: (as: Array) => Option = RA.last /** * Get all but the first element of an array, creating a new array, or `None` if the array is empty @@ -424,9 +306,7 @@ export function last(as: Array): Option { * * @since 2.0.0 */ -export function tail(as: Array): Option> { - return isEmpty(as) ? none : some(as.slice(1)) -} +export const tail: (as: Array) => Option> = RA.tail as any /** * Get all but the last element of an array, creating a new array, or `None` if the array is empty @@ -440,10 +320,7 @@ export function tail(as: Array): Option> { * * @since 2.0.0 */ -export function init(as: Array): Option> { - const len = as.length - return len === 0 ? none : some(as.slice(0, len - 1)) -} +export const init: (as: Array) => Option> = RA.init as any /** * Keep only a number of elements from the start of an array, creating a new array. @@ -456,9 +333,7 @@ export function init(as: Array): Option> { * * @since 2.0.0 */ -export function takeLeft(n: number): (as: Array) => Array { - return as => as.slice(0, n) -} +export const takeLeft: (n: number) => (as: Array) => Array = RA.takeLeft as any /** * Keep only a number of elements from the end of an array, creating a new array. @@ -471,9 +346,7 @@ export function takeLeft(n: number): (as: Array) => Array { * * @since 2.0.0 */ -export function takeRight(n: number): (as: Array) => Array { - return as => (n === 0 ? empty : as.slice(-n)) -} +export const takeRight: (n: number) => (as: Array) => Array = RA.takeRight as any /** * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new array @@ -488,27 +361,10 @@ export function takeRight(n: number): (as: Array) => Array { export function takeLeftWhile(refinement: Refinement): (as: Array) => Array export function takeLeftWhile(predicate: Predicate): (as: Array) => Array export function takeLeftWhile(predicate: Predicate): (as: Array) => Array { - return as => { - const i = spanIndexUncurry(as, predicate) - const init = Array(i) - for (let j = 0; j < i; j++) { - init[j] = as[j] - } - return init - } -} - -const spanIndexUncurry = (as: Array, predicate: Predicate): number => { - const l = as.length - let i = 0 - for (; i < l; i++) { - if (!predicate(as[i])) { - break - } - } - return i + return RA.takeLeftWhile(predicate) as any } +/* tslint:disable:readonly-keyword */ /** * Split an array into two parts: * 1. the longest initial subarray for which all elements satisfy the specified predicate @@ -526,20 +382,9 @@ export function spanLeft( ): (as: Array) => { init: Array; rest: Array } export function spanLeft(predicate: Predicate): (as: Array) => { init: Array; rest: Array } export function spanLeft(predicate: Predicate): (as: Array) => { init: Array; rest: Array } { - return as => { - const i = spanIndexUncurry(as, predicate) - const init = Array(i) - for (let j = 0; j < i; j++) { - init[j] = as[j] - } - const l = as.length - const rest = Array(l - i) - for (let j = i; j < l; j++) { - rest[j - i] = as[j] - } - return { init, rest } - } + return RA.spanLeft(predicate) as any } +/* tslint:enable:readonly-keyword */ /** * Drop a number of elements from the start of an array, creating a new array @@ -551,9 +396,7 @@ export function spanLeft(predicate: Predicate): (as: Array) => { init: * * @since 2.0.0 */ -export function dropLeft(n: number): (as: Array) => Array { - return as => as.slice(n, as.length) -} +export const dropLeft: (n: number) => (as: Array) => Array = RA.dropLeft as any /** * Drop a number of elements from the end of an array, creating a new array @@ -565,9 +408,7 @@ export function dropLeft(n: number): (as: Array) => Array { * * @since 2.0.0 */ -export function dropRight(n: number): (as: Array) => Array { - return as => as.slice(0, as.length - n) -} +export const dropRight: (n: number) => (as: Array) => Array = RA.dropRight as any /** * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new array @@ -579,17 +420,7 @@ export function dropRight(n: number): (as: Array) => Array { * * @since 2.0.0 */ -export function dropLeftWhile(predicate: Predicate): (as: Array) => Array { - return as => { - const i = spanIndexUncurry(as, predicate) - const l = as.length - const rest = Array(l - i) - for (let j = i; j < l; j++) { - rest[j - i] = as[j] - } - return rest - } -} +export const dropLeftWhile: (predicate: Predicate) => (as: Array) => Array = RA.dropLeftWhile as any /** * Find the first index for which a predicate holds @@ -603,17 +434,7 @@ export function dropLeftWhile(predicate: Predicate): (as: Array) => Arr * * @since 2.0.0 */ -export function findIndex(predicate: Predicate): (as: Array) => Option { - return as => { - const len = as.length - for (let i = 0; i < len; i++) { - if (predicate(as[i])) { - return some(i) - } - } - return none - } -} +export const findIndex: (predicate: Predicate) => (as: Array) => Option = RA.findIndex /** * Find the first element which satisfies a predicate (or a refinement) function @@ -629,15 +450,7 @@ export function findIndex(predicate: Predicate): (as: Array) => Option< export function findFirst(refinement: Refinement): (as: Array) => Option export function findFirst(predicate: Predicate): (as: Array) => Option export function findFirst(predicate: Predicate): (as: Array) => Option { - return as => { - const len = as.length - for (let i = 0; i < len; i++) { - if (predicate(as[i])) { - return some(as[i]) - } - } - return none - } + return RA.findFirst(predicate) } /** @@ -659,18 +472,7 @@ export function findFirst(predicate: Predicate): (as: Array) => Option< * * @since 2.0.0 */ -export function findFirstMap(f: (a: A) => Option): (as: Array) => Option { - return as => { - const len = as.length - for (let i = 0; i < len; i++) { - const v = f(as[i]) - if (isSome(v)) { - return v - } - } - return none - } -} +export const findFirstMap: (f: (a: A) => Option) => (as: Array) => Option = RA.findFirstMap /** * Find the last element which satisfies a predicate function @@ -686,15 +488,7 @@ export function findFirstMap(f: (a: A) => Option): (as: Array) => Op export function findLast(refinement: Refinement): (as: Array) => Option export function findLast(predicate: Predicate): (as: Array) => Option export function findLast(predicate: Predicate): (as: Array) => Option { - return as => { - const len = as.length - for (let i = len - 1; i >= 0; i--) { - if (predicate(as[i])) { - return some(as[i]) - } - } - return none - } + return RA.findLast(predicate) } /** @@ -716,18 +510,7 @@ export function findLast(predicate: Predicate): (as: Array) => Option(f: (a: A) => Option): (as: Array) => Option { - return as => { - const len = as.length - for (let i = len - 1; i >= 0; i--) { - const v = f(as[i]) - if (isSome(v)) { - return v - } - } - return none - } -} +export const findLastMap: (f: (a: A) => Option) => (as: Array) => Option = RA.findLastMap /** * Returns the index of the last element of the list which matches the predicate @@ -747,38 +530,17 @@ export function findLastMap(f: (a: A) => Option): (as: Array) => Opt * * @since 2.0.0 */ -export function findLastIndex(predicate: Predicate): (as: Array) => Option { - return as => { - const len = as.length - for (let i = len - 1; i >= 0; i--) { - if (predicate(as[i])) { - return some(i) - } - } - return none - } -} +export const findLastIndex: (predicate: Predicate) => (as: Array) => Option = RA.findLastIndex /** * @since 2.0.0 */ -export function copy(as: Array): Array { - const l = as.length - const r = Array(l) - for (let i = 0; i < l; i++) { - r[i] = as[i] - } - return r -} +export const copy: (as: Array) => Array = RA.toArray /** * @since 2.0.0 */ -export function unsafeInsertAt(i: number, a: A, as: Array): Array { - const xs = copy(as) - xs.splice(i, 0, a) - return xs -} +export const unsafeInsertAt: (i: number, a: A, as: Array) => Array = RA.unsafeInsertAt as any /** * Insert an element at the specified index, creating a new array, or returning `None` if the index is out of bounds @@ -791,22 +553,12 @@ export function unsafeInsertAt(i: number, a: A, as: Array): Array { * * @since 2.0.0 */ -export function insertAt(i: number, a: A): (as: Array) => Option> { - return as => (i < 0 || i > as.length ? none : some(unsafeInsertAt(i, a, as))) -} +export const insertAt: (i: number, a: A) => (as: Array) => Option> = RA.insertAt as any /** * @since 2.0.0 */ -export function unsafeUpdateAt(i: number, a: A, as: Array): Array { - if (as[i] === a) { - return as - } else { - const xs = copy(as) - xs[i] = a - return xs - } -} +export const unsafeUpdateAt: (i: number, a: A, as: Array) => Array = RA.unsafeUpdateAt as any /** * Change the element at the specified index, creating a new array, or returning `None` if the index is out of bounds @@ -820,18 +572,12 @@ export function unsafeUpdateAt(i: number, a: A, as: Array): Array { * * @since 2.0.0 */ -export function updateAt(i: number, a: A): (as: Array) => Option> { - return as => (isOutOfBound(i, as) ? none : some(unsafeUpdateAt(i, a, as))) -} +export const updateAt: (i: number, a: A) => (as: Array) => Option> = RA.updateAt as any /** * @since 2.0.0 */ -export function unsafeDeleteAt(i: number, as: Array): Array { - const xs = copy(as) - xs.splice(i, 1) - return xs -} +export const unsafeDeleteAt: (i: number, as: Array) => Array = RA.unsafeDeleteAt as any /** * Delete the element at the specified index, creating a new array, or returning `None` if the index is out of bounds @@ -845,9 +591,7 @@ export function unsafeDeleteAt(i: number, as: Array): Array { * * @since 2.0.0 */ -export function deleteAt(i: number): (as: Array) => Option> { - return as => (isOutOfBound(i, as) ? none : some(unsafeDeleteAt(i, as))) -} +export const deleteAt: (i: number) => (as: Array) => Option> = RA.deleteAt as any /** * Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out @@ -863,9 +607,7 @@ export function deleteAt(i: number): (as: Array) => Option> { * * @since 2.0.0 */ -export function modifyAt(i: number, f: (a: A) => A): (as: Array) => Option> { - return as => (isOutOfBound(i, as) ? none : some(unsafeUpdateAt(i, f(as[i]), as))) -} +export const modifyAt: (i: number, f: (a: A) => A) => (as: Array) => Option> = RA.modifyAt as any /** * Reverse an array, creating a new array @@ -877,9 +619,7 @@ export function modifyAt(i: number, f: (a: A) => A): (as: Array) => Option * * @since 2.0.0 */ -export function reverse(as: Array): Array { - return copy(as).reverse() -} +export const reverse: (as: Array) => Array = RA.reverse as any /** * Extracts from an array of `Either` all the `Right` elements. All the `Right` elements are extracted in order @@ -892,17 +632,7 @@ export function reverse(as: Array): Array { * * @since 2.0.0 */ -export function rights(as: Array>): Array { - const r: Array = [] - const len = as.length - for (let i = 0; i < len; i++) { - const a = as[i] - if (a._tag === 'Right') { - r.push(a.right) - } - } - return r -} +export const rights: (as: Array>) => Array = RA.rights as any /** * Extracts from an array of `Either` all the `Left` elements. All the `Left` elements are extracted in order @@ -915,17 +645,7 @@ export function rights(as: Array>): Array { * * @since 2.0.0 */ -export function lefts(as: Array>): Array { - const r: Array = [] - const len = as.length - for (let i = 0; i < len; i++) { - const a = as[i] - if (a._tag === 'Left') { - r.push(a.left) - } - } - return r -} +export const lefts: (as: Array>) => Array = RA.lefts as any /** * Sort the elements of an array in increasing order, creating a new array @@ -938,9 +658,7 @@ export function lefts(as: Array>): Array { * * @since 2.0.0 */ -export function sort(O: Ord): (as: Array) => Array { - return as => copy(as).sort(O.compare) -} +export const sort: (O: Ord) => (as: Array) => Array = RA.sort as any /** * Apply a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one @@ -953,14 +671,7 @@ export function sort(O: Ord): (as: Array) => Array { * * @since 2.0.0 */ -export function zipWith(fa: Array, fb: Array, f: (a: A, b: B) => C): Array { - const fc = [] - const len = Math.min(fa.length, fb.length) - for (let i = 0; i < len; i++) { - fc[i] = f(fa[i], fb[i]) - } - return fc -} +export const zipWith: (fa: Array, fb: Array, f: (a: A, b: B) => C) => Array = RA.zipWith as any /** * Takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the @@ -973,9 +684,7 @@ export function zipWith(fa: Array, fb: Array, f: (a: A, b: B) => * * @since 2.0.0 */ -export function zip(fa: Array, fb: Array): Array<[A, B]> { - return zipWith(fa, fb, (a, b) => [a, b]) -} +export const zip: (fa: Array, fb: Array) => Array<[A, B]> = RA.zip as any /** * The function is reverse of `zip`. Takes an array of pairs and return two corresponding arrays @@ -987,17 +696,7 @@ export function zip(fa: Array, fb: Array): Array<[A, B]> { * * @since 2.0.0 */ -export function unzip(as: Array<[A, B]>): [Array, Array] { - const fa = [] - const fb = [] - - for (let i = 0; i < as.length; i++) { - fa[i] = as[i][0] - fb[i] = as[i][1] - } - - return [fa, fb] -} +export const unzip: (as: Array<[A, B]>) => [Array, Array] = RA.unzip as any /** * Rotate an array to the right by `n` steps @@ -1009,18 +708,7 @@ export function unzip(as: Array<[A, B]>): [Array, Array] { * * @since 2.0.0 */ -export function rotate(n: number): (as: Array) => Array { - return as => { - const len = as.length - if (n === 0 || len <= 1 || len === Math.abs(n)) { - return as - } else if (n < 0) { - return rotate(len + n)(as) - } else { - return as.slice(-n).concat(as.slice(0, len - n)) - } - } -} +export const rotate: (n: number) => (as: Array) => Array = RA.rotate as any /** * Test if a value is a member of an array. Takes a `Eq` as a single @@ -1036,19 +724,7 @@ export function rotate(n: number): (as: Array) => Array { * * @since 2.0.0 */ -export function elem(E: Eq): (a: A, as: Array) => boolean { - return (a, as) => { - const predicate = (element: A) => E.equals(element, a) - let i = 0 - const len = as.length - for (; i < len; i++) { - if (predicate(as[i])) { - return true - } - } - return false - } -} +export const elem: (E: Eq) => (a: A, as: Array) => boolean = RA.elem /** * Remove duplicates from an array, keeping the first occurrence of an element. @@ -1061,21 +737,7 @@ export function elem(E: Eq): (a: A, as: Array) => boolean { * * @since 2.0.0 */ -export function uniq(E: Eq): (as: Array) => Array { - const elemS = elem(E) - return as => { - const r: Array = [] - const len = as.length - let i = 0 - for (; i < len; i++) { - const a = as[i] - if (!elemS(a, r)) { - r.push(a) - } - } - return len === r.length ? as : r - } -} +export const uniq: (E: Eq) => (as: Array) => Array = RA.uniq as any /** * Sort the elements of an array in increasing order, where elements are compared using first `ords[0]`, then `ords[1]`, @@ -1104,10 +766,7 @@ export function uniq(E: Eq): (as: Array) => Array { * * @since 2.0.0 */ -export function sortBy(ords: Array>): (as: Array) => Array { - const M = getOrdMonoid() - return sort(ords.reduce(M.concat, M.empty)) -} +export const sortBy: (ords: Array>) => (as: Array) => Array = RA.sortBy as any /** * A useful recursion pattern for processing an array to produce a new array, often used for "chopping" up the input @@ -1128,18 +787,7 @@ export function sortBy(ords: Array>): (as: Array) => Array { * * @since 2.0.0 */ -export function chop(f: (as: NonEmptyArray) => [B, Array]): (as: Array) => Array { - return as => { - const result: Array = [] - let cs: Array = as - while (isNonEmpty(cs)) { - const [b, c] = f(cs) - result.push(b) - cs = c - } - return result - } -} +export const chop: (f: (as: NonEmptyArray) => [B, Array]) => (as: Array) => Array = RA.chop as any /** * Splits an array into two pieces, the first piece has `n` elements. @@ -1151,9 +799,7 @@ export function chop(f: (as: NonEmptyArray) => [B, Array]): (as: Arr * * @since 2.0.0 */ -export function splitAt(n: number): (as: Array) => [Array, Array] { - return as => [as.slice(0, n), as.slice(n)] -} +export const splitAt: (n: number) => (as: Array) => [Array, Array] = RA.splitAt as any /** * Splits an array into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of @@ -1174,9 +820,7 @@ export function splitAt(n: number): (as: Array) => [Array, Array] { * * @since 2.0.0 */ -export function chunksOf(n: number): (as: Array) => Array> { - return as => (as.length === 0 ? empty : isOutOfBound(n - 1, as) ? [as] : chop(splitAt(n))(as)) -} +export const chunksOf: (n: number) => (as: Array) => Array> = RA.chunksOf as any /** * Array comprehension @@ -1220,14 +864,7 @@ export function comprehension( f: (...xs: Array) => R, g: (...xs: Array) => boolean = () => true ): Array { - const go = (scope: Array, input: Array>): Array => { - if (input.length === 0) { - return g(...scope) ? [f(...scope)] : empty - } else { - return array.chain(input[0], x => go(snoc(scope, x), input.slice(1))) - } - } - return go(empty, input) + return RA.comprehension(input as any, f, g) as any } /** @@ -1241,14 +878,7 @@ export function comprehension( * * @since 2.0.0 */ -export function union(E: Eq): (xs: Array, ys: Array) => Array { - const elemE = elem(E) - return (xs, ys) => - concat( - xs, - ys.filter(a => !elemE(a, xs)) - ) -} +export const union: (E: Eq) => (xs: Array, ys: Array) => Array = RA.union as any /** * Creates an array of unique values that are included in all given arrays using a `Eq` for equality @@ -1262,10 +892,7 @@ export function union(E: Eq): (xs: Array, ys: Array) => Array { * * @since 2.0.0 */ -export function intersection(E: Eq): (xs: Array, ys: Array) => Array { - const elemE = elem(E) - return (xs, ys) => xs.filter(a => elemE(a, ys)) -} +export const intersection: (E: Eq) => (xs: Array, ys: Array) => Array = RA.intersection as any /** * Creates an array of array values not included in the other given array using a `Eq` for equality @@ -1279,15 +906,12 @@ export function intersection(E: Eq): (xs: Array, ys: Array) => Array * * @since 2.0.0 */ -export function difference(E: Eq): (xs: Array, ys: Array) => Array { - const elemE = elem(E) - return (xs, ys) => xs.filter(a => !elemE(a, ys)) -} +export const difference: (E: Eq) => (xs: Array, ys: Array) => Array = RA.difference as any /** * @since 2.0.0 */ -export const of = (a: A): Array => [a] +export const of: (a: A) => Array = RA.of as any /** * @since 2.0.0 @@ -1304,174 +928,36 @@ export const array: Monad1 & FunctorWithIndex1 & FoldableWithIndex1 = { URI, - map: (fa, f) => fa.map(a => f(a)), - mapWithIndex: (fa, f) => fa.map((a, i) => f(i, a)), - compact: as => array.filterMap(as, identity), - separate: (fa: Array>): Separated, Array> => { - const left: Array = [] - const right: Array = [] - for (const e of fa) { - if (e._tag === 'Left') { - left.push(e.left) - } else { - right.push(e.right) - } - } - return { - left, - right - } - }, - filter: (as: Array, predicate: Predicate): Array => { - return as.filter(predicate) - }, - filterMap: (as, f) => array.filterMapWithIndex(as, (_, a) => f(a)), - partition: (fa: Array, predicate: Predicate): Separated, Array> => { - return array.partitionWithIndex(fa, (_, a) => predicate(a)) - }, - partitionMap: (fa, f) => array.partitionMapWithIndex(fa, (_, a) => f(a)), + map: RA.readonlyArray.map as any, + mapWithIndex: RA.readonlyArray.mapWithIndex as any, + compact: RA.readonlyArray.compact as any, + separate: RA.readonlyArray.separate as any, + filter: RA.readonlyArray.filter as any, + filterMap: RA.readonlyArray.filterMap as any, + partition: RA.readonlyArray.partition as any, + partitionMap: RA.readonlyArray.partitionMap as any, of, - ap: (fab, fa) => flatten(array.map(fab, f => array.map(fa, f))), - chain: (fa, f) => { - let resLen = 0 - const l = fa.length - const temp = new Array(l) - for (let i = 0; i < l; i++) { - const e = fa[i] - const arr = f(e) - resLen += arr.length - temp[i] = arr - } - const r = Array(resLen) - let start = 0 - for (let i = 0; i < l; i++) { - const arr = temp[i] - const l = arr.length - for (let j = 0; j < l; j++) { - r[j + start] = arr[j] - } - start += l - } - return r - }, - reduce: (fa, b, f) => array.reduceWithIndex(fa, b, (_, b, a) => f(b, a)), - foldMap: M => { - const foldMapWithIndexM = array.foldMapWithIndex(M) - return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) - }, - reduceRight: (fa, b, f) => array.reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), - unfold: (b: B, f: (b: B) => Option<[A, B]>): Array => { - const ret: Array = [] - let bb = b - while (true) { - const mt = f(bb) - if (isSome(mt)) { - const [a, b] = mt.value - ret.push(a) - bb = b - } else { - break - } - } - return ret - }, - traverse: (F: Applicative): ((ta: Array, f: (a: A) => HKT) => HKT>) => { - const traverseWithIndexF = array.traverseWithIndex(F) - return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) - }, - sequence: (F: Applicative) => (ta: Array>): HKT> => { - return array.reduce(ta, F.of(array.zero()), (fas, fa) => - F.ap( - F.map(fas, as => (a: A) => snoc(as, a)), - fa - ) - ) - }, - zero: () => empty, - alt: (fx, f) => concat(fx, f()), - extend: (fa, f) => fa.map((_, i, as) => f(as.slice(i))), - wither: (F: Applicative): ((ta: Array, f: (a: A) => HKT>) => HKT>) => { - const traverseF = array.traverse(F) - return (wa, f) => F.map(traverseF(wa, f), array.compact) - }, - wilt: ( - F: Applicative - ): ((wa: Array, f: (a: A) => HKT>) => HKT, Array>>) => { - const traverseF = array.traverse(F) - return (wa, f) => F.map(traverseF(wa, f), array.separate) - }, - reduceWithIndex: (fa, b, f) => { - const l = fa.length - let r = b - for (let i = 0; i < l; i++) { - r = f(i, r, fa[i]) - } - return r - }, - foldMapWithIndex: M => (fa, f) => fa.reduce((b, a, i) => M.concat(b, f(i, a)), M.empty), - reduceRightWithIndex: (fa, b, f) => fa.reduceRight((b, a, i) => f(i, a, b), b), - traverseWithIndex: (F: Applicative) => ( - ta: Array, - f: (i: number, a: A) => HKT - ): HKT> => { - return array.reduceWithIndex(ta, F.of>(array.zero()), (i, fbs, a) => - F.ap( - F.map(fbs, bs => (b: B) => snoc(bs, b)), - f(i, a) - ) - ) - }, - partitionMapWithIndex: ( - fa: Array, - f: (i: number, a: A) => Either - ): Separated, Array> => { - const left: Array = [] - const right: Array = [] - for (let i = 0; i < fa.length; i++) { - const e = f(i, fa[i]) - if (e._tag === 'Left') { - left.push(e.left) - } else { - right.push(e.right) - } - } - return { - left, - right - } - }, - partitionWithIndex: ( - fa: Array, - predicateWithIndex: (i: number, a: A) => boolean - ): Separated, Array> => { - const left: Array = [] - const right: Array = [] - for (let i = 0; i < fa.length; i++) { - const a = fa[i] - if (predicateWithIndex(i, a)) { - right.push(a) - } else { - left.push(a) - } - } - return { - left, - right - } - }, - filterMapWithIndex: (fa: Array, f: (i: number, a: A) => Option): Array => { - const result: Array = [] - for (let i = 0; i < fa.length; i++) { - const optionB = f(i, fa[i]) - if (isSome(optionB)) { - result.push(optionB.value) - } - } - return result - }, - filterWithIndex: (fa: Array, predicateWithIndex: (i: number, a: A) => boolean): Array => { - return fa.filter((a, i) => predicateWithIndex(i, a)) - } + ap: RA.readonlyArray.ap as any, + chain: RA.readonlyArray.chain as any, + reduce: RA.readonlyArray.reduce as any, + foldMap: RA.readonlyArray.foldMap as any, + reduceRight: RA.readonlyArray.reduceRight as any, + unfold: RA.readonlyArray.unfold as any, + traverse: RA.readonlyArray.traverse as any, + sequence: RA.readonlyArray.sequence as any, + zero: RA.readonlyArray.zero as any, + alt: RA.readonlyArray.alt as any, + extend: RA.readonlyArray.extend as any, + wither: RA.readonlyArray.wither as any, + wilt: RA.readonlyArray.wilt as any, + reduceWithIndex: RA.readonlyArray.reduceWithIndex as any, + foldMapWithIndex: RA.readonlyArray.foldMapWithIndex as any, + reduceRightWithIndex: RA.readonlyArray.reduceRightWithIndex as any, + traverseWithIndex: RA.readonlyArray.traverseWithIndex as any, + partitionMapWithIndex: RA.readonlyArray.partitionMapWithIndex as any, + partitionWithIndex: RA.readonlyArray.partitionWithIndex as any, + filterMapWithIndex: RA.readonlyArray.filterMapWithIndex as any, + filterWithIndex: RA.readonlyArray.filterWithIndex as any } const { diff --git a/src/Const.ts b/src/Const.ts index 135b51d83..26e1667e0 100644 --- a/src/Const.ts +++ b/src/Const.ts @@ -14,7 +14,7 @@ import { pipeable } from './pipeable' declare module './HKT' { interface URItoKind2 { - Const: Const + readonly Const: Const } } diff --git a/src/Either.ts b/src/Either.ts index cc24a5928..975ba935f 100644 --- a/src/Either.ts +++ b/src/Either.ts @@ -54,7 +54,7 @@ import { Witherable2C } from './Witherable' declare module './HKT' { interface URItoKind2 { - Either: Either + readonly Either: Either } } diff --git a/src/Eq.ts b/src/Eq.ts index c1ca994b3..12fbe807f 100644 --- a/src/Eq.ts +++ b/src/Eq.ts @@ -11,10 +11,11 @@ */ import { Contravariant1 } from './Contravariant' import { pipeable } from './pipeable' +import { ReadonlyRecord } from './ReadonlyRecord' declare module './HKT' { interface URItoKind { - Eq: Eq + readonly Eq: Eq } } @@ -71,7 +72,7 @@ export const eqBoolean: Eq = eqStrict /** * @since 2.0.0 */ -export function getStructEq(eqs: { [K in keyof O]: Eq }): Eq { +export function getStructEq>(eqs: { [K in keyof O]: Eq }): Eq { return fromEquals((x, y) => { for (const k in eqs) { if (!eqs[k].equals(x[k], y[k])) { @@ -96,7 +97,7 @@ export function getStructEq(eqs: { [K in keyof * * @since 2.0.0 */ -export function getTupleEq>>( +export function getTupleEq>>( ...eqs: T ): Eq<{ [K in keyof T]: T[K] extends Eq ? A : never }> { return fromEquals((x, y) => eqs.every((E, i) => E.equals(x[i], y[i]))) diff --git a/src/Foldable.ts b/src/Foldable.ts index f89e6ccf3..84545f01e 100644 --- a/src/Foldable.ts +++ b/src/Foldable.ts @@ -276,8 +276,8 @@ export function intercalate(M: Monoid, F: Foldable1): ( export function intercalate(M: Monoid, F: Foldable): (sep: M, fm: HKT) => M export function intercalate(M: Monoid, F: Foldable): (sep: M, fm: HKT) => M { interface Acc { - init: boolean - acc: M + readonly init: boolean + readonly acc: M } return (sep, fm) => { const go = ({ init, acc }: Acc, x: M): Acc => diff --git a/src/FoldableWithIndex.ts b/src/FoldableWithIndex.ts index c4d7f716d..56c4aafb9 100644 --- a/src/FoldableWithIndex.ts +++ b/src/FoldableWithIndex.ts @@ -31,6 +31,8 @@ import { import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3, URIS4, Kind4 } from './HKT' import { Monoid } from './Monoid' +/* tslint:disable:readonly-array */ + /** * @since 2.0.0 */ @@ -191,6 +193,8 @@ export interface FoldableWithIndexComposition22C B } +/* tslint:enable:readonly-array */ + /** * @since 2.0.0 */ diff --git a/src/FunctorWithIndex.ts b/src/FunctorWithIndex.ts index 94353d49e..808deb658 100644 --- a/src/FunctorWithIndex.ts +++ b/src/FunctorWithIndex.ts @@ -80,6 +80,8 @@ export interface FunctorWithIndex4 extends Functor4 { readonly mapWithIndex: (fa: Kind4, f: (i: I, a: A) => B) => Kind4 } +/* tslint:disable:readonly-array */ + /** * @since 2.0.0 */ @@ -149,6 +151,8 @@ export interface FunctorWithIndexComposition22C Kind2> } +/* tslint:enable:readonly-array */ + /** * @since 2.0.0 */ diff --git a/src/IO.ts b/src/IO.ts index 03d10e22a..9dee87383 100644 --- a/src/IO.ts +++ b/src/IO.ts @@ -101,7 +101,7 @@ import { ChainRec1 } from './ChainRec' declare module './HKT' { interface URItoKind { - IO: IO + readonly IO: IO } } diff --git a/src/IOEither.ts b/src/IOEither.ts index 09af49ce9..3692321ae 100644 --- a/src/IOEither.ts +++ b/src/IOEither.ts @@ -25,7 +25,7 @@ const T = getEitherM(io) declare module './HKT' { interface URItoKind2 { - IOEither: IOEither + readonly IOEither: IOEither } } @@ -175,7 +175,9 @@ export function getFilterable(M: Monoid): Filterable2C { /** * @since 2.4.0 */ -export function fromEitherK, B>(f: (...a: A) => Either): (...a: A) => IOEither { +export function fromEitherK, B>( + f: (...a: A) => Either +): (...a: A) => IOEither { return (...a) => fromEither(f(...a)) } diff --git a/src/IORef.ts b/src/IORef.ts index 343f5ac7b..a5807346a 100644 --- a/src/IORef.ts +++ b/src/IORef.ts @@ -15,7 +15,7 @@ import { IO } from './IO' * @since 2.0.0 */ export class IORef { - read: IO + readonly read: IO constructor(private value: A) { this.read = () => this.value } diff --git a/src/Identity.ts b/src/Identity.ts index f07a21b92..7e539933e 100644 --- a/src/Identity.ts +++ b/src/Identity.ts @@ -16,7 +16,7 @@ import { pipeable } from './pipeable' declare module './HKT' { interface URItoKind { - Identity: Identity + readonly Identity: Identity } } diff --git a/src/Map.ts b/src/Map.ts index b9e2968d1..60b7dd0a2 100644 --- a/src/Map.ts +++ b/src/Map.ts @@ -1,29 +1,28 @@ /** * @since 2.0.0 */ -import { Applicative } from './Applicative' -import { Separated } from './Compactable' -import { Either, isLeft } from './Either' -import { Eq, fromEquals } from './Eq' +import { Eq } from './Eq' import { Filterable2 } from './Filterable' import { FilterableWithIndex2C } from './FilterableWithIndex' import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable' -import { Predicate } from './function' import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' import { Magma } from './Magma' import { Monoid } from './Monoid' -import { isNone, isSome, none, Option, option, some } from './Option' +import { Option } from './Option' import { Ord } from './Ord' +import { pipeable } from './pipeable' +import * as RM from './ReadonlyMap' import { Semigroup } from './Semigroup' import { Show } from './Show' import { TraversableWithIndex2C } from './TraversableWithIndex' import { Unfoldable, Unfoldable1 } from './Unfoldable' import { Witherable2C } from './Witherable' -import { pipeable } from './pipeable' + +/* tslint:disable:readonly-array */ declare module './HKT' { interface URItoKind2 { - Map: Map + readonly Map: Map } } @@ -40,110 +39,61 @@ export type URI = typeof URI /** * @since 2.0.0 */ -export function getShow(SK: Show, SA: Show): Show> { - return { - show: m => { - let elements = '' - m.forEach((a, k) => { - elements += `[${SK.show(k)}, ${SA.show(a)}], ` - }) - if (elements !== '') { - elements = elements.substring(0, elements.length - 2) - } - return `new Map([${elements}])` - } - } -} +export const getShow: (SK: Show, SA: Show) => Show> = RM.getShow /** * Calculate the number of key/value pairs in a map * * @since 2.0.0 */ -export function size(d: Map): number { - return d.size -} +export const size: (d: Map) => number = RM.size /** * Test whether or not a map is empty * * @since 2.0.0 */ -export function isEmpty(d: Map): boolean { - return d.size === 0 -} +export const isEmpty: (d: Map) => boolean = RM.isEmpty /** * Test whether or not a key exists in a map * * @since 2.0.0 */ -export function member(E: Eq): (k: K, m: Map) => boolean { - const lookupE = lookup(E) - return (k, m) => isSome(lookupE(k, m)) -} +export const member: (E: Eq) => (k: K, m: Map) => boolean = RM.member /** * Test whether or not a value is a member of a map * * @since 2.0.0 */ -export function elem(E: Eq): (a: A, m: Map) => boolean { - return (a, m) => { - const values = m.values() - let e: { done?: boolean; value: A } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = values.next()).done) { - const v = e.value - if (E.equals(a, v)) { - return true - } - } - return false - } -} +export const elem: (E: Eq) => (a: A, m: Map) => boolean = RM.elem /** * Get a sorted array of the keys contained in a map * * @since 2.0.0 */ -export function keys(O: Ord): (m: Map) => Array { - return m => Array.from(m.keys()).sort(O.compare) -} +export const keys: (O: Ord) => (m: Map) => Array = RM.keys as any /** * Get a sorted array of the values contained in a map * * @since 2.0.0 */ -export function values(O: Ord): (m: Map) => Array { - return m => Array.from(m.values()).sort(O.compare) -} +export const values: (O: Ord) => (m: Map) => Array = RM.values as any /** * @since 2.0.0 */ -export function collect(O: Ord): (f: (k: K, a: A) => B) => (m: Map) => Array { - const keysO = keys(O) - return (f: (k: K, a: A) => B) => (m: Map): Array => { - const out: Array = [] - const ks = keysO(m) - for (const key of ks) { - out.push(f(key, m.get(key)!)) - } - return out - } -} +export const collect: (O: Ord) => (f: (k: K, a: A) => B) => (m: Map) => Array = RM.collect as any /** * Get a sorted of the key/value pairs contained in a map * * @since 2.0.0 */ -export function toArray(O: Ord): (m: Map) => Array<[K, A]> { - return collect(O)((k, a) => [k, a]) -} +export const toArray: (O: Ord) => (m: Map) => Array<[K, A]> = RM.toReadonlyArray as any /** * Unfolds a map into a list of key/value pairs @@ -153,12 +103,7 @@ export function toArray(O: Ord): (m: Map) => Array<[K, A]> { export function toUnfoldable(O: Ord, U: Unfoldable1): (d: Map) => Kind export function toUnfoldable(O: Ord, U: Unfoldable): (d: Map) => HKT export function toUnfoldable(O: Ord, U: Unfoldable): (d: Map) => HKT { - const toArrayO = toArray(O) - return d => { - const arr = toArrayO(d) - const len = arr.length - return U.unfold(0, b => (b < len ? some([arr[b], b + 1]) : none)) - } + return RM.toUnfoldable(O, U) as any } /** @@ -166,86 +111,33 @@ export function toUnfoldable(O: Ord, U: Unfoldable): (d: Map(E: Eq): (k: K, a: A) => (m: Map) => Map { - const lookupWithKeyE = lookupWithKey(E) - return (k, a) => m => { - const found = lookupWithKeyE(k, m) - if (isNone(found)) { - const r = new Map(m) - r.set(k, a) - return r - } else if (found.value[1] !== a) { - const r = new Map(m) - r.set(found.value[0], a) - return r - } - return m - } -} +export const insertAt: (E: Eq) => (k: K, a: A) => (m: Map) => Map = RM.insertAt as any /** * Delete a key and value from a map * * @since 2.0.0 */ -export function deleteAt(E: Eq): (k: K) => (m: Map) => Map { - const lookupWithKeyE = lookupWithKey(E) - return k => m => { - const found = lookupWithKeyE(k, m) - if (isSome(found)) { - const r = new Map(m) - r.delete(found.value[0]) - return r - } - return m - } -} +export const deleteAt: (E: Eq) => (k: K) => (m: Map) => Map = RM.deleteAt as any /** * @since 2.0.0 */ -export function updateAt(E: Eq): (k: K, a: A) => (m: Map) => Option> { - const lookupWithKeyE = lookupWithKey(E) - return (k, a) => m => { - const found = lookupWithKeyE(k, m) - if (isNone(found)) { - return none - } - const r = new Map(m) - r.set(found.value[0], a) - return some(r) - } -} +export const updateAt: (E: Eq) => (k: K, a: A) => (m: Map) => Option> = RM.updateAt as any /** * @since 2.0.0 */ -export function modifyAt(E: Eq): (k: K, f: (a: A) => A) => (m: Map) => Option> { - const lookupWithKeyE = lookupWithKey(E) - return (k, f) => m => { - const found = lookupWithKeyE(k, m) - if (isNone(found)) { - return none - } - const r = new Map(m) - r.set(found.value[0], f(found.value[1])) - return some(r) - } -} +export const modifyAt: ( + E: Eq +) => (k: K, f: (a: A) => A) => (m: Map) => Option> = RM.modifyAt as any /** * Delete a key and value from a map, returning the value as well as the subsequent map * * @since 2.0.0 */ -export function pop(E: Eq): (k: K) => (m: Map) => Option<[A, Map]> { - const lookupE = lookup(E) - const deleteAtE = deleteAt(E) - return k => { - const deleteAtEk = deleteAtE(k) - return m => option.map(lookupE(k, m), a => [a, deleteAtEk(m)]) - } -} +export const pop: (E: Eq) => (k: K) => (m: Map) => Option<[A, Map]> = RM.pop as any /** * Lookup the value for a key in a `Map`. @@ -253,52 +145,21 @@ export function pop(E: Eq): (k: K) => (m: Map) => Option<[A, Map< * * @since 2.0.0 */ -export function lookupWithKey(E: Eq): (k: K, m: Map) => Option<[K, A]> { - return (k: K, m: Map) => { - const entries = m.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [ka, a] = e.value - if (E.equals(ka, k)) { - return some([ka, a]) - } - } - return none - } -} +export const lookupWithKey: (E: Eq) => (k: K, m: Map) => Option<[K, A]> = RM.lookupWithKey as any /** * Lookup the value for a key in a `Map`. * * @since 2.0.0 */ -export function lookup(E: Eq): (k: K, m: Map) => Option { - const lookupWithKeyE = lookupWithKey(E) - return (k, m) => option.map(lookupWithKeyE(k, m), ([_, a]) => a) -} +export const lookup: (E: Eq) => (k: K, m: Map) => Option = RM.lookup /** * Test whether or not one Map contains all of the keys and values contained in another Map * * @since 2.0.0 */ -export function isSubmap(SK: Eq, SA: Eq): (d1: Map, d2: Map) => boolean { - const lookupWithKeyS = lookupWithKey(SK) - return (d1: Map, d2: Map): boolean => { - const entries = d1.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - const d2OptA = lookupWithKeyS(k, d2) - if (isNone(d2OptA) || !SK.equals(k, d2OptA.value[0]) || !SA.equals(a, d2OptA.value[1])) { - return false - } - } - return true - } -} +export const isSubmap: (SK: Eq, SA: Eq) => (d1: Map, d2: Map) => boolean = RM.isSubmap /** * @since 2.0.0 @@ -308,53 +169,21 @@ export const empty = new Map() /** * @since 2.0.0 */ -export function getEq(SK: Eq, SA: Eq): Eq> { - const isSubmap_ = isSubmap(SK, SA) - return fromEquals((x, y) => isSubmap_(x, y) && isSubmap_(y, x)) -} +export const getEq: (SK: Eq, SA: Eq) => Eq> = RM.getEq /** * Gets `Monoid` instance for Maps given `Semigroup` instance for their values * * @since 2.0.0 */ -export function getMonoid(SK: Eq, SA: Semigroup): Monoid> { - const lookupWithKeyS = lookupWithKey(SK) - return { - concat: (mx, my) => { - if (mx === empty) { - return my - } - if (my === empty) { - return mx - } - const r = new Map(mx) - const entries = my.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - const mxOptA = lookupWithKeyS(k, mx) - if (isSome(mxOptA)) { - r.set(mxOptA.value[0], SA.concat(mxOptA.value[1], a)) - } else { - r.set(k, a) - } - } - return r - }, - empty - } -} +export const getMonoid: (SK: Eq, SA: Semigroup) => Monoid> = RM.getMonoid as any /** * Create a map with one key/value pair * * @since 2.0.0 */ -export function singleton(k: K, a: A): Map { - return new Map([[k, a]]) -} +export const singleton: (k: K, a: A) => Map = RM.singleton as any /** * Create a map from a foldable collection of key/value pairs, using the @@ -379,260 +208,37 @@ export function fromFoldable( ): (fka: Kind) => Map export function fromFoldable(E: Eq, M: Magma, F: Foldable): (fka: HKT) => Map export function fromFoldable(E: Eq, M: Magma, F: Foldable): (fka: HKT) => Map { - return (fka: HKT) => { - const lookupWithKeyE = lookupWithKey(E) - return F.reduce<[K, A], Map>(fka, new Map(), (b, [k, a]) => { - const bOpt = lookupWithKeyE(k, b) - if (isSome(bOpt)) { - b.set(bOpt.value[0], M.concat(bOpt.value[1], a)) - } else { - b.set(k, a) - } - return b - }) - } -} - -const _mapWithIndex = (fa: Map, f: (k: K, a: A) => B): Map => { - const m = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [key, a] = e.value - m.set(key, f(key, a)) - } - return m -} - -const _partitionMapWithIndex = ( - fa: Map, - f: (k: K, a: A) => Either -): Separated, Map> => { - const left = new Map() - const right = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - const ei = f(k, a) - if (isLeft(ei)) { - left.set(k, ei.left) - } else { - right.set(k, ei.right) - } - } - return { - left, - right - } -} - -const _partitionWithIndex = (fa: Map, p: (k: K, a: A) => boolean): Separated, Map> => { - const left = new Map() - const right = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - if (p(k, a)) { - right.set(k, a) - } else { - left.set(k, a) - } - } - return { - left, - right - } -} - -const _filterMapWithIndex = (fa: Map, f: (k: K, a: A) => Option): Map => { - const m = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - const o = f(k, a) - if (isSome(o)) { - m.set(k, o.value) - } - } - return m -} - -const _filterWithIndex = (fa: Map, p: (k: K, a: A) => boolean): Map => { - const m = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, a] = e.value - if (p(k, a)) { - m.set(k, a) - } - } - return m + return RM.fromFoldable(E, M, F) as any } /** * @since 2.0.0 */ -export function getFilterableWithIndex(): FilterableWithIndex2C { - return { - ...map_, - _E: undefined as any, - mapWithIndex: _mapWithIndex, - partitionMapWithIndex: _partitionMapWithIndex, - partitionWithIndex: _partitionWithIndex, - filterMapWithIndex: _filterMapWithIndex, - filterWithIndex: _filterWithIndex - } -} +export const getFilterableWithIndex: () => FilterableWithIndex2C< + URI, + K, + K +> = RM.getFilterableWithIndex as any /** * @since 2.0.0 */ -export function getWitherable(O: Ord): Witherable2C & TraversableWithIndex2C { - const keysO = keys(O) - - const reduceWithIndex = (fa: Map, b: B, f: (k: K, b: B, a: A) => B): B => { - let out: B = b - const ks = keysO(fa) - const len = ks.length - for (let i = 0; i < len; i++) { - const k = ks[i] - out = f(k, out, fa.get(k)!) - } - return out - } - - const foldMapWithIndex = (M: Monoid) => (fa: Map, f: (k: K, a: A) => M): M => { - let out: M = M.empty - const ks = keysO(fa) - const len = ks.length - for (let i = 0; i < len; i++) { - const k = ks[i] - out = M.concat(out, f(k, fa.get(k)!)) - } - return out - } - - const reduceRightWithIndex = (fa: Map, b: B, f: (k: K, a: A, b: B) => B): B => { - let out: B = b - const ks = keysO(fa) - const len = ks.length - for (let i = len - 1; i >= 0; i--) { - const k = ks[i] - out = f(k, fa.get(k)!, out) - } - return out - } - - const traverseWithIndex = ( - F: Applicative - ): ((ta: Map, f: (k: K, a: A) => HKT) => HKT>) => { - return (ta: Map, f: (k: K, a: A) => HKT) => { - let fm: HKT> = F.of(empty) - const entries = ta.entries() - let e: { done?: boolean; value: [K, A] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [key, a] = e.value - fm = F.ap( - F.map(fm, m => (b: B) => new Map(m).set(key, b)), - f(key, a) - ) - } - return fm - } - } - - const traverse = (F: Applicative): ((ta: Map, f: (a: A) => HKT) => HKT>) => { - const traverseWithIndexF = traverseWithIndex(F) - return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) - } - - const sequence = (F: Applicative): ((ta: Map>) => HKT>) => { - const traverseWithIndexF = traverseWithIndex(F) - return ta => traverseWithIndexF(ta, (_, a) => a) - } - - return { - ...map_, - _E: undefined as any, - reduce: (fa, b, f) => reduceWithIndex(fa, b, (_, b, a) => f(b, a)), - foldMap: M => { - const foldMapWithIndexM = foldMapWithIndex(M) - return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) - }, - reduceRight: (fa, b, f) => reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), - traverse, - sequence, - mapWithIndex: _mapWithIndex, - reduceWithIndex, - foldMapWithIndex, - reduceRightWithIndex, - traverseWithIndex, - wilt: ( - F: Applicative - ): ((wa: Map, f: (a: A) => HKT>) => HKT, Map>>) => { - const traverseF = traverse(F) - return (wa, f) => F.map(traverseF(wa, f), map_.separate) - }, - wither: (F: Applicative): ((wa: Map, f: (a: A) => HKT>) => HKT>) => { - const traverseF = traverse(F) - return (wa, f) => F.map(traverseF(wa, f), map_.compact) - } - } -} +export const getWitherable: ( + O: Ord +) => Witherable2C & TraversableWithIndex2C = RM.getWitherable as any /** * @since 2.0.0 */ export const map_: Filterable2 = { URI, - map: (fa, f) => _mapWithIndex(fa, (_, a) => f(a)), - compact: (fa: Map>): Map => { - const m = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, Option] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, oa] = e.value - if (isSome(oa)) { - m.set(k, oa.value) - } - } - return m - }, - separate: (fa: Map>): Separated, Map> => { - const left = new Map() - const right = new Map() - const entries = fa.entries() - let e: { done?: boolean; value: [K, Either] } - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = entries.next()).done) { - const [k, ei] = e.value - if (isLeft(ei)) { - left.set(k, ei.left) - } else { - right.set(k, ei.right) - } - } - return { - left, - right - } - }, - filter: (fa: Map, p: Predicate): Map => _filterWithIndex(fa, (_, a) => p(a)), - filterMap: (fa, f) => _filterMapWithIndex(fa, (_, a) => f(a)), - partition: (fa: Map, predicate: Predicate): Separated, Map> => - _partitionWithIndex(fa, (_, a) => predicate(a)), - partitionMap: (fa, f) => _partitionMapWithIndex(fa, (_, a) => f(a)) + map: RM.readonlyMap.map as any, + compact: RM.readonlyMap.compact as any, + separate: RM.readonlyMap.separate as any, + filter: RM.readonlyMap.filter as any, + filterMap: RM.readonlyMap.filterMap as any, + partition: RM.readonlyMap.partition as any, + partitionMap: RM.readonlyMap.partitionMap as any } const { filter, filterMap, map, partition, partitionMap, compact, separate } = pipeable(map_) diff --git a/src/Monoid.ts b/src/Monoid.ts index 693f6bd22..7b133a258 100644 --- a/src/Monoid.ts +++ b/src/Monoid.ts @@ -3,21 +3,22 @@ */ import { Bounded } from './Bounded' import { Endomorphism, identity } from './function' +import { ReadonlyRecord } from './ReadonlyRecord' import { fold as foldSemigroup, getDualSemigroup, getFunctionSemigroup, getJoinSemigroup, getMeetSemigroup, + getStructSemigroup, + getTupleSemigroup, Semigroup, semigroupAll, semigroupAny, semigroupProduct, semigroupString, semigroupSum, - semigroupVoid, - getStructSemigroup, - getTupleSemigroup + semigroupVoid } from './Semigroup' /** @@ -82,9 +83,9 @@ export const monoidVoid: Monoid = { /** * @since 2.0.0 */ -export function fold(M: Monoid): (as: Array) => A { - const foldSemigroupM = foldSemigroup(M) - return as => foldSemigroupM(M.empty, as) +export function fold(M: Monoid): (as: ReadonlyArray) => A { + const foldM = foldSemigroup(M) + return as => foldM(M.empty, as) } /** @@ -101,7 +102,7 @@ export function fold(M: Monoid): (as: Array) => A { * * @since 2.0.0 */ -export function getTupleMonoid>>( +export function getTupleMonoid>>( ...monoids: T ): Monoid<{ [K in keyof T]: T[K] extends Semigroup ? A : never }> { return { @@ -143,7 +144,7 @@ export function getEndomorphismMonoid(): Monoid> { /** * @since 2.0.0 */ -export function getStructMonoid( +export function getStructMonoid>( monoids: { [K in keyof O]: Monoid } ): Monoid { const empty: any = {} diff --git a/src/NonEmptyArray.ts b/src/NonEmptyArray.ts index 77d575d55..9a0766f4b 100644 --- a/src/NonEmptyArray.ts +++ b/src/NonEmptyArray.ts @@ -3,24 +3,26 @@ * * @since 2.0.0 */ -import { Monad1 } from './Monad' -import * as A from './Array' +import { Alt1 } from './Alt' import { Comonad1 } from './Comonad' -import { FunctorWithIndex1 } from './FunctorWithIndex' -import { TraversableWithIndex1 } from './TraversableWithIndex' -import { FoldableWithIndex1 } from './FoldableWithIndex' -import { Ord } from './Ord' -import { getMeetSemigroup, getJoinSemigroup, Semigroup, fold as sfold } from './Semigroup' -import { Option, some, none } from './Option' import { Eq } from './Eq' +import { FoldableWithIndex1 } from './FoldableWithIndex' import { Predicate, Refinement } from './function' -import { Show } from './Show' +import { FunctorWithIndex1 } from './FunctorWithIndex' +import { Monad1 } from './Monad' +import { Option } from './Option' +import { Ord } from './Ord' import { pipeable } from './pipeable' -import { Alt1 } from './Alt' +import * as RNEA from './ReadonlyNonEmptyArray' +import { Semigroup } from './Semigroup' +import { Show } from './Show' +import { TraversableWithIndex1 } from './TraversableWithIndex' + +/* tslint:disable:readonly-array */ declare module './HKT' { interface URItoKind { - NonEmptyArray: NonEmptyArray + readonly NonEmptyArray: NonEmptyArray } } @@ -34,12 +36,14 @@ export const URI = 'NonEmptyArray' */ export type URI = typeof URI +/* tslint:disable:readonly-keyword */ /** * @since 2.0.0 */ export interface NonEmptyArray extends Array { 0: A } +/* tslint:enable:readonly-keyword */ /** * Append an element to the front of an array, creating a new non empty array @@ -51,7 +55,7 @@ export interface NonEmptyArray extends Array { * * @since 2.0.0 */ -export const cons: (head: A, tail: Array) => NonEmptyArray = A.cons +export const cons: (head: A, tail: Array) => NonEmptyArray = RNEA.cons as any /** * Append an element to the end of an array, creating a new non empty array @@ -63,67 +67,51 @@ export const cons: (head: A, tail: Array) => NonEmptyArray = A.cons * * @since 2.0.0 */ -export const snoc: (init: Array, end: A) => NonEmptyArray = A.snoc +export const snoc: (init: Array, end: A) => NonEmptyArray = RNEA.snoc as any /** * Builds a `NonEmptyArray` from an `Array` returning `none` if `as` is an empty array * * @since 2.0.0 */ -export function fromArray(as: Array): Option> { - return A.isNonEmpty(as) ? some(as) : none -} +export const fromArray: (as: Array) => Option> = RNEA.fromArray as any /** * @since 2.0.0 */ -export const getShow: (S: Show) => Show> = A.getShow +export const getShow: (S: Show) => Show> = RNEA.getShow /** * @since 2.0.0 */ -export function head(nea: NonEmptyArray): A { - return nea[0] -} +export const head: (nea: NonEmptyArray) => A = RNEA.head /** * @since 2.0.0 */ -export function tail(nea: NonEmptyArray): Array { - return nea.slice(1) -} +export const tail: (nea: NonEmptyArray) => Array = RNEA.tail as any /** * @since 2.0.0 */ -export const reverse: (nea: NonEmptyArray) => NonEmptyArray = A.reverse as any +export const reverse: (nea: NonEmptyArray) => NonEmptyArray = RNEA.reverse as any /** * @since 2.0.0 */ -export function min(ord: Ord): (nea: NonEmptyArray) => A { - const S = getMeetSemigroup(ord) - return nea => nea.reduce(S.concat) -} +export const min: (ord: Ord) => (nea: NonEmptyArray) => A = RNEA.min /** * @since 2.0.0 */ -export function max(ord: Ord): (nea: NonEmptyArray) => A { - const S = getJoinSemigroup(ord) - return nea => nea.reduce(S.concat) -} +export const max: (ord: Ord) => (nea: NonEmptyArray) => A = RNEA.max /** * Builds a `Semigroup` instance for `NonEmptyArray` * * @since 2.0.0 */ -export function getSemigroup(): Semigroup> { - return { - concat: concat - } -} +export const getSemigroup: () => Semigroup> = RNEA.getSemigroup as any /** * @example @@ -136,7 +124,7 @@ export function getSemigroup(): Semigroup> { * * @since 2.0.0 */ -export const getEq: (E: Eq) => Eq> = A.getEq +export const getEq: (E: Eq) => Eq> = RNEA.getEq /** * Group equal, consecutive elements of an array into non empty arrays. @@ -160,27 +148,7 @@ export function group( (as: Array): Array> } export function group(E: Eq): (as: Array) => Array> { - return as => { - const len = as.length - if (len === 0) { - return A.empty - } - const r: Array> = [] - let head: A = as[0] - let nea: NonEmptyArray = [head] - for (let i = 1; i < len; i++) { - const x = as[i] - if (E.equals(x, head)) { - nea.push(x) - } else { - r.push(nea) - head = x - nea = [head] - } - } - r.push(nea) - return r - } + return RNEA.group(E) as any } /** @@ -194,11 +162,7 @@ export function group(E: Eq): (as: Array) => Array> { * * @since 2.0.0 */ -export function groupSort(O: Ord): (as: Array) => Array> { - const sortO = A.sort(O) - const groupO = group(O) - return as => groupO(sortO(as)) -} +export const groupSort: (O: Ord) => (as: Array) => Array> = RNEA.groupSort as any /** * Splits an array into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning @@ -214,27 +178,14 @@ export function groupSort(O: Ord): (as: Array) => Array(f: (a: A) => string): (as: Array) => Record> { - return as => { - const r: Record> = {} - for (const a of as) { - const k = f(a) - if (r.hasOwnProperty(k)) { - r[k].push(a) - } else { - r[k] = cons(a, []) - } - } - return r - } -} +export const groupBy: ( + f: (a: A) => string +) => (as: Array) => Record> = RNEA.groupBy as any /** * @since 2.0.0 */ -export function last(nea: NonEmptyArray): A { - return nea[nea.length - 1] -} +export const last: (nea: NonEmptyArray) => A = RNEA.last /** * Get all but the last element of a non empty array, creating a new array. @@ -247,42 +198,48 @@ export function last(nea: NonEmptyArray): A { * * @since 2.2.0 */ -export function init(nea: NonEmptyArray): Array { - return nea.slice(0, -1) -} +export const init: (nea: NonEmptyArray) => Array = RNEA.init as any /** * @since 2.0.0 */ -export function sort(O: Ord): (nea: NonEmptyArray) => NonEmptyArray { - return A.sort(O) as any -} +export const sort: (O: Ord) => (nea: NonEmptyArray) => NonEmptyArray = RNEA.sort as any /** * @since 2.0.0 */ -export function insertAt(i: number, a: A): (nea: NonEmptyArray) => Option> { - return A.insertAt(i, a) as any -} +export const insertAt: ( + i: number, + a: A +) => (nea: NonEmptyArray) => Option> = RNEA.insertAt as any /** * @since 2.0.0 */ -export function updateAt(i: number, a: A): (nea: NonEmptyArray) => Option> { - return A.updateAt(i, a) as any -} +export const updateAt: ( + i: number, + a: A +) => (nea: NonEmptyArray) => Option> = RNEA.updateAt as any /** * @since 2.0.0 */ -export function modifyAt(i: number, f: (a: A) => A): (nea: NonEmptyArray) => Option> { - return A.modifyAt(i, f) as any -} +export const modifyAt: ( + i: number, + f: (a: A) => A +) => (nea: NonEmptyArray) => Option> = RNEA.modifyAt as any /** * @since 2.0.0 */ -export const copy: (nea: NonEmptyArray) => NonEmptyArray = A.copy as any +export function copy(nea: NonEmptyArray): NonEmptyArray { + const l = nea.length + const as = Array(l) + for (let i = 0; i < l; i++) { + as[i] = nea[i] + } + return as as any +} /** * @since 2.0.0 @@ -292,22 +249,20 @@ export function filter( ): (nea: NonEmptyArray) => Option> export function filter(predicate: Predicate): (nea: NonEmptyArray) => Option> export function filter(predicate: Predicate): (nea: NonEmptyArray) => Option> { - return filterWithIndex((_, a) => predicate(a)) + return RNEA.filter(predicate) as any } /** * @since 2.0.0 */ -export function filterWithIndex( +export const filterWithIndex: ( predicate: (i: number, a: A) => boolean -): (nea: NonEmptyArray) => Option> { - return nea => fromArray(nea.filter((a, i) => predicate(i, a))) -} +) => (nea: NonEmptyArray) => Option> = RNEA.filterWithIndex as any /** * @since 2.0.0 */ -export const of: (a: A) => NonEmptyArray = A.of as any +export const of: (a: A) => NonEmptyArray = RNEA.of as any /** * @since 2.2.0 @@ -315,9 +270,14 @@ export const of: (a: A) => NonEmptyArray = A.of as any export function concat(fx: Array, fy: NonEmptyArray): NonEmptyArray export function concat(fx: NonEmptyArray, fy: Array): NonEmptyArray export function concat(fx: Array, fy: Array): Array { - return fx.concat(fy) + return RNEA.concat(fx as any, fy as any) as any } +/** + * @since 2.5.0 + */ +export const fold: (S: Semigroup) => (fa: NonEmptyArray) => A = RNEA.fold + /** * @since 2.0.0 */ @@ -328,23 +288,23 @@ export const nonEmptyArray: Monad1 & FoldableWithIndex1 & Alt1 = { URI, - map: A.array.map as any, - mapWithIndex: A.array.mapWithIndex as any, + map: RNEA.readonlyNonEmptyArray.map as any, + mapWithIndex: RNEA.readonlyNonEmptyArray.mapWithIndex as any, of, - ap: A.array.ap as any, - chain: A.array.chain as any, - extend: A.array.extend as any, + ap: RNEA.readonlyNonEmptyArray.ap as any, + chain: RNEA.readonlyNonEmptyArray.chain as any, + extend: RNEA.readonlyNonEmptyArray.extend as any, extract: head, - reduce: A.array.reduce, - foldMap: A.array.foldMap, - reduceRight: A.array.reduceRight, - traverse: A.array.traverse as any, - sequence: A.array.sequence as any, - reduceWithIndex: A.array.reduceWithIndex, - foldMapWithIndex: A.array.foldMapWithIndex, - reduceRightWithIndex: A.array.reduceRightWithIndex, - traverseWithIndex: A.array.traverseWithIndex as any, - alt: (fx, fy) => concat(fx, fy()) + reduce: RNEA.readonlyNonEmptyArray.reduce, + foldMap: RNEA.readonlyNonEmptyArray.foldMap, + reduceRight: RNEA.readonlyNonEmptyArray.reduceRight, + traverse: RNEA.readonlyNonEmptyArray.traverse as any, + sequence: RNEA.readonlyNonEmptyArray.sequence as any, + reduceWithIndex: RNEA.readonlyNonEmptyArray.reduceWithIndex, + foldMapWithIndex: RNEA.readonlyNonEmptyArray.foldMapWithIndex, + reduceRightWithIndex: RNEA.readonlyNonEmptyArray.reduceRightWithIndex, + traverseWithIndex: RNEA.readonlyNonEmptyArray.traverseWithIndex as any, + alt: RNEA.readonlyNonEmptyArray.alt as any } const { @@ -364,13 +324,9 @@ const { reduceWithIndex } = pipeable(nonEmptyArray) -const fold = (S: Semigroup) => (fa: NonEmptyArray) => sfold(S)(head(fa), tail(fa)) - -const foldMapWithIndex = (S: Semigroup) => (f: (i: number, a: A) => S) => (fa: NonEmptyArray) => - fa.slice(1).reduce((s, a, i) => S.concat(s, f(i + 1, a)), f(0, fa[0])) +const foldMapWithIndex = RNEA.foldMapWithIndex -const foldMap = (S: Semigroup) => (f: (a: A) => S) => (fa: NonEmptyArray) => - fa.slice(1).reduce((s, a) => S.concat(s, f(a)), f(fa[0])) +const foldMap = RNEA.foldMap export { /** @@ -405,10 +361,6 @@ export { * @since 2.0.0 */ flatten, - /** - * @since 2.5.0 - */ - fold, /** * @since 2.0.0 */ diff --git a/src/Option.ts b/src/Option.ts index aaf31546f..77c1e0603 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -50,7 +50,7 @@ import { MonadThrow1 } from './MonadThrow' declare module './HKT' { interface URItoKind { - Option: Option + readonly Option: Option } } diff --git a/src/Ord.ts b/src/Ord.ts index a8ab72927..63e60204d 100644 --- a/src/Ord.ts +++ b/src/Ord.ts @@ -18,7 +18,7 @@ import { Monoid } from './Monoid' declare module './HKT' { interface URItoKind { - Ord: Ord + readonly Ord: Ord } } @@ -259,7 +259,7 @@ export function getMonoid(): Monoid> { * * @since 2.0.0 */ -export function getTupleOrd>>( +export function getTupleOrd>>( ...ords: T ): Ord<{ [K in keyof T]: T[K] extends Ord ? A : never }> { const len = ords.length diff --git a/src/Reader.ts b/src/Reader.ts index ce1f4554a..dbdffd304 100644 --- a/src/Reader.ts +++ b/src/Reader.ts @@ -18,7 +18,7 @@ const T = getReaderM(identity) declare module './HKT' { interface URItoKind2 { - Reader: Reader + readonly Reader: Reader } } diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts index 1cfcf6477..accaeebe2 100644 --- a/src/ReaderEither.ts +++ b/src/ReaderEither.ts @@ -19,7 +19,7 @@ const T = getEitherM(reader) declare module './HKT' { interface URItoKind3 { - ReaderEither: ReaderEither + readonly ReaderEither: ReaderEither } } @@ -154,7 +154,7 @@ export function getReaderValidation( /** * @since 2.4.0 */ -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => ReaderEither { return (...a) => fromEither(f(...a)) diff --git a/src/ReaderTask.ts b/src/ReaderTask.ts index 1db764265..9e7391676 100644 --- a/src/ReaderTask.ts +++ b/src/ReaderTask.ts @@ -17,7 +17,7 @@ const T = getReaderM(TA.task) declare module './HKT' { interface URItoKind2 { - ReaderTask: ReaderTask + readonly ReaderTask: ReaderTask } } @@ -104,7 +104,7 @@ export function local(f: (f: Q) => R): (ma: ReaderTask) => Reader /** * @since 2.4.0 */ -export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => ReaderTask { +export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => ReaderTask { return (...a) => fromIO(f(...a)) } @@ -118,7 +118,9 @@ export function chainIOK(f: (a: A) => IO): (ma: ReaderTask) => /** * @since 2.4.0 */ -export function fromTaskK, B>(f: (...a: A) => Task): (...a: A) => ReaderTask { +export function fromTaskK, B>( + f: (...a: A) => Task +): (...a: A) => ReaderTask { return (...a) => fromTask(f(...a)) } diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts index b038627ef..4d47160d7 100644 --- a/src/ReaderTaskEither.ts +++ b/src/ReaderTaskEither.ts @@ -26,7 +26,7 @@ const T = getReaderM(TE.taskEither) declare module './HKT' { interface URItoKind3 { - ReaderTaskEither: ReaderTaskEither + readonly ReaderTaskEither: ReaderTaskEither } } @@ -251,7 +251,7 @@ export function getReaderTaskValidation( /** * @since 2.4.0 */ -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => ReaderTaskEither { return (...a) => fromEither(f(...a)) @@ -269,7 +269,7 @@ export function chainEitherK( /** * @since 2.4.0 */ -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => ReaderTaskEither { return (...a) => fromIOEither(f(...a)) @@ -287,7 +287,7 @@ export function chainIOEitherK( /** * @since 2.4.0 */ -export function fromTaskEitherK, B>( +export function fromTaskEitherK, B>( f: (...a: A) => TaskEither ): (...a: A) => ReaderTaskEither { return (...a) => fromTaskEither(f(...a)) diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts new file mode 100644 index 000000000..b2dc65db7 --- /dev/null +++ b/src/ReadonlyArray.ts @@ -0,0 +1,1678 @@ +/** + * @since 2.5.0 + */ +import { Alternative1 } from './Alternative' +import { Applicative } from './Applicative' +import { Compactable1, Separated } from './Compactable' +import { Either } from './Either' +import { Eq } from './Eq' +import { Extend1 } from './Extend' +import { FilterableWithIndex1 } from './FilterableWithIndex' +import { Foldable1 } from './Foldable' +import { FoldableWithIndex1 } from './FoldableWithIndex' +import { Predicate, Refinement, identity } from './function' +import { FunctorWithIndex1 } from './FunctorWithIndex' +import { HKT } from './HKT' +import { Monad1 } from './Monad' +import { Monoid } from './Monoid' +import { ReadonlyNonEmptyArray } from './ReadonlyNonEmptyArray' +import { isSome, none, Option, some } from './Option' +import { fromCompare, getMonoid as getOrdMonoid, Ord, ordNumber } from './Ord' +import { pipeable } from './pipeable' +import { Show } from './Show' +import { TraversableWithIndex1 } from './TraversableWithIndex' +import { Unfoldable1 } from './Unfoldable' +import { Witherable1 } from './Witherable' + +declare module './HKT' { + interface URItoKind { + readonly ReadonlyArray: ReadonlyArray + } +} + +/** + * @since 2.5.0 + */ +export const URI = 'ReadonlyArray' + +/** + * @since 2.5.0 + */ +export type URI = typeof URI + +/** + * @since 2.5.0 + */ +// tslint:disable-next-line: readonly-array +export function fromArray(as: Array): ReadonlyArray { + const l = as.length + if (l === 0) { + return empty + } + const ras = Array(l) + for (let i = 0; i < l; i++) { + ras[i] = as[i] + } + return ras +} + +/** + * @since 2.5.0 + */ +// tslint:disable-next-line: readonly-array +export function toArray(ras: ReadonlyArray): Array { + const l = ras.length + const as = Array(l) + for (let i = 0; i < l; i++) { + as[i] = ras[i] + } + return as +} + +/** + * @since 2.5.0 + */ +export function getShow(S: Show): Show> { + return { + show: as => `[${as.map(S.show).join(', ')}]` + } +} + +const concat = (x: ReadonlyArray, y: ReadonlyArray): ReadonlyArray => { + const lenx = x.length + if (lenx === 0) { + return y + } + const leny = y.length + if (leny === 0) { + return x + } + const r = Array(lenx + leny) + for (let i = 0; i < lenx; i++) { + r[i] = x[i] + } + for (let i = 0; i < leny; i++) { + r[i + lenx] = y[i] + } + return r +} + +/** + * Returns a `Monoid` for `ReadonlyArray` + * + * @example + * import { getMonoid } from 'fp-ts/lib/ReadonlyArray' + * + * const M = getMonoid() + * assert.deepStrictEqual(M.concat([1, 2], [3, 4]), [1, 2, 3, 4]) + * + * @since 2.5.0 + */ +export function getMonoid(): Monoid> { + return { + concat, + empty + } +} + +/** + * Derives an `Eq` over the `ReadonlyArray` of a given element type from the `Eq` of that type. The derived `Eq` defines two + * arrays as equal if all elements of both arrays are compared equal pairwise with the given `E`. In case of arrays of + * different lengths, the result is non equality. + * + * @example + * import { eqString } from 'fp-ts/lib/Eq' + * import { getEq } from 'fp-ts/lib/ReadonlyArray' + * + * const E = getEq(eqString) + * assert.strictEqual(E.equals(['a', 'b'], ['a', 'b']), true) + * assert.strictEqual(E.equals(['a'], []), false) + * + * @since 2.5.0 + */ +export function getEq(E: Eq): Eq> { + return { + equals: (xs, ys) => xs === ys || (xs.length === ys.length && xs.every((x, i) => E.equals(x, ys[i]))) + } +} + +/** + * Derives an `Ord` over the `ReadonlyArray` of a given element type from the `Ord` of that type. The ordering between two such + * arrays is equal to: the first non equal comparison of each arrays elements taken pairwise in increasing order, in + * case of equality over all the pairwise elements; the longest array is considered the greatest, if both arrays have + * the same length, the result is equality. + * + * @example + * import { getOrd } from 'fp-ts/lib/ReadonlyArray' + * import { ordString } from 'fp-ts/lib/Ord' + * + * const O = getOrd(ordString) + * assert.strictEqual(O.compare(['b'], ['a']), 1) + * assert.strictEqual(O.compare(['a'], ['a']), 0) + * assert.strictEqual(O.compare(['a'], ['b']), -1) + * + * + * @since 2.5.0 + */ +export function getOrd(O: Ord): Ord> { + return fromCompare((a, b) => { + const aLen = a.length + const bLen = b.length + const len = Math.min(aLen, bLen) + for (let i = 0; i < len; i++) { + const ordering = O.compare(a[i], b[i]) + if (ordering !== 0) { + return ordering + } + } + return ordNumber.compare(aLen, bLen) + }) +} + +/** + * An empty array + * + * @since 2.5.0 + */ +export const empty: ReadonlyArray = [] + +/** + * Return a list of length `n` with element `i` initialized with `f(i)` + * + * @example + * import { makeBy } from 'fp-ts/lib/ReadonlyArray' + * + * const double = (n: number): number => n * 2 + * assert.deepStrictEqual(makeBy(5, double), [0, 2, 4, 6, 8]) + * + * @since 2.5.0 + */ +export function makeBy(n: number, f: (i: number) => A): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const r: Array = [] + for (let i = 0; i < n; i++) { + r.push(f(i)) + } + return r +} + +/** + * Create an array containing a range of integers, including both endpoints + * + * @example + * import { range } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(range(1, 5), [1, 2, 3, 4, 5]) + * + * @since 2.5.0 + */ +export function range(start: number, end: number): ReadonlyArray { + return makeBy(end - start + 1, i => start + i) +} + +/** + * Create an array containing a value repeated the specified number of times + * + * @example + * import { replicate } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(replicate(3, 'a'), ['a', 'a', 'a']) + * + * @since 2.5.0 + */ +export function replicate(n: number, a: A): ReadonlyArray { + return makeBy(n, () => a) +} + +/** + * Removes one level of nesting + * + * @example + * import { flatten } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(flatten([[1], [2], [3]]), [1, 2, 3]) + * + * @since 2.5.0 + */ +export function flatten(mma: ReadonlyArray>): ReadonlyArray { + let rLen = 0 + const len = mma.length + for (let i = 0; i < len; i++) { + rLen += mma[i].length + } + const r = Array(rLen) + let start = 0 + for (let i = 0; i < len; i++) { + const arr = mma[i] + const l = arr.length + for (let j = 0; j < l; j++) { + r[j + start] = arr[j] + } + start += l + } + return r +} + +/** + * Break an array into its first element and remaining elements + * + * @example + * import { foldLeft } from 'fp-ts/lib/ReadonlyArray' + * + * const len: (as: ReadonlyArray) => number = foldLeft(() => 0, (_, tail) => 1 + len(tail)) + * assert.strictEqual(len([1, 2, 3]), 3) + * + * @since 2.5.0 + */ +export function foldLeft( + onNil: () => B, + onCons: (head: A, tail: ReadonlyArray) => B +): (as: ReadonlyArray) => B { + return as => (isEmpty(as) ? onNil() : onCons(as[0], as.slice(1))) +} + +/** + * Break an array into its initial elements and the last element + * + * @since 2.5.0 + */ +export function foldRight( + onNil: () => B, + onCons: (init: ReadonlyArray, last: A) => B +): (as: ReadonlyArray) => B { + return as => (isEmpty(as) ? onNil() : onCons(as.slice(0, as.length - 1), as[as.length - 1])) +} + +/** + * Same as `reduce` but it carries over the intermediate steps + * + * ```ts + * import { scanLeft } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(scanLeft(10, (b, a: number) => b - a)([1, 2, 3]), [10, 9, 7, 4]) + * ``` + * + * @since 2.5.0 + */ +export function scanLeft(b: B, f: (b: B, a: A) => B): (as: ReadonlyArray) => ReadonlyArray { + return as => { + const l = as.length + // tslint:disable-next-line: readonly-array + const r: Array = new Array(l + 1) + r[0] = b + for (let i = 0; i < l; i++) { + r[i + 1] = f(r[i], as[i]) + } + return r + } +} + +/** + * Fold an array from the right, keeping all intermediate results instead of only the final result + * + * @example + * import { scanRight } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(scanRight(10, (a: number, b) => b - a)([1, 2, 3]), [4, 5, 7, 10]) + * + * @since 2.5.0 + */ +export function scanRight(b: B, f: (a: A, b: B) => B): (as: ReadonlyArray) => ReadonlyArray { + return as => { + const l = as.length + // tslint:disable-next-line: readonly-array + const r: Array = new Array(l + 1) + r[l] = b + for (let i = l - 1; i >= 0; i--) { + r[i] = f(as[i], r[i + 1]) + } + return r + } +} + +/** + * Test whether an array is empty + * + * @example + * import { isEmpty } from 'fp-ts/lib/ReadonlyArray' + * + * assert.strictEqual(isEmpty([]), true) + * + * @since 2.5.0 + */ +export function isEmpty(as: ReadonlyArray): boolean { + return as.length === 0 +} + +/** + * Test whether an array is non empty narrowing down the type to `NonEmptyReadonlyArray` + * + * @since 2.5.0 + */ +export function isNonEmpty(as: ReadonlyArray): as is ReadonlyNonEmptyArray { + return as.length > 0 +} + +/** + * Test whether an array contains a particular index + * + * @since 2.5.0 + */ +export function isOutOfBound(i: number, as: ReadonlyArray): boolean { + return i < 0 || i >= as.length +} + +/** + * This function provides a safe way to read a value at a particular index from an array + * + * @example + * import { lookup } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(lookup(1, [1, 2, 3]), some(2)) + * assert.deepStrictEqual(lookup(3, [1, 2, 3]), none) + * + * @since 2.5.0 + */ +export function lookup(i: number, as: ReadonlyArray): Option { + return isOutOfBound(i, as) ? none : some(as[i]) +} + +/** + * Attaches an element to the front of an array, creating a new non empty array + * + * @example + * import { cons } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(cons(0, [1, 2, 3]), [0, 1, 2, 3]) + * + * @since 2.5.0 + */ +export function cons(head: A, tail: ReadonlyArray): ReadonlyNonEmptyArray { + const len = tail.length + const r = Array(len + 1) + for (let i = 0; i < len; i++) { + r[i + 1] = tail[i] + } + r[0] = head + return (r as unknown) as ReadonlyNonEmptyArray +} + +/** + * Append an element to the end of an array, creating a new non empty array + * + * @example + * import { snoc } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4]) + * + * @since 2.5.0 + */ +export function snoc(init: ReadonlyArray, end: A): ReadonlyNonEmptyArray { + const len = init.length + const r = Array(len + 1) + for (let i = 0; i < len; i++) { + r[i] = init[i] + } + r[len] = end + return (r as unknown) as ReadonlyNonEmptyArray +} + +/** + * Get the first element in an array, or `None` if the array is empty + * + * @example + * import { head } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(head([1, 2, 3]), some(1)) + * assert.deepStrictEqual(head([]), none) + * + * @since 2.5.0 + */ +export function head(as: ReadonlyArray): Option { + return isEmpty(as) ? none : some(as[0]) +} + +/** + * Get the last element in an array, or `None` if the array is empty + * + * @example + * import { last } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(last([1, 2, 3]), some(3)) + * assert.deepStrictEqual(last([]), none) + * + * @since 2.5.0 + */ +export function last(as: ReadonlyArray): Option { + return lookup(as.length - 1, as) +} + +/** + * Get all but the first element of an array, creating a new array, or `None` if the array is empty + * + * @example + * import { tail } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(tail([1, 2, 3]), some([2, 3])) + * assert.deepStrictEqual(tail([]), none) + * + * @since 2.5.0 + */ +export function tail(as: ReadonlyArray): Option> { + return isEmpty(as) ? none : some(as.slice(1)) +} + +/** + * Get all but the last element of an array, creating a new array, or `None` if the array is empty + * + * @example + * import { init } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(init([1, 2, 3]), some([1, 2])) + * assert.deepStrictEqual(init([]), none) + * + * @since 2.5.0 + */ +export function init(as: ReadonlyArray): Option> { + const len = as.length + return len === 0 ? none : some(as.slice(0, len - 1)) +} + +/** + * Keep only a number of elements from the start of an array, creating a new array. + * `n` must be a natural number + * + * @example + * import { takeLeft } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(takeLeft(2)([1, 2, 3]), [1, 2]) + * + * @since 2.5.0 + */ +export function takeLeft(n: number): (as: ReadonlyArray) => ReadonlyArray { + return as => as.slice(0, n) +} + +/** + * Keep only a number of elements from the end of an array, creating a new array. + * `n` must be a natural number + * + * @example + * import { takeRight } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(takeRight(2)([1, 2, 3, 4, 5]), [4, 5]) + * + * @since 2.5.0 + */ +export function takeRight(n: number): (as: ReadonlyArray) => ReadonlyArray { + return as => (n === 0 ? empty : as.slice(-n)) +} + +/** + * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new array + * + * @example + * import { takeLeftWhile } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(takeLeftWhile((n: number) => n % 2 === 0)([2, 4, 3, 6]), [2, 4]) + * + * @since 2.5.0 + */ +export function takeLeftWhile(refinement: Refinement): (as: ReadonlyArray) => ReadonlyArray +export function takeLeftWhile(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray +export function takeLeftWhile(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray { + return as => { + const i = spanIndexUncurry(as, predicate) + const init = Array(i) + for (let j = 0; j < i; j++) { + init[j] = as[j] + } + return init + } +} + +const spanIndexUncurry = (as: ReadonlyArray, predicate: Predicate): number => { + const l = as.length + let i = 0 + for (; i < l; i++) { + if (!predicate(as[i])) { + break + } + } + return i +} + +/** + * @since 2.5.0 + */ +export interface Spanned { + readonly init: ReadonlyArray + readonly rest: ReadonlyArray +} + +/** + * Split an array into two parts: + * 1. the longest initial subarray for which all elements satisfy the specified predicate + * 2. the remaining elements + * + * @example + * import { spanLeft } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(spanLeft((n: number) => n % 2 === 1)([1, 3, 2, 4, 5]), { init: [1, 3], rest: [2, 4, 5] }) + * + * @since 2.5.0 + */ +export function spanLeft(refinement: Refinement): (as: ReadonlyArray) => Spanned +export function spanLeft(predicate: Predicate): (as: ReadonlyArray) => Spanned +export function spanLeft(predicate: Predicate): (as: ReadonlyArray) => Spanned { + return as => { + const i = spanIndexUncurry(as, predicate) + const init = Array(i) + for (let j = 0; j < i; j++) { + init[j] = as[j] + } + const l = as.length + const rest = Array(l - i) + for (let j = i; j < l; j++) { + rest[j - i] = as[j] + } + return { init, rest } + } +} + +/** + * Drop a number of elements from the start of an array, creating a new array + * + * @example + * import { dropLeft } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(dropLeft(2)([1, 2, 3]), [3]) + * + * @since 2.5.0 + */ +export function dropLeft(n: number): (as: ReadonlyArray) => ReadonlyArray { + return as => as.slice(n, as.length) +} + +/** + * Drop a number of elements from the end of an array, creating a new array + * + * @example + * import { dropRight } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(dropRight(2)([1, 2, 3, 4, 5]), [1, 2, 3]) + * + * @since 2.5.0 + */ +export function dropRight(n: number): (as: ReadonlyArray) => ReadonlyArray { + return as => as.slice(0, as.length - n) +} + +/** + * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new array + * + * @example + * import { dropLeftWhile } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(dropLeftWhile((n: number) => n % 2 === 1)([1, 3, 2, 4, 5]), [2, 4, 5]) + * + * @since 2.5.0 + */ +export function dropLeftWhile(predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray { + return as => { + const i = spanIndexUncurry(as, predicate) + const l = as.length + const rest = Array(l - i) + for (let j = i; j < l; j++) { + rest[j - i] = as[j] + } + return rest + } +} + +/** + * Find the first index for which a predicate holds + * + * @example + * import { findIndex } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(findIndex((n: number) => n === 2)([1, 2, 3]), some(1)) + * assert.deepStrictEqual(findIndex((n: number) => n === 2)([]), none) + * + * @since 2.5.0 + */ +export function findIndex(predicate: Predicate): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = 0; i < len; i++) { + if (predicate(as[i])) { + return some(i) + } + } + return none + } +} + +/** + * Find the first element which satisfies a predicate (or a refinement) function + * + * @example + * import { findFirst } from 'fp-ts/lib/ReadonlyArray' + * import { some } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(findFirst((x: { a: number, b: number }) => x.a === 1)([{ a: 1, b: 1 }, { a: 1, b: 2 }]), some({ a: 1, b: 1 })) + * + * @since 2.5.0 + */ +export function findFirst(refinement: Refinement): (as: ReadonlyArray) => Option +export function findFirst(predicate: Predicate): (as: ReadonlyArray) => Option +export function findFirst(predicate: Predicate): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = 0; i < len; i++) { + if (predicate(as[i])) { + return some(as[i]) + } + } + return none + } +} + +/** + * Find the first element returned by an option based selector function + * + * @example + * import { findFirstMap } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * interface Person { + * name: string + * age?: number + * } + * + * const persons: ReadonlyArray = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }] + * + * // returns the name of the first person that has an age + * assert.deepStrictEqual(findFirstMap((p: Person) => (p.age === undefined ? none : some(p.name)))(persons), some('Mary')) + * + * @since 2.5.0 + */ +export function findFirstMap(f: (a: A) => Option): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = 0; i < len; i++) { + const v = f(as[i]) + if (isSome(v)) { + return v + } + } + return none + } +} + +/** + * Find the last element which satisfies a predicate function + * + * @example + * import { findLast } from 'fp-ts/lib/ReadonlyArray' + * import { some } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(findLast((x: { a: number, b: number }) => x.a === 1)([{ a: 1, b: 1 }, { a: 1, b: 2 }]), some({ a: 1, b: 2 })) + * + * @since 2.5.0 + */ +export function findLast(refinement: Refinement): (as: ReadonlyArray) => Option +export function findLast(predicate: Predicate): (as: ReadonlyArray) => Option +export function findLast(predicate: Predicate): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = len - 1; i >= 0; i--) { + if (predicate(as[i])) { + return some(as[i]) + } + } + return none + } +} + +/** + * Find the last element returned by an option based selector function + * + * @example + * import { findLastMap } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * interface Person { + * name: string + * age?: number + * } + * + * const persons: ReadonlyArray = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }] + * + * // returns the name of the last person that has an age + * assert.deepStrictEqual(findLastMap((p: Person) => (p.age === undefined ? none : some(p.name)))(persons), some('Joey')) + * + * @since 2.5.0 + */ +export function findLastMap(f: (a: A) => Option): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = len - 1; i >= 0; i--) { + const v = f(as[i]) + if (isSome(v)) { + return v + } + } + return none + } +} + +/** + * Returns the index of the last element of the list which matches the predicate + * + * @example + * import { findLastIndex } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * interface X { + * a: number + * b: number + * } + * const xs: ReadonlyArray = [{ a: 1, b: 0 }, { a: 1, b: 1 }] + * assert.deepStrictEqual(findLastIndex((x: { a: number }) => x.a === 1)(xs), some(1)) + * assert.deepStrictEqual(findLastIndex((x: { a: number }) => x.a === 4)(xs), none) + * + * + * @since 2.5.0 + */ +export function findLastIndex(predicate: Predicate): (as: ReadonlyArray) => Option { + return as => { + const len = as.length + for (let i = len - 1; i >= 0; i--) { + if (predicate(as[i])) { + return some(i) + } + } + return none + } +} + +/** + * @since 2.5.0 + */ +export function unsafeInsertAt(i: number, a: A, as: ReadonlyArray): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const xs = [...as] + xs.splice(i, 0, a) + return xs +} + +/** + * Insert an element at the specified index, creating a new array, or returning `None` if the index is out of bounds + * + * @example + * import { insertAt } from 'fp-ts/lib/ReadonlyArray' + * import { some } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(insertAt(2, 5)([1, 2, 3, 4]), some([1, 2, 5, 3, 4])) + * + * @since 2.5.0 + */ +export function insertAt(i: number, a: A): (as: ReadonlyArray) => Option> { + return as => (i < 0 || i > as.length ? none : some(unsafeInsertAt(i, a, as))) +} + +/** + * @since 2.5.0 + */ +export function unsafeUpdateAt(i: number, a: A, as: ReadonlyArray): ReadonlyArray { + if (as[i] === a) { + return as + } else { + // tslint:disable-next-line: readonly-array + const xs = [...as] + xs[i] = a + return xs + } +} + +/** + * Change the element at the specified index, creating a new array, or returning `None` if the index is out of bounds + * + * @example + * import { updateAt } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(updateAt(1, 1)([1, 2, 3]), some([1, 1, 3])) + * assert.deepStrictEqual(updateAt(1, 1)([]), none) + * + * @since 2.5.0 + */ +export function updateAt(i: number, a: A): (as: ReadonlyArray) => Option> { + return as => (isOutOfBound(i, as) ? none : some(unsafeUpdateAt(i, a, as))) +} + +/** + * @since 2.5.0 + */ +export function unsafeDeleteAt(i: number, as: ReadonlyArray): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const xs = [...as] + xs.splice(i, 1) + return xs +} + +/** + * Delete the element at the specified index, creating a new array, or returning `None` if the index is out of bounds + * + * @example + * import { deleteAt } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * assert.deepStrictEqual(deleteAt(0)([1, 2, 3]), some([2, 3])) + * assert.deepStrictEqual(deleteAt(1)([]), none) + * + * @since 2.5.0 + */ +export function deleteAt(i: number): (as: ReadonlyArray) => Option> { + return as => (isOutOfBound(i, as) ? none : some(unsafeDeleteAt(i, as))) +} + +/** + * Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out + * of bounds + * + * @example + * import { modifyAt } from 'fp-ts/lib/ReadonlyArray' + * import { some, none } from 'fp-ts/lib/Option' + * + * const double = (x: number): number => x * 2 + * assert.deepStrictEqual(modifyAt(1, double)([1, 2, 3]), some([1, 4, 3])) + * assert.deepStrictEqual(modifyAt(1, double)([]), none) + * + * @since 2.5.0 + */ +export function modifyAt(i: number, f: (a: A) => A): (as: ReadonlyArray) => Option> { + return as => (isOutOfBound(i, as) ? none : some(unsafeUpdateAt(i, f(as[i]), as))) +} + +/** + * Reverse an array, creating a new array + * + * @example + * import { reverse } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(reverse([1, 2, 3]), [3, 2, 1]) + * + * @since 2.5.0 + */ +export function reverse(as: ReadonlyArray): ReadonlyArray { + return [...as].reverse() +} + +/** + * Extracts from an array of `Either` all the `Right` elements. All the `Right` elements are extracted in order + * + * @example + * import { rights } from 'fp-ts/lib/ReadonlyArray' + * import { right, left } from 'fp-ts/lib/Either' + * + * assert.deepStrictEqual(rights([right(1), left('foo'), right(2)]), [1, 2]) + * + * @since 2.5.0 + */ +export function rights(as: ReadonlyArray>): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const r: Array = [] + const len = as.length + for (let i = 0; i < len; i++) { + const a = as[i] + if (a._tag === 'Right') { + r.push(a.right) + } + } + return r +} + +/** + * Extracts from an array of `Either` all the `Left` elements. All the `Left` elements are extracted in order + * + * @example + * import { lefts } from 'fp-ts/lib/ReadonlyArray' + * import { left, right } from 'fp-ts/lib/Either' + * + * assert.deepStrictEqual(lefts([right(1), left('foo'), right(2)]), ['foo']) + * + * @since 2.5.0 + */ +export function lefts(as: ReadonlyArray>): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const r: Array = [] + const len = as.length + for (let i = 0; i < len; i++) { + const a = as[i] + if (a._tag === 'Left') { + r.push(a.left) + } + } + return r +} + +/** + * Sort the elements of an array in increasing order, creating a new array + * + * @example + * import { sort } from 'fp-ts/lib/ReadonlyArray' + * import { ordNumber } from 'fp-ts/lib/Ord' + * + * assert.deepStrictEqual(sort(ordNumber)([3, 2, 1]), [1, 2, 3]) + * + * @since 2.5.0 + */ +export function sort(O: Ord): (as: ReadonlyArray) => ReadonlyArray { + return as => [...as].sort(O.compare) +} + +/** + * Apply a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one + * input array is short, excess elements of the longer array are discarded. + * + * @example + * import { zipWith } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(zipWith([1, 2, 3], ['a', 'b', 'c', 'd'], (n, s) => s + n), ['a1', 'b2', 'c3']) + * + * @since 2.5.0 + */ +export function zipWith(fa: ReadonlyArray, fb: ReadonlyArray, f: (a: A, b: B) => C): ReadonlyArray { + // tslint:disable-next-line: readonly-array + const fc: Array = [] + const len = Math.min(fa.length, fb.length) + for (let i = 0; i < len; i++) { + fc[i] = f(fa[i], fb[i]) + } + return fc +} + +/** + * Takes two arrays and returns an array of corresponding pairs. If one input array is short, excess elements of the + * longer array are discarded + * + * @example + * import { zip } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(zip([1, 2, 3], ['a', 'b', 'c', 'd']), [[1, 'a'], [2, 'b'], [3, 'c']]) + * + * @since 2.5.0 + */ +export function zip(fa: ReadonlyArray, fb: ReadonlyArray): ReadonlyArray { + return zipWith(fa, fb, (a, b) => [a, b]) +} + +/** + * The function is reverse of `zip`. Takes an array of pairs and return two corresponding arrays + * + * @example + * import { unzip } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(unzip([[1, 'a'], [2, 'b'], [3, 'c']]), [[1, 2, 3], ['a', 'b', 'c']]) + * + * @since 2.5.0 + */ +export function unzip(as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] { + // tslint:disable-next-line: readonly-array + const fa: Array = [] + // tslint:disable-next-line: readonly-array + const fb: Array = [] + + for (let i = 0; i < as.length; i++) { + fa[i] = as[i][0] + fb[i] = as[i][1] + } + + return [fa, fb] +} + +/** + * Rotate an array to the right by `n` steps + * + * @example + * import { rotate } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(rotate(2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) + * + * @since 2.5.0 + */ +export function rotate(n: number): (as: ReadonlyArray) => ReadonlyArray { + return as => { + const len = as.length + if (n === 0 || len <= 1 || len === Math.abs(n)) { + return as + } else if (n < 0) { + return rotate(len + n)(as) + } else { + return as.slice(-n).concat(as.slice(0, len - n)) + } + } +} + +/** + * Test if a value is a member of an array. Takes a `Eq` as a single + * argument which returns the function to use to search for a value of type `A` in + * an array of type `ReadonlyArray`. + * + * @example + * import { elem } from 'fp-ts/lib/ReadonlyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.strictEqual(elem(eqNumber)(1, [1, 2, 3]), true) + * assert.strictEqual(elem(eqNumber)(4, [1, 2, 3]), false) + * + * @since 2.5.0 + */ +export function elem(E: Eq): (a: A, as: ReadonlyArray) => boolean { + return (a, as) => { + const predicate = (element: A) => E.equals(element, a) + let i = 0 + const len = as.length + for (; i < len; i++) { + if (predicate(as[i])) { + return true + } + } + return false + } +} + +/** + * Remove duplicates from an array, keeping the first occurrence of an element. + * + * @example + * import { uniq } from 'fp-ts/lib/ReadonlyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.deepStrictEqual(uniq(eqNumber)([1, 2, 1]), [1, 2]) + * + * @since 2.5.0 + */ +export function uniq(E: Eq): (as: ReadonlyArray) => ReadonlyArray { + const elemS = elem(E) + return as => { + // tslint:disable-next-line: readonly-array + const r: Array = [] + const len = as.length + let i = 0 + for (; i < len; i++) { + const a = as[i] + if (!elemS(a, r)) { + r.push(a) + } + } + return len === r.length ? as : r + } +} + +/** + * Sort the elements of an array in increasing order, where elements are compared using first `ords[0]`, then `ords[1]`, + * etc... + * + * @example + * import { sortBy } from 'fp-ts/lib/ReadonlyArray' + * import { ord, ordString, ordNumber } from 'fp-ts/lib/Ord' + * + * interface Person { + * name: string + * age: number + * } + * const byName = ord.contramap(ordString, (p: Person) => p.name) + * const byAge = ord.contramap(ordNumber, (p: Person) => p.age) + * + * const sortByNameByAge = sortBy([byName, byAge]) + * + * const persons = [{ name: 'a', age: 1 }, { name: 'b', age: 3 }, { name: 'c', age: 2 }, { name: 'b', age: 2 }] + * assert.deepStrictEqual(sortByNameByAge(persons), [ + * { name: 'a', age: 1 }, + * { name: 'b', age: 2 }, + * { name: 'b', age: 3 }, + * { name: 'c', age: 2 } + * ]) + * + * @since 2.5.0 + */ +export function sortBy(ords: ReadonlyArray>): (as: ReadonlyArray) => ReadonlyArray { + const M = getOrdMonoid() + return sort(ords.reduce(M.concat, M.empty)) +} + +/** + * A useful recursion pattern for processing an array to produce a new array, often used for "chopping" up the input + * array. Typically chop is called with some function that will consume an initial prefix of the array and produce a + * value and the rest of the array. + * + * @example + * import { Eq, eqNumber } from 'fp-ts/lib/Eq' + * import { chop, spanLeft } from 'fp-ts/lib/ReadonlyArray' + * + * const group = (S: Eq): ((as: ReadonlyArray) => ReadonlyArray>) => { + * return chop(as => { + * const { init, rest } = spanLeft((a: A) => S.equals(a, as[0]))(as) + * return [init, rest] + * }) + * } + * assert.deepStrictEqual(group(eqNumber)([1, 1, 2, 3, 3, 4]), [[1, 1], [2], [3, 3], [4]]) + * + * @since 2.5.0 + */ +export function chop( + f: (as: ReadonlyNonEmptyArray) => readonly [B, ReadonlyArray] +): (as: ReadonlyArray) => ReadonlyArray { + return as => { + // tslint:disable-next-line: readonly-array + const result: Array = [] + let cs: ReadonlyArray = as + while (isNonEmpty(cs)) { + const [b, c] = f(cs) + result.push(b) + cs = c + } + return result + } +} + +/** + * Splits an array into two pieces, the first piece has `n` elements. + * + * @example + * import { splitAt } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(splitAt(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4, 5]]) + * + * @since 2.5.0 + */ +export function splitAt(n: number): (as: ReadonlyArray) => readonly [ReadonlyArray, ReadonlyArray] { + return as => [as.slice(0, n), as.slice(n)] +} + +/** + * Splits an array into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of + * the array. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive + * definition of `chunksOf`; it satisfies the property that + * + * ```ts + * chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys))) + * ``` + * + * whenever `n` evenly divides the length of `xs`. + * + * @example + * import { chunksOf } from 'fp-ts/lib/ReadonlyArray' + * + * assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]]) + * + * + * @since 2.5.0 + */ +export function chunksOf(n: number): (as: ReadonlyArray) => ReadonlyArray> { + return as => (as.length === 0 ? empty : isOutOfBound(n - 1, as) ? [as] : chop(splitAt(n))(as)) +} + +/** + * Array comprehension + * + * ``` + * [ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ] + * ``` + * + * @example + * import { comprehension } from 'fp-ts/lib/ReadonlyArray' + * import { tuple } from 'fp-ts/lib/function' + * + * assert.deepStrictEqual(comprehension([[1, 2, 3], ['a', 'b']], tuple, (a, b) => (a + b.length) % 2 === 0), [ + * [1, 'a'], + * [1, 'b'], + * [3, 'a'], + * [3, 'b'] + * ]) + * + * @since 2.5.0 + */ +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray, ReadonlyArray], + f: (a: A, b: B, c: C, d: D) => R, + g?: (a: A, b: B, c: C, d: D) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray], + f: (a: A, b: B, c: C) => R, + g?: (a: A, b: B, c: C) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray], + f: (a: A) => R, + g?: (a: A) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray, ReadonlyArray], + f: (a: A, b: B) => R, + g?: (a: A, b: B) => boolean +): ReadonlyArray +export function comprehension( + input: readonly [ReadonlyArray], + f: (a: A) => boolean, + g?: (a: A) => R +): ReadonlyArray +export function comprehension( + input: ReadonlyArray>, + f: (...xs: ReadonlyArray) => R, + g: (...xs: ReadonlyArray) => boolean = () => true +): ReadonlyArray { + const go = (scope: ReadonlyArray, input: ReadonlyArray>): ReadonlyArray => { + if (input.length === 0) { + return g(...scope) ? [f(...scope)] : empty + } else { + return readonlyArray.chain(input[0], x => go(snoc(scope, x), input.slice(1))) + } + } + return go(empty, input) +} + +/** + * Creates an array of unique values, in order, from all given arrays using a `Eq` for equality comparisons + * + * @example + * import { union } from 'fp-ts/lib/ReadonlyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.deepStrictEqual(union(eqNumber)([1, 2], [2, 3]), [1, 2, 3]) + * + * @since 2.5.0 + */ +export function union(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { + const elemE = elem(E) + return (xs, ys) => + concat( + xs, + ys.filter(a => !elemE(a, xs)) + ) +} + +/** + * Creates an array of unique values that are included in all given arrays using a `Eq` for equality + * comparisons. The order and references of result values are determined by the first array. + * + * @example + * import { intersection } from 'fp-ts/lib/ReadonlyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.deepStrictEqual(intersection(eqNumber)([1, 2], [2, 3]), [2]) + * + * @since 2.5.0 + */ +export function intersection(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { + const elemE = elem(E) + return (xs, ys) => xs.filter(a => elemE(a, ys)) +} + +/** + * Creates an array of array values not included in the other given array using a `Eq` for equality + * comparisons. The order and references of result values are determined by the first array. + * + * @example + * import { difference } from 'fp-ts/lib/ReadonlyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.deepStrictEqual(difference(eqNumber)([1, 2], [2, 3]), [1]) + * + * @since 2.5.0 + */ +export function difference(E: Eq): (xs: ReadonlyArray, ys: ReadonlyArray) => ReadonlyArray { + const elemE = elem(E) + return (xs, ys) => xs.filter(a => !elemE(a, ys)) +} + +/** + * @since 2.5.0 + */ +export const of = (a: A): ReadonlyArray => [a] + +/** + * @since 2.5.0 + */ +export const readonlyArray: Monad1 & + Foldable1 & + Unfoldable1 & + TraversableWithIndex1 & + Alternative1 & + Extend1 & + Compactable1 & + FilterableWithIndex1 & + Witherable1 & + FunctorWithIndex1 & + FoldableWithIndex1 = { + URI, + map: (fa, f) => fa.map(a => f(a)), + mapWithIndex: (fa, f) => fa.map((a, i) => f(i, a)), + compact: as => readonlyArray.filterMap(as, identity), + separate: (fa: ReadonlyArray>): Separated, ReadonlyArray> => { + // tslint:disable-next-line: readonly-array + const left: Array = [] + // tslint:disable-next-line: readonly-array + const right: Array = [] + for (const e of fa) { + if (e._tag === 'Left') { + left.push(e.left) + } else { + right.push(e.right) + } + } + return { + left, + right + } + }, + filter: (as: ReadonlyArray, predicate: Predicate): ReadonlyArray => { + return as.filter(predicate) + }, + filterMap: (as, f) => readonlyArray.filterMapWithIndex(as, (_, a) => f(a)), + partition: (fa: ReadonlyArray, predicate: Predicate): Separated, ReadonlyArray> => { + return readonlyArray.partitionWithIndex(fa, (_, a) => predicate(a)) + }, + partitionMap: (fa, f) => readonlyArray.partitionMapWithIndex(fa, (_, a) => f(a)), + of, + ap: (fab, fa) => flatten(readonlyArray.map(fab, f => readonlyArray.map(fa, f))), + chain: (fa, f) => { + let resLen = 0 + const l = fa.length + const temp = new Array(l) + for (let i = 0; i < l; i++) { + const e = fa[i] + const arr = f(e) + resLen += arr.length + temp[i] = arr + } + const r = Array(resLen) + let start = 0 + for (let i = 0; i < l; i++) { + const arr = temp[i] + const l = arr.length + for (let j = 0; j < l; j++) { + r[j + start] = arr[j] + } + start += l + } + return r + }, + reduce: (fa, b, f) => readonlyArray.reduceWithIndex(fa, b, (_, b, a) => f(b, a)), + foldMap: M => { + const foldMapWithIndexM = readonlyArray.foldMapWithIndex(M) + return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) + }, + reduceRight: (fa, b, f) => readonlyArray.reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), + unfold: (b: B, f: (b: B) => Option): ReadonlyArray => { + // tslint:disable-next-line: readonly-array + const ret: Array = [] + let bb: B = b + while (true) { + const mt = f(bb) + if (isSome(mt)) { + const [a, b] = mt.value + ret.push(a) + bb = b + } else { + break + } + } + return ret + }, + traverse: ( + F: Applicative + ): ((ta: ReadonlyArray, f: (a: A) => HKT) => HKT>) => { + const traverseWithIndexF = readonlyArray.traverseWithIndex(F) + return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) + }, + sequence: (F: Applicative) => (ta: ReadonlyArray>): HKT> => { + return readonlyArray.reduce(ta, F.of(readonlyArray.zero()), (fas, fa) => + F.ap( + F.map(fas, as => (a: A) => snoc(as, a)), + fa + ) + ) + }, + zero: () => empty, + alt: (fx, f) => concat(fx, f()), + extend: (fa, f) => fa.map((_, i, as) => f(as.slice(i))), + wither: ( + F: Applicative + ): ((ta: ReadonlyArray, f: (a: A) => HKT>) => HKT>) => { + const traverseF = readonlyArray.traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyArray.compact) + }, + wilt: ( + F: Applicative + ): (( + wa: ReadonlyArray, + f: (a: A) => HKT> + ) => HKT, ReadonlyArray>>) => { + const traverseF = readonlyArray.traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyArray.separate) + }, + reduceWithIndex: (fa, b, f) => { + const l = fa.length + let r = b + for (let i = 0; i < l; i++) { + r = f(i, r, fa[i]) + } + return r + }, + foldMapWithIndex: M => (fa, f) => fa.reduce((b, a, i) => M.concat(b, f(i, a)), M.empty), + reduceRightWithIndex: (fa, b, f) => fa.reduceRight((b, a, i) => f(i, a, b), b), + traverseWithIndex: (F: Applicative) => ( + ta: ReadonlyArray, + f: (i: number, a: A) => HKT + ): HKT> => { + return readonlyArray.reduceWithIndex(ta, F.of>(readonlyArray.zero()), (i, fbs, a) => + F.ap( + F.map(fbs, bs => (b: B) => snoc(bs, b)), + f(i, a) + ) + ) + }, + partitionMapWithIndex: ( + fa: ReadonlyArray, + f: (i: number, a: A) => Either + ): Separated, ReadonlyArray> => { + // tslint:disable-next-line: readonly-array + const left: Array = [] + // tslint:disable-next-line: readonly-array + const right: Array = [] + for (let i = 0; i < fa.length; i++) { + const e = f(i, fa[i]) + if (e._tag === 'Left') { + left.push(e.left) + } else { + right.push(e.right) + } + } + return { + left, + right + } + }, + partitionWithIndex: ( + fa: ReadonlyArray, + predicateWithIndex: (i: number, a: A) => boolean + ): Separated, ReadonlyArray> => { + // tslint:disable-next-line: readonly-array + const left: Array = [] + // tslint:disable-next-line: readonly-array + const right: Array = [] + for (let i = 0; i < fa.length; i++) { + const a = fa[i] + if (predicateWithIndex(i, a)) { + right.push(a) + } else { + left.push(a) + } + } + return { + left, + right + } + }, + filterMapWithIndex: (fa: ReadonlyArray, f: (i: number, a: A) => Option): ReadonlyArray => { + // tslint:disable-next-line: readonly-array + const result: Array = [] + for (let i = 0; i < fa.length; i++) { + const optionB = f(i, fa[i]) + if (isSome(optionB)) { + result.push(optionB.value) + } + } + return result + }, + filterWithIndex: (fa: ReadonlyArray, predicateWithIndex: (i: number, a: A) => boolean): ReadonlyArray => { + return fa.filter((a, i) => predicateWithIndex(i, a)) + } +} + +const { + alt, + ap, + apFirst, + apSecond, + chain, + chainFirst, + duplicate, + extend, + filter, + filterMap, + filterMapWithIndex, + filterWithIndex, + foldMap, + foldMapWithIndex, + map, + mapWithIndex, + partition, + partitionMap, + partitionMapWithIndex, + partitionWithIndex, + reduce, + reduceRight, + reduceRightWithIndex, + reduceWithIndex, + compact, + separate +} = pipeable(readonlyArray) + +export { + /** + * @since 2.5.0 + */ + alt, + /** + * @since 2.5.0 + */ + ap, + /** + * @since 2.5.0 + */ + apFirst, + /** + * @since 2.5.0 + */ + apSecond, + /** + * @since 2.5.0 + */ + chain, + /** + * @since 2.5.0 + */ + chainFirst, + /** + * @since 2.5.0 + */ + duplicate, + /** + * @since 2.5.0 + */ + extend, + /** + * @since 2.5.0 + */ + filter, + /** + * @since 2.5.0 + */ + filterMap, + /** + * @since 2.5.0 + */ + filterMapWithIndex, + /** + * @since 2.5.0 + */ + filterWithIndex, + /** + * @since 2.5.0 + */ + foldMap, + /** + * @since 2.5.0 + */ + foldMapWithIndex, + /** + * @since 2.5.0 + */ + map, + /** + * @since 2.5.0 + */ + mapWithIndex, + /** + * @since 2.5.0 + */ + partition, + /** + * @since 2.5.0 + */ + partitionMap, + /** + * @since 2.5.0 + */ + partitionMapWithIndex, + /** + * @since 2.5.0 + */ + partitionWithIndex, + /** + * @since 2.5.0 + */ + reduce, + /** + * @since 2.5.0 + */ + reduceRight, + /** + * @since 2.5.0 + */ + reduceRightWithIndex, + /** + * @since 2.5.0 + */ + reduceWithIndex, + /** + * @since 2.5.0 + */ + compact, + /** + * @since 2.5.0 + */ + separate +} diff --git a/src/ReadonlyMap.ts b/src/ReadonlyMap.ts new file mode 100644 index 000000000..71df4a289 --- /dev/null +++ b/src/ReadonlyMap.ts @@ -0,0 +1,712 @@ +/** + * @since 2.5.0 + */ +import { Applicative } from './Applicative' +import { Separated } from './Compactable' +import { Either, isLeft } from './Either' +import { Eq, fromEquals } from './Eq' +import { Filterable2 } from './Filterable' +import { FilterableWithIndex2C } from './FilterableWithIndex' +import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable' +import { Predicate } from './function' +import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' +import { Magma } from './Magma' +import { Monoid } from './Monoid' +import { isNone, isSome, none, Option, option, some } from './Option' +import { Ord } from './Ord' +import { Semigroup } from './Semigroup' +import { Show } from './Show' +import { TraversableWithIndex2C } from './TraversableWithIndex' +import { Unfoldable, Unfoldable1 } from './Unfoldable' +import { Witherable2C } from './Witherable' +import { pipeable } from './pipeable' + +declare module './HKT' { + interface URItoKind2 { + readonly ReadonlyMap: ReadonlyMap + } +} + +/** + * @since 2.5.0 + */ +export const URI = 'ReadonlyMap' + +/** + * @since 2.5.0 + */ +export type URI = typeof URI + +/** + * @since 2.5.0 + */ +export function fromMap(m: Map): ReadonlyMap { + return new Map(m) +} + +/** + * @since 2.5.0 + */ +export function toMap(m: ReadonlyMap): Map { + return new Map(m) +} + +/** + * @since 2.5.0 + */ +export function getShow(SK: Show, SA: Show): Show> { + return { + show: m => { + let elements = '' + m.forEach((a, k) => { + elements += `[${SK.show(k)}, ${SA.show(a)}], ` + }) + if (elements !== '') { + elements = elements.substring(0, elements.length - 2) + } + return `new Map([${elements}])` + } + } +} + +/** + * Calculate the number of key/value pairs in a map + * + * @since 2.5.0 + */ +export function size(d: ReadonlyMap): number { + return d.size +} + +/** + * Test whether or not a map is empty + * + * @since 2.5.0 + */ +export function isEmpty(d: ReadonlyMap): boolean { + return d.size === 0 +} + +/** + * Test whether or not a key exists in a map + * + * @since 2.5.0 + */ +export function member(E: Eq): (k: K, m: ReadonlyMap) => boolean { + const lookupE = lookup(E) + return (k, m) => isSome(lookupE(k, m)) +} + +interface Next { + readonly done?: boolean + readonly value: A +} + +/** + * Test whether or not a value is a member of a map + * + * @since 2.5.0 + */ +export function elem(E: Eq): (a: A, m: ReadonlyMap) => boolean { + return (a, m) => { + const values = m.values() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = values.next()).done) { + const v = e.value + if (E.equals(a, v)) { + return true + } + } + return false + } +} + +/** + * Get a sorted array of the keys contained in a map + * + * @since 2.5.0 + */ +export function keys(O: Ord): (m: ReadonlyMap) => ReadonlyArray { + return m => Array.from(m.keys()).sort(O.compare) +} + +/** + * Get a sorted array of the values contained in a map + * + * @since 2.5.0 + */ +export function values(O: Ord): (m: ReadonlyMap) => ReadonlyArray { + return m => Array.from(m.values()).sort(O.compare) +} + +/** + * @since 2.5.0 + */ +export function collect(O: Ord): (f: (k: K, a: A) => B) => (m: ReadonlyMap) => ReadonlyArray { + const keysO = keys(O) + return (f: (k: K, a: A) => B) => (m: ReadonlyMap): ReadonlyArray => { + // tslint:disable-next-line: readonly-array + const out: Array = [] + const ks = keysO(m) + for (const key of ks) { + out.push(f(key, m.get(key)!)) + } + return out + } +} + +/** + * Get a sorted of the key/value pairs contained in a map + * + * @since 2.5.0 + */ +export function toReadonlyArray(O: Ord): (m: ReadonlyMap) => ReadonlyArray { + return collect(O)((k, a) => [k, a] as const) +} + +/** + * Unfolds a map into a list of key/value pairs + * + * @since 2.5.0 + */ +export function toUnfoldable( + O: Ord, + U: Unfoldable1 +): (d: ReadonlyMap) => Kind +export function toUnfoldable(O: Ord, U: Unfoldable): (d: ReadonlyMap) => HKT +export function toUnfoldable(O: Ord, U: Unfoldable): (d: ReadonlyMap) => HKT { + const toArrayO = toReadonlyArray(O) + return d => { + const arr = toArrayO(d) + const len = arr.length + return U.unfold(0, b => (b < len ? some([arr[b], b + 1]) : none)) + } +} + +/** + * Insert or replace a key/value pair in a map + * + * @since 2.5.0 + */ +export function insertAt(E: Eq): (k: K, a: A) => (m: ReadonlyMap) => ReadonlyMap { + const lookupWithKeyE = lookupWithKey(E) + return (k, a) => m => { + const found = lookupWithKeyE(k, m) + if (isNone(found)) { + const r = new Map(m) + r.set(k, a) + return r + } else if (found.value[1] !== a) { + const r = new Map(m) + r.set(found.value[0], a) + return r + } + return m + } +} + +/** + * Delete a key and value from a map + * + * @since 2.5.0 + */ +export function deleteAt(E: Eq): (k: K) => (m: ReadonlyMap) => ReadonlyMap { + const lookupWithKeyE = lookupWithKey(E) + return k => m => { + const found = lookupWithKeyE(k, m) + if (isSome(found)) { + const r = new Map(m) + r.delete(found.value[0]) + return r + } + return m + } +} + +/** + * @since 2.5.0 + */ +export function updateAt(E: Eq): (k: K, a: A) => (m: ReadonlyMap) => Option> { + const lookupWithKeyE = lookupWithKey(E) + return (k, a) => m => { + const found = lookupWithKeyE(k, m) + if (isNone(found)) { + return none + } + const r = new Map(m) + r.set(found.value[0], a) + return some(r) + } +} + +/** + * @since 2.5.0 + */ +export function modifyAt( + E: Eq +): (k: K, f: (a: A) => A) => (m: ReadonlyMap) => Option> { + const lookupWithKeyE = lookupWithKey(E) + return (k, f) => m => { + const found = lookupWithKeyE(k, m) + if (isNone(found)) { + return none + } + const r = new Map(m) + r.set(found.value[0], f(found.value[1])) + return some(r) + } +} + +/** + * Delete a key and value from a map, returning the value as well as the subsequent map + * + * @since 2.5.0 + */ +export function pop(E: Eq): (k: K) => (m: ReadonlyMap) => Option]> { + const lookupE = lookup(E) + const deleteAtE = deleteAt(E) + return k => { + const deleteAtEk = deleteAtE(k) + return m => option.map(lookupE(k, m), a => [a, deleteAtEk(m)]) + } +} + +/** + * Lookup the value for a key in a `Map`. + * If the result is a `Some`, the existing key is also returned. + * + * @since 2.5.0 + */ +export function lookupWithKey(E: Eq): (k: K, m: ReadonlyMap) => Option { + return (k: K, m: ReadonlyMap) => { + const entries = m.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [ka, a] = e.value + if (E.equals(ka, k)) { + return some([ka, a]) + } + } + return none + } +} + +/** + * Lookup the value for a key in a `Map`. + * + * @since 2.5.0 + */ +export function lookup(E: Eq): (k: K, m: ReadonlyMap) => Option { + const lookupWithKeyE = lookupWithKey(E) + return (k, m) => option.map(lookupWithKeyE(k, m), ([_, a]) => a) +} + +/** + * Test whether or not one Map contains all of the keys and values contained in another Map + * + * @since 2.5.0 + */ +export function isSubmap(SK: Eq, SA: Eq): (d1: ReadonlyMap, d2: ReadonlyMap) => boolean { + const lookupWithKeyS = lookupWithKey(SK) + return (d1: ReadonlyMap, d2: ReadonlyMap): boolean => { + const entries = d1.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + const d2OptA = lookupWithKeyS(k, d2) + if (isNone(d2OptA) || !SK.equals(k, d2OptA.value[0]) || !SA.equals(a, d2OptA.value[1])) { + return false + } + } + return true + } +} + +/** + * @since 2.5.0 + */ +export const empty: ReadonlyMap = new Map() + +/** + * @since 2.5.0 + */ +export function getEq(SK: Eq, SA: Eq): Eq> { + const isSubmap_ = isSubmap(SK, SA) + return fromEquals((x, y) => isSubmap_(x, y) && isSubmap_(y, x)) +} + +/** + * Gets `Monoid` instance for Maps given `Semigroup` instance for their values + * + * @since 2.5.0 + */ +export function getMonoid(SK: Eq, SA: Semigroup): Monoid> { + const lookupWithKeyS = lookupWithKey(SK) + return { + concat: (mx, my) => { + if (mx === empty) { + return my + } + if (my === empty) { + return mx + } + const r = new Map(mx) + const entries = my.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + const mxOptA = lookupWithKeyS(k, mx) + if (isSome(mxOptA)) { + r.set(mxOptA.value[0], SA.concat(mxOptA.value[1], a)) + } else { + r.set(k, a) + } + } + return r + }, + empty + } +} + +/** + * Create a map with one key/value pair + * + * @since 2.5.0 + */ +export function singleton(k: K, a: A): ReadonlyMap { + return new Map([[k, a]]) +} + +/** + * Create a map from a foldable collection of key/value pairs, using the + * specified `Magma` to combine values for duplicate keys. + * + * @since 2.5.0 + */ +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable3 +): (fka: Kind3) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable2 +): (fka: Kind2) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable1 +): (fka: Kind) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyMap +export function fromFoldable( + E: Eq, + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyMap { + return (fka: HKT) => { + const lookupWithKeyE = lookupWithKey(E) + return F.reduce>(fka, new Map(), (b, [k, a]) => { + const bOpt = lookupWithKeyE(k, b) + if (isSome(bOpt)) { + b.set(bOpt.value[0], M.concat(bOpt.value[1], a)) + } else { + b.set(k, a) + } + return b + }) + } +} + +const _mapWithIndex = (fa: ReadonlyMap, f: (k: K, a: A) => B): ReadonlyMap => { + const m = new Map() + const entries = fa.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [key, a] = e.value + m.set(key, f(key, a)) + } + return m +} + +const _partitionMapWithIndex = ( + fa: ReadonlyMap, + f: (k: K, a: A) => Either +): Separated, ReadonlyMap> => { + const left = new Map() + const right = new Map() + const entries = fa.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + const ei = f(k, a) + if (isLeft(ei)) { + left.set(k, ei.left) + } else { + right.set(k, ei.right) + } + } + return { + left, + right + } +} + +const _partitionWithIndex = ( + fa: ReadonlyMap, + p: (k: K, a: A) => boolean +): Separated, ReadonlyMap> => { + const left = new Map() + const right = new Map() + const entries = fa.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + if (p(k, a)) { + right.set(k, a) + } else { + left.set(k, a) + } + } + return { + left, + right + } +} + +const _filterMapWithIndex = (fa: ReadonlyMap, f: (k: K, a: A) => Option): ReadonlyMap => { + const m = new Map() + const entries = fa.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + const o = f(k, a) + if (isSome(o)) { + m.set(k, o.value) + } + } + return m +} + +const _filterWithIndex = (fa: ReadonlyMap, p: (k: K, a: A) => boolean): ReadonlyMap => { + const m = new Map() + const entries = fa.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, a] = e.value + if (p(k, a)) { + m.set(k, a) + } + } + return m +} + +/** + * @since 2.5.0 + */ +export function getFilterableWithIndex(): FilterableWithIndex2C { + return { + ...readonlyMap, + _E: undefined as any, + mapWithIndex: _mapWithIndex, + partitionMapWithIndex: _partitionMapWithIndex, + partitionWithIndex: _partitionWithIndex, + filterMapWithIndex: _filterMapWithIndex, + filterWithIndex: _filterWithIndex + } +} + +/** + * @since 2.5.0 + */ +export function getWitherable(O: Ord): Witherable2C & TraversableWithIndex2C { + const keysO = keys(O) + + const reduceWithIndex = (fa: ReadonlyMap, b: B, f: (k: K, b: B, a: A) => B): B => { + let out: B = b + const ks = keysO(fa) + const len = ks.length + for (let i = 0; i < len; i++) { + const k = ks[i] + out = f(k, out, fa.get(k)!) + } + return out + } + + const foldMapWithIndex = (M: Monoid) => (fa: ReadonlyMap, f: (k: K, a: A) => M): M => { + let out: M = M.empty + const ks = keysO(fa) + const len = ks.length + for (let i = 0; i < len; i++) { + const k = ks[i] + out = M.concat(out, f(k, fa.get(k)!)) + } + return out + } + + const reduceRightWithIndex = (fa: ReadonlyMap, b: B, f: (k: K, a: A, b: B) => B): B => { + let out: B = b + const ks = keysO(fa) + const len = ks.length + for (let i = len - 1; i >= 0; i--) { + const k = ks[i] + out = f(k, fa.get(k)!, out) + } + return out + } + + const traverseWithIndex = ( + F: Applicative + ): ((ta: ReadonlyMap, f: (k: K, a: A) => HKT) => HKT>) => { + return (ta: ReadonlyMap, f: (k: K, a: A) => HKT) => { + let fm: HKT> = F.of(empty) + const entries = ta.entries() + let e: Next + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [key, a] = e.value + fm = F.ap( + F.map(fm, m => (b: B) => new Map(m).set(key, b)), + f(key, a) + ) + } + return fm + } + } + + const traverse = ( + F: Applicative + ): ((ta: ReadonlyMap, f: (a: A) => HKT) => HKT>) => { + const traverseWithIndexF = traverseWithIndex(F) + return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) + } + + const sequence = (F: Applicative): ((ta: ReadonlyMap>) => HKT>) => { + const traverseWithIndexF = traverseWithIndex(F) + return ta => traverseWithIndexF(ta, (_, a) => a) + } + + return { + ...readonlyMap, + _E: undefined as any, + reduce: (fa, b, f) => reduceWithIndex(fa, b, (_, b, a) => f(b, a)), + foldMap: M => { + const foldMapWithIndexM = foldMapWithIndex(M) + return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) + }, + reduceRight: (fa, b, f) => reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), + traverse, + sequence, + mapWithIndex: _mapWithIndex, + reduceWithIndex, + foldMapWithIndex, + reduceRightWithIndex, + traverseWithIndex, + wilt: ( + F: Applicative + ): (( + wa: ReadonlyMap, + f: (a: A) => HKT> + ) => HKT, ReadonlyMap>>) => { + const traverseF = traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyMap.separate) + }, + wither: ( + F: Applicative + ): ((wa: ReadonlyMap, f: (a: A) => HKT>) => HKT>) => { + const traverseF = traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyMap.compact) + } + } +} + +/** + * @since 2.5.0 + */ +export const readonlyMap: Filterable2 = { + URI, + map: (fa, f) => _mapWithIndex(fa, (_, a) => f(a)), + compact: (fa: ReadonlyMap>): ReadonlyMap => { + const m = new Map() + const entries = fa.entries() + let e: Next]> + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, oa] = e.value + if (isSome(oa)) { + m.set(k, oa.value) + } + } + return m + }, + separate: (fa: ReadonlyMap>): Separated, ReadonlyMap> => { + const left = new Map() + const right = new Map() + const entries = fa.entries() + let e: Next]> + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = entries.next()).done) { + const [k, ei] = e.value + if (isLeft(ei)) { + left.set(k, ei.left) + } else { + right.set(k, ei.right) + } + } + return { + left, + right + } + }, + filter: (fa: ReadonlyMap, p: Predicate): ReadonlyMap => _filterWithIndex(fa, (_, a) => p(a)), + filterMap: (fa, f) => _filterMapWithIndex(fa, (_, a) => f(a)), + partition: (fa: ReadonlyMap, predicate: Predicate): Separated, ReadonlyMap> => + _partitionWithIndex(fa, (_, a) => predicate(a)), + partitionMap: (fa, f) => _partitionMapWithIndex(fa, (_, a) => f(a)) +} + +const { filter, filterMap, map, partition, partitionMap, compact, separate } = pipeable(readonlyMap) + +export { + /** + * @since 2.5.0 + */ + filter, + /** + * @since 2.5.0 + */ + filterMap, + /** + * @since 2.5.0 + */ + map, + /** + * @since 2.5.0 + */ + partition, + /** + * @since 2.5.0 + */ + partitionMap, + /** + * @since 2.5.0 + */ + compact, + /** + * @since 2.5.0 + */ + separate +} diff --git a/src/ReadonlyNonEmptyArray.ts b/src/ReadonlyNonEmptyArray.ts new file mode 100644 index 000000000..e2af7d869 --- /dev/null +++ b/src/ReadonlyNonEmptyArray.ts @@ -0,0 +1,458 @@ +/** + * Data structure which represents non-empty arrays + * + * @since 2.5.0 + */ +import { Alt1 } from './Alt' +import { Comonad1 } from './Comonad' +import { Eq } from './Eq' +import { FoldableWithIndex1 } from './FoldableWithIndex' +import { Predicate, Refinement } from './function' +import { FunctorWithIndex1 } from './FunctorWithIndex' +import { Monad1 } from './Monad' +import { NonEmptyArray } from './NonEmptyArray' +import { none, Option, some } from './Option' +import { Ord } from './Ord' +import { pipeable } from './pipeable' +import * as RA from './ReadonlyArray' +import { ReadonlyRecord } from './ReadonlyRecord' +import { getJoinSemigroup, getMeetSemigroup, Semigroup } from './Semigroup' +import { Show } from './Show' +import { TraversableWithIndex1 } from './TraversableWithIndex' + +declare module './HKT' { + interface URItoKind { + readonly ReadonlyNonEmptyArray: ReadonlyNonEmptyArray + } +} + +/** + * @since 2.5.0 + */ +export const URI = 'ReadonlyNonEmptyArray' + +/** + * @since 2.5.0 + */ +export type URI = typeof URI + +/** + * @since 2.5.0 + */ +export interface ReadonlyNonEmptyArray extends ReadonlyArray { + readonly 0: A +} + +/** + * Append an element to the front of an array, creating a new non empty array + * + * @example + * import { cons } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * + * assert.deepStrictEqual(cons(1, [2, 3, 4]), [1, 2, 3, 4]) + * + * @since 2.5.0 + */ +export const cons: (head: A, tail: ReadonlyArray) => ReadonlyNonEmptyArray = RA.cons + +/** + * Append an element to the end of an array, creating a new non empty array + * + * @example + * import { snoc } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * + * assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4]) + * + * @since 2.5.0 + */ +export const snoc: (init: ReadonlyArray, end: A) => ReadonlyNonEmptyArray = RA.snoc + +/** + * Builds a `ReadonlyNonEmptyArray` from an array returning `none` if `as` is an empty array + * + * @since 2.5.0 + */ +export function fromReadonlyArray(as: ReadonlyArray): Option> { + return RA.isNonEmpty(as) ? some(as) : none +} + +/** + * @since 2.5.0 + */ +// tslint:disable-next-line: readonly-array +export function fromArray(as: Array): Option> { + return fromReadonlyArray(RA.fromArray(as)) +} + +/** + * @since 2.5.0 + */ +export const getShow: (S: Show) => Show> = RA.getShow + +/** + * @since 2.5.0 + */ +export function head(nea: ReadonlyNonEmptyArray): A { + return nea[0] +} + +/** + * @since 2.5.0 + */ +export function tail(nea: ReadonlyNonEmptyArray): ReadonlyArray { + return nea.slice(1) +} + +/** + * @since 2.5.0 + */ +export const reverse: (nea: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray = RA.reverse as any + +/** + * @since 2.5.0 + */ +export function min(ord: Ord): (nea: ReadonlyNonEmptyArray) => A { + const S = getMeetSemigroup(ord) + return nea => nea.reduce(S.concat) +} + +/** + * @since 2.5.0 + */ +export function max(ord: Ord): (nea: ReadonlyNonEmptyArray) => A { + const S = getJoinSemigroup(ord) + return nea => nea.reduce(S.concat) +} + +/** + * Builds a `Semigroup` instance for `ReadonlyNonEmptyArray` + * + * @since 2.5.0 + */ +export function getSemigroup(): Semigroup> { + return { + concat: concat + } +} + +/** + * @example + * import { getEq, cons } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * const E = getEq(eqNumber) + * assert.strictEqual(E.equals(cons(1, [2]), [1, 2]), true) + * assert.strictEqual(E.equals(cons(1, [2]), [1, 3]), false) + * + * @since 2.5.0 + */ +export const getEq: (E: Eq) => Eq> = RA.getEq + +/** + * Group equal, consecutive elements of an array into non empty arrays. + * + * @example + * import { cons, group } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * import { ordNumber } from 'fp-ts/lib/Ord' + * + * assert.deepStrictEqual(group(ordNumber)([1, 2, 1, 1]), [ + * cons(1, []), + * cons(2, []), + * cons(1, [1]) + * ]) + * + * @since 2.5.0 + */ +export function group( + E: Eq +): { + (as: ReadonlyNonEmptyArray): ReadonlyNonEmptyArray> + (as: ReadonlyArray): ReadonlyArray> +} +export function group(E: Eq): (as: ReadonlyArray) => ReadonlyArray> { + return as => { + const len = as.length + if (len === 0) { + return RA.empty + } + // tslint:disable-next-line: readonly-array + const r: Array> = [] + let head: A = as[0] + let nea: NonEmptyArray = [head] + for (let i = 1; i < len; i++) { + const x = as[i] + if (E.equals(x, head)) { + nea.push(x) + } else { + r.push(nea) + head = x + nea = [head] + } + } + r.push(nea) + return r + } +} + +/** + * Sort and then group the elements of an array into non empty arrays. + * + * @example + * import { cons, groupSort } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * import { ordNumber } from 'fp-ts/lib/Ord' + * + * assert.deepStrictEqual(groupSort(ordNumber)([1, 2, 1, 1]), [cons(1, [1, 1]), cons(2, [])]) + * + * @since 2.5.0 + */ +export function groupSort(O: Ord): (as: ReadonlyArray) => ReadonlyArray> { + const sortO = RA.sort(O) + const groupO = group(O) + return as => groupO(sortO(as)) +} + +/** + * Splits an array into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning + * function on each element, and grouping the results according to values returned + * + * @example + * import { cons, groupBy } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * + * assert.deepStrictEqual(groupBy((s: string) => String(s.length))(['foo', 'bar', 'foobar']), { + * '3': cons('foo', ['bar']), + * '6': cons('foobar', []) + * }) + * + * @since 2.5.0 + */ +export function groupBy( + f: (a: A) => string +): (as: ReadonlyArray) => ReadonlyRecord> { + return as => { + const r: Record> = {} + for (const a of as) { + const k = f(a) + if (r.hasOwnProperty(k)) { + r[k].push(a) + } else { + r[k] = [a] + } + } + return r + } +} + +/** + * @since 2.5.0 + */ +export function last(nea: ReadonlyNonEmptyArray): A { + return nea[nea.length - 1] +} + +/** + * Get all but the last element of a non empty array, creating a new array. + * + * @example + * import { init } from 'fp-ts/lib/ReadonlyNonEmptyArray' + * + * assert.deepStrictEqual(init([1, 2, 3]), [1, 2]) + * assert.deepStrictEqual(init([1]), []) + * + * @since 2.5.0 + */ +export function init(nea: ReadonlyNonEmptyArray): ReadonlyArray { + return nea.slice(0, -1) +} + +/** + * @since 2.5.0 + */ +export function sort(O: Ord): (nea: ReadonlyNonEmptyArray) => ReadonlyNonEmptyArray { + return RA.sort(O) as any +} + +/** + * @since 2.5.0 + */ +export function insertAt(i: number, a: A): (nea: ReadonlyNonEmptyArray) => Option> { + return RA.insertAt(i, a) as any +} + +/** + * @since 2.5.0 + */ +export function updateAt(i: number, a: A): (nea: ReadonlyNonEmptyArray) => Option> { + return RA.updateAt(i, a) as any +} + +/** + * @since 2.5.0 + */ +export function modifyAt( + i: number, + f: (a: A) => A +): (nea: ReadonlyNonEmptyArray) => Option> { + return RA.modifyAt(i, f) as any +} + +/** + * @since 2.5.0 + */ +export function filter( + refinement: Refinement +): (nea: ReadonlyNonEmptyArray) => Option> +export function filter(predicate: Predicate): (nea: ReadonlyNonEmptyArray) => Option> +export function filter( + predicate: Predicate +): (nea: ReadonlyNonEmptyArray) => Option> { + return filterWithIndex((_, a) => predicate(a)) +} + +/** + * @since 2.5.0 + */ +export function filterWithIndex( + predicate: (i: number, a: A) => boolean +): (nea: ReadonlyNonEmptyArray) => Option> { + return nea => fromReadonlyArray(nea.filter((a, i) => predicate(i, a))) +} + +/** + * @since 2.5.0 + */ +export const of: (a: A) => ReadonlyNonEmptyArray = RA.of as any + +/** + * @since 2.5.0 + */ +export function concat(fx: ReadonlyArray, fy: ReadonlyNonEmptyArray): ReadonlyNonEmptyArray +export function concat(fx: ReadonlyNonEmptyArray, fy: ReadonlyArray): ReadonlyNonEmptyArray +export function concat(fx: ReadonlyArray, fy: ReadonlyArray): ReadonlyArray { + return fx.concat(fy) +} + +/** + * @since 2.5.0 + */ +export function fold(S: Semigroup): (fa: ReadonlyNonEmptyArray) => A { + return fa => fa.reduce(S.concat) +} + +/** + * @since 2.5.0 + */ +export const readonlyNonEmptyArray: Monad1 & + Comonad1 & + TraversableWithIndex1 & + FunctorWithIndex1 & + FoldableWithIndex1 & + Alt1 = { + URI, + map: RA.readonlyArray.map as any, + mapWithIndex: RA.readonlyArray.mapWithIndex as any, + of, + ap: RA.readonlyArray.ap as any, + chain: RA.readonlyArray.chain as any, + extend: RA.readonlyArray.extend as any, + extract: head, + reduce: RA.readonlyArray.reduce, + foldMap: RA.readonlyArray.foldMap, + reduceRight: RA.readonlyArray.reduceRight, + traverse: RA.readonlyArray.traverse as any, + sequence: RA.readonlyArray.sequence as any, + reduceWithIndex: RA.readonlyArray.reduceWithIndex, + foldMapWithIndex: RA.readonlyArray.foldMapWithIndex, + reduceRightWithIndex: RA.readonlyArray.reduceRightWithIndex, + traverseWithIndex: RA.readonlyArray.traverseWithIndex as any, + alt: (fx, fy) => concat(fx, fy()) +} + +const { + ap, + apFirst, + apSecond, + chain, + chainFirst, + duplicate, + extend, + flatten, + map, + mapWithIndex, + reduce, + reduceRight, + reduceRightWithIndex, + reduceWithIndex +} = pipeable(readonlyNonEmptyArray) + +const foldMapWithIndex = (S: Semigroup) => (f: (i: number, a: A) => S) => (fa: ReadonlyNonEmptyArray) => + fa.slice(1).reduce((s, a, i) => S.concat(s, f(i + 1, a)), f(0, fa[0])) + +const foldMap = (S: Semigroup) => (f: (a: A) => S) => (fa: ReadonlyNonEmptyArray) => + fa.slice(1).reduce((s, a) => S.concat(s, f(a)), f(fa[0])) + +export { + /** + * @since 2.5.0 + */ + ap, + /** + * @since 2.5.0 + */ + apFirst, + /** + * @since 2.5.0 + */ + apSecond, + /** + * @since 2.5.0 + */ + chain, + /** + * @since 2.5.0 + */ + chainFirst, + /** + * @since 2.5.0 + */ + duplicate, + /** + * @since 2.5.0 + */ + extend, + /** + * @since 2.5.0 + */ + flatten, + /** + * @since 2.5.0 + */ + foldMap, + /** + * @since 2.5.0 + */ + foldMapWithIndex, + /** + * @since 2.5.0 + */ + map, + /** + * @since 2.5.0 + */ + mapWithIndex, + /** + * @since 2.5.0 + */ + reduce, + /** + * @since 2.5.0 + */ + reduceRight, + /** + * @since 2.5.0 + */ + reduceRightWithIndex, + /** + * @since 2.5.0 + */ + reduceWithIndex +} diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts new file mode 100644 index 000000000..b09ca2d78 --- /dev/null +++ b/src/ReadonlyRecord.ts @@ -0,0 +1,939 @@ +/** + * @since 2.5.0 + */ +import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative' +import { Compactable1, Separated } from './Compactable' +import { Either } from './Either' +import { Eq, fromEquals } from './Eq' +import { FilterableWithIndex1, PredicateWithIndex, RefinementWithIndex } from './FilterableWithIndex' +import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable' +import { FoldableWithIndex1 } from './FoldableWithIndex' +import { identity, Predicate } from './function' +import { FunctorWithIndex1 } from './FunctorWithIndex' +import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' +import { Magma } from './Magma' +import { Monoid } from './Monoid' +import { isNone, isSome, none, Option, some as optionSome } from './Option' +import { Semigroup } from './Semigroup' +import { Show } from './Show' +import { TraversableWithIndex1 } from './TraversableWithIndex' +import { Unfoldable, Unfoldable1 } from './Unfoldable' +import { Witherable1 } from './Witherable' +import { pipeable } from './pipeable' + +/** + * @since 2.5.0 + */ +export type ReadonlyRecord = Readonly> + +declare module './HKT' { + interface URItoKind { + readonly ReadonlyRecord: ReadonlyRecord + } +} + +/** + * @since 2.5.0 + */ +export const URI = 'ReadonlyRecord' + +/** + * @since 2.5.0 + */ +export type URI = typeof URI + +/** + * @since 2.5.0 + */ +export function fromRecord(r: Record): ReadonlyRecord { + return Object.assign({}, r) +} + +/** + * @since 2.5.0 + */ +export function toRecord(r: ReadonlyRecord): Record { + return Object.assign({}, r) +} + +/** + * @since 2.5.0 + */ +export function getShow(S: Show): Show> { + return { + show: r => { + const elements = collect((k, a: A) => `${JSON.stringify(k)}: ${S.show(a)}`)(r).join(', ') + return elements === '' ? '{}' : `{ ${elements} }` + } + } +} + +/** + * Calculate the number of key/value pairs in a record + * + * @since 2.5.0 + */ +export function size(r: ReadonlyRecord): number { + return Object.keys(r).length +} + +/** + * Test whether a record is empty + * + * @since 2.5.0 + */ +export function isEmpty(r: ReadonlyRecord): boolean { + return Object.keys(r).length === 0 +} + +/** + * @since 2.5.0 + */ +export function keys(r: ReadonlyRecord): ReadonlyArray { + return (Object.keys(r) as any).sort() +} + +/** + * Map a record into an array + * + * @example + * import {collect} from 'fp-ts/lib/ReadonlyRecord' + * + * const x: { a: string, b: boolean } = { a: 'foo', b: false } + * assert.deepStrictEqual( + * collect((key, val) => ({key: key, value: val}))(x), + * [{key: 'a', value: 'foo'}, {key: 'b', value: false}] + * ) + * + * @since 2.5.0 + */ +export function collect(f: (k: K, a: A) => B): (r: ReadonlyRecord) => ReadonlyArray { + return r => { + // tslint:disable-next-line: readonly-array + const out: Array = [] + for (const key of keys(r)) { + out.push(f(key, r[key])) + } + return out + } +} + +/** + * @since 2.5.0 + */ +export const toReadonlyArray: ( + r: ReadonlyRecord +) => ReadonlyArray = collect((k, a) => [k, a]) + +/** + * Unfolds a record into a list of key/value pairs + * + * @since 2.5.0 + */ +export function toUnfoldable( + U: Unfoldable1 +): (r: ReadonlyRecord) => Kind +export function toUnfoldable( + U: Unfoldable +): (r: ReadonlyRecord) => HKT +export function toUnfoldable(U: Unfoldable): (r: ReadonlyRecord) => HKT { + return r => { + const arr = toReadonlyArray(r) + const len = arr.length + return U.unfold(0, b => (b < len ? optionSome([arr[b], b + 1]) : none)) + } +} + +/** + * Insert or replace a key/value pair in a record + * + * @since 2.5.0 + */ +export function insertAt( + k: K, + a: A +): (r: ReadonlyRecord) => ReadonlyRecord +export function insertAt(k: string, a: A): (r: ReadonlyRecord) => ReadonlyRecord { + return r => { + if (r[k] === a) { + return r + } + const out: Record = Object.assign({}, r) + out[k] = a + return out + } +} + +const _hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * @since 2.5.0 + */ +export function hasOwnProperty(k: string, r: ReadonlyRecord): k is K { + return _hasOwnProperty.call(r, k) +} + +/** + * Delete a key and value from a map + * + * @since 2.5.0 + */ +export function deleteAt( + k: K +): (r: ReadonlyRecord) => ReadonlyRecord, A> +export function deleteAt(k: string): (r: ReadonlyRecord) => ReadonlyRecord { + return (r: ReadonlyRecord) => { + if (!_hasOwnProperty.call(r, k)) { + return r + } + const out: Record = Object.assign({}, r) + delete out[k] + return out + } +} + +/** + * @since 2.5.0 + */ +export function updateAt( + k: string, + a: A +): (r: ReadonlyRecord) => Option> { + return (r: ReadonlyRecord) => { + if (!hasOwnProperty(k, r)) { + return none + } + if (r[k] === a) { + return optionSome(r) + } + const out: Record = Object.assign({}, r) + out[k] = a + return optionSome(out) + } +} + +/** + * @since 2.5.0 + */ +export function modifyAt( + k: string, + f: (a: A) => A +): (r: ReadonlyRecord) => Option> { + return (r: ReadonlyRecord) => { + if (!hasOwnProperty(k, r)) { + return none + } + const out: Record = Object.assign({}, r) + out[k] = f(r[k]) + return optionSome(out) + } +} + +/** + * Delete a key and value from a map, returning the value as well as the subsequent map + * + * @since 2.5.0 + */ +export function pop( + k: K +): ( + r: ReadonlyRecord +) => Option, A>]> +export function pop(k: string): (r: ReadonlyRecord) => Option]> { + const deleteAtk = deleteAt(k) + return r => { + const oa = lookup(k, r) + return isNone(oa) ? none : optionSome([oa.value, deleteAtk(r)]) + } +} + +/** + * Test whether one record contains all of the keys and values contained in another record + * + * @since 2.5.0 + */ +export function isSubrecord(E: Eq): (x: ReadonlyRecord, y: ReadonlyRecord) => boolean { + return (x, y) => { + for (const k in x) { + if (!_hasOwnProperty.call(y, k) || !E.equals(x[k], y[k])) { + return false + } + } + return true + } +} + +/** + * @since 2.5.0 + */ +export function getEq(E: Eq): Eq> +export function getEq(E: Eq): Eq> { + const isSubrecordE = isSubrecord(E) + return fromEquals((x, y) => isSubrecordE(x, y) && isSubrecordE(y, x)) +} + +/** + * Returns a `Semigroup` instance for records given a `Semigroup` instance for their values + * + * @example + * import { semigroupSum } from 'fp-ts/lib/Semigroup' + * import { getMonoid } from 'fp-ts/lib/ReadonlyRecord' + * + * const M = getMonoid(semigroupSum) + * assert.deepStrictEqual(M.concat({ foo: 123 }, { foo: 456 }), { foo: 579 }) + * + * @since 2.5.0 + */ +export function getMonoid(S: Semigroup): Monoid> +export function getMonoid(S: Semigroup): Monoid> { + return { + concat: (x, y) => { + if (x === empty) { + return y + } + if (y === empty) { + return x + } + const keys = Object.keys(y) + const len = keys.length + if (len === 0) { + return x + } + const r: Record = { ...x } + for (let i = 0; i < len; i++) { + const k = keys[i] + r[k] = _hasOwnProperty.call(x, k) ? S.concat(x[k], y[k]) : y[k] + } + return r + }, + empty + } +} + +/** + * Lookup the value for a key in a record + * + * @since 2.5.0 + */ +export function lookup(k: string, r: ReadonlyRecord): Option { + return _hasOwnProperty.call(r, k) ? optionSome(r[k]) : none +} + +/** + * @since 2.5.0 + */ +export const empty: ReadonlyRecord = {} + +/** + * Map a record passing the keys to the iterating function + * + * @since 2.5.0 + */ +export function mapWithIndex( + f: (k: K, a: A) => B +): (fa: ReadonlyRecord) => ReadonlyRecord +export function mapWithIndex( + f: (k: string, a: A) => B +): (fa: ReadonlyRecord) => ReadonlyRecord { + return fa => readonlyRecord.mapWithIndex(fa, f) +} + +/** + * Map a record passing the values to the iterating function + * + * @since 2.5.0 + */ +export function map(f: (a: A) => B): (fa: ReadonlyRecord) => ReadonlyRecord +export function map(f: (a: A) => B): (fa: ReadonlyRecord) => ReadonlyRecord { + return mapWithIndex((_, a) => f(a)) +} + +/** + * @since 2.5.0 + */ +export function reduceWithIndex( + b: B, + f: (k: K, b: B, a: A) => B +): (fa: ReadonlyRecord) => B +export function reduceWithIndex(b: B, f: (k: string, b: B, a: A) => B): (fa: ReadonlyRecord) => B { + return fa => readonlyRecord.reduceWithIndex(fa, b, f) +} + +/** + * @since 2.5.0 + */ +export function foldMapWithIndex( + M: Monoid +): (f: (k: K, a: A) => M) => (fa: ReadonlyRecord) => M +export function foldMapWithIndex( + M: Monoid +): (f: (k: string, a: A) => M) => (fa: ReadonlyRecord) => M { + const foldMapWithIndexM = readonlyRecord.foldMapWithIndex(M) + return f => fa => foldMapWithIndexM(fa, f) +} + +/** + * @since 2.5.0 + */ +export function reduceRightWithIndex( + b: B, + f: (k: K, a: A, b: B) => B +): (fa: ReadonlyRecord) => B +export function reduceRightWithIndex( + b: B, + f: (k: string, a: A, b: B) => B +): (fa: ReadonlyRecord) => B { + return fa => readonlyRecord.reduceRightWithIndex(fa, b, f) +} + +/** + * Create a record with one key/value pair + * + * @since 2.5.0 + */ +export function singleton(k: K, a: A): ReadonlyRecord { + return { [k]: a } as any +} + +/** + * @since 2.5.0 + */ +export function traverseWithIndex( + F: Applicative3 +): ( + f: (k: K, a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverseWithIndex( + F: Applicative3C +): ( + f: (k: K, a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverseWithIndex( + F: Applicative2 +): ( + f: (k: K, a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverseWithIndex( + F: Applicative2C +): ( + f: (k: K, a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverseWithIndex( + F: Applicative1 +): ( + f: (k: K, a: A) => Kind +) => (ta: ReadonlyRecord) => Kind> +export function traverseWithIndex( + F: Applicative +): (f: (k: K, a: A) => HKT) => (ta: ReadonlyRecord) => HKT> +export function traverseWithIndex( + F: Applicative +): (f: (k: string, a: A) => HKT) => (ta: ReadonlyRecord) => HKT> { + const traverseWithIndexF = readonlyRecord.traverseWithIndex(F) + return f => ta => traverseWithIndexF(ta, f) +} + +/** + * @since 2.5.0 + */ +export function traverse( + F: Applicative3 +): ( + f: (a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverse( + F: Applicative3C +): ( + f: (a: A) => Kind3 +) => (ta: ReadonlyRecord) => Kind3> +export function traverse( + F: Applicative2 +): ( + f: (a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverse( + F: Applicative2C +): ( + f: (a: A) => Kind2 +) => (ta: ReadonlyRecord) => Kind2> +export function traverse( + F: Applicative1 +): (f: (a: A) => Kind) => (ta: ReadonlyRecord) => Kind> +export function traverse( + F: Applicative +): (f: (a: A) => HKT) => (ta: ReadonlyRecord) => HKT> +export function traverse( + F: Applicative +): (f: (a: A) => HKT) => (ta: ReadonlyRecord) => HKT> { + const traverseWithIndexF = traverseWithIndex(F) + return f => traverseWithIndexF((_, a) => f(a)) +} + +/** + * @since 2.5.0 + */ +export function sequence( + F: Applicative3 +): (ta: ReadonlyRecord>) => Kind3> +export function sequence( + F: Applicative3C +): (ta: ReadonlyRecord>) => Kind3> +export function sequence( + F: Applicative2 +): (ta: ReadonlyRecord>) => Kind2> +export function sequence( + F: Applicative2C +): (ta: ReadonlyRecord>) => Kind2> +export function sequence( + F: Applicative1 +): (ta: ReadonlyRecord>) => Kind> +export function sequence( + F: Applicative +): (ta: ReadonlyRecord>) => HKT> +export function sequence( + F: Applicative +): (ta: ReadonlyRecord>) => HKT> { + return traverseWithIndex(F)((_, a) => a) +} + +/** + * @since 2.5.0 + */ +export function partitionMapWithIndex( + f: (key: K, a: A) => Either +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> +export function partitionMapWithIndex( + f: (key: string, a: A) => Either +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> { + return fa => readonlyRecord.partitionMapWithIndex(fa, f) +} + +/** + * @since 2.5.0 + */ +export function partitionWithIndex( + refinementWithIndex: RefinementWithIndex +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> +export function partitionWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> +export function partitionWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => Separated, ReadonlyRecord> { + return fa => readonlyRecord.partitionWithIndex(fa, predicateWithIndex) +} + +/** + * @since 2.5.0 + */ +export function filterMapWithIndex( + f: (key: K, a: A) => Option +): (fa: ReadonlyRecord) => ReadonlyRecord +export function filterMapWithIndex( + f: (key: string, a: A) => Option +): (fa: ReadonlyRecord) => ReadonlyRecord { + return fa => readonlyRecord.filterMapWithIndex(fa, f) +} + +/** + * @since 2.5.0 + */ +export function filterWithIndex( + refinementWithIndex: RefinementWithIndex +): (fa: ReadonlyRecord) => ReadonlyRecord +export function filterWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => ReadonlyRecord +export function filterWithIndex( + predicateWithIndex: PredicateWithIndex +): (fa: ReadonlyRecord) => ReadonlyRecord { + return fa => readonlyRecord.filterWithIndex(fa, predicateWithIndex) +} + +/** + * Create a record from a foldable collection of key/value pairs, using the + * specified `Magma` to combine values for duplicate keys. + * + * @since 2.5.0 + */ +export function fromFoldable( + M: Magma, + F: Foldable3 +): (fka: Kind3) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable2 +): (fka: Kind2) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable1 +): (fka: Kind) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyRecord +export function fromFoldable( + M: Magma, + F: Foldable +): (fka: HKT) => ReadonlyRecord { + const fromFoldableMapM = fromFoldableMap(M, F) + return fka => fromFoldableMapM(fka, identity) +} + +/** + * Create a record from a foldable collection using the specified functions to + * + * - map to key/value pairs + * - combine values for duplicate keys. + * + * @example + * import { getLastSemigroup } from 'fp-ts/lib/Semigroup' + * import { readonlyArray, zip } from 'fp-ts/lib/ReadonlyArray' + * import { identity } from 'fp-ts/lib/function' + * import { ReadonlyRecord, fromFoldableMap } from 'fp-ts/lib/ReadonlyRecord' + * + * // like lodash `zipObject` or ramda `zipObj` + * export const zipObject = (keys: ReadonlyArray, values: ReadonlyArray): ReadonlyRecord => + * fromFoldableMap(getLastSemigroup(), readonlyArray)(zip(keys, values), identity) + * + * assert.deepStrictEqual(zipObject(['a', 'b'], [1, 2, 3]), { a: 1, b: 2 }) + * + * // build a record from a field + * interface User { + * id: string + * name: string + * } + * + * const users: ReadonlyArray = [ + * { id: 'id1', name: 'name1' }, + * { id: 'id2', name: 'name2' }, + * { id: 'id1', name: 'name3' } + * ] + * + * assert.deepStrictEqual(fromFoldableMap(getLastSemigroup(), readonlyArray)(users, user => [user.id, user]), { + * id1: { id: 'id1', name: 'name3' }, + * id2: { id: 'id2', name: 'name2' } + * }) + * + * @since 2.5.0 + */ +export function fromFoldableMap( + M: Magma, + F: Foldable3 +): (fa: Kind3, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable2 +): (fa: Kind2, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable1 +): (fa: Kind, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable +): (fa: HKT, f: (a: A) => readonly [K, B]) => ReadonlyRecord +export function fromFoldableMap( + M: Magma, + F: Foldable +): (fa: HKT, f: (a: A) => readonly [string, B]) => ReadonlyRecord { + return (ta: HKT, f: (a: A) => readonly [string, B]) => { + return F.reduce>(ta, {}, (r, a) => { + const [k, b] = f(a) + r[k] = _hasOwnProperty.call(r, k) ? M.concat(r[k], b) : b + return r + }) + } +} + +/** + * @since 2.5.0 + */ +export function every(predicate: Predicate): (r: ReadonlyRecord) => boolean { + return r => { + for (const k in r) { + if (!predicate(r[k])) { + return false + } + } + return true + } +} + +/** + * @since 2.5.0 + */ +export function some(predicate: (a: A) => boolean): (r: ReadonlyRecord) => boolean { + return r => { + for (const k in r) { + if (predicate(r[k])) { + return true + } + } + return false + } +} + +/** + * @since 2.5.0 + */ +export function elem(E: Eq): (a: A, fa: ReadonlyRecord) => boolean { + return (a, fa) => { + for (const k in fa) { + if (E.equals(fa[k], a)) { + return true + } + } + return false + } +} + +/** + * @since 2.5.0 + */ +export const readonlyRecord: FunctorWithIndex1 & + Foldable1 & + TraversableWithIndex1 & + Compactable1 & + FilterableWithIndex1 & + Witherable1 & + FoldableWithIndex1 = { + URI, + map: (fa, f) => readonlyRecord.mapWithIndex(fa, (_, a) => f(a)), + reduce: (fa, b, f) => readonlyRecord.reduceWithIndex(fa, b, (_, b, a) => f(b, a)), + foldMap: M => { + const foldMapWithIndexM = readonlyRecord.foldMapWithIndex(M) + return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) + }, + reduceRight: (fa, b, f) => readonlyRecord.reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), + traverse: ( + F: Applicative + ): ((ta: ReadonlyRecord, f: (a: A) => HKT) => HKT>) => { + const traverseWithIndexF = readonlyRecord.traverseWithIndex(F) + return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) + }, + sequence, + compact: (fa: ReadonlyRecord>): ReadonlyRecord => { + const r: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + const optionA = fa[key] + if (isSome(optionA)) { + r[key] = optionA.value + } + } + return r + }, + separate: ( + fa: ReadonlyRecord> + ): Separated, ReadonlyRecord> => { + const left: Record = {} + const right: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + const e = fa[key] + switch (e._tag) { + case 'Left': + left[key] = e.left + break + case 'Right': + right[key] = e.right + break + } + } + return { + left, + right + } + }, + filter: (fa: ReadonlyRecord, predicate: Predicate): ReadonlyRecord => { + return readonlyRecord.filterWithIndex(fa, (_, a) => predicate(a)) + }, + filterMap: (fa, f) => readonlyRecord.filterMapWithIndex(fa, (_, a) => f(a)), + partition: ( + fa: ReadonlyRecord, + predicate: Predicate + ): Separated, ReadonlyRecord> => { + return readonlyRecord.partitionWithIndex(fa, (_, a) => predicate(a)) + }, + partitionMap: (fa, f) => readonlyRecord.partitionMapWithIndex(fa, (_, a) => f(a)), + wither: ( + F: Applicative + ): ((wa: ReadonlyRecord, f: (a: A) => HKT>) => HKT>) => { + const traverseF = readonlyRecord.traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyRecord.compact) + }, + wilt: ( + F: Applicative + ): (( + wa: ReadonlyRecord, + f: (a: A) => HKT> + ) => HKT, ReadonlyRecord>>) => { + const traverseF = readonlyRecord.traverse(F) + return (wa, f) => F.map(traverseF(wa, f), readonlyRecord.separate) + }, + mapWithIndex: (fa: ReadonlyRecord, f: (k: string, a: A) => B) => { + const out: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + out[key] = f(key, fa[key]) + } + return out + }, + reduceWithIndex: (fa, b, f) => { + let out = b + const keys = Object.keys(fa).sort() + const len = keys.length + for (let i = 0; i < len; i++) { + const k = keys[i] + out = f(k, out, fa[k]) + } + return out + }, + foldMapWithIndex: M => (fa, f) => { + let out = M.empty + const keys = Object.keys(fa).sort() + const len = keys.length + for (let i = 0; i < len; i++) { + const k = keys[i] + out = M.concat(out, f(k, fa[k])) + } + return out + }, + reduceRightWithIndex: (fa, b, f) => { + let out = b + const keys = Object.keys(fa).sort() + const len = keys.length + for (let i = len - 1; i >= 0; i--) { + const k = keys[i] + out = f(k, fa[k], out) + } + return out + }, + traverseWithIndex: (F: Applicative) => ( + ta: ReadonlyRecord, + f: (k: string, a: A) => HKT + ) => { + const keys = Object.keys(ta) + if (keys.length === 0) { + return F.of(empty) + } + let fr: HKT> = F.of({}) + for (const key of keys) { + fr = F.ap( + F.map(fr, r => (b: B) => { + r[key] = b + return r + }), + f(key, ta[key]) + ) + } + return fr + }, + partitionMapWithIndex: (fa: ReadonlyRecord, f: (key: string, a: A) => Either) => { + const left: Record = {} + const right: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + const e = f(key, fa[key]) + switch (e._tag) { + case 'Left': + left[key] = e.left + break + case 'Right': + right[key] = e.right + break + } + } + return { + left, + right + } + }, + partitionWithIndex: (fa: ReadonlyRecord, predicateWithIndex: PredicateWithIndex) => { + const left: Record = {} + const right: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + const a = fa[key] + if (predicateWithIndex(key, a)) { + right[key] = a + } else { + left[key] = a + } + } + return { + left, + right + } + }, + filterMapWithIndex: (fa: ReadonlyRecord, f: (key: string, a: A) => Option) => { + const r: Record = {} + const keys = Object.keys(fa) + for (const key of keys) { + const optionB = f(key, fa[key]) + if (isSome(optionB)) { + r[key] = optionB.value + } + } + return r + }, + filterWithIndex: (fa: ReadonlyRecord, predicateWithIndex: PredicateWithIndex) => { + const out: Record = {} + let changed = false + for (const key in fa) { + if (_hasOwnProperty.call(fa, key)) { + const a = fa[key] + if (predicateWithIndex(key, a)) { + out[key] = a + } else { + changed = true + } + } + } + return changed ? out : fa + } +} + +const { filter, filterMap, foldMap, partition, partitionMap, reduce, reduceRight, compact, separate } = pipeable( + readonlyRecord +) + +export { + /** + * @since 2.5.0 + */ + filter, + /** + * @since 2.5.0 + */ + filterMap, + /** + * @since 2.5.0 + */ + foldMap, + /** + * @since 2.5.0 + */ + partition, + /** + * @since 2.5.0 + */ + partitionMap, + /** + * @since 2.5.0 + */ + reduce, + /** + * @since 2.5.0 + */ + reduceRight, + /** + * @since 2.5.0 + */ + compact, + /** + * @since 2.5.0 + */ + separate +} diff --git a/src/ReadonlySet.ts b/src/ReadonlySet.ts new file mode 100644 index 000000000..8563cd24b --- /dev/null +++ b/src/ReadonlySet.ts @@ -0,0 +1,454 @@ +/** + * @since 2.5.0 + */ +import { Either } from './Either' +import { Monoid } from './Monoid' +import { Ord } from './Ord' +import { Semigroup } from './Semigroup' +import { Eq, fromEquals } from './Eq' +import { Predicate, not, Refinement, identity } from './function' +import { Separated } from './Compactable' +import { Option } from './Option' +import { Show } from './Show' + +/** + * @since 2.5.0 + */ +export function fromSet(s: Set): ReadonlySet { + return new Set(s) +} + +/** + * @since 2.5.0 + */ +export function toSet(s: ReadonlySet): Set { + return new Set(s) +} + +/** + * @since 2.5.0 + */ +export function getShow(S: Show): Show> { + return { + show: s => { + let elements = '' + s.forEach(a => { + elements += S.show(a) + ', ' + }) + if (elements !== '') { + elements = elements.substring(0, elements.length - 2) + } + return `new Set([${elements}])` + } + } +} + +/** + * @since 2.5.0 + */ +export const empty: ReadonlySet = new Set() + +/** + * @since 2.5.0 + */ +export function toReadonlyArray(O: Ord): (set: ReadonlySet) => ReadonlyArray { + return x => { + // tslint:disable-next-line: readonly-array + const r: Array = [] + x.forEach(e => r.push(e)) + return r.sort(O.compare) + } +} + +/** + * @since 2.5.0 + */ +export function getEq(E: Eq): Eq> { + const subsetE = isSubset(E) + return fromEquals((x, y) => subsetE(x, y) && subsetE(y, x)) +} + +interface Next { + readonly done?: boolean + readonly value: A +} + +/** + * @since 2.5.0 + */ +export function some(predicate: Predicate): (set: ReadonlySet) => boolean { + return set => { + const values = set.values() + let e: Next + let found = false + // tslint:disable-next-line: strict-boolean-expressions + while (!found && !(e = values.next()).done) { + found = predicate(e.value) + } + return found + } +} + +/** + * Projects a Set through a function + * + * @since 2.5.0 + */ +export function map(E: Eq): (f: (x: A) => B) => (set: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return f => set => { + const r = new Set() + set.forEach(e => { + const v = f(e) + if (!elemE(v, r)) { + r.add(v) + } + }) + return r + } +} + +/** + * @since 2.5.0 + */ +export function every(predicate: Predicate): (set: ReadonlySet) => boolean { + return not(some(not(predicate))) +} + +/** + * @since 2.5.0 + */ +export function chain(E: Eq): (f: (x: A) => ReadonlySet) => (set: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return f => set => { + const r = new Set() + set.forEach(e => { + f(e).forEach(e => { + if (!elemE(e, r)) { + r.add(e) + } + }) + }) + return r + } +} + +/** + * `true` if and only if every element in the first set is an element of the second set + * + * @since 2.5.0 + */ +export function isSubset(E: Eq): (x: ReadonlySet, y: ReadonlySet) => boolean { + const elemE = elem(E) + return (x, y) => every((a: A) => elemE(a, y))(x) +} + +/** + * @since 2.5.0 + */ +export function filter(refinement: Refinement): (set: ReadonlySet) => ReadonlySet +export function filter(predicate: Predicate): (set: ReadonlySet) => ReadonlySet +export function filter(predicate: Predicate): (set: ReadonlySet) => ReadonlySet { + return set => { + const values = set.values() + let e: Next + const r = new Set() + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = values.next()).done) { + const value = e.value + if (predicate(value)) { + r.add(value) + } + } + return r + } +} + +/** + * @since 2.5.0 + */ +export function partition( + refinement: Refinement +): (set: ReadonlySet) => Separated, ReadonlySet> +export function partition( + predicate: Predicate +): (set: ReadonlySet) => Separated, ReadonlySet> +export function partition( + predicate: Predicate +): (set: ReadonlySet) => Separated, ReadonlySet> { + return set => { + const values = set.values() + let e: Next + const right = new Set() + const left = new Set() + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = values.next()).done) { + const value = e.value + if (predicate(value)) { + right.add(value) + } else { + left.add(value) + } + } + return { left, right } + } +} + +/** + * Test if a value is a member of a set + * + * @since 2.5.0 + */ +export function elem(E: Eq): (a: A, set: ReadonlySet) => boolean { + return (a, set) => { + const values = set.values() + let e: Next + let found = false + // tslint:disable-next-line: strict-boolean-expressions + while (!found && !(e = values.next()).done) { + found = E.equals(a, e.value) + } + return found + } +} + +/** + * Form the union of two sets + * + * @since 2.5.0 + */ +export function union(E: Eq): (set: ReadonlySet, y: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return (x, y) => { + if (x === empty) { + return y + } + if (y === empty) { + return x + } + const r = new Set(x) + y.forEach(e => { + if (!elemE(e, r)) { + r.add(e) + } + }) + return r + } +} + +/** + * The set of elements which are in both the first and second set + * + * @since 2.5.0 + */ +export function intersection(E: Eq): (set: ReadonlySet, y: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return (x, y) => { + if (x === empty || y === empty) { + return empty + } + const r = new Set() + x.forEach(e => { + if (elemE(e, y)) { + r.add(e) + } + }) + return r + } +} + +/** + * @since 2.5.0 + */ +export function partitionMap( + EB: Eq, + EC: Eq +): (f: (a: A) => Either) => (set: ReadonlySet) => Separated, ReadonlySet> { + return (f: (a: A) => Either) => (set: ReadonlySet) => { + const values = set.values() + let e: Next + const left = new Set() + const right = new Set() + const hasB = elem(EB) + const hasC = elem(EC) + // tslint:disable-next-line: strict-boolean-expressions + while (!(e = values.next()).done) { + const v = f(e.value) + switch (v._tag) { + case 'Left': + if (!hasB(v.left, left)) { + left.add(v.left) + } + break + case 'Right': + if (!hasC(v.right, right)) { + right.add(v.right) + } + break + } + } + return { left, right } + } +} + +/** + * Form the set difference (`x` - `y`) + * + * @example + * import { difference } from 'fp-ts/lib/ReadonlySet' + * import { eqNumber } from 'fp-ts/lib/Eq' + * + * assert.deepStrictEqual(difference(eqNumber)(new Set([1, 2]), new Set([1, 3])), new Set([2])) + * + * + * @since 2.5.0 + */ +export function difference(E: Eq): (x: ReadonlySet, y: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return (x, y) => filter((a: A) => !elemE(a, y))(x) +} + +/** + * @since 2.5.0 + */ +export function getUnionMonoid(E: Eq): Monoid> { + return { + concat: union(E), + empty + } +} + +/** + * @since 2.5.0 + */ +export function getIntersectionSemigroup(E: Eq): Semigroup> { + return { + concat: intersection(E) + } +} + +/** + * @since 2.5.0 + */ +export function reduce(O: Ord): (b: B, f: (b: B, a: A) => B) => (fa: ReadonlySet) => B { + const toArrayO = toReadonlyArray(O) + return (b, f) => fa => toArrayO(fa).reduce(f, b) +} + +/** + * @since 2.5.0 + */ +export function foldMap(O: Ord, M: Monoid): (f: (a: A) => M) => (fa: ReadonlySet) => M { + const toArrayO = toReadonlyArray(O) + return f => fa => toArrayO(fa).reduce((b, a) => M.concat(b, f(a)), M.empty) +} + +/** + * Create a set with one element + * + * @since 2.5.0 + */ +export function singleton(a: A): ReadonlySet { + return new Set([a]) +} + +/** + * Insert a value into a set + * + * @since 2.5.0 + */ +export function insert(E: Eq): (a: A) => (set: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return a => set => { + if (!elemE(a, set)) { + const r = new Set(set) + r.add(a) + return r + } else { + return set + } + } +} + +/** + * Delete a value from a set + * + * @since 2.5.0 + */ +export function remove(E: Eq): (a: A) => (set: ReadonlySet) => ReadonlySet { + return a => set => filter((ax: A) => !E.equals(a, ax))(set) +} + +/** + * Create a set from an array + * + * @since 2.5.0 + */ +export function fromArray(E: Eq): (as: ReadonlyArray) => ReadonlySet { + return as => { + const len = as.length + const r = new Set() + const has = elem(E) + for (let i = 0; i < len; i++) { + const a = as[i] + if (!has(a, r)) { + r.add(a) + } + } + return r + } +} + +/** + * @since 2.5.0 + */ +export function compact(E: Eq): (fa: ReadonlySet>) => ReadonlySet { + return filterMap(E)(identity) +} + +/** + * @since 2.5.0 + */ +export function separate( + EE: Eq, + EA: Eq +): (fa: ReadonlySet>) => Separated, ReadonlySet> { + return fa => { + const elemEE = elem(EE) + const elemEA = elem(EA) + const left: Set = new Set() + const right: Set = new Set() + fa.forEach(e => { + switch (e._tag) { + case 'Left': + if (!elemEE(e.left, left)) { + left.add(e.left) + } + break + case 'Right': + if (!elemEA(e.right, right)) { + right.add(e.right) + } + break + } + }) + return { left, right } + } +} + +/** + * @since 2.5.0 + */ +export function filterMap(E: Eq): (f: (a: A) => Option) => (fa: ReadonlySet) => ReadonlySet { + const elemE = elem(E) + return f => fa => { + const r: Set = new Set() + fa.forEach(a => { + const ob = f(a) + if (ob._tag === 'Some' && !elemE(ob.value, r)) { + r.add(ob.value) + } + }) + return r + } +} diff --git a/src/ReadonlyTuple.ts b/src/ReadonlyTuple.ts new file mode 100644 index 000000000..7dcdfa859 --- /dev/null +++ b/src/ReadonlyTuple.ts @@ -0,0 +1,196 @@ +/** + * @since 2.5.0 + */ +import { Applicative, Applicative2C } from './Applicative' +import { Apply2C } from './Apply' +import { Bifunctor2 } from './Bifunctor' +import { Chain2C } from './Chain' +import { ChainRec2C } from './ChainRec' +import { Comonad2 } from './Comonad' +import { Either } from './Either' +import { Foldable2 } from './Foldable' +import { HKT } from './HKT' +import { Monad2C } from './Monad' +import { Monoid } from './Monoid' +import { Semigroup } from './Semigroup' +import { Semigroupoid2 } from './Semigroupoid' +import { Traversable2 } from './Traversable' +import { pipeable } from './pipeable' + +declare module './HKT' { + interface URItoKind2 { + readonly ReadonlyTuple: readonly [A, E] + } +} + +/** + * @since 2.5.0 + */ +export const URI = 'ReadonlyTuple' + +/** + * @since 2.5.0 + */ +export type URI = typeof URI + +/** + * @since 2.5.0 + */ +export function fst(sa: readonly [A, S]): A { + return sa[0] +} + +/** + * @since 2.5.0 + */ +export function snd(sa: readonly [A, S]): S { + return sa[1] +} + +/** + * @since 2.5.0 + */ +export function swap(sa: readonly [A, S]): readonly [S, A] { + return [snd(sa), fst(sa)] +} + +/** + * @since 2.5.0 + */ +export function getApply(S: Semigroup): Apply2C { + return { + URI, + _E: undefined as any, + map: readonlyTuple.map, + ap: (fab, fa) => [fst(fab)(fst(fa)), S.concat(snd(fab), snd(fa))] + } +} + +const of = (M: Monoid) => (a: A): readonly [A, S] => { + return [a, M.empty] +} + +/** + * @since 2.5.0 + */ +export function getApplicative(M: Monoid): Applicative2C { + return { + ...getApply(M), + of: of(M) + } +} + +/** + * @since 2.5.0 + */ +export function getChain(S: Semigroup): Chain2C { + return { + ...getApply(S), + chain: (fa, f) => { + const [b, s] = f(fst(fa)) + return [b, S.concat(snd(fa), s)] + } + } +} + +/** + * @since 2.5.0 + */ +export function getMonad(M: Monoid): Monad2C { + return { + ...getChain(M), + of: of(M) + } +} + +/** + * @since 2.5.0 + */ +export function getChainRec(M: Monoid): ChainRec2C { + const chainRec = (a: A, f: (a: A) => readonly [Either, S]): readonly [B, S] => { + let result: readonly [Either, S] = f(a) + let acc: S = M.empty + let s: Either = fst(result) + while (s._tag === 'Left') { + acc = M.concat(acc, snd(result)) + result = f(s.left) + s = fst(result) + } + return [s.right, M.concat(acc, snd(result))] + } + + return { + ...getChain(M), + chainRec + } +} + +/** + * @since 2.5.0 + */ +export const readonlyTuple: Semigroupoid2 & + Bifunctor2 & + Comonad2 & + Foldable2 & + Traversable2 = { + URI, + compose: (ba, ae) => [fst(ba), snd(ae)], + map: (ae, f) => [f(fst(ae)), snd(ae)], + bimap: (fea, f, g) => [g(fst(fea)), f(snd(fea))], + mapLeft: (fea, f) => [fst(fea), f(snd(fea))], + extract: fst, + extend: (ae, f) => [f(ae), snd(ae)], + reduce: (ae, b, f) => f(b, fst(ae)), + foldMap: _ => (ae, f) => f(fst(ae)), + reduceRight: (ae, b, f) => f(fst(ae), b), + traverse: (F: Applicative) => ( + as: readonly [A, S], + f: (a: A) => HKT + ): HKT => { + return F.map(f(fst(as)), b => [b, snd(as)]) + }, + sequence: (F: Applicative) => (fas: readonly [HKT, S]): HKT => { + return F.map(fst(fas), a => [a, snd(fas)]) + } +} + +const { bimap, compose, duplicate, extend, foldMap, map, mapLeft, reduce, reduceRight } = pipeable(readonlyTuple) + +export { + /** + * @since 2.5.0 + */ + bimap, + /** + * @since 2.5.0 + */ + compose, + /** + * @since 2.5.0 + */ + duplicate, + /** + * @since 2.5.0 + */ + extend, + /** + * @since 2.5.0 + */ + foldMap, + /** + * @since 2.5.0 + */ + map, + /** + * @since 2.5.0 + */ + mapLeft, + /** + * @since 2.5.0 + */ + reduce, + /** + * @since 2.5.0 + */ + reduceRight +} diff --git a/src/Record.ts b/src/Record.ts index fd1c3d25d..c2cdb0dbb 100644 --- a/src/Record.ts +++ b/src/Record.ts @@ -4,26 +4,29 @@ import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative' import { Compactable1, Separated } from './Compactable' import { Either } from './Either' -import { Eq, fromEquals } from './Eq' +import { Eq } from './Eq' import { FilterableWithIndex1, PredicateWithIndex, RefinementWithIndex } from './FilterableWithIndex' import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable' import { FoldableWithIndex1 } from './FoldableWithIndex' -import { identity, Predicate } from './function' +import { Predicate } from './function' import { FunctorWithIndex1 } from './FunctorWithIndex' import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' import { Magma } from './Magma' import { Monoid } from './Monoid' -import { isNone, isSome, none, Option, some as optionSome } from './Option' +import { Option } from './Option' +import { pipeable } from './pipeable' +import * as RR from './ReadonlyRecord' import { Semigroup } from './Semigroup' -import { Show, showString } from './Show' +import { Show } from './Show' import { TraversableWithIndex1 } from './TraversableWithIndex' import { Unfoldable, Unfoldable1 } from './Unfoldable' import { Witherable1 } from './Witherable' -import { pipeable } from './pipeable' + +/* tslint:disable:readonly-array */ declare module './HKT' { interface URItoKind { - Record: Record + readonly Record: Record } } @@ -40,41 +43,26 @@ export type URI = typeof URI /** * @since 2.0.0 */ -export function getShow(S: Show): Show> { - return { - show: r => { - const elements = collect((k, a: A) => `${showString.show(k)}: ${S.show(a)}`)(r).join(', ') - return elements === '' ? '{}' : `{ ${elements} }` - } - } -} +export const getShow: (S: Show) => Show> = RR.getShow /** * Calculate the number of key/value pairs in a record * * @since 2.0.0 */ -export function size(r: Record): number { - return Object.keys(r).length -} +export const size: (r: Record) => number = RR.size /** * Test whether a record is empty * * @since 2.0.0 */ -export function isEmpty(r: Record): boolean { - return Object.keys(r).length === 0 -} - -const unorderedKeys = (r: Record): Array => Object.keys(r) as any +export const isEmpty: (r: Record) => boolean = RR.isEmpty /** * @since 2.0.0 */ -export function keys(r: Record): Array { - return unorderedKeys(r).sort() -} +export const keys: (r: Record) => Array = RR.keys as any /** * Map a record into an array @@ -90,20 +78,14 @@ export function keys(r: Record): Array { * * @since 2.0.0 */ -export function collect(f: (k: K, a: A) => B): (r: Record) => Array { - return r => { - const out: Array = [] - for (const key of keys(r)) { - out.push(f(key, r[key])) - } - return out - } -} +export const collect: ( + f: (k: K, a: A) => B +) => (r: Record) => Array = RR.collect as any /** * @since 2.0.0 */ -export const toArray: (r: Record) => Array<[K, A]> = collect((k, a) => [k, a]) +export const toArray: (r: Record) => Array<[K, A]> = RR.toReadonlyArray as any /** * Unfolds a record into a list of key/value pairs @@ -111,15 +93,11 @@ export const toArray: (r: Record) => Array<[K, A]> = * @since 2.0.0 */ export function toUnfoldable( - unfoldable: Unfoldable1 + U: Unfoldable1 ): (r: Record) => Kind -export function toUnfoldable(unfoldable: Unfoldable): (r: Record) => HKT -export function toUnfoldable(unfoldable: Unfoldable): (r: Record) => HKT { - return r => { - const arr = toArray(r) - const len = arr.length - return unfoldable.unfold(0, b => (b < len ? optionSome([arr[b], b + 1]) : none)) - } +export function toUnfoldable(U: Unfoldable): (r: Record) => HKT +export function toUnfoldable(U: Unfoldable): (r: Record) => HKT { + return RR.toUnfoldable(U) as any } /** @@ -129,24 +107,13 @@ export function toUnfoldable(unfoldable: Unfoldable): (r: Record(k: K, a: A): (r: Record) => Record export function insertAt(k: string, a: A): (r: Record) => Record { - return r => { - if (r[k] === a) { - return r - } - const out = Object.assign({}, r) - out[k] = a - return out - } + return RR.insertAt(k, a) as any } -const _hasOwnProperty = Object.prototype.hasOwnProperty - /** * @since 2.0.0 */ -export function hasOwnProperty(k: string, r: Record): k is K { - return _hasOwnProperty.call(r, k) -} +export const hasOwnProperty: (k: string, r: Record) => k is K = RR.hasOwnProperty /** * Delete a key and value from a map @@ -157,46 +124,19 @@ export function deleteAt( k: K ): (r: Record) => Record, A> export function deleteAt(k: string): (r: Record) => Record { - return r => { - if (!_hasOwnProperty.call(r, k)) { - return r - } - const out = Object.assign({}, r) - delete out[k] - return out - } + return RR.deleteAt(k) as any } /** * @since 2.0.0 */ -export function updateAt(k: string, a: A): (r: Record) => Option> { - return r => { - if (!hasOwnProperty(k, r)) { - return none - } - if (r[k] === a) { - return optionSome(r) - } - const out = Object.assign({}, r) - out[k] = a - return optionSome(out) - } -} +export const updateAt: (k: string, a: A) => (r: Record) => Option> = RR.updateAt /** * @since 2.0.0 */ -export function modifyAt(k: string, f: (a: A) => A): (r: Record) => Option> { - return r => { - if (!hasOwnProperty(k, r)) { - return none - } - const out = Object.assign({}, r) - out[k] = f(r[k]) - return optionSome(out) - } -} +export const modifyAt: (k: string, f: (a: A) => A) => (r: Record) => Option> = + RR.modifyAt /** * Delete a key and value from a map, returning the value as well as the subsequent map @@ -207,11 +147,7 @@ export function pop( k: K ): (r: Record) => Option<[A, Record, A>]> export function pop(k: string): (r: Record) => Option<[A, Record]> { - const deleteAtk = deleteAt(k) - return r => { - const oa = lookup(k, r) - return isNone(oa) ? none : optionSome([oa.value, deleteAtk(r)]) - } + return RR.pop(k) as any } /** @@ -219,24 +155,14 @@ export function pop(k: string): (r: Record) => Option<[A, Record(E: Eq): (x: Record, y: Record) => boolean { - return (x, y) => { - for (const k in x) { - if (!_hasOwnProperty.call(y, k) || !E.equals(x[k], y[k])) { - return false - } - } - return true - } -} +export const isSubrecord: (E: Eq) => (x: Record, y: Record) => boolean = RR.isSubrecord /** * @since 2.0.0 */ export function getEq(E: Eq): Eq> export function getEq(E: Eq): Eq> { - const isSubrecordE = isSubrecord(E) - return fromEquals((x, y) => isSubrecordE(x, y) && isSubrecordE(y, x)) + return RR.getEq(E) } /** @@ -253,28 +179,7 @@ export function getEq(E: Eq): Eq> { */ export function getMonoid(S: Semigroup): Monoid> export function getMonoid(S: Semigroup): Monoid> { - return { - concat: (x, y) => { - if (x === empty) { - return y - } - if (y === empty) { - return x - } - const keys = Object.keys(y) - const len = keys.length - if (len === 0) { - return x - } - const r: Record = { ...x } - for (let i = 0; i < len; i++) { - const k = keys[i] - r[k] = _hasOwnProperty.call(x, k) ? S.concat(x[k], y[k]) : y[k] - } - return r - }, - empty - } + return RR.getMonoid(S) } /** @@ -282,9 +187,7 @@ export function getMonoid(S: Semigroup): Monoid> { * * @since 2.0.0 */ -export function lookup(k: string, r: Record): Option { - return _hasOwnProperty.call(r, k) ? optionSome(r[k]) : none -} +export const lookup: (k: string, r: Record) => Option = RR.lookup /** * @since 2.0.0 @@ -298,7 +201,7 @@ export const empty: Record = {} */ export function mapWithIndex(f: (k: K, a: A) => B): (fa: Record) => Record export function mapWithIndex(f: (k: string, a: A) => B): (fa: Record) => Record { - return fa => record.mapWithIndex(fa, f) + return RR.mapWithIndex(f) } /** @@ -308,7 +211,7 @@ export function mapWithIndex(f: (k: string, a: A) => B): (fa: Record(f: (a: A) => B): (fa: Record) => Record export function map(f: (a: A) => B): (fa: Record) => Record { - return mapWithIndex((_, a) => f(a)) + return RR.map(f) } /** @@ -316,7 +219,7 @@ export function map(f: (a: A) => B): (fa: Record) => Record(b: B, f: (k: K, b: B, a: A) => B): (fa: Record) => B export function reduceWithIndex(b: B, f: (k: string, b: B, a: A) => B): (fa: Record) => B { - return fa => record.reduceWithIndex(fa, b, f) + return RR.reduceWithIndex(b, f) } /** @@ -326,8 +229,7 @@ export function foldMapWithIndex( M: Monoid ): (f: (k: K, a: A) => M) => (fa: Record) => M export function foldMapWithIndex(M: Monoid): (f: (k: string, a: A) => M) => (fa: Record) => M { - const foldMapWithIndexM = record.foldMapWithIndex(M) - return f => fa => foldMapWithIndexM(fa, f) + return RR.foldMapWithIndex(M) } /** @@ -335,7 +237,7 @@ export function foldMapWithIndex(M: Monoid): (f: (k: string, a: A) => M */ export function reduceRightWithIndex(b: B, f: (k: K, a: A, b: B) => B): (fa: Record) => B export function reduceRightWithIndex(b: B, f: (k: string, a: A, b: B) => B): (fa: Record) => B { - return fa => record.reduceRightWithIndex(fa, b, f) + return RR.reduceRightWithIndex(b, f) } /** @@ -343,9 +245,7 @@ export function reduceRightWithIndex(b: B, f: (k: string, a: A, b: B) => B * * @since 2.0.0 */ -export function singleton(k: K, a: A): Record { - return { [k]: a } as any -} +export const singleton: (k: K, a: A) => Record = RR.singleton /** * @since 2.0.0 @@ -375,8 +275,7 @@ export function traverseWithIndex( export function traverseWithIndex( F: Applicative ): (f: (k: string, a: A) => HKT) => (ta: Record) => HKT> { - const traverseWithIndexF = record.traverseWithIndex(F) - return f => ta => traverseWithIndexF(ta, f) + return RR.traverseWithIndex(F) } /** @@ -403,8 +302,7 @@ export function traverse( export function traverse( F: Applicative ): (f: (a: A) => HKT) => (ta: Record) => HKT> { - const traverseWithIndexF = traverseWithIndex(F) - return f => traverseWithIndexF((_, a) => f(a)) + return RR.traverse(F) } /** @@ -427,7 +325,7 @@ export function sequence( ): (ta: Record>) => Kind> export function sequence(F: Applicative): (ta: Record>) => HKT> export function sequence(F: Applicative): (ta: Record>) => HKT> { - return traverseWithIndex(F)((_, a) => a) + return RR.sequence(F) } /** @@ -439,7 +337,7 @@ export function partitionMapWithIndex( export function partitionMapWithIndex( f: (key: string, a: A) => Either ): (fa: Record) => Separated, Record> { - return fa => record.partitionMapWithIndex(fa, f) + return RR.partitionMapWithIndex(f) } /** @@ -454,7 +352,7 @@ export function partitionWithIndex( export function partitionWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Record) => Separated, Record> { - return fa => record.partitionWithIndex(fa, predicateWithIndex) + return RR.partitionWithIndex(predicateWithIndex) } /** @@ -466,7 +364,7 @@ export function filterMapWithIndex( export function filterMapWithIndex( f: (key: string, a: A) => Option ): (fa: Record) => Record { - return fa => record.filterMapWithIndex(fa, f) + return RR.filterMapWithIndex(f) } /** @@ -481,7 +379,7 @@ export function filterWithIndex( export function filterWithIndex( predicateWithIndex: PredicateWithIndex ): (fa: Record) => Record { - return fa => record.filterWithIndex(fa, predicateWithIndex) + return RR.filterWithIndex(predicateWithIndex) } /** @@ -504,8 +402,7 @@ export function fromFoldable( ): (fka: Kind) => Record export function fromFoldable(M: Magma, F: Foldable): (fka: HKT) => Record export function fromFoldable(M: Magma, F: Foldable): (fka: HKT) => Record { - const fromFoldableMapM = fromFoldableMap(M, F) - return fka => fromFoldableMapM(fka, identity) + return RR.fromFoldable(M, F) } /** @@ -565,56 +462,23 @@ export function fromFoldableMap( M: Magma, F: Foldable ): (fa: HKT, f: (a: A) => [string, B]) => Record { - return (ta: HKT, f: (a: A) => [string, B]) => { - return F.reduce>(ta, {}, (r, a) => { - const [k, b] = f(a) - r[k] = _hasOwnProperty.call(r, k) ? M.concat(r[k], b) : b - return r - }) - } + return RR.fromFoldableMap(M, F) } /** * @since 2.0.0 */ -export function every(predicate: Predicate): (r: Record) => boolean { - return r => { - for (const k in r) { - if (!predicate(r[k])) { - return false - } - } - return true - } -} +export const every: (predicate: Predicate) => (r: Record) => boolean = RR.every /** * @since 2.0.0 */ -export function some(predicate: (a: A) => boolean): (r: Record) => boolean { - return r => { - for (const k in r) { - if (predicate(r[k])) { - return true - } - } - return false - } -} +export const some: (predicate: (a: A) => boolean) => (r: Record) => boolean = RR.some /** * @since 2.0.0 */ -export function elem(E: Eq): (a: A, fa: Record) => boolean { - return (a, fa) => { - for (const k in fa) { - if (E.equals(fa[k], a)) { - return true - } - } - return false - } -} +export const elem: (E: Eq) => (a: A, fa: Record) => boolean = RR.elem /** * @since 2.0.0 @@ -627,192 +491,29 @@ export const record: FunctorWithIndex1 & Witherable1 & FoldableWithIndex1 = { URI, - map: (fa, f) => record.mapWithIndex(fa, (_, a) => f(a)), - reduce: (fa, b, f) => record.reduceWithIndex(fa, b, (_, b, a) => f(b, a)), - foldMap: M => { - const foldMapWithIndexM = record.foldMapWithIndex(M) - return (fa, f) => foldMapWithIndexM(fa, (_, a) => f(a)) - }, - reduceRight: (fa, b, f) => record.reduceRightWithIndex(fa, b, (_, a, b) => f(a, b)), - traverse: ( - F: Applicative - ): ((ta: Record, f: (a: A) => HKT) => HKT>) => { - const traverseWithIndexF = record.traverseWithIndex(F) - return (ta, f) => traverseWithIndexF(ta, (_, a) => f(a)) - }, + map: RR.readonlyRecord.map as any, + reduce: RR.readonlyRecord.reduce as any, + foldMap: RR.readonlyRecord.foldMap as any, + reduceRight: RR.readonlyRecord.reduceRight as any, + traverse: RR.readonlyRecord.traverse as any, sequence, - compact: (fa: Record>): Record => { - const r: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - const optionA = fa[key] - if (isSome(optionA)) { - r[key] = optionA.value - } - } - return r - }, - separate: (fa: Record>): Separated, Record> => { - const left: Record = {} - const right: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - const e = fa[key] - switch (e._tag) { - case 'Left': - left[key] = e.left - break - case 'Right': - right[key] = e.right - break - } - } - return { - left, - right - } - }, - filter: (fa: Record, predicate: Predicate): Record => { - return record.filterWithIndex(fa, (_, a) => predicate(a)) - }, - filterMap: (fa, f) => record.filterMapWithIndex(fa, (_, a) => f(a)), - partition: (fa: Record, predicate: Predicate): Separated, Record> => { - return record.partitionWithIndex(fa, (_, a) => predicate(a)) - }, - partitionMap: (fa, f) => record.partitionMapWithIndex(fa, (_, a) => f(a)), - wither: ( - F: Applicative - ): ((wa: Record, f: (a: A) => HKT>) => HKT>) => { - const traverseF = record.traverse(F) - return (wa, f) => F.map(traverseF(wa, f), record.compact) - }, - wilt: ( - F: Applicative - ): (( - wa: Record, - f: (a: A) => HKT> - ) => HKT, Record>>) => { - const traverseF = record.traverse(F) - return (wa, f) => F.map(traverseF(wa, f), record.separate) - }, - mapWithIndex: (fa: Record, f: (k: string, a: A) => B) => { - const out: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - out[key] = f(key, fa[key]) - } - return out - }, - reduceWithIndex: (fa, b, f) => { - let out = b - const keys = Object.keys(fa).sort() - const len = keys.length - for (let i = 0; i < len; i++) { - const k = keys[i] - out = f(k, out, fa[k]) - } - return out - }, - foldMapWithIndex: M => (fa, f) => { - let out = M.empty - const keys = Object.keys(fa).sort() - const len = keys.length - for (let i = 0; i < len; i++) { - const k = keys[i] - out = M.concat(out, f(k, fa[k])) - } - return out - }, - reduceRightWithIndex: (fa, b, f) => { - let out = b - const keys = Object.keys(fa).sort() - const len = keys.length - for (let i = len - 1; i >= 0; i--) { - const k = keys[i] - out = f(k, fa[k], out) - } - return out - }, - traverseWithIndex: (F: Applicative) => (ta: Record, f: (k: string, a: A) => HKT) => { - const keys = Object.keys(ta) - if (keys.length === 0) { - return F.of(empty) - } - let fr: HKT> = F.of({}) - for (const key of keys) { - fr = F.ap( - F.map(fr, r => (b: B) => { - r[key] = b - return r - }), - f(key, ta[key]) - ) - } - return fr - }, - partitionMapWithIndex: (fa: Record, f: (key: string, a: A) => Either) => { - const left: Record = {} - const right: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - const e = f(key, fa[key]) - switch (e._tag) { - case 'Left': - left[key] = e.left - break - case 'Right': - right[key] = e.right - break - } - } - return { - left, - right - } - }, - partitionWithIndex: (fa: Record, predicateWithIndex: PredicateWithIndex) => { - const left: Record = {} - const right: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - const a = fa[key] - if (predicateWithIndex(key, a)) { - right[key] = a - } else { - left[key] = a - } - } - return { - left, - right - } - }, - filterMapWithIndex: (fa: Record, f: (key: string, a: A) => Option) => { - const r: Record = {} - const keys = Object.keys(fa) - for (const key of keys) { - const optionB = f(key, fa[key]) - if (isSome(optionB)) { - r[key] = optionB.value - } - } - return r - }, - filterWithIndex: (fa: Record, predicateWithIndex: PredicateWithIndex) => { - const out: Record = {} - let changed = false - for (const key in fa) { - if (_hasOwnProperty.call(fa, key)) { - const a = fa[key] - if (predicateWithIndex(key, a)) { - out[key] = a - } else { - changed = true - } - } - } - return changed ? out : fa - } + compact: RR.readonlyRecord.compact as any, + separate: RR.readonlyRecord.separate as any, + filter: RR.readonlyRecord.filter as any, + filterMap: RR.readonlyRecord.filterMap as any, + partition: RR.readonlyRecord.partition as any, + partitionMap: RR.readonlyRecord.partitionMap as any, + wither: RR.readonlyRecord.wither as any, + wilt: RR.readonlyRecord.wilt as any, + mapWithIndex: RR.readonlyRecord.mapWithIndex as any, + reduceWithIndex: RR.readonlyRecord.reduceWithIndex as any, + foldMapWithIndex: RR.readonlyRecord.foldMapWithIndex as any, + reduceRightWithIndex: RR.readonlyRecord.reduceRightWithIndex as any, + traverseWithIndex: RR.readonlyRecord.traverseWithIndex as any, + partitionMapWithIndex: RR.readonlyRecord.partitionMapWithIndex as any, + partitionWithIndex: RR.readonlyRecord.partitionWithIndex as any, + filterMapWithIndex: RR.readonlyRecord.filterMapWithIndex as any, + filterWithIndex: RR.readonlyRecord.filterWithIndex as any } const { filter, filterMap, foldMap, partition, partitionMap, reduce, reduceRight, compact, separate } = pipeable(record) diff --git a/src/Ring.ts b/src/Ring.ts index 7394d54f3..fdce136a8 100644 --- a/src/Ring.ts +++ b/src/Ring.ts @@ -53,7 +53,7 @@ export function negate(ring: Ring): (a: A) => A { * * @since 2.0.0 */ -export function getTupleRing>>( +export function getTupleRing>>( ...rings: T ): Ring<{ [K in keyof T]: T[K] extends Ring ? A : never }> { return { diff --git a/src/Semigroup.ts b/src/Semigroup.ts index 79ad8c430..768d03d05 100644 --- a/src/Semigroup.ts +++ b/src/Semigroup.ts @@ -1,9 +1,10 @@ /** * @since 2.0.0 */ -import { Ord, max, min } from './Ord' import { identity } from './function' import { Magma } from './Magma' +import { max, min, Ord } from './Ord' +import { ReadonlyRecord } from './ReadonlyRecord' /** * A `Semigroup` is a `Magma` where `concat` is associative, that is: @@ -17,7 +18,7 @@ export interface Semigroup extends Magma {} /** * @since 2.0.0 */ -export function fold(S: Semigroup): (a: A, as: Array) => A { +export function fold(S: Semigroup): (a: A, as: ReadonlyArray) => A { return (a, as) => as.reduce(S.concat, a) } @@ -49,7 +50,7 @@ export function getLastSemigroup(): Semigroup { * * @since 2.0.0 */ -export function getTupleSemigroup>>( +export function getTupleSemigroup>>( ...semigroups: T ): Semigroup<{ [K in keyof T]: T[K] extends Semigroup ? A : never }> { return { @@ -78,7 +79,7 @@ export function getFunctionSemigroup(S: Semigroup): () => Semig /** * @since 2.0.0 */ -export function getStructSemigroup( +export function getStructSemigroup>( semigroups: { [K in keyof O]: Semigroup } ): Semigroup { return { diff --git a/src/Set.ts b/src/Set.ts index 5752c05ec..10869bba9 100644 --- a/src/Set.ts +++ b/src/Set.ts @@ -1,33 +1,21 @@ /** * @since 2.0.0 */ +import { Separated } from './Compactable' import { Either } from './Either' +import { Eq } from './Eq' +import { Predicate, Refinement } from './function' import { Monoid } from './Monoid' +import { Option } from './Option' import { Ord } from './Ord' +import * as RS from './ReadonlySet' import { Semigroup } from './Semigroup' -import { Eq, fromEquals } from './Eq' -import { Predicate, not, Refinement, identity } from './function' -import { Separated } from './Compactable' -import { Option } from './Option' import { Show } from './Show' /** * @since 2.0.0 */ -export function getShow(S: Show): Show> { - return { - show: s => { - let elements = '' - s.forEach(a => { - elements += S.show(a) + ', ' - }) - if (elements !== '') { - elements = elements.substring(0, elements.length - 2) - } - return `new Set([${elements}])` - } - } -} +export const getShow: (S: Show) => Show> = RS.getShow /** * @since 2.0.0 @@ -37,91 +25,42 @@ export const empty: Set = new Set() /** * @since 2.0.0 */ -export function toArray(O: Ord): (set: Set) => Array { - return x => { - const r: Array = [] - x.forEach(e => r.push(e)) - return r.sort(O.compare) - } -} +// tslint:disable-next-line: readonly-array +export const toArray: (O: Ord) => (set: Set) => Array = RS.toReadonlyArray as any /** * @since 2.0.0 */ -export function getEq(E: Eq): Eq> { - const subsetE = subset(E) - return fromEquals((x, y) => subsetE(x, y) && subsetE(y, x)) -} +export const getEq: (E: Eq) => Eq> = RS.getEq /** * @since 2.0.0 */ -export function some(predicate: Predicate): (set: Set) => boolean { - return set => { - const values = set.values() - let e: { done?: boolean; value: A } - let found = false - // tslint:disable-next-line: strict-boolean-expressions - while (!found && !(e = values.next()).done) { - found = predicate(e.value) - } - return found - } -} +export const some: (predicate: Predicate) => (set: Set) => boolean = RS.some /** * Projects a Set through a function * * @since 2.0.0 */ -export function map(E: Eq): (f: (x: A) => B) => (set: Set) => Set { - const elemE = elem(E) - return f => set => { - const r = new Set() - set.forEach(e => { - const v = f(e) - if (!elemE(v, r)) { - r.add(v) - } - }) - return r - } -} +export const map: (E: Eq) => (f: (x: A) => B) => (set: Set) => Set = RS.map as any /** * @since 2.0.0 */ -export function every(predicate: Predicate): (set: Set) => boolean { - return not(some(not(predicate))) -} +export const every: (predicate: Predicate) => (set: Set) => boolean = RS.every /** * @since 2.0.0 */ -export function chain(E: Eq): (f: (x: A) => Set) => (set: Set) => Set { - const elemE = elem(E) - return f => set => { - const r = new Set() - set.forEach(e => { - f(e).forEach(e => { - if (!elemE(e, r)) { - r.add(e) - } - }) - }) - return r - } -} +export const chain: (E: Eq) => (f: (x: A) => Set) => (set: Set) => Set = RS.chain as any /** * `true` if and only if every element in the first set is an element of the second set * * @since 2.0.0 */ -export function subset(E: Eq): (x: Set, y: Set) => boolean { - const elemE = elem(E) - return (x, y) => every((a: A) => elemE(a, y))(x) -} +export const subset: (E: Eq) => (x: Set, y: Set) => boolean = RS.isSubset /** * @since 2.0.0 @@ -129,19 +68,7 @@ export function subset(E: Eq): (x: Set, y: Set) => boolean { export function filter(refinement: Refinement): (set: Set) => Set export function filter(predicate: Predicate): (set: Set) => Set export function filter(predicate: Predicate): (set: Set) => Set { - return set => { - const values = set.values() - let e: { done?: boolean; value: A } - const r = new Set() - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = values.next()).done) { - const value = e.value - if (predicate(value)) { - r.add(value) - } - } - return r - } + return RS.filter(predicate) as any } /** @@ -150,22 +77,7 @@ export function filter(predicate: Predicate): (set: Set) => Set { export function partition(refinement: Refinement): (set: Set) => Separated, Set> export function partition(predicate: Predicate): (set: Set) => Separated, Set> export function partition(predicate: Predicate): (set: Set) => Separated, Set> { - return set => { - const values = set.values() - let e: { done?: boolean; value: A } - const right = new Set() - const left = new Set() - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = values.next()).done) { - const value = e.value - if (predicate(value)) { - right.add(value) - } else { - left.add(value) - } - } - return { left, right } - } + return RS.partition(predicate) as any } /** @@ -173,97 +85,29 @@ export function partition(predicate: Predicate): (set: Set) => Separate * * @since 2.0.0 */ -export function elem(E: Eq): (a: A, set: Set) => boolean { - return (a, set) => { - const values = set.values() - let e: { done?: boolean; value: A } - let found = false - // tslint:disable-next-line: strict-boolean-expressions - while (!found && !(e = values.next()).done) { - found = E.equals(a, e.value) - } - return found - } -} +export const elem: (E: Eq) => (a: A, set: Set) => boolean = RS.elem /** * Form the union of two sets * * @since 2.0.0 */ -export function union(E: Eq): (set: Set, y: Set) => Set { - const elemE = elem(E) - return (x, y) => { - if (x === empty) { - return y - } - if (y === empty) { - return x - } - const r = new Set(x) - y.forEach(e => { - if (!elemE(e, r)) { - r.add(e) - } - }) - return r - } -} +export const union: (E: Eq) => (set: Set, y: Set) => Set = RS.union as any /** * The set of elements which are in both the first and second set * * @since 2.0.0 */ -export function intersection(E: Eq): (set: Set, y: Set) => Set { - const elemE = elem(E) - return (x, y) => { - if (x === empty || y === empty) { - return empty - } - const r = new Set() - x.forEach(e => { - if (elemE(e, y)) { - r.add(e) - } - }) - return r - } -} +export const intersection: (E: Eq) => (set: Set, y: Set) => Set = RS.intersection as any /** * @since 2.0.0 */ -export function partitionMap( +export const partitionMap: ( EB: Eq, EC: Eq -): (f: (a: A) => Either) => (set: Set) => Separated, Set> { - return (f: (a: A) => Either) => (set: Set) => { - const values = set.values() - let e: { done?: boolean; value: A } - const left = new Set() - const right = new Set() - const hasB = elem(EB) - const hasC = elem(EC) - // tslint:disable-next-line: strict-boolean-expressions - while (!(e = values.next()).done) { - const v = f(e.value) - switch (v._tag) { - case 'Left': - if (!hasB(v.left, left)) { - left.add(v.left) - } - break - case 'Right': - if (!hasC(v.right, right)) { - right.add(v.right) - } - break - } - } - return { left, right } - } -} +) => (f: (a: A) => Either) => (set: Set) => Separated, Set> = RS.partitionMap as any /** * Form the set difference (`x` - `y`) @@ -277,81 +121,48 @@ export function partitionMap( * * @since 2.0.0 */ -export function difference(E: Eq): (x: Set, y: Set) => Set { - const elemE = elem(E) - return (x, y) => filter((a: A) => !elemE(a, y))(x) -} +export const difference: (E: Eq) => (x: Set, y: Set) => Set = RS.difference as any /** * @since 2.0.0 */ -export function getUnionMonoid(E: Eq): Monoid> { - return { - concat: union(E), - empty - } -} +export const getUnionMonoid: (E: Eq) => Monoid> = RS.getUnionMonoid as any /** * @since 2.0.0 */ -export function getIntersectionSemigroup(E: Eq): Semigroup> { - return { - concat: intersection(E) - } -} +export const getIntersectionSemigroup: (E: Eq) => Semigroup> = RS.getIntersectionSemigroup as any /** * @since 2.0.0 */ -export function reduce(O: Ord): (b: B, f: (b: B, a: A) => B) => (fa: Set) => B { - const toArrayO = toArray(O) - return (b, f) => fa => toArrayO(fa).reduce(f, b) -} +export const reduce: (O: Ord) => (b: B, f: (b: B, a: A) => B) => (fa: Set) => B = RS.reduce /** * @since 2.0.0 */ -export function foldMap(O: Ord, M: Monoid): (f: (a: A) => M) => (fa: Set) => M { - const toArrayO = toArray(O) - return f => fa => toArrayO(fa).reduce((b, a) => M.concat(b, f(a)), M.empty) -} +export const foldMap: (O: Ord, M: Monoid) => (f: (a: A) => M) => (fa: Set) => M = RS.foldMap /** * Create a set with one element * * @since 2.0.0 */ -export function singleton(a: A): Set { - return new Set([a]) -} +export const singleton: (a: A) => Set = RS.singleton as any /** * Insert a value into a set * * @since 2.0.0 */ -export function insert(E: Eq): (a: A) => (set: Set) => Set { - const elemE = elem(E) - return a => set => { - if (!elemE(a, set)) { - const r = new Set(set) - r.add(a) - return r - } else { - return set - } - } -} +export const insert: (E: Eq) => (a: A) => (set: Set) => Set = RS.insert as any /** * Delete a value from a set * * @since 2.0.0 */ -export function remove(E: Eq): (a: A) => (set: Set) => Set { - return a => set => filter((ax: A) => !E.equals(a, ax))(set) -} +export const remove: (E: Eq) => (a: A) => (set: Set) => Set = RS.remove as any /** * Checks an element is a member of a set; @@ -372,68 +183,23 @@ export function toggle(E: Eq): (a: A) => (set: Set) => Set { * * @since 2.0.0 */ -export function fromArray(E: Eq): (as: Array) => Set { - return as => { - const len = as.length - const r = new Set() - const has = elem(E) - for (let i = 0; i < len; i++) { - const a = as[i] - if (!has(a, r)) { - r.add(a) - } - } - return r - } -} +// tslint:disable-next-line: readonly-array +export const fromArray: (E: Eq) => (as: Array) => Set = RS.fromArray as any /** * @since 2.0.0 */ -export function compact(E: Eq): (fa: Set>) => Set { - return filterMap(E)(identity) -} +export const compact: (E: Eq) => (fa: Set>) => Set = RS.compact as any /** * @since 2.0.0 */ -export function separate(EE: Eq, EA: Eq): (fa: Set>) => Separated, Set> { - return fa => { - const elemEE = elem(EE) - const elemEA = elem(EA) - const left: Set = new Set() - const right: Set = new Set() - fa.forEach(e => { - switch (e._tag) { - case 'Left': - if (!elemEE(e.left, left)) { - left.add(e.left) - } - break - case 'Right': - if (!elemEA(e.right, right)) { - right.add(e.right) - } - break - } - }) - return { left, right } - } -} +export const separate: ( + EE: Eq, + EA: Eq +) => (fa: Set>) => Separated, Set> = RS.separate as any /** * @since 2.0.0 */ -export function filterMap(E: Eq): (f: (a: A) => Option) => (fa: Set) => Set { - const elemE = elem(E) - return f => fa => { - const r: Set = new Set() - fa.forEach(a => { - const ob = f(a) - if (ob._tag === 'Some' && !elemE(ob.value, r)) { - r.add(ob.value) - } - }) - return r - } -} +export const filterMap: (E: Eq) => (f: (a: A) => Option) => (fa: Set) => Set = RS.filterMap as any diff --git a/src/Show.ts b/src/Show.ts index 1554070aa..cd207e0d4 100644 --- a/src/Show.ts +++ b/src/Show.ts @@ -1,15 +1,20 @@ +/** + * @since 2.0.0 + */ +import { ReadonlyRecord } from './ReadonlyRecord' + /** * The `Show` type class represents those types which can be converted into * a human-readable `string` representation. * * While not required, it is recommended that for any expression `x`, the - * string `show x` be executable TypeScript code which evaluates to the same + * string `show(x)` be executable TypeScript code which evaluates to the same * value as the expression `x`. * * @since 2.0.0 */ export interface Show { - show: (a: A) => string + readonly show: (a: A) => string } /** @@ -36,7 +41,7 @@ export const showBoolean: Show = { /** * @since 2.0.0 */ -export function getStructShow(shows: { [K in keyof O]: Show }): Show { +export function getStructShow>(shows: { [K in keyof O]: Show }): Show { return { show: s => `{ ${Object.keys(shows) @@ -48,7 +53,7 @@ export function getStructShow(shows: { [K in k /** * @since 2.0.0 */ -export function getTupleShow>>( +export function getTupleShow>>( ...shows: T ): Show<{ [K in keyof T]: T[K] extends Show ? A : never }> { return { diff --git a/src/State.ts b/src/State.ts index ce08a3810..60e4e7c54 100644 --- a/src/State.ts +++ b/src/State.ts @@ -12,7 +12,7 @@ const T = getStateM(identity) declare module './HKT' { interface URItoKind2 { - State: State + readonly State: State } } @@ -26,12 +26,14 @@ export const URI = 'State' */ export type URI = typeof URI +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface State { (s: S): [A, S] } +/* tslint:enable:readonly-array */ /** * Run a computation in the `State` monad, discarding the final state diff --git a/src/StateReaderTaskEither.ts b/src/StateReaderTaskEither.ts index 647c92a05..563a1ec73 100644 --- a/src/StateReaderTaskEither.ts +++ b/src/StateReaderTaskEither.ts @@ -22,7 +22,7 @@ const T = getStateM(RTE.readerTaskEither) declare module './HKT' { interface URItoKind4 { - StateReaderTaskEither: StateReaderTaskEither + readonly StateReaderTaskEither: StateReaderTaskEither } } @@ -36,19 +36,23 @@ export const URI = 'StateReaderTaskEither' */ export type URI = typeof URI +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface StateReaderTaskEither { (s: S): ReaderTaskEither } +/* tslint:enable:readonly-array */ +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export function run(ma: StateReaderTaskEither, s: S, r: R): Promise> { return ma(s)(r)() } +/* tslint:enable:readonly-array */ /** * Run a computation in the `StateReaderTaskEither` monad, discarding the final state @@ -191,7 +195,7 @@ export const gets: (f: (s: S) => A) => StateReaderTa /** * @since 2.4.0 */ -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => StateReaderTaskEither { return (...a) => fromEither(f(...a)) @@ -209,7 +213,7 @@ export function chainEitherK( /** * @since 2.4.0 */ -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => StateReaderTaskEither { return (...a) => fromIOEither(f(...a)) @@ -227,7 +231,7 @@ export function chainIOEitherK( /** * @since 2.4.0 */ -export function fromTaskEitherK, B>( +export function fromTaskEitherK, B>( f: (...a: A) => TaskEither ): (...a: A) => StateReaderTaskEither { return (...a) => fromTaskEither(f(...a)) @@ -245,7 +249,7 @@ export function chainTaskEitherK( /** * @since 2.4.0 */ -export function fromReaderTaskEitherK, B>( +export function fromReaderTaskEitherK, B>( f: (...a: A) => ReaderTaskEither ): (...a: A) => StateReaderTaskEither { return (...a) => fromReaderTaskEither(f(...a)) diff --git a/src/StateT.ts b/src/StateT.ts index 8d0435d4b..78c8e2698 100644 --- a/src/StateT.ts +++ b/src/StateT.ts @@ -5,12 +5,14 @@ import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' import { Monad, Monad1, Monad2, Monad3 } from './Monad' import { State } from './State' +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface StateT { (s: S): HKT } +/* tslint:enable:readonly-array */ /** * @since 2.0.0 @@ -30,12 +32,14 @@ export interface StateM { readonly execState: (ma: StateT, s: S) => HKT } +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface StateT1 { (s: S): Kind } +/* tslint:enable:readonly-array */ /** * @since 2.0.0 @@ -55,12 +59,14 @@ export interface StateM1 { readonly execState: (ma: StateT1, s: S) => Kind } +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface StateT2 { (s: S): Kind2 } +/* tslint:enable:readonly-array */ /** * @since 2.0.0 @@ -80,12 +86,14 @@ export interface StateM2 { readonly execState: (ma: StateT2, s: S) => Kind2 } +/* tslint:disable:readonly-array */ /** * @since 2.0.0 */ export interface StateT3 { (s: S): Kind3 } +/* tslint:enable:readonly-array */ /** * @since 2.0.0 diff --git a/src/Store.ts b/src/Store.ts index ab96818cd..afe7bd325 100644 --- a/src/Store.ts +++ b/src/Store.ts @@ -9,7 +9,7 @@ import { pipeable } from './pipeable' declare module './HKT' { interface URItoKind2 { - Store: Store + readonly Store: Store } } diff --git a/src/Strong.ts b/src/Strong.ts index 0ca0ba6b1..ac7b74fe2 100644 --- a/src/Strong.ts +++ b/src/Strong.ts @@ -32,6 +32,8 @@ import { identity } from './function' import { HKT2, Kind2, Kind3, URIS2, URIS3, URIS4, Kind4 } from './HKT' import { Profunctor, Profunctor2, Profunctor3, Profunctor4 } from './Profunctor' +/* tslint:disable:readonly-array */ + /** * @since 2.0.0 */ diff --git a/src/Task.ts b/src/Task.ts index 02d9dafa8..2a017dbf4 100644 --- a/src/Task.ts +++ b/src/Task.ts @@ -13,7 +13,7 @@ import { Semigroup } from './Semigroup' declare module './HKT' { interface URItoKind { - Task: Task + readonly Task: Task } } @@ -102,7 +102,7 @@ export function of(a: A): Task { /** * @since 2.4.0 */ -export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => Task { +export function fromIOK, B>(f: (...a: A) => IO): (...a: A) => Task { return (...a) => fromIO(f(...a)) } diff --git a/src/TaskEither.ts b/src/TaskEither.ts index a4b6ba5fd..1197c9cab 100644 --- a/src/TaskEither.ts +++ b/src/TaskEither.ts @@ -27,7 +27,7 @@ const T = getEitherM(task) declare module './HKT' { interface URItoKind2 { - TaskEither: TaskEither + readonly TaskEither: TaskEither } } @@ -269,7 +269,7 @@ export function getFilterable(M: Monoid): Filterable2C { /** * @since 2.4.0 */ -export function fromEitherK, B>( +export function fromEitherK, B>( f: (...a: A) => Either ): (...a: A) => TaskEither { return (...a) => fromEither(f(...a)) @@ -285,7 +285,7 @@ export function chainEitherK(f: (a: A) => Either): (ma: TaskEithe /** * @since 2.4.0 */ -export function fromIOEitherK, B>( +export function fromIOEitherK, B>( f: (...a: A) => IOEither ): (...a: A) => TaskEither { return (...a) => fromIOEither(f(...a)) @@ -303,7 +303,7 @@ export function chainIOEitherK(f: (a: A) => IOEither): (ma: TaskE * * @since 2.5.0 */ -export function tryCatchK, B>( +export function tryCatchK, B>( f: (...a: A) => Promise, onRejected: (reason: unknown) => E ): (...a: A) => TaskEither { diff --git a/src/TaskThese.ts b/src/TaskThese.ts index ea5564e84..4b53343c7 100644 --- a/src/TaskThese.ts +++ b/src/TaskThese.ts @@ -19,7 +19,7 @@ const T = getTheseM(task) declare module './HKT' { interface URItoKind2 { - TaskThese: TaskThese + readonly TaskThese: TaskThese } } @@ -117,12 +117,14 @@ export function getMonad(S: Semigroup): Monad2C & MonadTask2C(e: E, a: A): (fa: TaskThese) => Task<[E, A]> { return fa => T.toTuple(fa, e, a) } +/* tslint:enable:readonly-array */ /** * @since 2.4.0 diff --git a/src/These.ts b/src/These.ts index 9f7afb38b..0a106e314 100644 --- a/src/These.ts +++ b/src/These.ts @@ -35,7 +35,7 @@ import { Traversable2 } from './Traversable' declare module './HKT' { interface URItoKind2 { - These: These + readonly These: These } } @@ -190,6 +190,7 @@ export function getMonad(S: Semigroup): Monad2C { } } +/* tslint:disable:readonly-array */ /** * @example * import { toTuple, left, right, both } from 'fp-ts/lib/These' @@ -203,6 +204,7 @@ export function getMonad(S: Semigroup): Monad2C { export function toTuple(e: E, a: A): (fa: These) => [E, A] { return fa => (isLeft(fa) ? [fa.left, a] : isRight(fa) ? [e, fa.right] : [fa.left, fa.right]) } +/* tslint:enable:readonly-array */ /** * Returns an `E` value if possible diff --git a/src/TheseT.ts b/src/TheseT.ts index a57eb420a..179c65aef 100644 --- a/src/TheseT.ts +++ b/src/TheseT.ts @@ -30,6 +30,7 @@ export interface TheseM { readonly left: (e: E) => TheseT readonly right: (a: A) => TheseT readonly both: (e: E, a: A) => TheseT + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT, e: E, a: A) => HKT readonly getMonad: ( S: Semigroup @@ -66,6 +67,7 @@ export interface TheseM1 { readonly left: (e: E) => TheseT1 readonly right: (a: A) => TheseT1 readonly both: (e: E, a: A) => TheseT1 + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT1, e: E, a: A) => Kind readonly getMonad: ( S: Semigroup @@ -102,6 +104,7 @@ export interface TheseM2 { readonly left: (e: E) => TheseT2 readonly right: (a: A) => TheseT2 readonly both: (e: E, a: A) => TheseT2 + // tslint:disable-next-line: readonly-array readonly toTuple: (fa: TheseT2, e: E, a: A) => Kind2 readonly getMonad: ( S: Semigroup diff --git a/src/Traced.ts b/src/Traced.ts index 9d661f91e..f8020a431 100644 --- a/src/Traced.ts +++ b/src/Traced.ts @@ -8,7 +8,7 @@ import { pipeable } from './pipeable' declare module './HKT' { interface URItoKind2 { - Traced: Traced + readonly Traced: Traced } } @@ -38,6 +38,7 @@ export function tracks(M: Monoid

, f: (a: A) => P): (wa: Traced) = return wa => wa(f(wa(M.empty))) } +// tslint:disable:readonly-array /** * Get the current position * @@ -46,7 +47,9 @@ export function tracks(M: Monoid

, f: (a: A) => P): (wa: Traced) = export function listen(wa: Traced): Traced { return e => [wa(e), e] } +// tslint:enable:readonly-array +// tslint:disable:readonly-array /** * Get a value which depends on the current position * @@ -55,6 +58,7 @@ export function listen(wa: Traced): Traced { export function listens(f: (p: P) => B): (wa: Traced) => Traced { return wa => e => [wa(e), f(e)] } +// tslint:enable:readonly-array /** * Apply a function to the current position diff --git a/src/Tree.ts b/src/Tree.ts index 4f26e0dd4..66613123a 100644 --- a/src/Tree.ts +++ b/src/Tree.ts @@ -19,9 +19,11 @@ import { Show } from './Show' import { Traversable1 } from './Traversable' import { pipeable } from './pipeable' +// tslint:disable:readonly-array + declare module './HKT' { interface URItoKind { - Tree: Tree + readonly Tree: Tree } } @@ -231,7 +233,7 @@ export const tree: Monad1 & Foldable1 & Traversable1 & Comonad1 tree.chain(fab, f => tree.map(fa, f)), // <- derived + ap: (fab, fa) => tree.chain(fab, f => tree.map(fa, f)), chain: (fa: Tree, f: (a: A) => Tree): Tree => { const { value, forest } = f(fa.value) const concat = getMonoid>().concat diff --git a/src/Tuple.ts b/src/Tuple.ts index 4e60cff9c..ff7fd1b0e 100644 --- a/src/Tuple.ts +++ b/src/Tuple.ts @@ -1,25 +1,26 @@ /** * @since 2.0.0 */ -import { Applicative, Applicative2C } from './Applicative' +import { Applicative2C } from './Applicative' import { Apply2C } from './Apply' import { Bifunctor2 } from './Bifunctor' import { Chain2C } from './Chain' import { ChainRec2C } from './ChainRec' import { Comonad2 } from './Comonad' -import { Either } from './Either' import { Foldable2 } from './Foldable' -import { HKT } from './HKT' import { Monad2C } from './Monad' import { Monoid } from './Monoid' +import { pipeable } from './pipeable' +import * as RT from './ReadonlyTuple' import { Semigroup } from './Semigroup' import { Semigroupoid2 } from './Semigroupoid' import { Traversable2 } from './Traversable' -import { pipeable } from './pipeable' + +// tslint:disable:readonly-array declare module './HKT' { interface URItoKind2 { - Tuple: [A, E] + readonly Tuple: [A, E] } } @@ -36,115 +37,59 @@ export type URI = typeof URI /** * @since 2.0.0 */ -export function fst(sa: [A, S]): A { - return sa[0] -} +export const fst: (sa: [A, S]) => A = RT.fst /** * @since 2.0.0 */ -export function snd(sa: [A, S]): S { - return sa[1] -} +export const snd: (sa: [A, S]) => S = RT.snd /** * @since 2.0.0 */ -export function swap(sa: [A, S]): [S, A] { - return [snd(sa), fst(sa)] -} +export const swap: (sa: [A, S]) => [S, A] = RT.swap as any /** * @since 2.0.0 */ -export function getApply(S: Semigroup): Apply2C { - return { - URI, - _E: undefined as any, - map: tuple.map, - ap: (fab, fa) => [fst(fab)(fst(fa)), S.concat(snd(fab), snd(fa))] - } -} - -const of = (M: Monoid) => (a: A): [A, S] => { - return [a, M.empty] -} +export const getApply: (S: Semigroup) => Apply2C = RT.getApply as any /** * @since 2.0.0 */ -export function getApplicative(M: Monoid): Applicative2C { - return { - ...getApply(M), - of: of(M) - } -} +export const getApplicative: (M: Monoid) => Applicative2C = RT.getApplicative as any /** * @since 2.0.0 */ -export function getChain(S: Semigroup): Chain2C { - return { - ...getApply(S), - chain: (fa, f) => { - const [b, s] = f(fst(fa)) - return [b, S.concat(snd(fa), s)] - } - } -} +export const getChain: (S: Semigroup) => Chain2C = RT.getChain as any /** * @since 2.0.0 */ -export function getMonad(M: Monoid): Monad2C { - return { - ...getChain(M), - of: of(M) - } -} +export const getMonad: (M: Monoid) => Monad2C = RT.getMonad as any /** * @since 2.0.0 */ -export function getChainRec(M: Monoid): ChainRec2C { - const chainRec = (a: A, f: (a: A) => [Either, S]): [B, S] => { - let result: [Either, S] = f(a) - let acc: S = M.empty - let s: Either = fst(result) - while (s._tag === 'Left') { - acc = M.concat(acc, snd(result)) - result = f(s.left) - s = fst(result) - } - return [s.right, M.concat(acc, snd(result))] - } - - return { - ...getChain(M), - chainRec - } -} +export const getChainRec: (M: Monoid) => ChainRec2C = RT.getChainRec as any /** * @since 2.0.0 */ export const tuple: Semigroupoid2 & Bifunctor2 & Comonad2 & Foldable2 & Traversable2 = { URI, - compose: (ba, ae) => [fst(ba), snd(ae)], - map: (ae, f) => [f(fst(ae)), snd(ae)], - bimap: (fea, f, g) => [g(fst(fea)), f(snd(fea))], - mapLeft: (fea, f) => [fst(fea), f(snd(fea))], + compose: RT.readonlyTuple.compose as any, + map: RT.readonlyTuple.map as any, + bimap: RT.readonlyTuple.bimap as any, + mapLeft: RT.readonlyTuple.mapLeft as any, extract: fst, - extend: (ae, f) => [f(ae), snd(ae)], - reduce: (ae, b, f) => f(b, fst(ae)), - foldMap: _ => (ae, f) => f(fst(ae)), - reduceRight: (ae, b, f) => f(fst(ae), b), - traverse: (F: Applicative) => (as: [A, S], f: (a: A) => HKT): HKT => { - return F.map(f(fst(as)), b => [b, snd(as)]) - }, - sequence: (F: Applicative) => (fas: [HKT, S]): HKT => { - return F.map(fst(fas), a => [a, snd(fas)]) - } + extend: RT.readonlyTuple.extend as any, + reduce: RT.readonlyTuple.reduce as any, + foldMap: RT.readonlyTuple.foldMap as any, + reduceRight: RT.readonlyTuple.reduceRight as any, + traverse: RT.readonlyTuple.traverse as any, + sequence: RT.readonlyTuple.sequence as any } const { bimap, compose, duplicate, extend, foldMap, map, mapLeft, reduce, reduceRight } = pipeable(tuple) diff --git a/src/Unfoldable.ts b/src/Unfoldable.ts index d261ecb9c..0f4861f96 100644 --- a/src/Unfoldable.ts +++ b/src/Unfoldable.ts @@ -6,6 +6,8 @@ import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' import { Option } from './Option' +// tslint:disable:readonly-array + /** * @since 2.0.0 */ diff --git a/src/Witherable.ts b/src/Witherable.ts index adca2d8b7..38c8df3fe 100644 --- a/src/Witherable.ts +++ b/src/Witherable.ts @@ -32,44 +32,44 @@ export interface Witherable extends Traversable, Filterable { /** * Partition a structure with effects */ - wilt: Wilt + readonly wilt: Wilt /** * Filter a structure with effects */ - wither: Wither + readonly wither: Wither } /** * @since 2.0.0 */ export interface Witherable1 extends Traversable1, Filterable1 { - wilt: Wilt1 - wither: Wither1 + readonly wilt: Wilt1 + readonly wither: Wither1 } /** * @since 2.0.0 */ export interface Witherable2 extends Traversable2, Filterable2 { - wilt: Wilt2 - wither: Wither2 + readonly wilt: Wilt2 + readonly wither: Wither2 } /** * @since 2.0.0 */ export interface Witherable2C extends Traversable2C, Filterable2C { - wilt: Wilt2C - wither: Wither2C + readonly wilt: Wilt2C + readonly wither: Wither2C } /** * @since 2.0.0 */ export interface Witherable3 extends Traversable3, Filterable3 { - wilt: Wilt3 - wither: Wither3 + readonly wilt: Wilt3 + readonly wither: Wither3 } /** diff --git a/src/Writer.ts b/src/Writer.ts index f86bb779c..0fcce7322 100644 --- a/src/Writer.ts +++ b/src/Writer.ts @@ -12,7 +12,7 @@ const T = getWriterM(identity) declare module './HKT' { interface URItoKind2 { - Writer: Writer + readonly Writer: Writer } } @@ -26,12 +26,14 @@ export const URI = 'Writer' */ export type URI = typeof URI +// tslint:disable:readonly-array /** * @since 2.0.0 */ export interface Writer { (): [A, W] } +// tslint:enable:readonly-array /** * @since 2.0.0 @@ -50,20 +52,25 @@ export const execWriter: (fa: Writer) => W = T.execWriter */ export const tell: (w: W) => Writer = T.tell +// tslint:disable:readonly-array /** * Modifies the result to include the changes to the accumulator * * @since 2.0.0 */ export const listen: (fa: Writer) => Writer = T.listen +// tslint:enable:readonly-array +// tslint:disable:readonly-array /** * Applies the returned function to the accumulator * * @since 2.0.0 */ export const pass: (fa: Writer W]>) => Writer = T.pass +// tslint:enable:readonly-array +// tslint:disable:readonly-array /** * Projects a value from modifications made to the accumulator during an action * @@ -72,6 +79,7 @@ export const pass: (fa: Writer W]>) => Writer = T.p export function listens(f: (w: W) => B): (fa: Writer) => Writer { return fa => T.listens(fa, f) } +// tslint:enable:readonly-array /** * Modify the final accumulator value by applying a function diff --git a/src/WriterT.ts b/src/WriterT.ts index 029dd9fc8..c95fb5dee 100644 --- a/src/WriterT.ts +++ b/src/WriterT.ts @@ -5,6 +5,8 @@ import { HKT, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from './HKT' import { Monad, Monad1, Monad2, Monad2C, Monad3 } from './Monad' import { Monoid } from './Monoid' +// tslint:disable:readonly-array + /** * @since 2.4.0 */ diff --git a/src/function.ts b/src/function.ts index 142fce16b..f5ee4a744 100644 --- a/src/function.ts +++ b/src/function.ts @@ -40,7 +40,7 @@ export interface Endomorphism { * * @since 2.0.0 */ -export interface FunctionN, B> { +export interface FunctionN, B> { (...args: A): B } @@ -139,27 +139,27 @@ export function flip(f: (a: A, b: B) => C): (b: B, a: A) => C { * * @since 2.0.0 */ -export function flow, B>(ab: (...a: A) => B): (...a: A) => B -export function flow, B, C>(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C -export function flow, B, C, D>( +export function flow, B>(ab: (...a: A) => B): (...a: A) => B +export function flow, B, C>(ab: (...a: A) => B, bc: (b: B) => C): (...a: A) => C +export function flow, B, C, D>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D ): (...a: A) => D -export function flow, B, C, D, E>( +export function flow, B, C, D, E>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E ): (...a: A) => E -export function flow, B, C, D, E, F>( +export function flow, B, C, D, E, F>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F ): (...a: A) => F -export function flow, B, C, D, E, F, G>( +export function flow, B, C, D, E, F, G>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -167,7 +167,7 @@ export function flow, B, C, D, E, F, G>( ef: (e: E) => F, fg: (f: F) => G ): (...a: A) => G -export function flow, B, C, D, E, F, G, H>( +export function flow, B, C, D, E, F, G, H>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -176,7 +176,7 @@ export function flow, B, C, D, E, F, G, H>( fg: (f: F) => G, gh: (g: G) => H ): (...a: A) => H -export function flow, B, C, D, E, F, G, H, I>( +export function flow, B, C, D, E, F, G, H, I>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -186,7 +186,7 @@ export function flow, B, C, D, E, F, G, H, I>( gh: (g: G) => H, hi: (h: H) => I ): (...a: A) => I -export function flow, B, C, D, E, F, G, H, I, J>( +export function flow, B, C, D, E, F, G, H, I, J>( ab: (...a: A) => B, bc: (b: B) => C, cd: (c: C) => D, @@ -250,7 +250,7 @@ export function flow( /** * @since 2.0.0 */ -export function tuple>(...t: T): T { +export function tuple>(...t: T): T { return t } @@ -287,7 +287,7 @@ export function absurd(_: never): A { * * @since 2.4.0 */ -export function tupled, B>(f: (...a: A) => B): (a: A) => B { +export function tupled, B>(f: (...a: A) => B): (a: A) => B { return a => f(...a) } @@ -296,6 +296,6 @@ export function tupled, B>(f: (...a: A) => B): (a: A) = * * @since 2.4.0 */ -export function untupled, B>(f: (a: A) => B): (...a: A) => B { +export function untupled, B>(f: (a: A) => B): (...a: A) => B { return (...a) => f(a) } diff --git a/src/index.ts b/src/index.ts index f443c8133..8099fe524 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,6 +69,12 @@ import * as readerEither from './ReaderEither' import * as readerT from './ReaderT' import * as readerTask from './ReaderTask' import * as readerTaskEither from './ReaderTaskEither' +import * as readonlyArray from './ReadonlyArray' +import * as readonlyMap from './ReadonlyMap' +import * as readonlyNonEmptyArray from './ReadonlyNonEmptyArray' +import * as readonlyRecord from './ReadonlyRecord' +import * as readonlySet from './ReadonlySet' +import * as readonlyTuple from './ReadonlyTuple' import * as record from './Record' import * as ring from './Ring' import * as semigroup from './Semigroup' @@ -357,6 +363,30 @@ export { * @since 2.0.0 */ readerTaskEither, + /** + * @since 2.5.0 + */ + readonlyArray, + /** + * @since 2.5.0 + */ + readonlyMap, + /** + * @since 2.5.0 + */ + readonlyNonEmptyArray, + /** + * @since 2.5.0 + */ + readonlyRecord, + /** + * @since 2.5.0 + */ + readonlySet, + /** + * @since 2.5.0 + */ + readonlyTuple, /** * @since 2.3.0 */ diff --git a/src/pipeable.ts b/src/pipeable.ts index 82b06dd73..b2be59779 100644 --- a/src/pipeable.ts +++ b/src/pipeable.ts @@ -1310,7 +1310,7 @@ const isMonadThrow = (I: any): I is MonadThrow => typeof I.throwError === * @since 2.0.0 */ export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain4 ? PipeableChain4 : I extends Apply4 @@ -1339,7 +1339,7 @@ export function pipeable( (I extends Semigroupoid4 ? PipeableSemigroupoid4 : {}) & (I extends MonadThrow4 ? PipeableMonadThrow4 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain3 ? PipeableChain3 : I extends Apply3 @@ -1368,7 +1368,7 @@ export function pipeable( (I extends Semigroupoid3 ? PipeableSemigroupoid3 : {}) & (I extends MonadThrow3 ? PipeableMonadThrow3 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain3C ? PipeableChain3C : I extends Apply3C @@ -1397,7 +1397,7 @@ export function pipeable( (I extends Semigroupoid3C ? PipeableSemigroupoid3C : {}) & (I extends MonadThrow3C ? PipeableMonadThrow3C : {}) export function pipeable( - I: { URI: F; _E: E } & I + I: { readonly URI: F; readonly _E: E } & I ): (I extends Chain2C ? PipeableChain2C : I extends Apply2C @@ -1425,7 +1425,7 @@ export function pipeable( (I extends Semigroupoid2C ? PipeableSemigroupoid2C : {}) & (I extends MonadThrow2C ? PipeableMonadThrow2C : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain2 ? PipeableChain2 : I extends Apply2 @@ -1454,7 +1454,7 @@ export function pipeable( (I extends Semigroupoid2 ? PipeableSemigroupoid2 : {}) & (I extends MonadThrow2 ? PipeableMonadThrow2 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain1 ? PipeableChain1 : I extends Apply1 @@ -1480,7 +1480,7 @@ export function pipeable( : {}) & (I extends MonadThrow1 ? PipeableMonadThrow1 : {}) export function pipeable( - I: { URI: F } & I + I: { readonly URI: F } & I ): (I extends Chain ? PipeableChain : I extends Apply @@ -1508,7 +1508,7 @@ export function pipeable( (I extends Profunctor ? PipeableProfunctor : {}) & (I extends Semigroupoid ? PipeableSemigroupoid : {}) & (I extends MonadThrow ? PipeableMonadThrow : {}) -export function pipeable(I: { URI: F } & I): Record { +export function pipeable(I: { readonly URI: F } & I): Record { const r: any = {} if (isFunctor(I)) { const map: PipeableFunctor['map'] = f => fa => I.map(fa, f) diff --git a/test/Apply.ts b/test/Apply.ts index f357a8335..0a824feed 100644 --- a/test/Apply.ts +++ b/test/Apply.ts @@ -1,6 +1,6 @@ import * as assert from 'assert' import { sequenceS, sequenceT } from '../src/Apply' -import { array, getMonoid } from '../src/Array' +import { readonlyArray, getMonoid } from '../src/ReadonlyArray' import { either, getValidation, left, right } from '../src/Either' import { none, option, some } from '../src/Option' import { pipe } from '../src/pipeable' @@ -13,11 +13,11 @@ describe('Apply', () => { assert.deepStrictEqual(sequenceTOption(some(1), some('2'), none), none) // #914 - const a1 = [1, 2, 3] - const a2 = ['a', 'b', 'c'] - const a3 = [true, false] + const a1: ReadonlyArray = [1, 2, 3] + const a2: ReadonlyArray = ['a', 'b', 'c'] + const a3: ReadonlyArray = [true, false] assert.deepStrictEqual( - pipe(sequenceT(array)(a1, a2, a3), arr => arr.map(([x, y, z]) => `(${x}, ${y}, ${z})`)), + pipe(sequenceT(readonlyArray)(a1, a2, a3), arr => arr.map(([x, y, z]) => `(${x}, ${y}, ${z})`)), [ '(1, a, true)', '(1, a, false)', @@ -59,11 +59,11 @@ describe('Apply', () => { assert.deepStrictEqual(adoValidation({ a: left(['error1']), b: left(['error2']) }), left(['error1', 'error2'])) // #914 - const a1 = [1, 2, 3] - const a2 = ['a', 'b', 'c'] - const a3 = [true, false] + const a1: ReadonlyArray = [1, 2, 3] + const a2: ReadonlyArray = ['a', 'b', 'c'] + const a3: ReadonlyArray = [true, false] assert.deepStrictEqual( - pipe(sequenceS(array)({ a1, a2, a3 }), arr => arr.map(({ a1, a2, a3 }) => `(${a1}, ${a2}, ${a3})`)), + pipe(sequenceS(readonlyArray)({ a1, a2, a3 }), arr => arr.map(({ a1, a2, a3 }) => `(${a1}, ${a2}, ${a3})`)), [ '(1, a, true)', '(1, a, false)', diff --git a/test/Array.ts b/test/Array.ts index dbbe881b3..2b44b7348 100644 --- a/test/Array.ts +++ b/test/Array.ts @@ -70,6 +70,8 @@ import * as C from '../src/Const' import { showString } from '../src/Show' import { isDeepStrictEqual } from 'util' +// tslint:disable:readonly-array + const p = (n: number) => n > 2 describe('Array', () => { @@ -254,19 +256,19 @@ describe('Array', () => { it('findFirst', () => { assert.deepStrictEqual(findFirst(x => x === 2)([]), O.none) assert.deepStrictEqual( - findFirst((x: { a: number; b: number }) => x.a === 1)([ + findFirst((x: { readonly a: number; readonly b: number }) => x.a === 1)([ { a: 1, b: 1 }, { a: 1, b: 2 } ]), O.some({ a: 1, b: 1 }) ) interface A { - type: 'A' - a: number + readonly type: 'A' + readonly a: number } interface B { - type: 'B' + readonly type: 'B' } type AOrB = A | B @@ -293,14 +295,14 @@ describe('Array', () => { it('findLast', () => { assert.deepStrictEqual(findLast(x => x === 2)([]), O.none) assert.deepStrictEqual( - findLast((x: { a: number; b: number }) => x.a === 1)([ + findLast((x: { readonly a: number; readonly b: number }) => x.a === 1)([ { a: 1, b: 1 }, { a: 1, b: 2 } ]), O.some({ a: 1, b: 2 }) ) assert.deepStrictEqual( - findLast((x: { a: number; b: number }) => x.a === 1)([ + findLast((x: { readonly a: number; readonly b: number }) => x.a === 1)([ { a: 1, b: 2 }, { a: 2, b: 1 } ]), @@ -319,8 +321,8 @@ describe('Array', () => { it('findLastIndex', () => { interface X { - a: number - b: number + readonly a: number + readonly b: number } const xs: Array = [ { a: 1, b: 0 }, @@ -520,8 +522,8 @@ describe('Array', () => { it('uniq', () => { interface A { - a: string - b: number + readonly a: string + readonly b: number } const eqA = eq.contramap(ordNumber, (f: A) => f.b) @@ -553,8 +555,8 @@ describe('Array', () => { it('sortBy', () => { interface Person { - name: string - age: number + readonly name: string + readonly age: number } const byName = ord.contramap(ordString, (p: Person) => p.name) const byAge = ord.contramap(ordNumber, (p: Person) => p.age) @@ -837,7 +839,7 @@ describe('Array', () => { it('should be safe when calling map with a binary function', () => { interface Foo { - bar: () => number + readonly bar: () => number } const f = (a: number, x?: Foo) => (x !== undefined ? `${a}${x.bar()}` : `${a}`) const res = array.map([1, 2], f) diff --git a/test/Console.ts b/test/Console.ts index 74922be53..cfd49932a 100644 --- a/test/Console.ts +++ b/test/Console.ts @@ -5,6 +5,7 @@ describe('Console', () => { it('log', () => { // tslint:disable-next-line:no-console const log_ = console.log + // tslint:disable-next-line: readonly-array const logger: Array = [] // tslint:disable-next-line:no-console console.log = (a: any) => { @@ -19,6 +20,7 @@ describe('Console', () => { it('info', () => { // tslint:disable-next-line:no-console const info_ = console.info + // tslint:disable-next-line: readonly-array const logger: Array = [] // tslint:disable-next-line:no-console console.info = (a: any) => { @@ -33,6 +35,7 @@ describe('Console', () => { it('error', () => { // tslint:disable-next-line:no-console const error_ = console.error + // tslint:disable-next-line: readonly-array const logger: Array = [] // tslint:disable-next-line:no-console console.error = (a: any) => { @@ -47,6 +50,7 @@ describe('Console', () => { it('warn', () => { // tslint:disable-next-line:no-console const warn_ = console.warn + // tslint:disable-next-line: readonly-array const logger: Array = [] // tslint:disable-next-line:no-console console.warn = (a: any) => { diff --git a/test/Either.ts b/test/Either.ts index f22c550ef..79aef9f19 100644 --- a/test/Either.ts +++ b/test/Either.ts @@ -173,8 +173,8 @@ describe('Either', () => { _.left(true) ) interface Person { - name: string - age: number + readonly name: string + readonly age: number } const person: Person = { name: 'Giulio', age: 45 } assert.deepStrictEqual(_.stringifyJSON(person, _.toError), _.right('{"name":"Giulio","age":45}')) diff --git a/test/Eq.ts b/test/Eq.ts index 9a0555db0..85c731cd9 100644 --- a/test/Eq.ts +++ b/test/Eq.ts @@ -11,12 +11,12 @@ describe('Eq', () => { }) interface Person { - name: string - age: number + readonly name: string + readonly age: number } it('fromEquals', () => { interface A { - x: number + readonly x: number } let nbCall = 0 const S1 = fromEquals((a, b) => { diff --git a/test/FoldableWithIndex.ts b/test/FoldableWithIndex.ts index 07df78884..0af0b025c 100644 --- a/test/FoldableWithIndex.ts +++ b/test/FoldableWithIndex.ts @@ -1,18 +1,18 @@ import * as assert from 'assert' -import { array } from '../src/Array' +import { readonlyArray } from '../src/ReadonlyArray' import { getFoldableWithIndexComposition } from '../src/FoldableWithIndex' import { monoidString } from '../src/Monoid' describe('FoldableWithIndex', () => { it('getFoldableWithIndexComposition', () => { - const arrayOfArray = getFoldableWithIndexComposition(array, array) - const fa = [ + const arrayOfArray = getFoldableWithIndexComposition(readonlyArray, readonlyArray) + const fa: ReadonlyArray> = [ ['a', 'b'], ['c', 'd'] ] assert.deepStrictEqual( - arrayOfArray.reduceWithIndex(fa, '', ([i, j]: [number, number], b: string, a: string) => b + a + i + j), + arrayOfArray.reduceWithIndex(fa, '', ([i, j], b: string, a: string) => b + a + i + j), 'a00b01c10d11' ) @@ -22,7 +22,7 @@ describe('FoldableWithIndex', () => { ) assert.deepStrictEqual( - arrayOfArray.reduceRightWithIndex(fa, '', ([i, j]: [number, number], a: string, b: string) => b + a + i + j), + arrayOfArray.reduceRightWithIndex(fa, '', ([i, j], a: string, b: string) => b + a + i + j), 'd11c10b01a00' ) }) diff --git a/test/FunctorWithIndex.ts b/test/FunctorWithIndex.ts index 947fc2473..72b9ab988 100644 --- a/test/FunctorWithIndex.ts +++ b/test/FunctorWithIndex.ts @@ -5,7 +5,7 @@ import { getFunctorWithIndexComposition } from '../src/FunctorWithIndex' describe('FunctorWithIndex', () => { it('getFunctorComposition', () => { const arrayOfArray = getFunctorWithIndexComposition(array, array) - const f = ([i, j]: [number, number], a: string) => a + i + j + const f = ([i, j]: readonly [number, number], a: string) => a + i + j assert.deepStrictEqual( arrayOfArray.mapWithIndex( [ diff --git a/test/IO.ts b/test/IO.ts index f8a5e3b1b..130ecedcd 100644 --- a/test/IO.ts +++ b/test/IO.ts @@ -19,6 +19,7 @@ describe('IO', () => { it('getSemigroup', () => { const S = getSemigroup(semigroupSum) + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): IO => () => log.push(message) assert.deepStrictEqual(S.concat(append('a'), append('b'))(), 3) @@ -27,6 +28,7 @@ describe('IO', () => { it('getMonoid', () => { const M = getMonoid(monoidSum) + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): IO => () => log.push(message) assert.deepStrictEqual(M.concat(append('a'), M.empty)(), 1) diff --git a/test/IOEither.ts b/test/IOEither.ts index f7728a24f..f6ae2a1ff 100644 --- a/test/IOEither.ts +++ b/test/IOEither.ts @@ -190,6 +190,7 @@ describe('IOEither', () => { }) describe('bracket', () => { + // tslint:disable-next-line: readonly-array let log: Array = [] const acquireFailure = _.left('acquire failure') diff --git a/test/Map.ts b/test/Map.ts index 0832aece4..68069c18d 100644 --- a/test/Map.ts +++ b/test/Map.ts @@ -12,7 +12,7 @@ import { ord, ordString, fromCompare, ordNumber } from '../src/Ord' import { showString, getStructShow, Show } from '../src/Show' interface User { - id: string + readonly id: string } const ordUser = ord.contramap(ordString, (u: User) => u.id) @@ -22,11 +22,11 @@ const eqUser: Eq = { equals: ordUser.equals } const p = ((n: number): boolean => n > 2) as Refinement interface Key { - id: number + readonly id: number } interface Value { - value: number + readonly value: number } const eqKey: Eq = fromEquals((x, y) => x.id % 3 === y.id % 3) @@ -157,7 +157,7 @@ describe('Map', () => { assert.deepStrictEqual(collectO(f)(m2), [2, 3]) const collect = M.collect(ordKey) - const g = (k: Key, a: Value): [number, number] => [k.id, a.value] + const g = (k: Key, a: Value): readonly [number, number] => [k.id, a.value] assert.deepStrictEqual( collect(g)( new Map([ diff --git a/test/NonEmptyArray.ts b/test/NonEmptyArray.ts index edaab3680..2ff572416 100644 --- a/test/NonEmptyArray.ts +++ b/test/NonEmptyArray.ts @@ -3,14 +3,14 @@ import * as C from '../src/Const' import { eqNumber } from '../src/Eq' import { identity } from '../src/function' import * as I from '../src/Identity' -import { fold, monoidString, monoidSum } from '../src/Monoid' +import * as M from '../src/Monoid' import { concat, cons, copy, filter, filterWithIndex, - fold as nfold, + fold, foldMap, foldMapWithIndex, fromArray, @@ -37,7 +37,7 @@ import { } from '../src/NonEmptyArray' import { isSome, none, option, some } from '../src/Option' import { ordNumber } from '../src/Ord' -import { semigroupSum, semigroupString } from '../src/Semigroup' +import { semigroupString, semigroupSum } from '../src/Semigroup' import { showString } from '../src/Show' describe('NonEmptyArray', () => { @@ -69,12 +69,12 @@ describe('NonEmptyArray', () => { }) it('chain', () => { - const f = (a: number) => [a, 4] as [number, number] + const f = (a: number): NonEmptyArray => [a, 4] assert.deepStrictEqual(nonEmptyArray.chain([1, 2], f), [1, 4, 2, 4]) }) it('extend', () => { - const sum = fold(monoidSum) + const sum = fold(M.monoidSum) assert.deepStrictEqual(nonEmptyArray.extend([1, 2, 3, 4], sum), [10, 9, 7, 4]) }) @@ -117,7 +117,7 @@ describe('NonEmptyArray', () => { }) it('foldMap', () => { - const foldMap = nonEmptyArray.foldMap(monoidString) + const foldMap = nonEmptyArray.foldMap(M.monoidString) assert.deepStrictEqual(foldMap(['a', 'b', 'c'], identity), 'abc') }) @@ -174,9 +174,7 @@ describe('NonEmptyArray', () => { }) it('reverse', () => { - const result = reverse([1, 2, 3]) - const expected = [3, 2, 1] - assert.deepStrictEqual(result, expected) + assert.deepStrictEqual(reverse([1, 2, 3]), [3, 2, 1]) }) it('groupBy', () => { @@ -207,7 +205,7 @@ describe('NonEmptyArray', () => { const a2 = make2(1) const a3 = make2(2) const a4 = make2(3) - const arr: NonEmptyArray<{ x: number }> = [a1, a2, a3] + const arr: NonEmptyArray<{ readonly x: number }> = [a1, a2, a3] assert.deepStrictEqual(updateAt(0, a4)(arr), some([a4, a2, a3])) assert.deepStrictEqual(updateAt(-1, a4)(arr), none) assert.deepStrictEqual(updateAt(3, a4)(arr), none) @@ -276,7 +274,7 @@ describe('NonEmptyArray', () => { it('foldMapWithIndex', () => { assert.deepStrictEqual( - nonEmptyArray.foldMapWithIndex(monoidString)(['a', 'b'], (i, a) => i + a), + nonEmptyArray.foldMapWithIndex(M.monoidString)(['a', 'b'], (i, a) => i + a), '0a1b' ) }) @@ -299,11 +297,10 @@ describe('NonEmptyArray', () => { ) // FoldableWithIndex compatibility - const M = monoidString const f = (i: number, s: string): string => s + i assert.deepStrictEqual( - nonEmptyArray.foldMapWithIndex(M)(['a', 'bb'], f), - nonEmptyArray.traverseWithIndex(C.getApplicative(M))(['a', 'bb'], (i, a) => C.make(f(i, a))) + nonEmptyArray.foldMapWithIndex(M.monoidString)(['a', 'bb'], f), + nonEmptyArray.traverseWithIndex(C.getApplicative(M.monoidString))(['a', 'bb'], (i, a) => C.make(f(i, a))) ) // FunctorWithIndex compatibility @@ -348,7 +345,7 @@ describe('NonEmptyArray', () => { }) it('fold', () => { - const f = nfold(semigroupString) + const f = fold(semigroupString) assert.deepStrictEqual(f(['a']), 'a') assert.deepStrictEqual(f(['a', 'bb']), 'abb') }) diff --git a/test/Option.ts b/test/Option.ts index 564645926..10295b203 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -90,10 +90,10 @@ describe('Option', () => { it('mapNullable', () => { interface X { - a?: { - b?: { - c?: { - d: number + readonly a?: { + readonly b?: { + readonly c?: { + readonly d: number } } } @@ -374,8 +374,8 @@ describe('Option', () => { const isString = O.getRefinement(f) assert.deepStrictEqual(isString('s'), true) assert.deepStrictEqual(isString(1), false) - type A = { type: 'A' } - type B = { type: 'B' } + type A = { readonly type: 'A' } + type B = { readonly type: 'B' } type C = A | B const isA = O.getRefinement(c => (c.type === 'A' ? O.some(c) : O.none)) assert.deepStrictEqual(isA({ type: 'A' }), true) diff --git a/test/Ord.ts b/test/Ord.ts index b428defc9..4b0004cfe 100644 --- a/test/Ord.ts +++ b/test/Ord.ts @@ -1,5 +1,5 @@ import * as assert from 'assert' -import { sort } from '../src/Array' +import { sort } from '../src/ReadonlyArray' import { ord, between, @@ -26,8 +26,8 @@ describe('Ord', () => { }) it('getMonoid', () => { - type T = [number, string] - const tuples: Array = [ + type T = readonly [number, string] + const tuples: ReadonlyArray = [ [2, 'c'], [1, 'b'], [2, 'a'], @@ -114,7 +114,7 @@ describe('Ord', () => { assert.deepStrictEqual(O1.equals(0, 1), false) assert.deepStrictEqual(O1.equals(1, 1), true) interface A { - x: number + readonly x: number } let nbCall = 0 const O2 = fromCompare((a, b) => { diff --git a/test/Reader.ts b/test/Reader.ts index bfa760fb9..600d56f19 100644 --- a/test/Reader.ts +++ b/test/Reader.ts @@ -5,7 +5,7 @@ import { monoidSum } from '../src/Monoid' import { pipe } from '../src/pipeable' interface Env { - count: number + readonly count: number } describe('Reader', () => { @@ -33,7 +33,7 @@ describe('Reader', () => { it('local', () => { interface E { - name: string + readonly name: string } const x = pipe( (s: string) => s.length, @@ -46,7 +46,7 @@ describe('Reader', () => { const x = (s: string) => s.length const y = R.reader.promap( x, - (a: { name: string }) => a.name, + (a: { readonly name: string }) => a.name, n => n >= 2 ) assert.deepStrictEqual(y({ name: 'foo' }), true) diff --git a/test/ReaderEither.ts b/test/ReaderEither.ts index 034f5f4b5..1cee6a5b5 100644 --- a/test/ReaderEither.ts +++ b/test/ReaderEither.ts @@ -109,11 +109,14 @@ describe('ReaderEither', () => { }) it('asks', () => { - assert.deepStrictEqual(_.asks((r: { a: number }) => r.a)({ a: 1 }), E.right(1)) + assert.deepStrictEqual(_.asks((r: { readonly a: number }) => r.a)({ a: 1 }), E.right(1)) }) it('local', () => { - assert.deepStrictEqual(_.local((n: number) => ({ a: n }))((r: { a: number }) => E.right(r.a))(1), E.right(1)) + assert.deepStrictEqual( + _.local((n: number) => ({ a: n }))((r: { readonly a: number }) => E.right(r.a))(1), + E.right(1) + ) }) describe('getReaderValidation', () => { diff --git a/test/ReaderTask.ts b/test/ReaderTask.ts index a0a48b281..314a028ff 100644 --- a/test/ReaderTask.ts +++ b/test/ReaderTask.ts @@ -91,6 +91,7 @@ describe('ReaderTask', () => { }) it('sequence parallel', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.ReaderTask<{}, number> => _.fromTask(() => Promise.resolve(log.push(message))) const t1 = _.readerTask.chain(append('start 1'), () => append('end 1')) @@ -102,6 +103,7 @@ describe('ReaderTask', () => { }) it('sequence series', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.ReaderTask<{}, number> => _.fromTask(() => Promise.resolve(log.push(message))) const t1 = _.readerTask.chain(append('start 1'), () => append('end 1')) diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts index 394de4b8b..fe1febf83 100644 --- a/test/ReaderTaskEither.ts +++ b/test/ReaderTaskEither.ts @@ -187,22 +187,23 @@ describe('ReaderTaskEither', () => { assert.deepStrictEqual(e3, E.left('b')) }) - it('fromPredicate', () => { + it('fromPredicate', async () => { const predicate = (n: number) => n >= 2 const gt2 = _.fromPredicate(predicate, n => `Invalid number ${n}`) const refinement = (u: string | number): u is number => typeof u === 'number' const isNumber = _.fromPredicate(refinement, u => `Invalid number ${String(u)}`) - const rtes = [gt2(3), gt2(1), isNumber(4)] - return Promise.all(rtes.map(rte => _.run(rte, {}))).then(([e1, e2, e3]) => { - assert.deepStrictEqual(e1, E.right(3)) - assert.deepStrictEqual(e2, E.left('Invalid number 1')) - assert.deepStrictEqual(e3, E.right(4)) - }) + const e1 = await _.run(gt2(3), {}) + const e2 = await _.run(gt2(1), {}) + const e3 = await _.run(isNumber(4), {}) + assert.deepStrictEqual(e1, E.right(3)) + assert.deepStrictEqual(e2, E.left('Invalid number 1')) + assert.deepStrictEqual(e3, E.right(4)) }) it('sequence parallel', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.ReaderTaskEither<{}, void, number> => _.rightTask(() => Promise.resolve(log.push(message))) @@ -215,6 +216,7 @@ describe('ReaderTaskEither', () => { }) it('sequence series', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.ReaderTaskEither<{}, void, number> => _.rightTask(() => Promise.resolve(log.push(message))) @@ -404,6 +406,7 @@ describe('ReaderTaskEither', () => { }) describe('bracket', () => { + // tslint:disable-next-line: readonly-array let log: Array = [] const acquireFailure = _.left('acquire failure') diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts new file mode 100644 index 000000000..3bef0e3f6 --- /dev/null +++ b/test/ReadonlyArray.ts @@ -0,0 +1,823 @@ +import * as assert from 'assert' +import * as fc from 'fast-check' +import { isDeepStrictEqual } from 'util' +import * as C from '../src/Const' +import * as E from '../src/Either' +import * as Eq from '../src/Eq' +import * as F from '../src/function' +import * as I from '../src/Identity' +import * as M from '../src/Monoid' +import * as O from '../src/Option' +import * as Ord from '../src/Ord' +import * as _ from '../src/ReadonlyArray' +import { showString } from '../src/Show' + +describe('ReadonlyArray', () => { + const as: ReadonlyArray = [1, 2, 3] + + it('alt', () => { + assert.deepStrictEqual( + _.readonlyArray.alt([1, 2], () => [3, 4]), + [1, 2, 3, 4] + ) + }) + + it('getMonoid', () => { + const M = _.getMonoid() + assert.deepStrictEqual(M.concat([1, 2], [3, 4]), [1, 2, 3, 4]) + assert.deepStrictEqual(M.concat([1, 2], M.empty), [1, 2]) + assert.deepStrictEqual(M.concat(M.empty, [1, 2]), [1, 2]) + }) + + it('getEq', () => { + const O = _.getEq(Ord.ordString) + assert.deepStrictEqual(O.equals([], []), true, '[] ]') + assert.deepStrictEqual(O.equals(['a'], ['a']), true, '[a], [a]') + assert.deepStrictEqual(O.equals(['a', 'b'], ['a', 'b']), true, '[a, b], [a, b]') + assert.deepStrictEqual(O.equals(['a'], []), false, '[a] []') + assert.deepStrictEqual(O.equals([], ['a']), false, '[], [a]') + assert.deepStrictEqual(O.equals(['a'], ['b']), false, '[a], [b]') + assert.deepStrictEqual(O.equals(['a', 'b'], ['b', 'a']), false, '[a, b], [b, a]') + assert.deepStrictEqual(O.equals(['a', 'a'], ['a']), false, '[a, a], [a]') + }) + + it('getOrd', () => { + const O = _.getOrd(Ord.ordString) + assert.deepStrictEqual(O.compare([], []), 0, '[] ]') + assert.deepStrictEqual(O.compare(['a'], ['a']), 0, '[a], [a]') + + assert.deepStrictEqual(O.compare(['b'], ['a']), 1, '[b], [a]') + assert.deepStrictEqual(O.compare(['a'], ['b']), -1, '[a], [b]') + + assert.deepStrictEqual(O.compare(['a'], []), 1, '[a] []') + assert.deepStrictEqual(O.compare([], ['a']), -1, '[], [a]') + assert.deepStrictEqual(O.compare(['a', 'a'], ['a']), 1, '[a, a], [a]') + assert.deepStrictEqual(O.compare(['a', 'a'], ['b']), -1, '[a, a], [a]') + + assert.deepStrictEqual(O.compare(['a', 'a'], ['a', 'a']), 0, '[a, a], [a, a]') + assert.deepStrictEqual(O.compare(['a', 'b'], ['a', 'b']), 0, '[a, b], [a, b]') + + assert.deepStrictEqual(O.compare(['a', 'a'], ['a', 'b']), -1, '[a, a], [a, b]') + assert.deepStrictEqual(O.compare(['a', 'b'], ['a', 'a']), 1, '[a, b], [a, a]') + + assert.deepStrictEqual(O.compare(['a', 'b'], ['b', 'a']), -1, '[a, b], [b, a]') + assert.deepStrictEqual(O.compare(['b', 'a'], ['a', 'a']), 1, '[b, a], [a, a]') + assert.deepStrictEqual(O.compare(['b', 'a'], ['a', 'b']), 1, '[b, b], [a, a]') + assert.deepStrictEqual(O.compare(['b', 'b'], ['b', 'a']), 1, '[b, b], [b, a]') + assert.deepStrictEqual(O.compare(['b', 'a'], ['b', 'b']), -1, '[b, a], [b, b]') + }) + + it('ap', () => { + const as = _.readonlyArray.ap([x => x * 2, x => x * 3], [1, 2, 3]) + assert.deepStrictEqual(as, [2, 4, 6, 3, 6, 9]) + }) + + it('traverse', () => { + const tfanone: ReadonlyArray = [1, 2] + const f = (n: number): O.Option => (n % 2 === 0 ? O.none : O.some(n)) + const fasnone = _.readonlyArray.traverse(O.option)(tfanone, f) + assert.deepStrictEqual(O.isNone(fasnone), true) + const tfa: ReadonlyArray = [1, 3] + const fas = _.readonlyArray.traverse(O.option)(tfa, f) + assert.deepStrictEqual(fas, O.some([1, 3])) + }) + + it('sequence', () => { + assert.deepStrictEqual(_.readonlyArray.sequence(O.option)([O.some(1), O.some(3)]), O.some([1, 3])) + assert.deepStrictEqual(_.readonlyArray.sequence(O.option)([O.some(1), O.none]), O.none) + }) + + it('unfold', () => { + const as = _.readonlyArray.unfold(5, n => (n > 0 ? O.some([n, n - 1]) : O.none)) + assert.deepStrictEqual(as, [5, 4, 3, 2, 1]) + }) + + it('isEmpty', () => { + assert.deepStrictEqual(_.isEmpty(as), false) + assert.deepStrictEqual(_.isEmpty([]), true) + }) + + it('isNotEmpty', () => { + assert.deepStrictEqual(_.isNonEmpty(as), true) + assert.deepStrictEqual(_.isNonEmpty([]), false) + }) + + it('cons', () => { + assert.deepStrictEqual(_.cons(0, as), [0, 1, 2, 3]) + assert.deepStrictEqual(_.cons([1], [[2]]), [[1], [2]]) + }) + + it('snoc', () => { + assert.deepStrictEqual(_.snoc(as, 4), [1, 2, 3, 4]) + assert.deepStrictEqual(_.snoc([[1]], [2]), [[1], [2]]) + }) + + it('head', () => { + assert.deepStrictEqual(_.head(as), O.some(1)) + assert.deepStrictEqual(_.head([]), O.none) + }) + + it('last', () => { + assert.deepStrictEqual(_.last(as), O.some(3)) + assert.deepStrictEqual(_.last([]), O.none) + }) + + it('tail', () => { + assert.deepStrictEqual(_.tail(as), O.some([2, 3])) + assert.deepStrictEqual(_.tail([]), O.none) + }) + + it('takeLeft', () => { + assert.deepStrictEqual(_.takeLeft(2)([]), []) + assert.deepStrictEqual(_.takeLeft(2)([1, 2, 3]), [1, 2]) + assert.deepStrictEqual(_.takeLeft(0)([1, 2, 3]), []) + }) + + it('takeRight', () => { + assert.deepStrictEqual(_.takeRight(2)([1, 2, 3, 4, 5]), [4, 5]) + assert.deepStrictEqual(_.takeRight(0)([1, 2, 3, 4, 5]), []) + assert.deepStrictEqual(_.takeRight(2)([]), []) + assert.deepStrictEqual(_.takeRight(5)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.takeRight(10)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + }) + + it('spanLeft', () => { + assert.deepStrictEqual(_.spanLeft((n: number) => n % 2 === 1)([1, 3, 2, 4, 5]), { init: [1, 3], rest: [2, 4, 5] }) + + // refinements + const xs: ReadonlyArray = [1, 'a', 3] + const isNumber = (u: string | number): u is number => typeof u === 'number' + const actual = _.spanLeft(isNumber)(xs) + assert.deepStrictEqual(actual, { init: [1], rest: ['a', 3] }) + }) + + it('takeLeftWhile', () => { + const f = (n: number) => n % 2 === 0 + assert.deepStrictEqual(_.takeLeftWhile(f)([2, 4, 3, 6]), [2, 4]) + assert.deepStrictEqual(_.takeLeftWhile(f)([]), []) + assert.deepStrictEqual(_.takeLeftWhile(f)([1, 2, 4]), []) + assert.deepStrictEqual(_.takeLeftWhile(f)([2, 4]), [2, 4]) + }) + + it('dropLeft', () => { + assert.deepStrictEqual(_.dropLeft(2)([1, 2, 3]), [3]) + assert.deepStrictEqual(_.dropLeft(10)([1, 2, 3]), []) + assert.deepStrictEqual(_.dropLeft(0)([1, 2, 3]), [1, 2, 3]) + }) + + it('dropRight', () => { + assert.deepStrictEqual(_.dropRight(2)([1, 2, 3, 4, 5]), [1, 2, 3]) + assert.deepStrictEqual(_.dropRight(10)([1, 2, 3, 4, 5]), []) + assert.deepStrictEqual(_.dropRight(0)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + }) + + it('dropLeftWhile', () => { + const f = (n: number) => n % 2 === 0 + const g = (n: number) => n % 2 === 1 + assert.deepStrictEqual(_.dropLeftWhile(f)([1, 3, 2, 4, 5]), [1, 3, 2, 4, 5]) + assert.deepStrictEqual(_.dropLeftWhile(g)([1, 3, 2, 4, 5]), [2, 4, 5]) + assert.deepStrictEqual(_.dropLeftWhile(f)([]), []) + assert.deepStrictEqual(_.dropLeftWhile(f)([2, 4, 1]), [1]) + assert.deepStrictEqual(_.dropLeftWhile(f)([2, 4]), []) + }) + + it('init', () => { + assert.deepStrictEqual(_.init(as), O.some([1, 2])) + assert.deepStrictEqual(_.init([]), O.none) + }) + + it('findIndex', () => { + assert.deepStrictEqual(_.findIndex(x => x === 2)([1, 2, 3]), O.some(1)) + assert.deepStrictEqual(_.findIndex(x => x === 2)([]), O.none) + }) + + it('findFirst', () => { + assert.deepStrictEqual(_.findFirst(x => x === 2)([]), O.none) + assert.deepStrictEqual( + _.findFirst((x: { readonly a: number; readonly b: number }) => x.a === 1)([ + { a: 1, b: 1 }, + { a: 1, b: 2 } + ]), + O.some({ a: 1, b: 1 }) + ) + interface A { + readonly type: 'A' + readonly a: number + } + + interface B { + readonly type: 'B' + } + + type AOrB = A | B + const isA = (x: AOrB): x is A => x.type === 'A' + const xs1: ReadonlyArray = [{ type: 'B' }, { type: 'A', a: 1 }, { type: 'A', a: 2 }] + assert.deepStrictEqual(_.findFirst(isA)(xs1), O.some({ type: 'A', a: 1 })) + const xs2: ReadonlyArray = [{ type: 'B' }] + assert.deepStrictEqual(_.findFirst(isA)(xs2), O.none) + assert.deepStrictEqual(_.findFirst((x: string | null) => x === null)([null, 'a']), O.some(null)) + }) + + const optionStringEq = O.getEq(Eq.eqString) + const multipleOf3: F.Predicate = (x: number) => x % 3 === 0 + const multipleOf3AsString = (x: number) => O.option.map(O.fromPredicate(multipleOf3)(x), x => `${x}`) + + it('`findFirstMap(arr, fun)` is equivalent to map and `head(mapOption(arr, fun)`', () => { + fc.assert( + fc.property(fc.array(fc.integer()), arr => + optionStringEq.equals( + _.findFirstMap(multipleOf3AsString)(arr), + _.head(_.readonlyArray.filterMap(arr, multipleOf3AsString)) + ) + ) + ) + }) + + it('findLast', () => { + assert.deepStrictEqual(_.findLast(x => x === 2)([]), O.none) + assert.deepStrictEqual( + _.findLast((x: { readonly a: number; readonly b: number }) => x.a === 1)([ + { a: 1, b: 1 }, + { a: 1, b: 2 } + ]), + O.some({ a: 1, b: 2 }) + ) + assert.deepStrictEqual( + _.findLast((x: { readonly a: number; readonly b: number }) => x.a === 1)([ + { a: 1, b: 2 }, + { a: 2, b: 1 } + ]), + O.some({ a: 1, b: 2 }) + ) + assert.deepStrictEqual(_.findLast((x: string | null) => x === null)(['a', null]), O.some(null)) + }) + + it('`findLastMap(arr, fun)` is equivalent to `last(mapOption(arr, fun))`', () => { + fc.assert( + fc.property(fc.array(fc.integer()), arr => + optionStringEq.equals( + _.findLastMap(multipleOf3AsString)(arr), + _.last(_.readonlyArray.filterMap(arr, multipleOf3AsString)) + ) + ) + ) + }) + + it('findLastIndex', () => { + interface X { + readonly a: number + readonly b: number + } + const xs: ReadonlyArray = [ + { a: 1, b: 0 }, + { a: 1, b: 1 } + ] + assert.deepStrictEqual(_.findLastIndex((x: X) => x.a === 1)(xs), O.some(1)) + assert.deepStrictEqual(_.findLastIndex((x: X) => x.a === 4)(xs), O.none) + assert.deepStrictEqual(_.findLastIndex((x: X) => x.a === 1)([]), O.none) + }) + + it('insertAt', () => { + assert.deepStrictEqual(_.insertAt(1, 1)([]), O.none) + assert.deepStrictEqual(_.insertAt(0, 1)([]), O.some([1])) + assert.deepStrictEqual(_.insertAt(2, 5)([1, 2, 3, 4]), O.some([1, 2, 5, 3, 4])) + }) + + it('unsafeUpdateAt', () => { + // should return the same reference if nothing changed + const x = { a: 1 } + const as: ReadonlyArray<{ readonly a: number }> = [x] + const result = _.unsafeUpdateAt(0, x, as) + assert.deepStrictEqual(result, as) + }) + + it('updateAt', () => { + assert.deepStrictEqual(_.updateAt(1, 1)(as), O.some([1, 1, 3])) + assert.deepStrictEqual(_.updateAt(1, 1)([]), O.none) + }) + + it('deleteAt', () => { + assert.deepStrictEqual(_.deleteAt(0)(as), O.some([2, 3])) + assert.deepStrictEqual(_.deleteAt(1)([]), O.none) + }) + + it('modifyAt', () => { + const double = (x: number): number => x * 2 + assert.deepStrictEqual(_.modifyAt(1, double)(as), O.some([1, 4, 3])) + assert.deepStrictEqual(_.modifyAt(1, double)([]), O.none) + }) + + it('sort', () => { + assert.deepStrictEqual(_.sort(Ord.ordNumber)([3, 2, 1]), [1, 2, 3]) + }) + + it('extend', () => { + const sum = (as: ReadonlyArray) => M.fold(M.monoidSum)(as) + assert.deepStrictEqual(_.readonlyArray.extend([1, 2, 3, 4], sum), [10, 9, 7, 4]) + assert.deepStrictEqual(_.readonlyArray.extend([1, 2, 3, 4], F.identity), [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]) + }) + + it('zipWith', () => { + assert.deepStrictEqual( + _.zipWith([1, 2, 3], ['a', 'b', 'c', 'd'], (n, s) => s + n), + ['a1', 'b2', 'c3'] + ) + }) + + it('zip', () => { + assert.deepStrictEqual(_.zip([1, 2, 3], ['a', 'b', 'c', 'd']), [ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]) + }) + + it('unzip', () => { + assert.deepStrictEqual( + _.unzip([ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]), + [ + [1, 2, 3], + ['a', 'b', 'c'] + ] + ) + }) + + it('rights', () => { + assert.deepStrictEqual(_.rights([E.right(1), E.left('foo'), E.right(2)]), [1, 2]) + assert.deepStrictEqual(_.rights([]), []) + }) + + it('lefts', () => { + assert.deepStrictEqual(_.lefts([E.right(1), E.left('foo'), E.right(2)]), ['foo']) + assert.deepStrictEqual(_.lefts([]), []) + }) + + it('flatten', () => { + assert.deepStrictEqual(_.flatten([[1], [2], [3]]), [1, 2, 3]) + }) + + it('rotate', () => { + assert.deepStrictEqual(_.rotate(1)([]), []) + assert.deepStrictEqual(_.rotate(1)([1]), [1]) + assert.deepStrictEqual(_.rotate(1)([1, 2]), [2, 1]) + assert.deepStrictEqual(_.rotate(2)([1, 2]), [1, 2]) + assert.deepStrictEqual(_.rotate(0)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.rotate(1)([1, 2, 3, 4, 5]), [5, 1, 2, 3, 4]) + assert.deepStrictEqual(_.rotate(2)([1, 2, 3, 4, 5]), [4, 5, 1, 2, 3]) + assert.deepStrictEqual(_.rotate(-1)([1, 2, 3, 4, 5]), [2, 3, 4, 5, 1]) + assert.deepStrictEqual(_.rotate(-2)([1, 2, 3, 4, 5]), [3, 4, 5, 1, 2]) + }) + + it('map', () => { + assert.deepStrictEqual( + _.readonlyArray.map([1, 2, 3], n => n * 2), + [2, 4, 6] + ) + }) + + it('mapWithIndex', () => { + assert.deepStrictEqual( + _.readonlyArray.mapWithIndex([1, 2, 3], (i, n) => n + i), + [1, 3, 5] + ) + }) + + it('ap', () => { + assert.deepStrictEqual(_.readonlyArray.ap([(n: number) => n * 2, (n: number) => n + 1], [1, 2, 3]), [ + 2, + 4, + 6, + 2, + 3, + 4 + ]) + }) + + it('chain', () => { + assert.deepStrictEqual( + _.readonlyArray.chain([1, 2, 3], n => [n, n + 1]), + [1, 2, 2, 3, 3, 4] + ) + }) + + it('reverse', () => { + assert.deepStrictEqual(_.reverse([1, 2, 3]), [3, 2, 1]) + }) + + it('reduce', () => { + assert.deepStrictEqual( + _.readonlyArray.reduce(['a', 'b', 'c'], '', (acc, a) => acc + a), + 'abc' + ) + }) + + it('foldMap', () => { + const foldMap = _.readonlyArray.foldMap(M.monoidString) + const x1: ReadonlyArray = ['a', 'b', 'c'] + const f1 = F.identity + assert.deepStrictEqual(foldMap(x1, f1), 'abc') + const x2: ReadonlyArray = [] + assert.deepStrictEqual(foldMap(x2, f1), '') + }) + + it('reduceRight', () => { + const reduceRight = _.readonlyArray.reduceRight + const x1: ReadonlyArray = ['a', 'b', 'c'] + const init1 = '' + const f1 = (a: string, acc: string) => acc + a + assert.deepStrictEqual(reduceRight(x1, init1, f1), 'cba') + const x2: ReadonlyArray = [] + assert.deepStrictEqual(reduceRight(x2, init1, f1), '') + }) + + it('foldLeft', () => { + const len: (as: ReadonlyArray) => number = _.foldLeft( + () => 0, + (_, tail) => 1 + len(tail) + ) + assert.deepStrictEqual(len([1, 2, 3]), 3) + }) + + it('foldRight', () => { + const len: (as: ReadonlyArray) => number = _.foldRight( + () => 0, + (init, _) => 1 + len(init) + ) + assert.deepStrictEqual(len([1, 2, 3]), 3) + }) + + it('scanLeft', () => { + const f = (b: number, a: number) => b - a + assert.deepStrictEqual(_.scanLeft(10, f)([1, 2, 3]), [10, 9, 7, 4]) + assert.deepStrictEqual(_.scanLeft(10, f)([0]), [10, 10]) + assert.deepStrictEqual(_.scanLeft(10, f)([]), [10]) + }) + + it('scanRight', () => { + const f = (b: number, a: number) => b - a + assert.deepStrictEqual(_.scanRight(10, f)([1, 2, 3]), [-8, 9, -7, 10]) + assert.deepStrictEqual(_.scanRight(10, f)([0]), [-10, 10]) + assert.deepStrictEqual(_.scanRight(10, f)([]), [10]) + }) + + it('uniq', () => { + interface A { + readonly a: string + readonly b: number + } + + const eqA = Eq.eq.contramap(Ord.ordNumber, (f: A) => f.b) + const arrA: A = { a: 'a', b: 1 } + const arrB: A = { a: 'b', b: 1 } + const arrC: A = { a: 'c', b: 2 } + const arrD: A = { a: 'd', b: 2 } + const arrUniq: ReadonlyArray = [arrA, arrC] + + assert.deepStrictEqual(_.uniq(eqA)(arrUniq), arrUniq, 'Preserve original array') + assert.deepStrictEqual(_.uniq(eqA)([arrA, arrB, arrC, arrD]), [arrA, arrC]) + assert.deepStrictEqual(_.uniq(eqA)([arrB, arrA, arrC, arrD]), [arrB, arrC]) + assert.deepStrictEqual(_.uniq(eqA)([arrA, arrA, arrC, arrD, arrA]), [arrA, arrC]) + assert.deepStrictEqual(_.uniq(eqA)([arrA, arrC]), [arrA, arrC]) + assert.deepStrictEqual(_.uniq(eqA)([arrC, arrA]), [arrC, arrA]) + assert.deepStrictEqual(_.uniq(Eq.eqBoolean)([true, false, true, false]), [true, false]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([]), []) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([-0, -0]), [-0]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([0, -0]), [0]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([1]), [1]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([2, 1, 2]), [2, 1]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([1, 2, 1]), [1, 2]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.uniq(Eq.eqNumber)([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.uniq(Eq.eqString)(['a', 'b', 'a']), ['a', 'b']) + assert.deepStrictEqual(_.uniq(Eq.eqString)(['a', 'b', 'A']), ['a', 'b', 'A']) + }) + + it('sortBy', () => { + interface Person { + readonly name: string + readonly age: number + } + const byName = Ord.ord.contramap(Ord.ordString, (p: Person) => p.name) + const byAge = Ord.ord.contramap(Ord.ordNumber, (p: Person) => p.age) + const sortByNameByAge = _.sortBy([byName, byAge]) + const persons: ReadonlyArray = [ + { name: 'a', age: 1 }, + { name: 'b', age: 3 }, + { name: 'c', age: 2 }, + { name: 'b', age: 2 } + ] + assert.deepStrictEqual(sortByNameByAge(persons), [ + { name: 'a', age: 1 }, + { name: 'b', age: 2 }, + { name: 'b', age: 3 }, + { name: 'c', age: 2 } + ]) + const sortByAgeByName = _.sortBy([byAge, byName]) + assert.deepStrictEqual(sortByAgeByName(persons), [ + { name: 'a', age: 1 }, + { name: 'b', age: 2 }, + { name: 'c', age: 2 }, + { name: 'b', age: 3 } + ]) + + assert.deepStrictEqual(_.sortBy([])(persons), persons) + }) + + it('compact', () => { + assert.deepStrictEqual(_.readonlyArray.compact([]), []) + assert.deepStrictEqual(_.readonlyArray.compact([O.some(1), O.some(2), O.some(3)]), [1, 2, 3]) + assert.deepStrictEqual(_.readonlyArray.compact([O.some(1), O.none, O.some(3)]), [1, 3]) + }) + + it('separate', () => { + assert.deepStrictEqual(_.readonlyArray.separate([]), { left: [], right: [] }) + assert.deepStrictEqual(_.readonlyArray.separate([E.left(123), E.right('123')]), { left: [123], right: ['123'] }) + }) + + it('filter', () => { + const filter = _.readonlyArray.filter + const g = (n: number) => n % 2 === 1 + assert.deepStrictEqual(filter([1, 2, 3], g), [1, 3]) + assert.deepStrictEqual(_.readonlyArray.filter([1, 2, 3], g), [1, 3]) + const x = filter([O.some(3), O.some(2), O.some(1)], O.isSome) + assert.deepStrictEqual(x, [O.some(3), O.some(2), O.some(1)]) + const y = filter([O.some(3), O.none, O.some(1)], O.isSome) + assert.deepStrictEqual(y, [O.some(3), O.some(1)]) + }) + + it('filterWithIndex', () => { + const f = (n: number) => n % 2 === 0 + assert.deepStrictEqual(_.readonlyArray.filterWithIndex(['a', 'b', 'c'], f), ['a', 'c']) + }) + + it('filterMap', () => { + const f = (n: number) => (n % 2 === 0 ? O.none : O.some(n)) + assert.deepStrictEqual(_.readonlyArray.filterMap(as, f), [1, 3]) + assert.deepStrictEqual(_.readonlyArray.filterMap([], f), []) + }) + + it('partitionMap', () => { + assert.deepStrictEqual(_.readonlyArray.partitionMap([], F.identity), { left: [], right: [] }) + assert.deepStrictEqual(_.readonlyArray.partitionMap([E.right(1), E.left('foo'), E.right(2)], F.identity), { + left: ['foo'], + right: [1, 2] + }) + }) + + it('partition', () => { + const partition = _.readonlyArray.partition + assert.deepStrictEqual( + partition([], (n: number) => n > 2), + { left: [], right: [] } + ) + assert.deepStrictEqual( + partition([1, 3], (n: number) => n > 2), + { left: [1], right: [3] } + ) + // refinements + const xs: ReadonlyArray = ['a', 'b', 1] + const isNumber = (x: string | number): x is number => typeof x === 'number' + const actual = partition(xs, isNumber) + assert.deepStrictEqual(actual, { left: ['a', 'b'], right: [1] }) + }) + + it('wither', () => { + const witherIdentity = _.readonlyArray.wither(I.identity) + const f = (n: number) => I.identity.of(n > 2 ? O.some(n + 1) : O.none) + assert.deepStrictEqual(witherIdentity([], f), I.identity.of([])) + assert.deepStrictEqual(witherIdentity([1, 3], f), I.identity.of([4])) + }) + + it('wilt', () => { + const wiltIdentity = _.readonlyArray.wilt(I.identity) + const f = (n: number) => I.identity.of(n > 2 ? E.right(n + 1) : E.left(n - 1)) + assert.deepStrictEqual(wiltIdentity([], f), I.identity.of({ left: [], right: [] })) + assert.deepStrictEqual(wiltIdentity([1, 3], f), I.identity.of({ left: [0], right: [4] })) + }) + + it('chop', () => { + const group = (E: Eq.Eq): ((as: ReadonlyArray) => ReadonlyArray>) => { + return _.chop(as => { + const { init, rest } = _.spanLeft((a: A) => E.equals(a, as[0]))(as) + return [init, rest] + }) + } + assert.deepStrictEqual(group(Eq.eqNumber)([1, 1, 2, 3, 3, 4]), [[1, 1], [2], [3, 3], [4]]) + }) + + it('splitAt', () => { + assert.deepStrictEqual(_.splitAt(2)([1, 2, 3, 4, 5]), [ + [1, 2], + [3, 4, 5] + ]) + assert.deepStrictEqual(_.splitAt(2)([]), [[], []]) + assert.deepStrictEqual(_.splitAt(2)([1]), [[1], []]) + assert.deepStrictEqual(_.splitAt(2)([1, 2]), [[1, 2], []]) + assert.deepStrictEqual(_.splitAt(-1)([1, 2]), [[1], [2]]) + assert.deepStrictEqual(_.splitAt(0)([1, 2]), [[], [1, 2]]) + assert.deepStrictEqual(_.splitAt(3)([1, 2]), [[1, 2], []]) + }) + + describe('chunksOf', () => { + it('should split an array into length-n pieces', () => { + assert.deepStrictEqual(_.chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]]) + assert.deepStrictEqual(_.chunksOf(2)([1, 2, 3, 4, 5, 6]), [ + [1, 2], + [3, 4], + [5, 6] + ]) + assert.deepStrictEqual(_.chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) + assert.deepStrictEqual(_.chunksOf(6)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]]) + assert.deepStrictEqual(_.chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]]) + assert.deepStrictEqual(_.chunksOf(0)([1, 2]), [[1, 2]]) + assert.deepStrictEqual(_.chunksOf(10)([1, 2]), [[1, 2]]) + assert.deepStrictEqual(_.chunksOf(-1)([1, 2]), [[1, 2]]) + }) + + // #897 + it('returns an empty array if provided an empty array', () => { + assert.deepStrictEqual(_.chunksOf(1)([]), []) + assert.deepStrictEqual(_.chunksOf(2)([]), []) + assert.deepStrictEqual(_.chunksOf(0)([]), []) + }) + + // #897 + it('should respect the law: RA.chunksOf(n)(xs).concat(RA.chunksOf(n)(ys)) == RA.chunksOf(n)(xs.concat(ys)))', () => { + const xs: ReadonlyArray = [] + const ys: ReadonlyArray = [1, 2] + assert.deepStrictEqual(_.chunksOf(2)(xs).concat(_.chunksOf(2)(ys)), _.chunksOf(2)(xs.concat(ys))) + fc.assert( + fc.property( + fc.array(fc.integer()).filter(xs => xs.length % 2 === 0), // Ensures `xs.length` is even + fc.array(fc.integer()), + fc.integer(1, 1).map(x => x * 2), // Generates `n` to be even so that it evenly divides `xs` + (xs, ys, n) => { + const as = _.chunksOf(n)(xs).concat(_.chunksOf(n)(ys)) + const bs = _.chunksOf(n)(xs.concat(ys)) + isDeepStrictEqual(as, bs) + } + ) + ) + }) + }) + + it('makeBy', () => { + const double = (n: number): number => n * 2 + assert.deepStrictEqual(_.makeBy(5, double), [0, 2, 4, 6, 8]) + }) + + it('range', () => { + assert.deepStrictEqual(_.range(0, 0), [0]) + assert.deepStrictEqual(_.range(1, 5), [1, 2, 3, 4, 5]) + assert.deepStrictEqual(_.range(10, 15), [10, 11, 12, 13, 14, 15]) + }) + + it('replicate', () => { + assert.deepStrictEqual(_.replicate(0, 'a'), []) + assert.deepStrictEqual(_.replicate(3, 'a'), ['a', 'a', 'a']) + }) + + it('comprehension', () => { + assert.deepStrictEqual( + _.comprehension([[1, 2, 3]], a => a * 2), + [2, 4, 6] + ) + assert.deepStrictEqual( + _.comprehension( + [ + [1, 2, 3], + ['a', 'b'] + ], + F.tuple + ), + [ + [1, 'a'], + [1, 'b'], + [2, 'a'], + [2, 'b'], + [3, 'a'], + [3, 'b'] + ] + ) + assert.deepStrictEqual( + _.comprehension( + [ + [1, 2, 3], + ['a', 'b'] + ], + F.tuple, + (a, b) => (a + b.length) % 2 === 0 + ), + [ + [1, 'a'], + [1, 'b'], + [3, 'a'], + [3, 'b'] + ] + ) + }) + + it('reduceWithIndex', () => { + assert.deepStrictEqual( + _.readonlyArray.reduceWithIndex(['a', 'b'], '', (i, b, a) => b + i + a), + '0a1b' + ) + }) + + it('foldMapWithIndex', () => { + assert.deepStrictEqual( + _.readonlyArray.foldMapWithIndex(M.monoidString)(['a', 'b'], (i, a) => i + a), + '0a1b' + ) + }) + + it('reduceRightWithIndex', () => { + assert.deepStrictEqual( + _.readonlyArray.reduceRightWithIndex(['a', 'b'], '', (i, a, b) => b + i + a), + '1b0a' + ) + }) + + it('traverseWithIndex', () => { + const ta: ReadonlyArray = ['a', 'bb'] + assert.deepStrictEqual( + _.readonlyArray.traverseWithIndex(O.option)(ta, (i, s) => (s.length >= 1 ? O.some(s + i) : O.none)), + O.some(['a0', 'bb1']) + ) + assert.deepStrictEqual( + _.readonlyArray.traverseWithIndex(O.option)(ta, (i, s) => (s.length > 1 ? O.some(s + i) : O.none)), + O.none + ) + + // FoldableWithIndex compatibility + const f = (i: number, s: string): string => s + i + assert.deepStrictEqual( + _.readonlyArray.foldMapWithIndex(M.monoidString)(ta, f), + _.readonlyArray.traverseWithIndex(C.getApplicative(M.monoidString))(ta, (i, a) => C.make(f(i, a))) + ) + + // FunctorWithIndex compatibility + assert.deepStrictEqual( + _.readonlyArray.mapWithIndex(ta, f), + _.readonlyArray.traverseWithIndex(I.identity)(ta, (i, a) => I.identity.of(f(i, a))) + ) + }) + + it('union', () => { + assert.deepStrictEqual(_.union(Eq.eqNumber)([1, 2], [3, 4]), [1, 2, 3, 4]) + assert.deepStrictEqual(_.union(Eq.eqNumber)([1, 2], [2, 3]), [1, 2, 3]) + assert.deepStrictEqual(_.union(Eq.eqNumber)([1, 2], [1, 2]), [1, 2]) + }) + + it('intersection', () => { + assert.deepStrictEqual(_.intersection(Eq.eqNumber)([1, 2], [3, 4]), []) + assert.deepStrictEqual(_.intersection(Eq.eqNumber)([1, 2], [2, 3]), [2]) + assert.deepStrictEqual(_.intersection(Eq.eqNumber)([1, 2], [1, 2]), [1, 2]) + }) + + it('difference', () => { + assert.deepStrictEqual(_.difference(Eq.eqNumber)([1, 2], [3, 4]), [1, 2]) + assert.deepStrictEqual(_.difference(Eq.eqNumber)([1, 2], [2, 3]), [1]) + assert.deepStrictEqual(_.difference(Eq.eqNumber)([1, 2], [1, 2]), []) + }) + + it('should be safe when calling map with a binary function', () => { + interface Foo { + readonly bar: () => number + } + const f = (a: number, x?: Foo) => (x !== undefined ? `${a}${x.bar()}` : `${a}`) + const res = _.readonlyArray.map([1, 2], f) + assert.deepStrictEqual(res, ['1', '2']) + }) + + it('getShow', () => { + const S = _.getShow(showString) + assert.deepStrictEqual(S.show([]), `[]`) + assert.deepStrictEqual(S.show(['a']), `["a"]`) + assert.deepStrictEqual(S.show(['a', 'b']), `["a", "b"]`) + }) + + it('fromArray', () => { + assert.strictEqual(_.fromArray([]), _.empty) + // tslint:disable-next-line: readonly-array + const as = [1, 2, 3] + const bs = _.fromArray(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) + + it('toArray', () => { + assert.deepStrictEqual(_.toArray(_.empty), []) + assert.notStrictEqual(_.toArray(_.empty), _.empty) + // tslint:disable-next-line: readonly-array + const as = [1, 2, 3] + const bs = _.toArray(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) +}) diff --git a/test/ReadonlyMap.ts b/test/ReadonlyMap.ts new file mode 100644 index 000000000..b2c58204e --- /dev/null +++ b/test/ReadonlyMap.ts @@ -0,0 +1,1059 @@ +import * as assert from 'assert' +import * as _ from '../src/ReadonlyMap' +import { semigroupSum, getStructSemigroup, getFirstSemigroup, getLastSemigroup } from '../src/Semigroup' +import { monoidString } from '../src/Monoid' +import { Refinement, identity } from '../src/function' +import { option, some, none, Option } from '../src/Option' +import { Eq, eqNumber, fromEquals } from '../src/Eq' +import { array } from '../src/Array' +import { Either, left, right } from '../src/Either' +import * as I from '../src/Identity' +import { ord, ordString, fromCompare, ordNumber } from '../src/Ord' +import { showString, getStructShow, Show } from '../src/Show' + +interface User { + readonly id: string +} + +const ordUser = ord.contramap(ordString, (u: User) => u.id) + +const eqUser: Eq = { equals: ordUser.equals } + +const p = ((n: number): boolean => n > 2) as Refinement + +interface Key { + readonly id: number +} + +interface Value { + readonly value: number +} + +const eqKey: Eq = fromEquals((x, y) => x.id % 3 === y.id % 3) + +const ordKey = fromCompare((x, y) => ordNumber.compare(x.id % 3, y.id % 3)) + +const eqValue: Eq = fromEquals((x, y) => x.value % 3 === y.value % 3) + +const semigroupValue = getStructSemigroup({ value: semigroupSum }) + +const key1 = { id: 1 } +const value1 = { value: 1 } +const repo = new Map([ + [key1, value1], + [{ id: 2 }, { value: 2 }] +]) + +describe('ReadonlyMap', () => { + it('size', () => { + const emptyMap = new Map() + const a1 = new Map([['a', 1]]) + assert.deepStrictEqual(_.size(emptyMap), 0) + assert.deepStrictEqual(_.size(a1), 1) + + assert.deepStrictEqual(_.size(_.empty), 0) + assert.deepStrictEqual(_.size(new Map()), 0) + assert.deepStrictEqual(_.size(new Map([['a', 1]])), 1) + }) + + it('isEmpty', () => { + const emptyMap = new Map() + const a1 = new Map([['a', 1]]) + assert.deepStrictEqual(_.isEmpty(emptyMap), true) + assert.deepStrictEqual(_.isEmpty(a1), false) + + assert.deepStrictEqual(_.isEmpty(_.empty), true) + assert.deepStrictEqual(_.isEmpty(new Map()), true) + assert.deepStrictEqual(_.isEmpty(new Map([['a', 1]])), false) + }) + + it('member', () => { + const a1b2 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const memberS = _.member(eqUser) + assert.deepStrictEqual(memberS({ id: 'a' }, a1b2), true) + assert.deepStrictEqual(memberS({ id: 'c' }, a1b2), false) + + const member = _.member(eqKey) + assert.deepStrictEqual(member({ id: 1 }, repo), true) + assert.deepStrictEqual(member({ id: 2 }, repo), true) + assert.deepStrictEqual(member({ id: 4 }, repo), true) + assert.deepStrictEqual(member({ id: 3 }, repo), false) + }) + + it('elem', () => { + const a1b2 = new Map([ + ['a', 1], + ['b', 2] + ]) + const elemS = _.elem(eqNumber) + assert.deepStrictEqual(elemS(2, a1b2), true) + assert.deepStrictEqual(elemS(3, a1b2), false) + + const elem = _.elem(eqValue) + assert.deepStrictEqual(elem({ value: 1 }, repo), true) + assert.deepStrictEqual(elem({ value: 2 }, repo), true) + assert.deepStrictEqual(elem({ value: 4 }, repo), true) + assert.deepStrictEqual(elem({ value: 3 }, repo), false) + }) + + it('keys', () => { + const m = new Map([ + [{ id: 'b' }, 2], + [{ id: 'a' }, 1] + ]) + const ks = _.keys(ordUser)(m) + assert.deepStrictEqual(ks, Array.from(m.keys()).sort(ordUser.compare)) + assert.deepStrictEqual(ks, [{ id: 'a' }, { id: 'b' }]) + + assert.deepStrictEqual( + _.keys(ordString)( + new Map([ + ['a', 1], + ['b', 2] + ]) + ), + ['a', 'b'] + ) + assert.deepStrictEqual( + _.keys(ordString)( + new Map([ + ['b', 2], + ['a', 1] + ]) + ), + ['a', 'b'] + ) + }) + + it('values', () => { + const m = new Map([ + [2, { id: 'b' }], + [1, { id: 'a' }] + ]) + const vals = _.values(ordUser)(m) + assert.deepStrictEqual(vals, Array.from(m.values()).sort(ordUser.compare)) + assert.deepStrictEqual(vals, [{ id: 'a' }, { id: 'b' }]) + }) + + it('collect', () => { + const m1 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const m2 = new Map([ + [{ id: 'b' }, 2], + [{ id: 'a' }, 1] + ]) + const collectO = _.collect(ordUser) + const f = (_k: User, a: number): number => a + 1 + assert.deepStrictEqual(collectO(f)(m1), [2, 3]) + assert.deepStrictEqual(collectO(f)(m2), [2, 3]) + + const collect = _.collect(ordKey) + const g = (k: Key, a: Value): readonly [number, number] => [k.id, a.value] + assert.deepStrictEqual( + collect(g)( + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ), + [ + [1, 1], + [2, 2] + ] + ) + assert.deepStrictEqual( + collect(g)( + new Map([ + [{ id: 2 }, { value: 2 }], + [{ id: 1 }, { value: 1 }] + ]) + ), + [ + [1, 1], + [2, 2] + ] + ) + assert.deepStrictEqual( + collect(g)( + new Map([ + [{ id: 4 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ), + [ + [4, 1], + [2, 2] + ] + ) + assert.deepStrictEqual( + collect(g)( + new Map([ + [{ id: 2 }, { value: 2 }], + [{ id: 4 }, { value: 1 }] + ]) + ), + [ + [4, 1], + [2, 2] + ] + ) + }) + + it('toReadonlyArray', () => { + const m1 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const m2 = new Map([ + [{ id: 'b' }, 2], + [{ id: 'a' }, 1] + ]) + const toArrayO = _.toReadonlyArray(ordUser) + assert.deepStrictEqual(toArrayO(m1), [ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + assert.deepStrictEqual(toArrayO(m2), [ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + + const toArray = _.toReadonlyArray(ordKey) + assert.deepStrictEqual( + toArray( + new Map([ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ]) + ), + [ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toArray( + new Map([ + [{ id: 2 }, 2], + [{ id: 1 }, 1] + ]) + ), + [ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toArray( + new Map([ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ]) + ), + [ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toArray( + new Map([ + [{ id: 2 }, 2], + [{ id: 4 }, 1] + ]) + ), + [ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ] + ) + }) + + it('toUnfoldable', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const toUnfoldableO = _.toUnfoldable(ordUser, array) + assert.deepStrictEqual(toUnfoldableO(a1), [[{ id: 'a' }, 1]]) + + const toUnfoldable = _.toUnfoldable(ordKey, array) + assert.deepStrictEqual( + toUnfoldable( + new Map([ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ]) + ), + [ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toUnfoldable( + new Map([ + [{ id: 2 }, 2], + [{ id: 1 }, 1] + ]) + ), + [ + [{ id: 1 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toUnfoldable( + new Map([ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ]) + ), + [ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ] + ) + assert.deepStrictEqual( + toUnfoldable( + new Map([ + [{ id: 2 }, 2], + [{ id: 4 }, 1] + ]) + ), + [ + [{ id: 4 }, 1], + [{ id: 2 }, 2] + ] + ) + }) + + it('insertAt', () => { + const emptyMap = new Map() + const a1 = new Map([[{ id: 'a' }, 1]]) + const a1b2 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const a2b2 = new Map([ + [{ id: 'a' }, 2], + [{ id: 'b' }, 2] + ]) + const a1b2c3 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2], + [{ id: 'c' }, 3] + ]) + const insertS = _.insertAt(eqUser) + assert.deepStrictEqual(insertS({ id: 'a' }, 1)(emptyMap), a1) + assert.deepStrictEqual(insertS({ id: 'a' }, 1)(a1b2), a1b2) + assert.deepStrictEqual(insertS({ id: 'a' }, 2)(a1b2), a2b2) + assert.deepStrictEqual(insertS({ id: 'c' }, 3)(a1b2), a1b2c3) + + const insert = _.insertAt(eqKey) + assert.deepStrictEqual(insert({ id: 1 }, { value: 1 })(_.empty), new Map([[{ id: 1 }, { value: 1 }]])) + const x = insert({ id: 1 }, value1)(repo) + assert.deepStrictEqual( + x, + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + assert.deepStrictEqual(x.get(key1), value1) + assert.deepStrictEqual( + insert({ id: 1 }, { value: 2 })(repo), + new Map([ + [{ id: 1 }, { value: 2 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + assert.deepStrictEqual( + insert({ id: 4 }, { value: 2 })(repo), + new Map([ + [{ id: 1 }, { value: 2 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + assert.deepStrictEqual( + insert({ id: 3 }, { value: 3 })(repo), + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }], + [{ id: 3 }, { value: 3 }] + ]) + ) + // should not modify the source + assert.deepStrictEqual( + repo, + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + }) + + it('deleteAt', () => { + const a1b2 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const a1b2_ = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const b2 = new Map([[{ id: 'b' }, 2]]) + const removeS = _.deleteAt(eqUser) + assert.deepStrictEqual(removeS({ id: 'a' })(a1b2), b2) + assert.deepStrictEqual(a1b2, a1b2_) + assert.deepStrictEqual(removeS({ id: 'c' })(a1b2), a1b2) + + const remove = _.deleteAt(eqKey) + assert.deepStrictEqual(remove({ id: 1 })(repo), new Map([[{ id: 2 }, { value: 2 }]])) + assert.deepStrictEqual(remove({ id: 4 })(repo), new Map([[{ id: 2 }, { value: 2 }]])) + assert.deepStrictEqual(remove({ id: 3 })(repo), repo) + // should not modify the source + assert.deepStrictEqual( + repo, + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + }) + + it('pop', () => { + const a1b2 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const b2 = new Map([[{ id: 'b' }, 2]]) + const popS = _.pop(eqUser) + assert.deepStrictEqual(popS({ id: 'a' })(a1b2), some([1, b2])) + assert.deepStrictEqual(popS({ id: 'c' })(a1b2), none) + + const pop = _.pop(eqKey) + assert.deepStrictEqual(pop({ id: 1 })(repo), some([{ value: 1 }, new Map([[{ id: 2 }, { value: 2 }]])])) + assert.deepStrictEqual(pop({ id: 4 })(repo), some([{ value: 1 }, new Map([[{ id: 2 }, { value: 2 }]])])) + assert.deepStrictEqual(pop({ id: 3 })(repo), none) + // should not modify the source + assert.deepStrictEqual( + repo, + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + }) + + it('lookupWithKey', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const lookupWithKeyS = _.lookupWithKey(eqUser) + assert.deepStrictEqual(lookupWithKeyS({ id: 'a' }, a1), some([{ id: 'a' }, 1])) + assert.deepStrictEqual(lookupWithKeyS({ id: 'b' }, a1), none) + + const lookupWithKey = _.lookupWithKey(eqKey) + assert.deepStrictEqual(lookupWithKey({ id: 1 }, repo), some([{ id: 1 }, { value: 1 }])) + assert.deepStrictEqual(lookupWithKey({ id: 4 }, repo), some([{ id: 1 }, { value: 1 }])) + assert.deepStrictEqual(lookupWithKey({ id: 3 }, repo), none) + }) + + it('lookup', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const lookupS = _.lookup(eqUser) + assert.deepStrictEqual(lookupS({ id: 'a' }, a1), some(1)) + assert.deepStrictEqual(lookupS({ id: 'b' }, a1), none) + + const lookup = _.lookup(eqKey) + assert.deepStrictEqual(lookup({ id: 1 }, repo), some({ value: 1 })) + assert.deepStrictEqual(lookup({ id: 4 }, repo), some({ value: 1 })) + assert.deepStrictEqual(lookup({ id: 3 }, repo), none) + }) + + it('isSubmap', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const a1b2 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 2] + ]) + const isSubmapS = _.isSubmap(eqUser, eqNumber) + assert.deepStrictEqual(isSubmapS(a1, a1b2), true) + + const isSubmap = _.isSubmap(eqKey, eqValue) + assert.deepStrictEqual(isSubmap(new Map([[{ id: 1 }, { value: 1 }]]), repo), true) + assert.deepStrictEqual(isSubmap(new Map([[{ id: 1 }, { value: 2 }]]), repo), false) + assert.deepStrictEqual(isSubmap(new Map([[{ id: 1 }, { value: 4 }]]), repo), true) + assert.deepStrictEqual(isSubmap(new Map([[{ id: 4 }, { value: 1 }]]), repo), true) + assert.deepStrictEqual(isSubmap(new Map([[{ id: 3 }, { value: 3 }]]), repo), false) + }) + + it('empty', () => { + assert.deepStrictEqual(_.empty, new Map()) + assert.deepStrictEqual(_.isEmpty(_.empty), true) + }) + + it('singleton', () => { + assert.deepStrictEqual( + _.singleton('k1', 0), + new Map([['k1', 0]]) + ) + }) + + it('getEq', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const a1_ = new Map([[{ id: 'a' }, 1]]) + const a2 = new Map([[{ id: 'a' }, 2]]) + const b1 = new Map([[{ id: 'b' }, 1]]) + const S = _.getEq(eqUser, eqNumber) + assert.deepStrictEqual(S.equals(a1, a1), true) + assert.deepStrictEqual(S.equals(a1, a1_), true) + assert.deepStrictEqual(S.equals(a1_, a1), true) + assert.deepStrictEqual(S.equals(a1, a2), false) + assert.deepStrictEqual(S.equals(a2, a1), false) + assert.deepStrictEqual(S.equals(a1, b1), false) + assert.deepStrictEqual(S.equals(b1, a1), false) + + const equals = _.getEq(eqKey, eqValue).equals + assert.deepStrictEqual(equals(repo, repo), true) + assert.deepStrictEqual( + equals( + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]), + repo + ), + true + ) + assert.deepStrictEqual( + equals( + new Map([ + [{ id: 1 }, { value: 2 }], + [{ id: 2 }, { value: 2 }] + ]), + repo + ), + false + ) + assert.deepStrictEqual( + equals( + new Map([ + [{ id: 1 }, { value: 4 }], + [{ id: 2 }, { value: 2 }] + ]), + repo + ), + true + ) + assert.deepStrictEqual( + equals( + new Map([ + [{ id: 4 }, { value: 1 }], + [{ id: 2 }, { value: 2 }] + ]), + repo + ), + true + ) + assert.deepStrictEqual( + equals( + new Map([ + [{ id: 3 }, { value: 3 }], + [{ id: 2 }, { value: 2 }] + ]), + repo + ), + false + ) + }) + + it('getMonoid', () => { + const d1 = new Map([ + [{ id: 'k1' }, 1], + [{ id: 'k2' }, 3] + ]) + const d2 = new Map([ + [{ id: 'k2' }, 2], + [{ id: 'k3' }, 4] + ]) + const expected = new Map([ + [{ id: 'k1' }, 1], + [{ id: 'k2' }, 5], + [{ id: 'k3' }, 4] + ]) + const M1 = _.getMonoid(eqUser, semigroupSum) + assert.deepStrictEqual(M1.concat(d1, d2), expected) + assert.deepStrictEqual(M1.concat(d1, M1.empty), d1) + assert.deepStrictEqual(M1.concat(M1.empty, d2), d2) + + const M2 = _.getMonoid(eqKey, semigroupValue) + assert.deepStrictEqual( + M2.concat(repo, new Map([[{ id: 3 }, { value: 3 }]])), + new Map([ + [{ id: 1 }, { value: 1 }], + [{ id: 2 }, { value: 2 }], + [{ id: 3 }, { value: 3 }] + ]) + ) + assert.deepStrictEqual( + M2.concat(repo, new Map([[{ id: 1 }, { value: 2 }]])), + new Map([ + [{ id: 1 }, { value: 3 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + assert.deepStrictEqual( + M2.concat(repo, new Map([[{ id: 4 }, { value: 2 }]])), + new Map([ + [{ id: 1 }, { value: 3 }], + [{ id: 2 }, { value: 2 }] + ]) + ) + }) + + describe('readonlyMap', () => { + describe('functor', () => { + it('map', () => { + const map = _.readonlyMap.map + const d1 = new Map([ + ['k1', 1], + ['k2', 2] + ]) + const expected = new Map([ + ['k1', 2], + ['k2', 4] + ]) + const double = (n: number): number => n * 2 + assert.deepStrictEqual(map(d1, double), expected) + }) + }) + + describe('filterable', () => { + it('compact', () => { + const compact = _.readonlyMap.compact + const fooBar = new Map>([ + ['foo', none], + ['bar', some(123)] + ]) + const bar = new Map([['bar', 123]]) + assert.deepStrictEqual(compact(fooBar), bar) + }) + + it('partitionMap', () => { + const partitionMap = _.readonlyMap.partitionMap + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const a0 = new Map([['a', 0]]) + const b4 = new Map([['b', 4]]) + const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1)) + assert.deepStrictEqual(partitionMap(emptyMap, f), { left: emptyMap, right: emptyMap }) + assert.deepStrictEqual(partitionMap(a1b3, f), { + left: a0, + right: b4 + }) + }) + + it('partition', () => { + const partition = _.readonlyMap.partition + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const a1 = new Map([['a', 1]]) + const b3 = new Map([['b', 3]]) + assert.deepStrictEqual(partition(emptyMap, p), { left: emptyMap, right: emptyMap }) + assert.deepStrictEqual(partition(a1b3, p), { + left: a1, + right: b3 + }) + }) + + it('separate', () => { + const separate = _.readonlyMap.separate + const fooBar = new Map>([ + ['foo', left(123)], + ['bar', right(123)] + ]) + const foo = new Map([['foo', 123]]) + const bar = new Map([['bar', 123]]) + assert.deepStrictEqual(separate(fooBar), { + left: foo, + right: bar + }) + }) + + it('filter', () => { + const filter = _.readonlyMap.filter + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const b3 = new Map([['b', 3]]) + assert.deepStrictEqual(filter(a1b3, p), b3) + + // refinements + const isNumber = (u: string | number): u is number => typeof u === 'number' + const y = new Map([ + ['a', 1], + ['b', 'foo'] + ]) + const a1 = new Map([['a', 1]]) + const actual = filter(y, isNumber) + assert.deepStrictEqual(actual, a1) + }) + + it('filterMap', () => { + const filterMap = _.readonlyMap.filterMap + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const b4 = new Map([['b', 4]]) + const f = (n: number) => (p(n) ? some(n + 1) : none) + assert.deepStrictEqual(filterMap(emptyMap, f), emptyMap) + assert.deepStrictEqual(filterMap(a1b3, f), b4) + }) + }) + }) + + describe('getWitherable', () => { + const W = _.getWitherable(ordUser) + + it('mapWithIndex', () => { + const mapWithIndex = W.mapWithIndex + const aa1 = new Map([[{ id: 'aa' }, 1]]) + const aa3 = new Map([[{ id: 'aa' }, 3]]) + assert.deepStrictEqual( + mapWithIndex(aa1, (k, a) => a + k.id.length), + aa3 + ) + }) + + it('reduce', () => { + const d1 = new Map([ + [{ id: 'k1' }, 'a'], + [{ id: 'k2' }, 'b'] + ]) + const reduceO = W.reduce + assert.deepStrictEqual( + reduceO(d1, '', (b, a) => b + a), + 'ab' + ) + const d2 = new Map([ + [{ id: 'k2' }, 'b'], + [{ id: 'k1' }, 'a'] + ]) + assert.deepStrictEqual( + reduceO(d2, '', (b, a) => b + a), + 'ab' + ) + }) + + it('foldMap', () => { + const foldMapOM = W.foldMap(monoidString) + const m = new Map([ + [{ id: 'a' }, 'a'], + [{ id: 'a' }, 'b'] + ]) + assert.deepStrictEqual(foldMapOM(m, identity), 'ab') + }) + + it('reduceRight', () => { + const reduceRightO = W.reduceRight + const m = new Map([ + [{ id: 'a' }, 'a'], + [{ id: 'b' }, 'b'] + ]) + const init = '' + const f = (a: string, acc: string) => acc + a + assert.deepStrictEqual(reduceRightO(m, init, f), 'ba') + }) + + it('reduceWithIndex', () => { + const d1 = new Map([ + [{ id: 'k1' }, 'a'], + [{ id: 'k2' }, 'b'] + ]) + const reduceWithIndexO = W.reduceWithIndex + assert.deepStrictEqual( + reduceWithIndexO(d1, '', (k, b, a) => b + k.id + a), + 'k1ak2b' + ) + const d2 = new Map([ + [{ id: 'k2' }, 'b'], + [{ id: 'k1' }, 'a'] + ]) + assert.deepStrictEqual( + reduceWithIndexO(d2, '', (k, b, a) => b + k.id + a), + 'k1ak2b' + ) + }) + + it('foldMapWithIndex', () => { + const foldMapWithIndexOM = W.foldMapWithIndex(monoidString) + const m = new Map([ + [{ id: 'k1' }, 'a'], + [{ id: 'k2' }, 'b'] + ]) + assert.deepStrictEqual( + foldMapWithIndexOM(m, (k, a) => k.id + a), + 'k1ak2b' + ) + }) + + it('reduceRightWithIndex', () => { + const reduceRightWithIndexO = W.reduceRightWithIndex + const m = new Map([ + [{ id: 'k1' }, 'a'], + [{ id: 'k2' }, 'b'] + ]) + assert.deepStrictEqual( + reduceRightWithIndexO(m, '', (k, a, b) => b + k.id + a), + 'k2bk1a' + ) + }) + + it('traverse', () => { + const optionTraverse = W.traverse(option) + const x = new Map([ + [{ id: 'k1' }, 1], + [{ id: 'k2' }, 2] + ]) + assert.deepStrictEqual( + optionTraverse(x, n => (n <= 2 ? some(n) : none)), + some(x) + ) + assert.deepStrictEqual( + optionTraverse(x, n => (n >= 2 ? some(n) : none)), + none + ) + }) + + it('sequence', () => { + const optionSequence = W.sequence(option) + const m1 = new Map>([ + [{ id: 'k1' }, some(1)], + [{ id: 'k2' }, some(2)] + ]) + const m2 = new Map>([ + [{ id: 'k1' }, none], + [{ id: 'k2' }, some(2)] + ]) + assert.deepStrictEqual( + optionSequence(m1), + some( + new Map([ + [{ id: 'k1' }, 1], + [{ id: 'k2' }, 2] + ]) + ) + ) + assert.deepStrictEqual(optionSequence(m2), none) + }) + + it('traverseWithIndex', () => { + const optionTraverseWithIndex = W.traverseWithIndex(option) + const d1 = new Map([ + [{ id: 'k1' }, 1], + [{ id: 'k2' }, 2] + ]) + const t1 = optionTraverseWithIndex( + d1, + (k, n): Option => (!ordUser.equals(k, { id: 'k1' }) ? some(n) : none) + ) + assert.deepStrictEqual(t1, none) + const d2 = new Map([ + [{ id: 'k1' }, 2], + [{ id: 'k2' }, 3] + ]) + const t2 = optionTraverseWithIndex( + d2, + (k, n): Option => (!ordUser.equals(k, { id: 'k3' }) ? some(n) : none) + ) + const expected = new Map([ + [{ id: 'k1' }, 2], + [{ id: 'k2' }, 3] + ]) + assert.deepStrictEqual(t2, some(expected)) + }) + + it('wither', () => { + const emptyMap = new Map() + const a1b3 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 3] + ]) + const b4 = new Map([[{ id: 'b' }, 4]]) + const witherIdentity = W.wither(I.identity) + const f = (n: number) => I.identity.of(p(n) ? some(n + 1) : none) + assert.deepStrictEqual(witherIdentity(emptyMap, f), I.identity.of(emptyMap)) + assert.deepStrictEqual(witherIdentity(a1b3, f), I.identity.of(b4)) + }) + + it('wilt', () => { + const emptyMap = new Map() + const a1b3 = new Map([ + [{ id: 'a' }, 1], + [{ id: 'b' }, 3] + ]) + const a0 = new Map([[{ id: 'a' }, 0]]) + const b4 = new Map([[{ id: 'b' }, 4]]) + const wiltIdentity = W.wilt(I.identity) + const f = (n: number) => I.identity.of(p(n) ? right(n + 1) : left(n - 1)) + assert.deepStrictEqual(wiltIdentity(emptyMap, f), I.identity.of({ left: emptyMap, right: emptyMap })) + assert.deepStrictEqual(wiltIdentity(a1b3, f), I.identity.of({ left: a0, right: b4 })) + }) + }) + + describe('getFilterableWithIndex', () => { + it('partitionMapWithIndex', () => { + const partitionMapWithIndex = _.getFilterableWithIndex().partitionMapWithIndex + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const a0 = new Map([['a', 0]]) + const b4 = new Map([['b', 4]]) + const f = (_: string, n: number) => (p(n) ? right(n + 1) : left(n - 1)) + assert.deepStrictEqual(partitionMapWithIndex(emptyMap, f), { left: emptyMap, right: emptyMap }) + assert.deepStrictEqual(partitionMapWithIndex(a1b3, f), { + left: a0, + right: b4 + }) + }) + + it('partitionWithIndex', () => { + const partitionWithIndex = _.getFilterableWithIndex().partitionWithIndex + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const a1 = new Map([['a', 1]]) + const b3 = new Map([['b', 3]]) + const f = (_: string, n: number) => p(n) + assert.deepStrictEqual(partitionWithIndex(emptyMap, f), { left: emptyMap, right: emptyMap }) + assert.deepStrictEqual(partitionWithIndex(a1b3, f), { + left: a1, + right: b3 + }) + }) + + it('filterMapWithIndex', () => { + const filterMapWithIndex = _.getFilterableWithIndex().filterMapWithIndex + const emptyMap = new Map() + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const b4 = new Map([['b', 4]]) + const f = (_: string, n: number) => (p(n) ? some(n + 1) : none) + assert.deepStrictEqual(filterMapWithIndex(emptyMap, f), emptyMap) + assert.deepStrictEqual(filterMapWithIndex(a1b3, f), b4) + }) + + it('filterWithIndex', () => { + const filterWithIndex = _.getFilterableWithIndex().filterWithIndex + const a1b3 = new Map([ + ['a', 1], + ['b', 3] + ]) + const b3 = new Map([['b', 3]]) + const f = (_: string, n: number) => p(n) + assert.deepStrictEqual(filterWithIndex(a1b3, f), b3) + + // refinements + const filterWithIndexStr = _.getFilterableWithIndex().filterWithIndex + const isNumber = (_: string, u: string | number): u is number => typeof u === 'number' + const y = new Map([ + ['a', 1], + ['b', 'foo'] + ]) + const a1 = new Map([['a', 1]]) + const actual = filterWithIndexStr(y, isNumber) + assert.deepStrictEqual(actual, a1) + }) + }) + + it('fromFoldable', () => { + const a1 = new Map([[{ id: 'a' }, 1]]) + const a2 = new Map([[{ id: 'a' }, 2]]) + const fromFoldableS1 = _.fromFoldable(eqUser, getFirstSemigroup(), array) + assert.deepStrictEqual(fromFoldableS1([[{ id: 'a' }, 1]]), a1) + assert.deepStrictEqual( + fromFoldableS1([ + [{ id: 'a' }, 1], + [{ id: 'a' }, 2] + ]), + a1 + ) + const fromFoldableS2 = _.fromFoldable(eqUser, getLastSemigroup(), array) + assert.deepStrictEqual( + fromFoldableS2([ + [{ id: 'a' }, 1], + [{ id: 'a' }, 2] + ]), + a2 + ) + }) + + it('getShow', () => { + const showUser: Show = getStructShow({ id: showString }) + const S = _.getShow(showUser, showString) + const m1 = new Map([]) + assert.deepStrictEqual(S.show(m1), `new Map([])`) + const m2 = new Map([[{ id: 'a' }, 'b']]) + assert.deepStrictEqual(S.show(m2), `new Map([[{ id: "a" }, "b"]])`) + const m3 = new Map([ + [{ id: 'a' }, 'b'], + [{ id: 'c' }, 'd'] + ]) + assert.deepStrictEqual(S.show(m3), `new Map([[{ id: "a" }, "b"], [{ id: "c" }, "d"]])`) + }) + + it('updateAt', () => { + const m1 = new Map([]) + assert.deepStrictEqual(_.updateAt(eqUser)({ id: 'a' }, 'a')(m1), none) + const m2 = new Map([[{ id: 'a' }, 'b']]) + assert.deepStrictEqual( + _.updateAt(eqUser)({ id: 'a' }, 'a')(m2), + some( + new Map([[{ id: 'a' }, 'a']]) + ) + ) + }) + + it('modifyAt', () => { + const m1 = new Map([]) + assert.deepStrictEqual(_.modifyAt(eqUser)({ id: 'a' }, (n: number) => n * 2)(m1), none) + const m2 = new Map([[{ id: 'a' }, 1]]) + assert.deepStrictEqual( + _.modifyAt(eqUser)({ id: 'a' }, (n: number) => n * 2)(m2), + some( + new Map([[{ id: 'a' }, 2]]) + ) + ) + }) + + it('fromMap', () => { + const as = new Map([[1, 'a']]) + const bs = _.fromMap(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) + + it('toMap', () => { + const as: ReadonlyMap = new Map([[1, 'a']]) + const bs = _.toMap(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) +}) diff --git a/test/ReadonlyNonEmptyArray.ts b/test/ReadonlyNonEmptyArray.ts new file mode 100644 index 000000000..c09665dad --- /dev/null +++ b/test/ReadonlyNonEmptyArray.ts @@ -0,0 +1,337 @@ +import * as assert from 'assert' +import * as C from '../src/Const' +import { eqNumber } from '../src/Eq' +import { identity } from '../src/function' +import * as I from '../src/Identity' +import * as M from '../src/Monoid' +import * as O from '../src/Option' +import * as Ord from '../src/Ord' +import * as _ from '../src/ReadonlyNonEmptyArray' +import * as S from '../src/Semigroup' +import { showString } from '../src/Show' + +describe('ReadonlyNonEmptyArray', () => { + it('head', () => { + assert.deepStrictEqual(_.head([1, 2]), 1) + }) + + it('tail', () => { + assert.deepStrictEqual(_.tail([1, 2]), [2]) + }) + + it('map', () => { + const double = (n: number) => n * 2 + assert.deepStrictEqual(_.readonlyNonEmptyArray.map([1, 2], double), [2, 4]) + }) + + it('mapWithIndex', () => { + const add = (i: number, n: number) => n + i + assert.deepStrictEqual(_.readonlyNonEmptyArray.mapWithIndex([1, 2], add), [1, 3]) + }) + + it('of', () => { + assert.deepStrictEqual(_.readonlyNonEmptyArray.of(1), [1]) + }) + + it('ap', () => { + const double = (n: number) => n * 2 + assert.deepStrictEqual(_.readonlyNonEmptyArray.ap([double, double], [1, 2]), [2, 4, 2, 4]) + }) + + it('chain', () => { + const f = (a: number): _.ReadonlyNonEmptyArray => [a, 4] + assert.deepStrictEqual(_.readonlyNonEmptyArray.chain([1, 2], f), [1, 4, 2, 4]) + }) + + it('extend', () => { + const sum = M.fold(M.monoidSum) + assert.deepStrictEqual(_.readonlyNonEmptyArray.extend([1, 2, 3, 4], sum), [10, 9, 7, 4]) + }) + + it('extract', () => { + assert.deepStrictEqual(_.readonlyNonEmptyArray.extract([1, 2, 3]), 1) + }) + + it('traverse', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.traverse(O.option)([1, 2, 3], n => (n >= 0 ? O.some(n) : O.none)), + O.some([1, 2, 3]) + ) + assert.deepStrictEqual( + _.readonlyNonEmptyArray.traverse(O.option)([1, 2, 3], n => (n >= 2 ? O.some(n) : O.none)), + O.none + ) + }) + + it('sequence', () => { + const sequence = _.readonlyNonEmptyArray.sequence(O.option) + assert.deepStrictEqual(sequence([O.some(1), O.some(2), O.some(3)]), O.some([1, 2, 3])) + assert.deepStrictEqual(sequence([O.none, O.some(2), O.some(3)]), O.none) + }) + + it('min', () => { + assert.deepStrictEqual(_.min(Ord.ordNumber)([2, 1, 3]), 1) + assert.deepStrictEqual(_.min(Ord.ordNumber)([3]), 3) + }) + + it('max', () => { + assert.deepStrictEqual(_.max(Ord.ordNumber)([1, 2, 3]), 3) + assert.deepStrictEqual(_.max(Ord.ordNumber)([1]), 1) + }) + + it('reduce', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.reduce(['a', 'b'], '', (b, a) => b + a), + 'ab' + ) + }) + + it('foldMap', () => { + const foldMap = _.readonlyNonEmptyArray.foldMap(M.monoidString) + assert.deepStrictEqual(foldMap(['a', 'b', 'c'], identity), 'abc') + }) + + it('reduceRight', () => { + const reduceRight = _.readonlyNonEmptyArray.reduceRight + const init1 = '' + const f = (a: string, acc: string) => acc + a + assert.deepStrictEqual(reduceRight(['a', 'b', 'c'], init1, f), 'cba') + }) + + it('fromReadonlyArray', () => { + assert.deepStrictEqual(_.fromReadonlyArray([]), O.none) + assert.deepStrictEqual(_.fromReadonlyArray([1]), O.some([1])) + assert.deepStrictEqual(_.fromReadonlyArray([1, 2]), O.some([1, 2])) + }) + + it('getSemigroup', () => { + const S = _.getSemigroup() + assert.deepStrictEqual(S.concat([1], [2]), [1, 2]) + assert.deepStrictEqual(S.concat([1, 2], [3, 4]), [1, 2, 3, 4]) + }) + + it('getEq', () => { + const S = _.getEq(eqNumber) + assert.deepStrictEqual(S.equals([1], [1]), true) + assert.deepStrictEqual(S.equals([1], [1, 2]), false) + }) + + it('group', () => { + assert.deepStrictEqual(_.group(Ord.ordNumber)([]), []) + + assert.deepStrictEqual(_.group(Ord.ordNumber)([1, 2, 1, 1]), [[1], [2], [1, 1]]) + + assert.deepStrictEqual(_.group(Ord.ordNumber)([1, 2, 1, 1, 3]), [[1], [2], [1, 1], [3]]) + }) + + it('groupSort', () => { + assert.deepStrictEqual(_.groupSort(Ord.ordNumber)([]), []) + assert.deepStrictEqual(_.groupSort(Ord.ordNumber)([1, 2, 1, 1]), [[1, 1, 1], [2]]) + }) + + it('last', () => { + assert.deepStrictEqual(_.last([1, 2, 3]), 3) + assert.deepStrictEqual(_.last([1]), 1) + }) + + it('init', () => { + assert.deepStrictEqual(_.init([1, 2, 3]), [1, 2]) + assert.deepStrictEqual(_.init([1]), []) + }) + + it('sort', () => { + assert.deepStrictEqual(_.sort(Ord.ordNumber)([3, 2, 1]), [1, 2, 3]) + }) + + it('reverse', () => { + assert.deepStrictEqual(_.reverse([1, 2, 3]), [3, 2, 1]) + }) + + it('groupBy', () => { + assert.deepStrictEqual(_.groupBy(_ => '')([]), {}) + assert.deepStrictEqual(_.groupBy(String)([1]), { '1': [1] }) + assert.deepStrictEqual(_.groupBy((s: string) => String(s.length))(['foo', 'bar', 'foobar']), { + '3': ['foo', 'bar'], + '6': ['foobar'] + }) + }) + + it('insertAt', () => { + const make = (x: number) => ({ x }) + const a1 = make(1) + const a2 = make(1) + const a3 = make(2) + const a4 = make(3) + assert.deepStrictEqual(_.insertAt(0, a4)([a1, a2, a3]), O.some([a4, a1, a2, a3])) + assert.deepStrictEqual(_.insertAt(-1, a4)([a1, a2, a3]), O.none) + assert.deepStrictEqual(_.insertAt(3, a4)([a1, a2, a3]), O.some([a1, a2, a3, a4])) + assert.deepStrictEqual(_.insertAt(1, a4)([a1, a2, a3]), O.some([a1, a4, a2, a3])) + assert.deepStrictEqual(_.insertAt(4, a4)([a1, a2, a3]), O.none) + }) + + it('updateAt', () => { + const make2 = (x: number) => ({ x }) + const a1 = make2(1) + const a2 = make2(1) + const a3 = make2(2) + const a4 = make2(3) + const arr: _.ReadonlyNonEmptyArray<{ readonly x: number }> = [a1, a2, a3] + assert.deepStrictEqual(_.updateAt(0, a4)(arr), O.some([a4, a2, a3])) + assert.deepStrictEqual(_.updateAt(-1, a4)(arr), O.none) + assert.deepStrictEqual(_.updateAt(3, a4)(arr), O.none) + assert.deepStrictEqual(_.updateAt(1, a4)(arr), O.some([a1, a4, a3])) + // should return the same reference if nothing changed + const r1 = _.updateAt(0, a1)(arr) + if (O.isSome(r1)) { + assert.deepStrictEqual(r1.value, arr) + } else { + assert.fail('is not a Some') + } + const r2 = _.updateAt(2, a3)(arr) + if (O.isSome(r2)) { + assert.deepStrictEqual(r2.value, arr) + } else { + assert.fail('is not a Some') + } + }) + + it('modifyAt', () => { + const double = (n: number): number => n * 2 + assert.deepStrictEqual(_.modifyAt(1, double)(_.cons(1, [])), O.none) + assert.deepStrictEqual(_.modifyAt(1, double)(_.cons(1, [2])), O.some(_.cons(1, [4]))) + }) + + it('filter', () => { + const make = (x: number) => ({ x }) + const a1 = make(1) + const a2 = make(1) + const a3 = make(2) + assert.deepStrictEqual(_.filter(({ x }) => x !== 1)([a1, a2, a3]), O.some([a3])) + assert.deepStrictEqual(_.filter(({ x }) => x !== 2)([a1, a2, a3]), O.some([a1, a2])) + assert.deepStrictEqual( + _.filter(({ x }) => { + return !(x === 1 || x === 2) + })([a1, a2, a3]), + O.none + ) + assert.deepStrictEqual(_.filter(({ x }) => x !== 10)([a1, a2, a3]), O.some([a1, a2, a3])) + + // refinements + const actual1 = _.filter(O.isSome)([O.some(3), O.some(2), O.some(1)]) + assert.deepStrictEqual(actual1, O.some([O.some(3), O.some(2), O.some(1)])) + const actual2 = _.filter(O.isSome)([O.some(3), O.none, O.some(1)]) + assert.deepStrictEqual(actual2, O.some([O.some(3), O.some(1)])) + }) + + it('filterWithIndex', () => { + assert.deepStrictEqual(_.filterWithIndex(i => i % 2 === 0)([1, 2, 3]), O.some([1, 3])) + assert.deepStrictEqual(_.filterWithIndex((i, a: number) => i % 2 === 1 && a > 2)([1, 2, 3]), O.none) + }) + + it('reduceWithIndex', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.reduceWithIndex(['a', 'b'], '', (i, b, a) => b + i + a), + '0a1b' + ) + }) + + it('foldMapWithIndex', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.foldMapWithIndex(M.monoidString)(['a', 'b'], (i, a) => i + a), + '0a1b' + ) + }) + + it('reduceRightWithIndex', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.reduceRightWithIndex(['a', 'b'], '', (i, a, b) => b + i + a), + '1b0a' + ) + }) + + it('traverseWithIndex', () => { + assert.deepStrictEqual( + _.readonlyNonEmptyArray.traverseWithIndex(O.option)(['a', 'bb'], (i, s) => + s.length >= 1 ? O.some(s + i) : O.none + ), + O.some(['a0', 'bb1']) + ) + assert.deepStrictEqual( + _.readonlyNonEmptyArray.traverseWithIndex(O.option)(['a', 'bb'], (i, s) => + s.length > 1 ? O.some(s + i) : O.none + ), + O.none + ) + + // FoldableWithIndex compatibility + const f = (i: number, s: string): string => s + i + assert.deepStrictEqual( + _.readonlyNonEmptyArray.foldMapWithIndex(M.monoidString)(['a', 'bb'], f), + _.readonlyNonEmptyArray.traverseWithIndex(C.getApplicative(M.monoidString))(['a', 'bb'], (i, a) => + C.make(f(i, a)) + ) + ) + + // FunctorWithIndex compatibility + assert.deepStrictEqual( + _.readonlyNonEmptyArray.mapWithIndex(['a', 'bb'], f), + _.readonlyNonEmptyArray.traverseWithIndex(I.identity)(['a', 'bb'], (i, a) => I.identity.of(f(i, a))) + ) + }) + + it('cons', () => { + assert.deepStrictEqual(_.cons(1, [2, 3, 4]), [1, 2, 3, 4]) + }) + + it('snoc', () => { + assert.deepStrictEqual(_.snoc([1, 2, 3], 4), [1, 2, 3, 4]) + }) + + it('getShow', () => { + const S = _.getShow(showString) + assert.deepStrictEqual(S.show(['a']), `["a"]`) + assert.deepStrictEqual(S.show(['a', 'b', 'c']), `["a", "b", "c"]`) + }) + + it('alt / concat', () => { + assert.deepStrictEqual(_.concat(['a'], []), ['a']) + assert.deepStrictEqual( + _.readonlyNonEmptyArray.alt(['a'], () => ['b']), + ['a', 'b'] + ) + }) + + it('foldMap', () => { + const f = _.foldMap(S.semigroupSum)((s: string) => s.length) + assert.deepStrictEqual(f(['a']), 1) + assert.deepStrictEqual(f(['a', 'bb']), 3) + }) + + it('foldMapWithIndex', () => { + const f = _.foldMapWithIndex(S.semigroupSum)((i: number, s: string) => s.length + i) + assert.deepStrictEqual(f(['a']), 1) + assert.deepStrictEqual(f(['a', 'bb']), 4) + }) + + it('fromArray', () => { + assert.strictEqual(_.fromArray([]), O.none) + // tslint:disable-next-line: readonly-array + const as = [1, 2, 3] + const bs = _.fromArray(as) + assert.deepStrictEqual(bs, O.some(as)) + assert.notStrictEqual((bs as any).value, as) + }) + + it('fromReadonlyArray', () => { + const as: ReadonlyArray = [1, 2, 3] + const bs = _.fromReadonlyArray(as) + assert.deepStrictEqual(bs, O.some(as)) + assert.strictEqual((bs as any).value, as) + }) + + it('fold', () => { + const f = _.fold(S.semigroupString) + assert.deepStrictEqual(f(['a']), 'a') + assert.deepStrictEqual(f(['a', 'bb']), 'abb') + }) +}) diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts new file mode 100644 index 000000000..5f4763026 --- /dev/null +++ b/test/ReadonlyRecord.ts @@ -0,0 +1,377 @@ +import * as assert from 'assert' +import * as _ from '../src/ReadonlyRecord' +import { semigroupSum, getLastSemigroup, getFirstSemigroup } from '../src/Semigroup' +import { monoidString } from '../src/Monoid' +import { identity } from '../src/function' +import { option, some, none, Option, getOrElse, isSome } from '../src/Option' +import { eqNumber } from '../src/Eq' +import { readonlyArray, zip } from '../src/ReadonlyArray' +import { left, right } from '../src/Either' +import * as I from '../src/Identity' +import { showString } from '../src/Show' + +const p = (n: number) => n > 2 + +const noPrototype = Object.create(null) + +describe('ReadonlyRecord', () => { + it('getMonoid', () => { + const d1 = { k1: 1, k2: 3 } + const d2 = { k2: 2, k3: 4 } + const M = _.getMonoid(semigroupSum) + assert.deepStrictEqual(M.concat(d1, d2), { k1: 1, k2: 5, k3: 4 }) + assert.deepStrictEqual(M.concat(d1, M.empty), d1) + assert.deepStrictEqual(M.concat(M.empty, d2), d2) + assert.deepStrictEqual(M.concat(d1, {}), d1) + }) + + it('map', () => { + const d1 = { k1: 1, k2: 2 } + const double = (n: number): number => n * 2 + assert.deepStrictEqual(_.map(double)(d1), { k1: 2, k2: 4 }) + assert.deepStrictEqual(_.readonlyRecord.map({ a: 1, b: 2 }, double), { a: 2, b: 4 }) + }) + + it('reduce', () => { + const d1 = { k1: 'a', k2: 'b' } + assert.deepStrictEqual( + _.readonlyRecord.reduce(d1, '', (b, a) => b + a), + 'ab' + ) + const d2 = { k2: 'b', k1: 'a' } + assert.deepStrictEqual( + _.readonlyRecord.reduce(d2, '', (b, a) => b + a), + 'ab' + ) + }) + + it('foldMap', () => { + const foldMap = _.readonlyRecord.foldMap(monoidString) + const x1 = { a: 'a', b: 'b' } + const f1 = identity + assert.deepStrictEqual(foldMap(x1, f1), 'ab') + }) + + it('reduceRight', () => { + const reduceRight = _.readonlyRecord.reduceRight + const x1 = { a: 'a', b: 'b' } + const init1 = '' + const f1 = (a: string, acc: string) => acc + a + assert.deepStrictEqual(reduceRight(x1, init1, f1), 'ba') + }) + + it('traverse', () => { + assert.deepStrictEqual( + _.traverse(option)((n: number) => (n <= 2 ? some(n) : none))({ k1: 1, k2: 2 }), + some({ k1: 1, k2: 2 }) + ) + assert.deepStrictEqual(_.traverse(option)((n: number) => (n >= 2 ? some(n) : none))({ k1: 1, k2: 2 }), none) + }) + + it('sequence', () => { + const sequence = _.sequence(option) + const x1 = { k1: some(1), k2: some(2) } + assert.deepStrictEqual(sequence(x1), some({ k1: 1, k2: 2 })) + const x2 = { k1: none, k2: some(2) } + assert.deepStrictEqual(sequence(x2), none) + }) + + it('getEq', () => { + assert.deepStrictEqual(_.getEq(eqNumber).equals({ a: 1 }, { a: 1 }), true) + assert.deepStrictEqual(_.getEq(eqNumber).equals({ a: 1 }, { a: 2 }), false) + assert.deepStrictEqual(_.getEq(eqNumber).equals({ a: 1 }, { b: 1 }), false) + assert.deepStrictEqual(_.getEq(eqNumber).equals(noPrototype, { b: 1 }), false) + }) + + it('lookup', () => { + assert.deepStrictEqual(_.lookup('a', { a: 1 }), some(1)) + assert.deepStrictEqual(_.lookup('b', { a: 1 }), none) + assert.deepStrictEqual(_.lookup('b', noPrototype), none) + }) + + it('fromFoldable', () => { + const First = getFirstSemigroup() + assert.deepStrictEqual(_.fromFoldable(First, readonlyArray)([['a', 1]]), { a: 1 }) + assert.deepStrictEqual( + _.fromFoldable( + First, + readonlyArray + )([ + ['a', 1], + ['a', 2] + ]), + { + a: 1 + } + ) + const Last = getLastSemigroup() + assert.deepStrictEqual( + _.fromFoldable( + Last, + readonlyArray + )([ + ['a', 1], + ['a', 2] + ]), + { + a: 2 + } + ) + }) + + it('toReadonlyArray', () => { + assert.deepStrictEqual(_.toReadonlyArray({ a: 1, b: 2 }), [ + ['a', 1], + ['b', 2] + ]) + assert.deepStrictEqual(_.toReadonlyArray({ b: 2, a: 1 }), [ + ['a', 1], + ['b', 2] + ]) + }) + + it('toUnfoldable', () => { + assert.deepStrictEqual(_.toUnfoldable(readonlyArray)({ a: 1 }), [['a', 1]]) + }) + + it('traverseWithIndex', () => { + const d1 = { k1: 1, k2: 2 } + const t1 = _.traverseWithIndex(option)((k, n: number): Option => (k !== 'k1' ? some(n) : none))(d1) + assert.deepStrictEqual(t1, none) + const t2 = _.traverseWithIndex(option)((): Option => none)(_.empty) + assert.deepStrictEqual(getOrElse((): _.ReadonlyRecord => _.empty)(t2), _.empty) + }) + + it('size', () => { + assert.deepStrictEqual(_.size({}), 0) + assert.deepStrictEqual(_.size({ a: 1 }), 1) + }) + + it('isEmpty', () => { + assert.deepStrictEqual(_.isEmpty({}), true) + assert.deepStrictEqual(_.isEmpty({ a: 1 }), false) + }) + + it('insertAt', () => { + assert.deepStrictEqual(_.insertAt('a', 1)({}), { a: 1 }) + assert.deepStrictEqual(_.insertAt('c', 3)({ a: 1, b: 2 }), { a: 1, b: 2, c: 3 }) + // should return the same reference if the value is already there + const x = { a: 1 } + assert.deepStrictEqual(_.insertAt('a', 1)(x), x) + }) + + it('deleteAt', () => { + assert.deepStrictEqual(_.deleteAt('a')({ a: 1, b: 2 }), { b: 2 }) + // should return the same reference if the key is missing + const x = { a: 1 } + assert.deepStrictEqual(_.deleteAt('b')(x), x) + assert.deepStrictEqual(_.deleteAt('b')(noPrototype), noPrototype) + }) + + it('pop', () => { + assert.deepStrictEqual(_.pop('a')({ a: 1, b: 2 }), some([1, { b: 2 }])) + assert.deepStrictEqual(_.pop('c')({ a: 1, b: 2 }), none) + }) + + it('compact', () => { + assert.deepStrictEqual(_.readonlyRecord.compact({ foo: none, bar: some(123) }), { bar: 123 }) + }) + + it('separate', () => { + assert.deepStrictEqual(_.readonlyRecord.separate({ foo: left(123), bar: right(123) }), { + left: { foo: 123 }, + right: { bar: 123 } + }) + }) + + it('filter', () => { + const d = { a: 1, b: 3 } + assert.deepStrictEqual(_.readonlyRecord.filter(d, p), { b: 3 }) + + // refinements + const isNumber = (u: string | number): u is number => typeof u === 'number' + const y: _.ReadonlyRecord = { a: 1, b: 'foo' } + const actual = _.readonlyRecord.filter(y, isNumber) + assert.deepStrictEqual(actual, { a: 1 }) + + assert.deepStrictEqual( + _.readonlyRecord.filter(y, _ => true), + y + ) + + const x = Object.assign(Object.create({ c: true }), { a: 1, b: 'foo' }) + assert.deepStrictEqual(_.readonlyRecord.filter(x, isNumber), { a: 1 }) + + assert.deepStrictEqual(_.readonlyRecord.filter(noPrototype, isNumber), noPrototype) + }) + + it('filterMap', () => { + const f = (n: number) => (p(n) ? some(n + 1) : none) + assert.deepStrictEqual(_.readonlyRecord.filterMap({}, f), {}) + assert.deepStrictEqual(_.readonlyRecord.filterMap({ a: 1, b: 3 }, f), { b: 4 }) + }) + + it('partition', () => { + assert.deepStrictEqual(_.readonlyRecord.partition({}, p), { left: {}, right: {} }) + assert.deepStrictEqual(_.readonlyRecord.partition({ a: 1, b: 3 }, p), { + left: { a: 1 }, + right: { b: 3 } + }) + }) + + it('partitionMap', () => { + const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1)) + assert.deepStrictEqual(_.readonlyRecord.partitionMap({}, f), { left: {}, right: {} }) + assert.deepStrictEqual(_.readonlyRecord.partitionMap({ a: 1, b: 3 }, f), { + left: { a: 0 }, + right: { b: 4 } + }) + }) + + it('wither', () => { + const witherIdentity = _.readonlyRecord.wither(I.identity) + const f = (n: number) => I.identity.of(p(n) ? some(n + 1) : none) + assert.deepStrictEqual(witherIdentity({}, f), I.identity.of<_.ReadonlyRecord>({})) + assert.deepStrictEqual(witherIdentity({ a: 1, b: 3 }, f), I.identity.of({ b: 4 })) + }) + + it('wilt', () => { + const wiltIdentity = _.readonlyRecord.wilt(I.identity) + const f = (n: number) => I.identity.of(p(n) ? right(n + 1) : left(n - 1)) + assert.deepStrictEqual(wiltIdentity({}, f), I.identity.of({ left: {}, right: {} })) + assert.deepStrictEqual(wiltIdentity({ a: 1, b: 3 }, f), I.identity.of({ left: { a: 0 }, right: { b: 4 } })) + }) + + it('reduceWithIndex', () => { + const d1 = { k1: 'a', k2: 'b' } + assert.deepStrictEqual(_.reduceWithIndex('', (k, b, a) => b + k + a)(d1), 'k1ak2b') + const d2 = { k2: 'b', k1: 'a' } + assert.deepStrictEqual(_.reduceWithIndex('', (k, b, a) => b + k + a)(d2), 'k1ak2b') + }) + + it('foldMapWithIndex', () => { + const x1 = { k1: 'a', k2: 'b' } + assert.deepStrictEqual(_.foldMapWithIndex(monoidString)((k, a) => k + a)(x1), 'k1ak2b') + }) + + it('reduceRightWithIndex', () => { + const x1 = { k1: 'a', k2: 'b' } + assert.deepStrictEqual(_.reduceRightWithIndex('', (k, a, b) => b + k + a)(x1), 'k2bk1a') + }) + + it('every', () => { + const x: _.ReadonlyRecord = { a: 1, b: 2 } + const y: _.ReadonlyRecord = { a: 1, b: 2 } + assert.deepStrictEqual(_.every((n: number) => n <= 2)(x), true) + assert.deepStrictEqual(_.every((n: number) => n <= 1)(y), false) + }) + + it('some', () => { + const x: _.ReadonlyRecord = { a: 1, b: 2 } + const y: _.ReadonlyRecord = { a: 1, b: 2 } + assert.deepStrictEqual(_.some((n: number) => n <= 1)(x), true) + assert.deepStrictEqual(_.some((n: number) => n <= 0)(y), false) + }) + + it('elem', () => { + assert.deepStrictEqual(_.elem(eqNumber)(1, { a: 1, b: 2 }), true) + assert.deepStrictEqual(_.elem(eqNumber)(3, { a: 1, b: 2 }), false) + }) + + it('fromFoldableMap', () => { + const zipObject = (keys: ReadonlyArray, values: ReadonlyArray): _.ReadonlyRecord => + _.fromFoldableMap(getLastSemigroup(), readonlyArray)(zip(keys, values), identity) + + assert.deepStrictEqual(zipObject(['a', 'b'], [1, 2, 3]), { a: 1, b: 2 }) + + interface User { + readonly id: string + readonly name: string + } + + const users: ReadonlyArray = [ + { id: 'id1', name: 'name1' }, + { id: 'id2', name: 'name2' }, + { id: 'id1', name: 'name3' } + ] + + assert.deepStrictEqual( + _.fromFoldableMap(getLastSemigroup(), readonlyArray)(users, user => [user.id, user]), + { + id1: { id: 'id1', name: 'name3' }, + id2: { id: 'id2', name: 'name2' } + } + ) + }) + + it('getShow', () => { + const S = _.getShow(showString) + assert.deepStrictEqual(S.show({}), `{}`) + assert.deepStrictEqual(S.show({ a: 'a' }), `{ "a": "a" }`) + assert.deepStrictEqual(S.show({ a: 'a', b: 'b' }), `{ "a": "a", "b": "b" }`) + }) + + it('singleton', () => { + assert.deepStrictEqual(_.singleton('a', 1), { a: 1 }) + }) + + it('hasOwnProperty', () => { + const x: _.ReadonlyRecord = { a: 1 } + assert.deepStrictEqual(_.hasOwnProperty('a', x), true) + assert.deepStrictEqual(_.hasOwnProperty('b', x), false) + }) + + it('partitionMapWithIndex', () => { + assert.deepStrictEqual(_.partitionMapWithIndex((k, a: number) => (a > 1 ? right(a) : left(k)))({ a: 1, b: 2 }), { + left: { a: 'a' }, + right: { b: 2 } + }) + }) + + it('partitionWithIndex', () => { + assert.deepStrictEqual(_.partitionWithIndex((_, a: number) => a > 1)({ a: 1, b: 2 }), { + left: { a: 1 }, + right: { b: 2 } + }) + }) + + it('filterMapWithIndex', () => { + assert.deepStrictEqual(_.filterMapWithIndex((_, a: number) => (a > 1 ? some(a) : none))({ a: 1, b: 2 }), { b: 2 }) + }) + + it('filterWithIndex', () => { + assert.deepStrictEqual(_.filterWithIndex((_, a: number) => a > 1)({ a: 1, b: 2 }), { b: 2 }) + }) + + it('updateAt', () => { + const x: _.ReadonlyRecord = { a: 1 } + assert.deepStrictEqual(_.updateAt('b', 2)(x), none) + assert.deepStrictEqual(_.updateAt('a', 2)(x), some({ a: 2 })) + const r = _.updateAt('a', 1)(x) + if (isSome(r)) { + assert.deepStrictEqual(r.value, x) + } else { + assert.fail() + } + }) + + it('modifyAt', () => { + const x: _.ReadonlyRecord = { a: 1 } + assert.deepStrictEqual(_.modifyAt('b', (n: number) => n * 2)(x), none) + assert.deepStrictEqual(_.modifyAt('a', (n: number) => n * 2)(x), some({ a: 2 })) + }) + + it('fromRecord', () => { + const as = { a: 1, b: 2 } + const bs = _.fromRecord(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) + + it('toRecord', () => { + const as: _.ReadonlyRecord = { a: 1, b: 2 } + const bs = _.toRecord(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) +}) diff --git a/test/ReadonlySet.ts b/test/ReadonlySet.ts new file mode 100644 index 000000000..f3a639c7b --- /dev/null +++ b/test/ReadonlySet.ts @@ -0,0 +1,248 @@ +import * as assert from 'assert' +import { left, right } from '../src/Either' +import { ordNumber } from '../src/Ord' +import * as _ from '../src/ReadonlySet' +import { Eq, eqNumber, eqString, eq, getStructEq } from '../src/Eq' +import { none, some as optionSome } from '../src/Option' +import { showString } from '../src/Show' +import { getMonoid } from '../src/Array' + +const gte2 = (n: number) => n >= 2 + +interface Foo { + readonly x: string +} +const foo = (x: string): Foo => ({ x }) +const fooEq: Eq = { + equals: (a: Foo, b: Foo) => a.x === b.x +} + +describe('ReadonlySet', () => { + it('toReadonlyArray', () => { + assert.deepStrictEqual(_.toReadonlyArray(ordNumber)(new Set()), []) + assert.deepStrictEqual(_.toReadonlyArray(ordNumber)(new Set([1, 2, 3])), [1, 2, 3]) + assert.deepStrictEqual(_.toReadonlyArray(ordNumber)(new Set([3, 2, 1])), [1, 2, 3]) + }) + + it('getEq', () => { + const S = _.getEq(eqNumber) + assert.deepStrictEqual(S.equals(new Set([1, 2, 3]), new Set([1, 2, 3])), true) + assert.deepStrictEqual(S.equals(new Set([1, 2, 3]), new Set([1, 2])), false) + assert.deepStrictEqual(S.equals(new Set([1, 2]), new Set([1, 2, 3])), false) + }) + + it('some', () => { + assert.deepStrictEqual(_.some((s: string) => s.trim() === '')(new Set()), false) + assert.deepStrictEqual(_.some(gte2)(new Set([1, 2, 3])), true) + assert.deepStrictEqual(_.some(gte2)(new Set([1])), false) + }) + + it('map', () => { + assert.deepStrictEqual(_.map(eqNumber)((n: number) => n % 2)(new Set([])), new Set([])) + assert.deepStrictEqual(_.map(eqNumber)((n: number) => n % 2)(new Set([1, 2, 3, 4])), new Set([0, 1])) + assert.deepStrictEqual(_.map(eqString)((n: number) => `${n % 2}`)(new Set([1, 2, 3, 4])), new Set(['0', '1'])) + }) + + it('every', () => { + assert.deepStrictEqual(_.every(gte2)(new Set([1, 2, 3])), false) + assert.deepStrictEqual(_.every(gte2)(new Set([2, 3])), true) + }) + + it('chain', () => { + assert.deepStrictEqual(_.chain(eqString)((n: number) => new Set([n.toString()]))(new Set([])), new Set([])) + assert.deepStrictEqual(_.chain(eqString)(() => new Set([]))(new Set([1, 2])), new Set([])) + assert.deepStrictEqual( + _.chain(eqString)((n: number) => new Set([`${n}`, `${n + 1}`]))(new Set([1, 2])), + new Set(['1', '2', '3']) + ) + }) + + it('isSubset', () => { + assert.deepStrictEqual(_.isSubset(eqNumber)(new Set([1, 2]), new Set([1, 2, 3])), true) + assert.deepStrictEqual(_.isSubset(eqNumber)(new Set([1, 2, 4]), new Set([1, 2, 3])), false) + }) + + it('filter', () => { + assert.deepStrictEqual(_.filter(gte2)(new Set([1, 2, 3])), new Set([2, 3])) + + // refinements + const isNumber = (u: string | number): u is number => typeof u === 'number' + const actual = _.filter(isNumber)(new Set([1, 'a', 2])) + assert.deepStrictEqual(actual, new Set([1, 2])) + }) + + it('partition', () => { + assert.deepStrictEqual(_.partition(() => true)(new Set([])), { right: new Set([]), left: new Set([]) }) + assert.deepStrictEqual(_.partition(() => true)(new Set([1])), { right: new Set([1]), left: new Set([]) }) + assert.deepStrictEqual(_.partition(() => false)(new Set([1])), { right: new Set([]), left: new Set([1]) }) + assert.deepStrictEqual(_.partition((n: number) => n % 2 === 0)(new Set([1, 2, 3, 4])), { + right: new Set([2, 4]), + left: new Set([1, 3]) + }) + + // refinements + const isNumber = (u: string | number): u is number => typeof u === 'number' + const actual = _.partition(isNumber)(new Set([1, 'a', 2])) + assert.deepStrictEqual(actual, { + left: new Set(['a']), + right: new Set([1, 2]) + }) + }) + + it('union', () => { + assert.deepStrictEqual(_.union(eqNumber)(new Set([1, 2]), new Set([1, 3])), new Set([1, 2, 3])) + }) + + it('intersection', () => { + assert.deepStrictEqual(_.intersection(eqNumber)(new Set([1, 2]), new Set([1, 3])), new Set([1])) + }) + + it('partitionMap', () => { + assert.deepStrictEqual(_.partitionMap(eqNumber, eqString)((n: number) => left(n))(new Set([])), { + left: new Set([]), + right: new Set([]) + }) + assert.deepStrictEqual( + _.partitionMap(eqNumber, eqString)((n: number) => (n % 2 === 0 ? left(n) : right(`${n}`)))(new Set([1, 2, 3])), + { + left: new Set([2]), + right: new Set(['1', '3']) + } + ) + const SL = getStructEq({ value: eqNumber }) + const SR = getStructEq({ value: eqString }) + assert.deepStrictEqual( + _.partitionMap( + SL, + SR + )((x: { readonly value: number }) => (x.value % 2 === 0 ? left({ value: 2 }) : right({ value: 'odd' })))( + new Set([{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]) + ), + { + left: new Set([{ value: 2 }]), + right: new Set([{ value: 'odd' }]) + } + ) + }) + + it('getUnionMonoid', () => { + const M = _.getUnionMonoid(eqNumber) + assert.deepStrictEqual(M.concat(new Set([1, 2]), new Set([1, 3])), new Set([1, 2, 3])) + assert.deepStrictEqual(M.concat(new Set([1, 2]), M.empty), new Set([1, 2])) + assert.deepStrictEqual(M.concat(M.empty, new Set([1, 3])), new Set([1, 3])) + }) + + it('getIntersectionSemigroup', () => { + const S = _.getIntersectionSemigroup(eqNumber) + assert.deepStrictEqual(S.concat(new Set([1, 2]), new Set([1, 3])), new Set([1])) + assert.deepStrictEqual(S.concat(new Set([1, 2]), _.empty), _.empty) + assert.deepStrictEqual(S.concat(_.empty, new Set([1, 3])), _.empty) + }) + + it('difference', () => { + assert.deepStrictEqual(_.difference(eqNumber)(new Set([1, 2]), new Set([1, 3])), new Set([2])) + }) + + it('reduce', () => { + assert.deepStrictEqual(_.reduce(ordNumber)('', (b, a) => b + a)(new Set([1, 2, 3])), '123') + assert.deepStrictEqual(_.reduce(ordNumber)('', (b, a) => b + a)(new Set([3, 2, 1])), '123') + }) + + it('foldMap', () => { + assert.deepStrictEqual(_.foldMap(ordNumber, getMonoid())(a => [a])(new Set([1, 2, 3])), [1, 2, 3]) + assert.deepStrictEqual(_.foldMap(ordNumber, getMonoid())(a => [a])(new Set([3, 2, 1])), [1, 2, 3]) + }) + + it('singleton', () => { + assert.deepStrictEqual(_.singleton(1), new Set([1])) + }) + + it('insert', () => { + const x = new Set([1, 2]) + assert.deepStrictEqual(_.insert(eqNumber)(3)(x), new Set([1, 2, 3])) + // should return the same ference if the element is already a member + assert.deepStrictEqual(_.insert(eqNumber)(2)(x), x) + }) + + it('remove', () => { + assert.deepStrictEqual(_.remove(eqNumber)(3)(new Set([1, 2])), new Set([1, 2])) + assert.deepStrictEqual(_.remove(eqNumber)(1)(new Set([1, 2])), new Set([2])) + }) + + it('fromArray', () => { + assert.deepStrictEqual(_.fromArray(eqNumber)([]), new Set([])) + assert.deepStrictEqual(_.fromArray(eqNumber)([1]), new Set([1])) + assert.deepStrictEqual(_.fromArray(eqNumber)([1, 1]), new Set([1])) + assert.deepStrictEqual(_.fromArray(eqNumber)([1, 2]), new Set([1, 2])) + + assert.deepStrictEqual(_.fromArray(fooEq)(['a', 'a', 'b'].map(foo)), new Set(['a', 'b'].map(foo))) + }) + + it('compact', () => { + assert.deepStrictEqual(_.compact(eqNumber)(new Set([optionSome(1), none, optionSome(2)])), new Set([1, 2])) + type R = { readonly id: string } + const S: Eq = eq.contramap(eqString, x => x.id) + assert.deepStrictEqual( + _.compact(S)(new Set([optionSome({ id: 'a' }), none, optionSome({ id: 'a' })])), + new Set([{ id: 'a' }]) + ) + }) + + it('separate', () => { + assert.deepStrictEqual(_.separate(eqString, eqNumber)(new Set([right(1), left('a'), right(2)])), { + left: new Set(['a']), + right: new Set([1, 2]) + }) + type L = { readonly error: string } + type R = { readonly id: string } + const SL: Eq = eq.contramap(eqString, x => x.error) + const SR: Eq = eq.contramap(eqString, x => x.id) + assert.deepStrictEqual( + _.separate( + SL, + SR + )(new Set([right({ id: 'a' }), left({ error: 'error' }), right({ id: 'a' }), left({ error: 'error' })])), + { + left: new Set([{ error: 'error' }]), + right: new Set([{ id: 'a' }]) + } + ) + }) + + it('filterMap', () => { + assert.deepStrictEqual( + _.filterMap(eqNumber)((s: string) => (s.length > 1 ? optionSome(s.length) : none))(new Set(['a', 'bb', 'ccc'])), + new Set([2, 3]) + ) + type R = { readonly id: string } + const S: Eq = eq.contramap(eqString, x => x.id) + assert.deepStrictEqual( + _.filterMap(S)((x: { readonly id: string }) => optionSome(x))(new Set([{ id: 'a' }, { id: 'a' }])), + new Set([{ id: 'a' }]) + ) + }) + + it('getShow', () => { + const S = _.getShow(showString) + const s1 = new Set([]) + assert.deepStrictEqual(S.show(s1), `new Set([])`) + const s2 = new Set(['a']) + assert.deepStrictEqual(S.show(s2), `new Set(["a"])`) + const s3 = new Set(['a', 'b']) + assert.deepStrictEqual(S.show(s3), `new Set(["a", "b"])`) + }) + + it('fromSet', () => { + const as = new Set(['a']) + const bs = _.fromSet(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) + + it('toSet', () => { + const as: ReadonlySet = new Set(['a']) + const bs = _.toSet(as) + assert.deepStrictEqual(bs, as) + assert.notStrictEqual(bs, as) + }) +}) diff --git a/test/ReadonlyTuple.ts b/test/ReadonlyTuple.ts new file mode 100644 index 000000000..e7d088230 --- /dev/null +++ b/test/ReadonlyTuple.ts @@ -0,0 +1,109 @@ +import * as assert from 'assert' +import { getMonoid } from '../src/Array' +import { left, right } from '../src/Either' +import { identity } from '../src/function' +import { monoidString } from '../src/Monoid' +import { none, option, some } from '../src/Option' +import * as RT from '../src/ReadonlyTuple' + +describe('ReadonlyTuple', () => { + it('compose', () => { + assert.deepStrictEqual(RT.readonlyTuple.compose([true, 2], [1, 'a']), [true, 'a']) + }) + + it('map', () => { + const double = (n: number): number => n * 2 + assert.deepStrictEqual(RT.readonlyTuple.map([1, 'a'], double), [2, 'a']) + }) + + it('extract', () => { + assert.deepStrictEqual(RT.readonlyTuple.extract([1, 'a']), 1) + }) + + it('extend', () => { + const f = (fa: readonly [number, string]): number => RT.snd(fa).length + RT.fst(fa) + assert.deepStrictEqual(RT.readonlyTuple.extend([1, 'bb'], f), [3, 'bb']) + }) + + describe('Bifunctor', () => { + it('bimap', () => { + const double = (n: number): number => n * 2 + const len = (s: string): number => s.length + assert.deepStrictEqual(RT.readonlyTuple.bimap([1, 'a'], len, double), [2, 1]) + }) + + it('mapLeft', () => { + const len = (s: string): number => s.length + assert.deepStrictEqual(RT.readonlyTuple.mapLeft([1, 'a'], len), [1, 1]) + }) + }) + + it('reduce', () => { + assert.deepStrictEqual( + RT.readonlyTuple.reduce(['b', 1], 'a', (acc, a) => acc + a), + 'ab' + ) + }) + + it('foldMap', () => { + assert.deepStrictEqual(RT.readonlyTuple.foldMap(monoidString)(['a', 1], identity), 'a') + }) + + it('reduceRight', () => { + assert.deepStrictEqual( + RT.readonlyTuple.reduceRight(['b', 1], 'a', (acc, a) => acc + a), + 'ba' + ) + }) + + it('swap', () => { + assert.deepStrictEqual(RT.swap([1, 'a']), ['a', 1]) + }) + + it('getApply', () => { + const apply = RT.getApply(monoidString) + const double = (n: number): number => n * 2 + assert.deepStrictEqual(apply.ap([double, 'a'], [1, 'b']), [2, 'ab']) + }) + + it('getApplicative', () => { + const applicative = RT.getApplicative(monoidString) + assert.deepStrictEqual(applicative.of(1), [1, '']) + }) + + it('getMonad', () => { + const monad = RT.getMonad(monoidString) + assert.deepStrictEqual( + monad.chain([1, 'a'], a => [a * 2, 'b']), + [2, 'ab'] + ) + }) + + it('chainRec', () => { + const { chainRec } = RT.getChainRec(getMonoid()) + function seqReq(upper: number): readonly [number, ReadonlyArray] { + return chainRec(1, init => [init >= upper ? right(init) : left(init + 1), [init]]) + } + const xs = RT.snd(seqReq(10000)) + assert.deepStrictEqual(xs.length, 10000) + assert.deepStrictEqual(xs[0], 1) + assert.deepStrictEqual(xs[xs.length - 1], 10000) + }) + + it('traverse', () => { + assert.deepStrictEqual( + RT.readonlyTuple.traverse(option)([2, 'a'], n => (n >= 2 ? some(n) : none)), + some([2, 'a']) + ) + assert.deepStrictEqual( + RT.readonlyTuple.traverse(option)([1, 'a'], n => (n >= 2 ? some(n) : none)), + none + ) + }) + + it('sequence', () => { + const sequence = RT.readonlyTuple.sequence(option) + assert.deepStrictEqual(sequence([some(2), 'a']), some([2, 'a'])) + assert.deepStrictEqual(sequence([none, 'a']), none) + }) +}) diff --git a/test/Record.ts b/test/Record.ts index 8ab7951d3..c0ccc101c 100644 --- a/test/Record.ts +++ b/test/Record.ts @@ -1,13 +1,13 @@ import * as assert from 'assert' -import * as R from '../src/Record' -import { semigroupSum, getLastSemigroup, getFirstSemigroup } from '../src/Semigroup' -import { monoidString } from '../src/Monoid' -import { identity } from '../src/function' -import { option, some, none, Option, getOrElse, isSome } from '../src/Option' -import { eqNumber } from '../src/Eq' -import { array, zip } from '../src/Array' import { left, right } from '../src/Either' +import { eqNumber } from '../src/Eq' +import { identity } from '../src/function' import * as I from '../src/Identity' +import { monoidString } from '../src/Monoid' +import { getOrElse, isSome, none, option, Option, some } from '../src/Option' +import { readonlyArray, zip } from '../src/ReadonlyArray' +import * as R from '../src/Record' +import { getFirstSemigroup, getLastSemigroup, semigroupSum } from '../src/Semigroup' import { showString } from '../src/Show' const p = (n: number) => n > 2 @@ -95,11 +95,11 @@ describe('Record', () => { it('fromFoldable', () => { const First = getFirstSemigroup() - assert.deepStrictEqual(R.fromFoldable(First, array)([['a', 1]]), { a: 1 }) + assert.deepStrictEqual(R.fromFoldable(First, readonlyArray)([['a', 1]]), { a: 1 }) assert.deepStrictEqual( R.fromFoldable( First, - array + readonlyArray )([ ['a', 1], ['a', 2] @@ -112,7 +112,7 @@ describe('Record', () => { assert.deepStrictEqual( R.fromFoldable( Last, - array + readonlyArray )([ ['a', 1], ['a', 2] @@ -135,7 +135,7 @@ describe('Record', () => { }) it('toUnfoldable', () => { - assert.deepStrictEqual(R.toUnfoldable(array)({ a: 1 }), [['a', 1]]) + assert.deepStrictEqual(R.toUnfoldable(readonlyArray)({ a: 1 }), [['a', 1]]) }) it('traverseWithIndex', () => { @@ -265,14 +265,14 @@ describe('Record', () => { it('every', () => { const x: Record = { a: 1, b: 2 } - const y: { [key: string]: number } = { a: 1, b: 2 } + const y: Record = { a: 1, b: 2 } assert.deepStrictEqual(R.every((n: number) => n <= 2)(x), true) assert.deepStrictEqual(R.every((n: number) => n <= 1)(y), false) }) it('some', () => { const x: Record = { a: 1, b: 2 } - const y: { [key: string]: number } = { a: 1, b: 2 } + const y: Record = { a: 1, b: 2 } assert.deepStrictEqual(R.some((n: number) => n <= 1)(x), true) assert.deepStrictEqual(R.some((n: number) => n <= 0)(y), false) }) @@ -283,24 +283,24 @@ describe('Record', () => { }) it('fromFoldableMap', () => { - const zipObject = (keys: Array, values: Array): Record => - R.fromFoldableMap(getLastSemigroup(), array)(zip(keys, values), identity) + const zipObject = (keys: ReadonlyArray, values: ReadonlyArray): Record => + R.fromFoldableMap(getLastSemigroup(), readonlyArray)(zip(keys, values), ([k, a]) => [k, a]) assert.deepStrictEqual(zipObject(['a', 'b'], [1, 2, 3]), { a: 1, b: 2 }) interface User { - id: string - name: string + readonly id: string + readonly name: string } - const users: Array = [ + const users: ReadonlyArray = [ { id: 'id1', name: 'name1' }, { id: 'id2', name: 'name2' }, { id: 'id1', name: 'name3' } ] assert.deepStrictEqual( - R.fromFoldableMap(getLastSemigroup(), array)(users, user => [user.id, user]), + R.fromFoldableMap(getLastSemigroup(), readonlyArray)(users, user => [user.id, user]), { id1: { id: 'id1', name: 'name3' }, id2: { id: 'id2', name: 'name2' } @@ -347,6 +347,10 @@ describe('Record', () => { assert.deepStrictEqual(R.filterWithIndex((_, a: number) => a > 1)({ a: 1, b: 2 }), { b: 2 }) }) + it('mapWithIndex', () => { + assert.deepStrictEqual(R.mapWithIndex((k, a: number) => k + String(a))({ a: 1, b: 2 }), { a: 'a1', b: 'b2' }) + }) + it('updateAt', () => { const x: Record = { a: 1 } assert.deepStrictEqual(R.updateAt('b', 2)(x), none) diff --git a/test/Semigroup.ts b/test/Semigroup.ts index e7bcb94b9..2064e8434 100644 --- a/test/Semigroup.ts +++ b/test/Semigroup.ts @@ -39,8 +39,8 @@ describe('Semigroup', () => { it('getObjectSemigroup', () => { type T = { - foo?: number - bar: string + readonly foo?: number + readonly bar: string } const foo: T = { foo: 123, diff --git a/test/Set.ts b/test/Set.ts index 2bc462f39..b714b9ea5 100644 --- a/test/Set.ts +++ b/test/Set.ts @@ -38,7 +38,7 @@ import { getMonoid } from '../src/Array' const gte2 = (n: number) => n >= 2 interface Foo { - x: string + readonly x: string } const foo = (x: string): Foo => ({ x }) const fooEq: Eq = { @@ -143,7 +143,7 @@ describe('Set', () => { partitionMap( SL, SR - )((x: { value: number }) => (x.value % 2 === 0 ? left({ value: 2 }) : right({ value: 'odd' })))( + )((x: { readonly value: number }) => (x.value % 2 === 0 ? left({ value: 2 }) : right({ value: 'odd' })))( new Set([{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]) ), { @@ -213,7 +213,7 @@ describe('Set', () => { it('compact', () => { assert.deepStrictEqual(compact(eqNumber)(new Set([optionSome(1), none, optionSome(2)])), new Set([1, 2])) - type R = { id: string } + type R = { readonly id: string } const S: Eq = eq.contramap(eqString, x => x.id) assert.deepStrictEqual( compact(S)(new Set([optionSome({ id: 'a' }), none, optionSome({ id: 'a' })])), @@ -226,8 +226,8 @@ describe('Set', () => { left: new Set(['a']), right: new Set([1, 2]) }) - type L = { error: string } - type R = { id: string } + type L = { readonly error: string } + type R = { readonly id: string } const SL: Eq = eq.contramap(eqString, x => x.error) const SR: Eq = eq.contramap(eqString, x => x.id) assert.deepStrictEqual( @@ -247,10 +247,10 @@ describe('Set', () => { filterMap(eqNumber)((s: string) => (s.length > 1 ? optionSome(s.length) : none))(new Set(['a', 'bb', 'ccc'])), new Set([2, 3]) ) - type R = { id: string } + type R = { readonly id: string } const S: Eq = eq.contramap(eqString, x => x.id) assert.deepStrictEqual( - filterMap(S)((x: { id: string }) => optionSome(x))(new Set([{ id: 'a' }, { id: 'a' }])), + filterMap(S)((x: { readonly id: string }) => optionSome(x))(new Set([{ id: 'a' }, { id: 'a' }])), new Set([{ id: 'a' }]) ) }) diff --git a/test/Task.ts b/test/Task.ts index 8a172994d..a923918e7 100644 --- a/test/Task.ts +++ b/test/Task.ts @@ -83,6 +83,7 @@ describe('Task', () => { describe('Traversable', () => { it('sequence parallel', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.Task => () => Promise.resolve(log.push(message)) const t1 = _.task.chain(append('start 1'), () => append('end 1')) @@ -94,6 +95,7 @@ describe('Task', () => { }) it('sequence series', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.Task => () => Promise.resolve(log.push(message)) const t1 = _.task.chain(append('start 1'), () => append('end 1')) diff --git a/test/TaskEither.ts b/test/TaskEither.ts index ae59c674e..e96504004 100644 --- a/test/TaskEither.ts +++ b/test/TaskEither.ts @@ -34,6 +34,7 @@ describe('TaskEither', () => { }) describe('bracket', () => { + // tslint:disable-next-line: readonly-array let log: Array = [] const acquireFailure = _.left('acquire failure') @@ -230,6 +231,7 @@ describe('TaskEither', () => { }) it('sequence parallel', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.TaskEither => _.rightTask(() => Promise.resolve(log.push(message))) @@ -242,6 +244,7 @@ describe('TaskEither', () => { }) it('sequence series', async () => { + // tslint:disable-next-line: readonly-array const log: Array = [] const append = (message: string): _.TaskEither => _.rightTask(() => Promise.resolve(log.push(message))) diff --git a/test/Traced.ts b/test/Traced.ts index 4f23667fd..4e2895770 100644 --- a/test/Traced.ts +++ b/test/Traced.ts @@ -6,9 +6,9 @@ import { pipe } from '../src/pipeable' // Adapted from https://chshersh.github.io/posts/2019-03-25-comonadic-builders interface Settings { - settingsHasLibrary: boolean - settingsGitHub: boolean - settingsTravis: boolean + readonly settingsHasLibrary: boolean + readonly settingsGitHub: boolean + readonly settingsTravis: boolean } const M: Monoid = getStructMonoid({ @@ -20,10 +20,10 @@ const M: Monoid = getStructMonoid({ const C = getComonad(M) interface Project { - projectName: string - projectHasLibrary: boolean - projectGitHub: boolean - projectTravis: boolean + readonly projectName: string + readonly projectHasLibrary: boolean + readonly projectGitHub: boolean + readonly projectTravis: boolean } interface ProjectBuilder extends Traced {} diff --git a/test/Tree.ts b/test/Tree.ts index 88f414637..9e499e59e 100644 --- a/test/Tree.ts +++ b/test/Tree.ts @@ -149,7 +149,7 @@ describe('Tree', () => { it('elem', () => { interface User { - id: number + readonly id: number } const S: Eq = eq.contramap(eqNumber, (user: User) => user.id) const users = make({ id: 1 }, [make({ id: 1 }, [make({ id: 3 }), make({ id: 4 })]), make({ id: 2 })]) diff --git a/test/Tuple.ts b/test/Tuple.ts index bf6d8dec3..d59f58caf 100644 --- a/test/Tuple.ts +++ b/test/Tuple.ts @@ -21,27 +21,11 @@ describe('Tuple', () => { }) it('extend', () => { + // tslint:disable-next-line: readonly-array const f = (fa: [number, string]): number => T.snd(fa).length + T.fst(fa) assert.deepStrictEqual(T.tuple.extend([1, 'bb'], f), [3, 'bb']) }) - // it('getApplicative', () => { - // const F = T.getApplicative(monoidString) - // const double = (n: number): number => n * 2 - // const x = F.of(double) - // const y = tuple('a', 1) - // const z = tuple('a', 2) - // assert.deepStrictEqual(F.ap(x, y), z) - // }) - - // it('getMonad', () => { - // const M = T.getMonad(monoidString) - // const f = (n: number) => M.of(n * 2) - // const x = tuple('a', 1) - // const y = tuple('a', 2) - // assert.deepStrictEqual(M.chain(x, f), y) - // }) - describe('Bifunctor', () => { it('bimap', () => { const double = (n: number): number => n * 2 @@ -98,6 +82,7 @@ describe('Tuple', () => { it('chainRec', () => { const { chainRec } = T.getChainRec(getMonoid()) + // tslint:disable-next-line: readonly-array function seqReq(upper: number): [number, Array] { return chainRec(1, init => [init >= upper ? right(init) : left(init + 1), [init]]) } diff --git a/test/Writer.ts b/test/Writer.ts index 2b66dabdc..56f3409a9 100644 --- a/test/Writer.ts +++ b/test/Writer.ts @@ -48,7 +48,7 @@ describe('Writer', () => { }) it('censor', () => { - const fa: W.Writer, number> = () => [1, ['a', 'b']] + const fa: W.Writer, number> = () => [1, ['a', 'b']] assert.deepStrictEqual( pipe( fa, diff --git a/test/function.ts b/test/function.ts index f823e99aa..6523e9edf 100644 --- a/test/function.ts +++ b/test/function.ts @@ -93,8 +93,8 @@ describe('function', () => { }) it('untupled', () => { - const f1 = (a: [number]): number => a[0] * 2 - const f2 = (a: [number, number]): number => a[0] + a[1] + const f1 = (a: readonly [number]): number => a[0] * 2 + const f2 = (a: readonly [number, number]): number => a[0] + a[1] const u1 = untupled(f1) const u2 = untupled(f2) assert.deepStrictEqual(u1(1), 2) diff --git a/test/index.ts b/test/index.ts index eca8210ad..9eb14cd5f 100644 --- a/test/index.ts +++ b/test/index.ts @@ -21,7 +21,7 @@ const getExportName = (name: string): string => { return name.substring(0, 1).toLowerCase() + name.substring(1) } -function getModuleNames(): Array { +function getModuleNames(): ReadonlyArray { return glob.sync('./src/**/*.ts').map(file => path.parse(file).name) } diff --git a/test/pipeable.ts b/test/pipeable.ts index 80126d9e5..5055e0c30 100644 --- a/test/pipeable.ts +++ b/test/pipeable.ts @@ -64,7 +64,7 @@ describe('pipeable', () => { it('Extend', () => { const { extend, duplicate } = pipeable(array) - assert.deepStrictEqual(extend((as: Array) => fold(monoidSum)(as))([1, 2, 3]), [6, 5, 3]) + assert.deepStrictEqual(extend((as: ReadonlyArray) => fold(monoidSum)(as))([1, 2, 3]), [6, 5, 3]) assert.deepStrictEqual(duplicate([1, 2, 3]), [[1, 2, 3], [2, 3], [3]]) }) diff --git a/tslint.json b/tslint.json index 6535265f4..9421fd906 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,5 @@ { - "extends": "tslint-config-standard", + "extends": ["tslint-config-standard", "tslint-immutable"], "rules": { "space-before-function-paren": false, "no-use-before-declare": false, @@ -9,6 +9,8 @@ "strict-boolean-expressions": true, "forin": true, "no-console": true, - "array-type": [true, "generic"] + "array-type": [true, "generic"], + "readonly-keyword": true, + "readonly-array": true } }