Skip to content

Commit

Permalink
Merge pull request #56 from MajorLift/jongsun/docs/230816-list-utils
Browse files Browse the repository at this point in the history
  • Loading branch information
MajorLift authored Oct 28, 2023
2 parents 7f0520f + 1fd6f84 commit d340f10
Show file tree
Hide file tree
Showing 31 changed files with 563 additions and 264 deletions.
23 changes: 2 additions & 21 deletions src/list/accumulate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ import { $N, Kind, Type, List } from '..'
* @template X - A list of types. The target of the accumulate operation.
* @template O - A type specifying the initial argument that will be taken by `F`.
* To use the first element of `X` as the initial argument, simply pass in `Function.Identity`.
* @returns A list of types containing the results of the accumulate operation.
*
* @example
* For example, we can use `_$accumulate` to derive the sum of k = 1 to n for all elements n in a list of natural number types.
*
* ```ts
* import { List } from "hkt-toolbelt";
*
* type SummationSum1to10 = List._$accumulate<NaturalNumber.Add, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], []>; // [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
* ```
*/
export type _$accumulate<
F extends Kind.Kind<(x: never) => Kind.Kind>,
Expand Down Expand Up @@ -65,58 +62,42 @@ interface Accumulate_T<F extends Kind.Kind<(x: never) => Kind.Kind>>
* @template O - A type specifying the initial argument that will be taken by `F`.
* To use first element of `X` as the initial argument, simply pass in `Function.Identity`.
* @template X - A list of types. The target of the accumulate operation.
* @returns A list of types containing the results of the accumulate operation.
*
* @example
* For example, we can use `Accumulate` to derive the sum of k = 1 to n for all elements n in a list of natural number types.
*
* ```ts
* import { $, List } from "hkt-toolbelt";
*
* type SummationSum1to10 = $<$<$<List.Accumulate, NaturalNumber.Add>, 0>, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>; // [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
* ```
*
* @example
* We can also use the `$N` applicator to invoke `Accumulate` with a list containing the required arguments
* This improves readability by allowing us to avoid nesting `$` calls three level deep.
*
* ```ts
* import { $N, List } from "hkt-toolbelt";
*
* type IsFalse = $N<List.Accumulate, [
* Boolean.Xor,
* true,
* [false, true, false, true]
* ]>; // [true, false, false, true]
* ```
*
* @example
* By partially applying only the first two arguments to `Accumulate`,
* we can define a type-level function that can apply the same operation to multiple list inputs.
*
* ```ts
* import { $, $N, List, Number } from hkt-toolbelt;
*
* type GetMax = $N<List.Accumulate, [Number.Max, Number.MIN_SAFE_INTEGER]>;
* type IsZero = $<GetMax, [-5, -4, -3, -2, -1, 0]; // [-5, -4, -3, -2, -1, 0]
* type IsHundred = $<GetMax, [1, -1, 10, -10, 100, -100]>; // [1, 1, 10, 10, 100, 100]
* ```
*
* @example
* Another use case for a partially-applied `Accumulate` function is to implement
* sophisticated higher-order functionality by passing it into other type-level functions.
*
* ```ts
* import { $, $N, List, Conditional, Number, String } from "hkt-toolbelt";
*
* type GetMinOrJoin = $N<Conditional.If, [
* $<Conditional.Extends, number[]>,
* $N<List.Accumulate, [Number.Min, Number.MAX_SAFE_INTEGER]>,
* $<String.Join, ", ">,
* ]>;
*
* type IsNegativeHundred = $<GetMinOrJoin, [1, -1, 10, -10, 100, -100]>; // [1, -1, -1, -10, -10, -100]
* type HelloWorld = $<GetMinOrJoin, ["hello", "world"]>; // "hello, world"
* ```
*/
export interface Accumulate extends Kind.Kind {
f(
Expand Down
55 changes: 55 additions & 0 deletions src/list/at.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import { DigitList, Kind, Type, Number, List, NaturalNumber } from '../'

/**
* `_$at` is a type-level function that retrieves and returns an element from a tuple type.
*
* It takes in two arguments: a tuple, and an integer specifying the index of the element to be accessed.
* Both positive and negative indices are supported, with negative indices being normalized into zero-based indices under the hood.
*
* @template T - A tuple type.
* @template POS - An integer type specifying the index of the element to be accessed.
* @returns The element of `T` at index `POS`.
*
* ## Edge Cases
*
* If `POS` is greater than or equal to the length of `T`, returns `never`.
* If `POS` is lesser than the negated length of `T`, returns `never`.
* If `POS` is not a numeric type, returns `never`.
*
* @example
* A negative index counts back from the end of the input tuple.
*
* type MyList = ['a', 'b', 'c', 'd', 'e'];
*
* type Head = List._$at<MyList, 0>; // 'a'
* type Tail = List._$at<MyList, -1>; // 'e'
*
* type IsNever = List._$at<MyList, 5>; // never
* type IsNever2 = List._$at<MyList, -6>; // never
* ```
*/
export type _$at<
T extends List.List,
POS extends Number.Number,
Expand All @@ -23,6 +51,33 @@ interface At_T<X extends Number.Number> extends Kind.Kind {
): Number._$isInteger<X> extends true ? _$at<typeof x, X> : never
}

/**
* `At` is a type-level function that retrieves and returns an element from a tuple type.
*
* It takes in two arguments: a tuple, and an integer specifying the index of the element to be accessed.
* Both positive and negative indices are supported, with negative indices being normalized into zero-based indices under the hood.
*
* @template T - A tuple type.
* @template POS - An integer type specifying the index of the element to be accessed.
* @returns The element of `T` at index `POS`.
*
* ## Edge Cases
*
* If `POS` is greater than or equal to the length of `T`, returns `never`.
* If `POS` is lesser than the negated length of `T`, returns `never`.
* If `POS` is not an integer type, returns `never`.
*
* @example
* A negative index counts back from the end of the input tuple.
*
* type MyList = ['a', 'b', 'c', 'd', 'e'];
*
* type Head = $<$<List.At, 0>, MyList>; // 'a'
* type Tail = $<$<List.At, -1>, MyList>; // 'e'
*
* type IsNever = $<$<List.At, 5>, MyList>; // never
* type IsNever2 = $<$<List.At, -6>, MyList>; // never
*/
export interface At extends Kind.Kind {
f(x: Type._$cast<this[Kind._], Number.Number>): At_T<typeof x>
}
36 changes: 12 additions & 24 deletions src/list/concat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,23 @@ import { Type, Kind, List } from '..'
* It takes in two arguments:
* `T`, the tuple to concatenate onto, and `U`, the tuple to concatenate.
*
* @template T - A tuple type.
* @template U - A tuple type, or an unknown.
* @template T - A tuple type to be concatenated onto.
* @template U - A tuple type to concatenate, or an unknown.
* @returns A tuple type.
*
* If `U` is not a tuple type, it will be pushed into `T` as its new last element.
*
* ## Basic Usage
*
* @example
* ```ts
* import { $, List } from 'hkt-toolbelt';
*
* type Result = List._$concat<[0, 1], [2, 3]>; // [0, 1, 2, 3]
* ```
*
* ## Advanced Usage
*
* @example
* Concatenating to a tuple with a rest parameter results in a tuple that contains the concatenated tuple.
*
* ```ts
* import { $, List } from 'hkt-toolbelt';
*
* List._$concat<[1, 2, ...string[]], ["foo"]>; // [1, 2, ...string[], "foo"]
* ```
* @example
* type Result = List._$concat<[1, 2, ...string[]], ["foo"]>; // [1, 2, ...string[], "foo"]
*
*/
export type _$concat<
Expand All @@ -46,29 +40,23 @@ interface Concat_T<U extends unknown> extends Kind.Kind {
* It takes in two arguments:
* `T`, the tuple to concatenate onto, and `U`, the tuple to concatenate.
*
* @template T - A tuple type.
* @template U - A tuple type, or an unknown.
* @template T - A tuple type to be concatenated onto.
* @template U - A tuple type to concatenate, or an unknown.
* @returns A tuple type.
*
* If `U` is not a tuple type, it will be pushed into `T` as its new last element.
*
* ## Basic Usage
*
* @example
* ```ts
* import { $, List } from 'hkt-toolbelt';
*
* type Result = $<$<List.Concat [2, 3]>, [1, 2]>; // [0, 1, 2, 3]
* ```
*
* ## Advanced Usage
*
* @example
* Concatenating to a tuple with a rest parameter results in a tuple that contains the concatenated tuple.
*
* ```ts
* import { $, List } from 'hkt-toolbelt';
*
* $<$<List.Concat, ["foo"]>, [1, 2, ...string[]]>; // [1, 2, ...string[], "foo"]
* ```
* @example
* type Result = $<$<List.Concat, ["foo"]>, [1, 2, ...string[]]>; // [1, 2, ...string[], "foo"]
*
*/
export interface Concat extends Kind.Kind {
Expand Down
22 changes: 22 additions & 0 deletions src/list/every.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { $, Boolean, Type, Kind } from '..'

/**
* `_$every` is a type-level function that checks if every element in a tuple satisfies a predicate.
*
* @template F - The predicate function.
* @template T - The tuple to check.
* @returns A boolean.
*
* @example
* type T0 = List._$every<$<Conditional.Extends, number>, [1, 2, 3]> // true
* type T1 = List._$every<$<Conditional.Extends, number>, [1, 2, 3, 'x']> // false
*/
export type _$every<
F extends Kind.Kind<(x: never) => boolean>,
T extends unknown[],
Expand All @@ -17,6 +28,17 @@ interface Every_T<F extends Kind.Kind<(x: never) => boolean>>
f(x: Type._$cast<this[Kind._], Kind._$inputOf<F>[]>): _$every<F, typeof x>
}

/**
* `Every` is a type-level function that checks if every element in a tuple satisfies a predicate.
*
* @template F - The predicate function.
* @template T - The tuple to check.
* @returns A boolean.
*
* @example
* type T0 = $<$<List.Every, $<Conditional.Extends, number>>, [1, 2, 3]> // true
* type T1 = $<$<List.Every, $<Conditional.Extends, number>>, [1, 2, 3, 'x']> // false
*/
export interface Every extends Kind.Kind {
f(
x: Type._$cast<this[Kind._], Kind.Kind<(x: never) => boolean>>
Expand Down
22 changes: 2 additions & 20 deletions src/list/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,12 @@ import { $, Type, Kind } from '..'
*
* @template F - A type-level function that returns a boolean type indicating whether a type should be included in the result.
* @template X - A list of types. The target of the filtering operation.
* @returns A list of types that satisfy the predicate `F`.
*
* @example
* For example, we can use `_$filter` to filter out negative elements from a tuple of numeric types:
*
* ```ts
* import { List } from "hkt-toolbelt";
*
* type FilteredNumbers = List._$filter<Conditional.IsPositive, [1, -2, 3, -4]>; // [1, 3]
* ```
*/
export type _$filter<
F extends Kind.Kind,
Expand Down Expand Up @@ -47,53 +44,38 @@ interface Filter_T<F extends Kind.Kind<(x: never) => boolean>>
*
* @template F - A type-level function that returns a boolean type indicating whether a type should be included in the result.
* @template X - A list of types. The target of the filtering operation.
* @returns A list of types that satisfy the predicate `F`.
*
* @example
* For example, we can define a filter for positive numbers and then apply it to a list:
*
* ```ts
* import { $, List, Conditional } from "hkt-toolbelt";
*
* type FilteredNumbers = $<$<List.Filter, Conditional.IsPositive>, [1, -2, 3, -4]>; // [1, 3]
* ```
*
* @example
* We can also use the `$N` applicator to invoke `Filter` with a list containing the required arguments
* This improves readability by allowing us to avoid nesting `$` calls.
*
* ```ts
* import { $N, List } from "hkt-toolbelt";
*
* type FilterZeros = $N<List.Filter, [
* $<Conditional.NotEquals, 0>,
* [1, 0, 2, 0, 3]
* ]>; // [1, 2, 3]
* ```
*
* @example
* By partially applying only the first argument to `Filter`,
* we can define a type-level function that can apply the same operation to multiple list inputs.
*
* ```ts
* import { $, List, Conditional } from hkt-toolbelt;
*
* type FilterZeros = $<List.Filter, $<Conditional.NotEquals, 0>>;
* type AllZero = $<FilterZeros, [0, 0, 0, 0, 0]>; // []
* type OneZero = $<FilterZeros, [0, 1, 2, 3, 4, 5]>; // [1, 2, 3, 4, 5]
* ```
*
* @example
* Another use case for a partially-applied `Filter` function is to implement
* sophisticated higher-order functionality by passing it into other type-level functions.
*
* ```ts
* import { $, $$, List, Conditional, String } from "hkt-toolbelt";
*
* type HelloWorld = $$<[
* $<List.Filter, $<Conditional.Extends, string>>
* $<String.Join, ", ">
* ], [42.42, null, "hello", undefined, "world"]> // "hello, world"
* ```
*/
export interface Filter extends Kind.Kind {
f(
Expand Down
27 changes: 27 additions & 0 deletions src/list/find.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { $, Type, Kind } from '..'

/**
* `_$find` is a type-level function that finds the first element in a list that satisfies a predicate.
*
* @template F - The predicate function.
* @template X - The list to search.
* @returns An element of `X`.
*
* @example
* type T0 = List._$find<$<Conditional.Equals, 3>, [1, 2, 3]> // 3
* type T1 = List._$find<$<Conditional.Equals, 4>, [1, 2, 3]> // never
*/
export type _$find<F extends Kind.Kind, X extends unknown[]> = X extends [
infer Head,
...infer Tail
Expand All @@ -9,10 +20,26 @@ export type _$find<F extends Kind.Kind, X extends unknown[]> = X extends [
: _$find<F, Tail>
: never

/**
* `List.Find_T` is an intermediate interface for currying.
*
* @template F - The condition function.
*/
interface Find_T<F extends Kind.Kind<(x: never) => boolean>> extends Kind.Kind {
f(x: Type._$cast<this[Kind._], Kind._$inputOf<F>[]>): _$find<F, typeof x>
}

/**
* `Find` is a type-level function that finds the first element in a list that satisfies a predicate.
*
* @template F - The predicate function.
* @template X - The list to search.
* @returns An element of `X`.
*
* @example
* type T0 = $<$<List.Find, $<Conditional.Equals, 3>>, [1, 2, 3]> // 3
* type T1 = $<$<List.Find, $<Conditional.Equals, 4>>, [1, 2, 3]> // never
*/
export interface Find extends Kind.Kind {
f(
x: Type._$cast<this[Kind._], Kind.Kind<(x: never) => boolean>>
Expand Down
20 changes: 20 additions & 0 deletions src/list/first.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
import { Type, Kind } from '..'

/**
* `_$first` is a type-level function that returns the first element of a tuple.
*
* @template T - The tuple to get the first element of.
* @returns An element of `T`.
*
* @example
* type T0 = List._$first<[1, 2, 3]> // 1
* type T1 = List._$first<[]> // never
*/
export type _$first<T extends unknown[]> = T extends [] ? never : T[0]

/**
* `First` is a type-level function that returns the first element of a tuple.
*
* @template T - The tuple to get the first element of.
* @returns An element of `T`.
*
* @example
* type T0 = $<List.First, [1, 2, 3]> // 1
* type T1 = $<List.First, []> // never
*/
export interface First extends Kind.Kind {
f(x: Type._$cast<this[Kind._], unknown[]>): _$first<typeof x>
}
Loading

0 comments on commit d340f10

Please sign in to comment.