Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More reified utilities #95

Merged
merged 21 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## [0.24.9]

- Reify `NaturalNumber.Add` to a value-level function.
- Add `Kind.JuxtN` to juxt kinds with an arity of greater than one.
- Add `NaturalNumber.FromHex` to convert a hexadecimal string to a decimal number.
- Add `NaturalNumber.ToHex` to convert a decimal number to a hexadecimal string.
- Reify `String.FromList` to a value-level function.
- Add `List.PadStart` to pad a list to a desired length with a padding value.
- Add `List.PadEnd` to pad a list to a desired length with a padding value.
- Add `String.PadStart` to pad a string to a desired length with a padding character.
- Add `String.PadEnd` to pad a string to a desired length with a padding character.
- Improve inference efficiency of `List.Collate` and `Kind.LazyPipe`.

## [0.24.8]

- Add `List.Count` to create a frequency map from a list.
Expand Down
22 changes: 5 additions & 17 deletions src/combinator/collate.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
import { Conditional, Kind, NaturalNumber, Number, Type } from '..'
import { Kind, Type } from '..'

interface _$collate2<
export interface _$collate2<
/**
* The number of arguments to expect.
*/
N extends number,
/**
* The current argument index.
*/
I extends Number.Number = 0,
/**
* The tuple of arguments applied so far.
*/
OUT extends unknown[] = [],
/**
* The next argument index, which is `I + 1`.
*/
NEXT extends Number.Number = NaturalNumber._$increment<I>,
/**
* Whether we have reached the end of the list of arguments.
*/
DONE extends boolean = Conditional._$equals<N, NEXT>
OUT extends unknown[] = []
> extends Kind.Kind {
f(
x: this[Kind._]
): DONE extends true
): N extends [...OUT, typeof x]['length']
? [...OUT, typeof x]
: _$collate2<N, NEXT, [...OUT, typeof x]>
: _$collate2<N, [...OUT, typeof x]>
}

/**
Expand Down
18 changes: 18 additions & 0 deletions src/digit-list/from-hex.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { $, Test, DigitList } from '..'

type FromHex_Spec = [
/**
* Can convert a list of hex digits to a list of decimal digits.
*/
Test.Expect<$<DigitList.FromHex, [['7'], ['1', '1']]>, ['1', '2', '3']>,

/**
* Can convert a list of hex digits to a list of decimal digits.
*/
Test.Expect<$<DigitList.FromHex, [['1', '1'], ['1', '1']]>, ['1', '8', '7']>,

/**
* Can convert zero.
*/
Test.Expect<$<DigitList.FromHex, [['0']]>, ['0']>
]
61 changes: 61 additions & 0 deletions src/digit-list/from-hex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Kind, Type, DigitList } from '..'

/**
* `_$fromHex` is a type-level function that takes in a list of hex digits
* `T`, and returns a list of decimal digits. The hex digits are represented
* as individual digit lists between ["0"] and ["1", "5"].
*
* @template T - The list of hex digits.
*
* @example
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = DigitList._$fromHex<[["7"], ["1", "1"]]>; // ["1", "2", "3"]
* ```
*/
export type _$fromHex<
T extends DigitList.DigitList[],
/**
* The unit value of the current place. Multiplies by 16 on every iteration.
*/
PLACE_MUL extends DigitList.DigitList = ['1'],
/**
* The current sum of places that have been processed so far.
*/
SUM extends DigitList.DigitList = ['0'],
/**
* The next place value, multiplied by 16.
*/
NEXT_PLACE_MUL extends DigitList.DigitList = DigitList._$multiply<
PLACE_MUL,
['1', '6']
>
> = T extends [
...infer Init extends DigitList.DigitList[],
infer Last extends DigitList.DigitList
]
? _$fromHex<
Init,
NEXT_PLACE_MUL,
DigitList._$add<SUM, DigitList._$multiply<PLACE_MUL, Last>>
>
: SUM

