-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:poteat/hkt-toolbelt
- Loading branch information
Showing
44 changed files
with
1,683 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { $, Combinator, Test, NaturalNumber, String } from '..' | ||
|
||
type Rewrite = $<Combinator.Fix, $<$<String.Replace, 'xyz'>, 'x'>> | ||
|
||
const rewrite = Combinator.fix(String.replace('xyz')('x')) | ||
|
||
type Fix_Spec = [ | ||
/** | ||
* Can find a fixed point. | ||
*/ | ||
Test.Expect<$<$<Combinator.Fix, NaturalNumber.Decrement>, 100>, 0>, | ||
|
||
/** | ||
* Can execute a term rewriting system. | ||
*/ | ||
Test.Expect<$<Rewrite, 'zyxyzyzyz'>, 'zyx'> | ||
] | ||
|
||
it('should find a fixed point', () => { | ||
expect(Combinator.fix(NaturalNumber.decrement)(100)).toBe(0) | ||
}) | ||
|
||
it('can execute a term rewriting system', () => { | ||
expect(Combinator.fix(rewrite)('zyxyzyzyz')).toBe('zyx') | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { $, Kind, Type, Conditional, Function } from '..' | ||
import { deepEqual } from '../_internal/deepEqual' | ||
|
||
/** | ||
* `_$fix` is a type-level function that takes in a kind `K` and a value `X`, | ||
* and returns a new value that is the result of applying `K` to `X` until it | ||
* no longer returns a kind. | ||
* | ||
* @template {Kind.Kind} K - The kind to fix. | ||
* @template {unknown} X - The value to fix. | ||
* | ||
* @example | ||
* ```ts | ||
* import { Combinator, NaturalNumber } from "hkt-toolbelt"; | ||
* type Result = Combinator._$fix<NaturalNumber.Decrement, 100> | ||
* // ^? 0 | ||
* ``` | ||
*/ | ||
export type _$fix< | ||
K extends Kind.Kind, | ||
X, | ||
NEXT_VALUE = $<K, Type._$cast<X, Kind._$inputOf<K>>> | ||
> = Conditional._$equals<NEXT_VALUE, X> extends true ? X : _$fix<K, NEXT_VALUE> | ||
|
||
interface Fix_T<K extends Kind.Kind> extends Kind.Kind { | ||
f(x: this[Kind._]): _$fix<K, typeof x> | ||
} | ||
|
||
/** | ||
* `Fix` is a type-level function that takes in a kind `K` and a value `X`, | ||
* and returns a new value that is the result of applying `K` to `X` until it | ||
* no longer returns a kind. | ||
* | ||
* @template {Kind.Kind} K - The kind to fix. | ||
* @template {unknown} X - The value to fix. | ||
* | ||
* @example | ||
* ```ts | ||
* import { Combinator, NaturalNumber } from "hkt-toolbelt"; | ||
* | ||
* type Result = $<$<Combinator.Fix, NaturalNumber.Decrement>, 100> | ||
* // ^? 0 | ||
* ``` | ||
*/ | ||
export interface Fix extends Kind.Kind { | ||
f(x: Type._$cast<this[Kind._], Kind.Kind>): Fix_T<typeof x> | ||
} | ||
|
||
/** | ||
* Given a higher-order type and a value, loop until a fixed point is found. | ||
* | ||
* @param {Kind.Kind} f - The kind for which the fixed-point sequence is calculated. | ||
* @param {unknown} x - The initial value to start the loop with. | ||
* | ||
* @example | ||
* ```ts | ||
* import { Combinator, NaturalNumber } from "hkt-toolbelt"; | ||
* | ||
* const result = Combinator.fix(NaturalNumber.decrement)(100) | ||
* // ^? 0 | ||
* ``` | ||
*/ | ||
export const fix = ((f: Function.Function) => (x: unknown) => { | ||
let value = x | ||
let prevValue = x | ||
|
||
do { | ||
prevValue = value | ||
value = f(value as never) | ||
} while (!deepEqual(prevValue, value)) | ||
|
||
return value | ||
}) as unknown as Kind._$reify<Fix> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export * from './apply-self' | ||
export * from './collate' | ||
export * from './fix-sequence' | ||
export * from './fix' | ||
export * from './recursive-kind' | ||
export * from './self' |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { $, List, Test } from '..' | ||
|
||
type EndsWith_Spec = [ | ||
/** | ||
* Can check if a list ends with a value. | ||
*/ | ||
Test.Expect<$<$<List.EndsWith, [3, 4, 5]>, [1, 2, 3, 4, 5]>, true>, | ||
|
||
/** | ||
* Can check if a list does not end with a value. | ||
*/ | ||
Test.Expect<$<$<List.EndsWith, [3, 4, 5]>, [1, 2, 3, 4]>, false>, | ||
|
||
/** | ||
* Can check if a list ends with an empty value. | ||
*/ | ||
Test.Expect<$<$<List.EndsWith, []>, [1, 2, 3]>, true> | ||
] | ||
|
||
it('should return true if the list ends with the value', () => { | ||
expect(List.endsWith([3, 4, 5])([1, 2, 3, 4, 5])).toBe(true) | ||
}) | ||
|
||
it('should return false if the list does not end with the value', () => { | ||
expect(List.endsWith([3, 4, 5])([1, 2, 3, 4])).toBe(false) | ||
}) | ||
|
||
it('should return true if the list ends with an empty value', () => { | ||
expect(List.endsWith([])([1, 2, 3])).toBe(true) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { Kind, Type, Conditional } from '..' | ||
|
||
/** | ||
* `_$endsWith` is a type-level function that takes in a list `X` and a list | ||
* `T`, and returns a boolean indicating whether `T` ends with `X`. | ||
* | ||
* @template X - The value to check. | ||
* @template T - The list to check. | ||
* | ||
* @returns A boolean indicating whether `T` ends with `X`. | ||
* | ||
* @example | ||
* For example, we can use `_$endsWith` to check if a list ends with a value: | ||
* | ||
* ```ts | ||
* import { List } from "hkt-toolbelt"; | ||
* | ||
* type Result = List._$endsWith<[3, 4, 5], [1, 2, 3, 4, 5]>; // true | ||
* ``` | ||
*/ | ||
export type _$endsWith<X extends unknown[], T extends unknown[]> = X extends [ | ||
...infer TailX, | ||
infer HeadX | ||
] | ||
? T extends [...infer TailT, infer HeadT] | ||
? Conditional._$equals<HeadX, HeadT> extends true | ||
? _$endsWith<TailX, TailT> | ||
: false | ||
: false | ||
: true | ||
|
||
interface EndsWith_T<X extends unknown[]> extends Kind.Kind { | ||
f(x: Type._$cast<this[Kind._], unknown[]>): _$endsWith<X, typeof x> | ||
} | ||
|
||
/** | ||
* `EndsWith` is a type-level function that takes in a list `X` and a list | ||
* `T`, and returns a boolean indicating whether `T` ends with `X`. | ||
* | ||
* @template X - The value to check. | ||
* @template T - The list to check. | ||
* | ||
* @returns A boolean indicating whether `T` ends with `X`. | ||
* | ||
* @example | ||
* For example, we can use `EndsWith` to check if a list ends with a value: | ||
* | ||
* ```ts | ||
* import { $, List } from "hkt-toolbelt"; | ||
* | ||
* type Result = $<$<List.EndsWith, [3, 4, 5]>, [1, 2, 3, 4, 5]>; // true | ||
* ``` | ||
*/ | ||
export interface EndsWith extends Kind.Kind { | ||
f(x: Type._$cast<this[Kind._], unknown[]>): EndsWith_T<typeof x> | ||
} | ||
|
||
/** | ||
* Given a list and a value, return a boolean indicating whether the list | ||
* ends with the value. | ||
* | ||
* @param {unknown[]} x - The value to check. | ||
* @param {unknown[]} values - The list to check. | ||
* | ||
* @example | ||
* ```ts | ||
* import { List } from "hkt-toolbelt"; | ||
* | ||
* const result = List.endsWith([3, 4, 5])([1, 2, 3, 4, 5]) | ||
* // ^? true | ||
* ``` | ||
*/ | ||
export const endsWith = (x: unknown[]) => (values: unknown[]) => { | ||
if (x.length === 0) return true | ||
|
||
for (let i = 0; i < x.length; i++) { | ||
if (x[i] !== values[values.length - x.length + i]) return false | ||
} | ||
|
||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { $, Conditional, Function, List, String, Test } from '..' | ||
|
||
type FindIndex_Spec = [ | ||
/** | ||
* Can find the index of a number present in a tuple. | ||
*/ | ||
Test.Expect<$<$<List.FindIndex, $<Conditional.Equals, 3>>, [1, 2, 3]>, 2>, | ||
|
||
/** | ||
* Can find the index of a string present in a tuple. | ||
*/ | ||
Test.Expect<$<$<List.FindIndex, String.IsString>, [42, 'bar']>, 1>, | ||
|
||
/** | ||
* Can find the index of an element in a tuple. | ||
*/ | ||
Test.Expect< | ||
$<$<List.FindIndex, $<Conditional.Equals, 'foo'>>, ['foo', 'bar']>, | ||
0 | ||
>, | ||
|
||
/** | ||
* Returns -1 if no element satisfies the predicate. | ||
*/ | ||
Test.Expect<$<$<List.FindIndex, $<Conditional.Equals, 42>>, [1, 2, 3]>, -1> | ||
] | ||
|
||
it('should return the index of the first element in the list that satisfies the predicate', () => { | ||
expect(List.findIndex(Conditional.equals('bar'))(['foo', 'bar', 'bar'])).toBe( | ||
1 | ||
) | ||
}) | ||
|
||
it('should return -1 if no element satisfies the predicate', () => { | ||
expect(List.findIndex(String.isString)([42])).toBe(-1) | ||
}) | ||
|
||
it('can find the index of a number present in a tuple', () => { | ||
expect(List.findIndex(Function.constant(true))([1, 2, 3])).toBe(0) | ||
}) | ||
|
||
it('can find the index of a string present in a tuple', () => { | ||
expect(List.findIndex(String.isString)(['foo', 'bar'])).toBe(0) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { $, Kind, Type, DigitList, Function } from '..' | ||
|
||
/** | ||
* `_$findIndex` is a type-level function that takes in a predicate `F` and a | ||
* list `T`, and returns the index of the first element in `T` that satisfies | ||
* the predicate. Returns `-1` if no element satisfies the predicate. | ||
* | ||
* @template {Kind.Kind<(x: never) => boolean>} F - The predicate to check. | ||
* @template {unknown[]} T - The list to check. | ||
* | ||
* @example | ||
* ```ts | ||
* type T0 = List._$findIndex<$<Conditional.Equals, 3>>, [1, 2, 3]> // 2 | ||
* ``` | ||
*/ | ||
export type _$findIndex< | ||
F extends Kind.Kind<(x: never) => boolean>, | ||
T extends unknown[], | ||
I extends DigitList.DigitList = ['0'] | ||
> = T extends [infer Head, ...infer Tail] | ||
? $<F, Type._$cast<Head, Kind._$inputOf<F>>> extends true | ||
? DigitList._$toNumber<I> | ||
: _$findIndex<F, Tail, DigitList._$increment<I>> | ||
: -1 | ||
|
||
interface FindIndex_T<F extends Kind.Kind<(x: never) => boolean>> | ||
extends Kind.Kind { | ||
f(x: Type._$cast<this[Kind._], unknown[]>): _$findIndex<F, typeof x> | ||
} | ||
|
||
/** | ||
* `FindIndex` is a type-level function that takes in a predicate `F` and a | ||
* list `T`, and returns the index of the first element in `T` that satisfies | ||
* the predicate. Returns `-1` if no element satisfies the predicate. | ||
* | ||
* @template {Kind.Kind<(x: never) => boolean>} F - The predicate to check. | ||
* @template {unknown[]} T - The list to check. | ||
* | ||
* @example | ||
* ```ts | ||
* type T0 = $<$<List.FindIndex, $<Conditional.Equals, 3>>, [1, 2, 3]> // 2 | ||
* ``` | ||
*/ | ||
export interface FindIndex extends Kind.Kind { | ||
f( | ||
x: Type._$cast<this[Kind._], Kind.Kind<(x: never) => boolean>> | ||
): FindIndex_T<typeof x> | ||
} | ||
|
||
/** | ||
* Given a predicate and a list, return the index of the first element in the | ||
* list that satisfies the predicate. Returns `-1` if no element satisfies the | ||
* predicate. | ||
* | ||
* @param {Kind.Kind<(x: never) => boolean>} f - The predicate to check. | ||
* @param {unknown[]} values - The list to check. | ||
* | ||
* @example | ||
* ```ts | ||
* import { List, String } from "hkt-toolbelt"; | ||
* | ||
* const result = List.findIndex(String.isString)(['foo', 'bar']) | ||
* // ^? 1 | ||
* ``` | ||
*/ | ||
export const findIndex = ((f: Function.Function) => (values: unknown[]) => | ||
values.findIndex(f as never)) as Kind._$reify<FindIndex> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.