/**
* `_$fromHex` is a type-level function that takes in a list of hex digits
* `T`, and returns a list of decimal digits. The hex digits are represented
* as individual digit lists between ["0"] and ["1", "5"].
*
* @template T - The list of hex digits.
*
* @example
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = $<DigitList.FromHex, [["7"], ["1", "1"]]>; // ["1", "2", "3"]
* ```
*/
export interface FromHex extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList[]>): _$fromHex<typeof x>
}
2 changes: 2 additions & 0 deletions src/digit-list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './digit-list'
export * from './divide-by-subtraction'
export * from './divide'
export * from './first'
export * from './from-hex'
export * from './from-string'
export * from './increment'
export * from './is-even'
Expand All @@ -17,6 +18,7 @@ export * from './pop'
export * from './shift'
export * from './signed-add'
export * from './subtract'
export * from './to-hex'
export * from './to-string'
export * from './to-number'
export * from './trim'
Expand Down
18 changes: 18 additions & 0 deletions src/digit-list/to-hex.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { $, Test, DigitList } from '..'

type ToHex_Spec = [
/**
* Can convert a digit list to hex.
*/
Test.Expect<$<DigitList.ToHex, ['1', '2', '3']>, [['7'], ['1', '1']]>,

/**
* Can convert a digit list to hex with a single digit.
*/
Test.Expect<$<DigitList.ToHex, ['1']>, [['1']]>,

/**
* Can convert a digit list to hex zero.
*/
Test.Expect<$<DigitList.ToHex, ['0']>, [['0']]>
]
44 changes: 44 additions & 0 deletions src/digit-list/to-hex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Type, DigitList, Kind } from '..'

/**
* `_$toHex` is a type-level function that takes in a digit list `T`, and
* returns a list of the hex digits of `T`, represented as individual digit
* lists between ["0"] and ["1", "5"].
*
* @template T - The digit list to convert.
*
* @example
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = DigitList._$toHex<["1", "2", "3"]>; // [["7"], ["1", "1"]]
* ```
*/
export type _$toHex<
T extends DigitList.DigitList,
O extends unknown[] = [],
DIV = DigitList._$divide<T, ['1', '6']>,
MOD = DigitList._$modulo<T, ['1', '6']>
> = 0 extends 1
? never
: DIV extends ['0']
? [MOD, ...O]
: _$toHex<Type._$cast<DIV, DigitList.DigitList>, [MOD, ...O]>

/**
* `ToHex` is a type-level function that takes in a digit list `T`, and
* returns a list of the hex digits of `T`, represented as individual digit
* lists between ["0"] and ["1", "5"].
*
* @template T - The digit list to convert.
*
* @example
* ```ts
* import { $, DigitList } from "hkt-toolbelt";
*
* type Result = $<DigitList.ToHex, ["1", "2", "3"]>; // [["7"], ["1", "1"]]
* ```
*/
export interface ToHex extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): _$toHex<typeof x>
}
1 change: 1 addition & 0 deletions src/kind/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './composable-pair'
export * from './compose'
export * from './curry'
export * from './input-of'
export * from './juxt-n'
export * from './juxt'
export * from './kind'
export * from './lazy-pipe'
Expand Down
17 changes: 17 additions & 0 deletions src/kind/juxt-n.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { $, Kind, NaturalNumber, Test } from '..'

type JuxtN_Spec = [
/**
* Apply a list of n-arity kinds to a value.
*/
Test.Expect<
$<$<$<Kind.JuxtN, [NaturalNumber.Increment, NaturalNumber.Add]>, 1>, 5>,
[2, 6]
>
]

it('should apply a list of n-arity kinds to a value', () => {
expect(
Kind.juxtN([NaturalNumber.increment, NaturalNumber.add])(1)(5)
).toEqual([2, 6])
})
99 changes: 99 additions & 0 deletions src/kind/juxt-n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { $, Kind, Type, Function } from '..'

/**
* `_$juxtN` is a type-level function that takes in a list of kinds `K` and
* a value `X`, and either returns a list of the results applied to `X`, or
* another instance of `_$juxtN` for any remaining kinds in the resultant list.
*
* Arity is determined by the maximal arity of the kinds in the list.
*
* @template {Kind.Kind[]} K - The list of kinds to apply.
* @template {unknown} X - The value to apply the kinds to.
*
* @example
* ```ts
* import { $, Kind, List } from "hkt-toolbelt";
*
* type MyJuxt = Kind._$juxtN<[NaturalNumber.Increment, NaturalNumber.Add], 1>;
* // ^? Kind.JuxtN_T<[2, NaturalNumber.Add_T<1>]>
*
* type Result = $<MyJuxt, 5>;
* // ^? [2, 6]
* ```
*/
export type _$juxtN<K extends unknown[], X> = {
[key in keyof K]: K[key] extends Kind.Kind
? $<K[key], Type._$cast<X, Kind._$inputOf<K[key]>>>
: K[key]
} extends infer NewK extends unknown[]
? Kind.Kind<never> extends NewK[number]
? JuxtN_T<NewK>
: NewK
: never

interface JuxtN_T<K extends unknown[]> extends Kind.Kind {
f(x: this[Kind._]): _$juxtN<K, typeof x>
}

/**
* `JuxtN` is a type-level function that takes in a list of kinds `K` and
* a value `X`, and either returns a list of the results applied to `X`, or
* another instance of `JuxtN` for any remaining kinds in the resultant list.
*
* Arity is determined by the maximal arity of the kinds in the list.
*
* @template {Kind.Kind[]} K - The list of kinds to apply.
* @template {unknown} X - The value to apply the kinds to.
*
* @example
* ```ts
* import { $, Kind, List } from "hkt-toolbelt";
*
* type MyJuxt = Kind.JuxtN<[NaturalNumber.Increment, NaturalNumber.Add], 1>;
* // ^? Kind.JuxtN_T<[2, NaturalNumber.Add_T<1>]>
*
* type Result = $<MyJuxt, 5>;
* // ^? [2, 6]
* ```
*/
export interface JuxtN extends Kind.Kind {
f(x: Type._$cast<this[Kind._], unknown[]>): JuxtN_T<typeof x>
}

/**
* Given a list of kinds and a value, apply the kinds to the value. If any of
* the kinds result in another kind, return a new instance of `JuxtN` for the
* remaining kinds and take in a new input value until all kinds have been
* exhausted.
*
* @param {Kind.Kind[]} fx - The list of kinds to apply.
* @param {unknown} x - The value to apply the kinds to.
*
* @example
* ```ts
* import { Kind, NaturalNumber } from "hkt-toolbelt";
*
* const result = Kind.juxtN([NaturalNumber.increment, NaturalNumber.add])(1)(2)
* // ^? [2, 3]
* ```
*/
export const juxtN = ((fx: Function.Function[]) => (x: unknown) => {
const results = []
let hasKindResult = false

for (const f of fx) {
const result = typeof f === 'function' ? f(x as never) : f

if (typeof result === 'function') {
hasKindResult = true
}

results.push(result)
}

if (hasKindResult) {
return juxtN(results as Function.Function[])
}

return results
}) as unknown as Kind._$reify<JuxtN>
2 changes: 1 addition & 1 deletion src/kind/lazy-pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type _$lazyPipe<T extends Kind.Kind[], X> = T extends [
? never
: $<Head, Type._$cast<X, Kind._$inputOf<Head>>> extends infer Result
? Result extends Kind.Kind
? LazyPipe_T<[Result, ...Tail]>
? LazyPipe_T<[LazyPipe_T<[Result]>, ...Tail]>
: _$lazyPipe<Tail, Result>
: never
: X
Expand Down
2 changes: 2 additions & 0 deletions src/list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export * from './map-n'
export * from './max-by'
export * from './min-by'
export * from './of'
export * from './pad-end'
export * from './pad-start'
export * from './pair'
export * from './pop'
export * from './pop-n'
Expand Down
30 changes: 30 additions & 0 deletions src/list/pad-end.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { $, Test, List } from '..'

type PadEnd_Spec = [
/**
* Can pad a list to a desired length.
*/
Test.Expect<$<$<$<List.PadEnd, 8>, 0>, []>, [0, 0, 0, 0, 0, 0, 0, 0]>,

/**
* Padding a list longer than the specified length results in the original list.
*/
Test.Expect<$<$<$<List.PadEnd, 2>, '0'>, [1, 2, 3]>, [1, 2, 3]>,

/**
* Can pad a list to a desired length.
*/
Test.Expect<$<$<$<List.PadEnd, 6>, 0>, [1, 2, 3]>, [1, 2, 3, 0, 0, 0]>
]

it('should pad a list to a desired length', () => {
expect(List.padEnd(8)(0)([])).toEqual([0, 0, 0, 0, 0, 0, 0, 0])
})

it('padding a list longer than the specified length results in the original list', () => {
expect(List.padEnd(2)('0')([1, 2, 3])).toEqual([1, 2, 3])
})

it('can pad a list to a desired length', () => {
expect(List.padEnd(6)(0)([1, 2, 3])).toEqual([1, 2, 3, 0, 0, 0])
})
Loading
Loading