Skip to content

Commit

Permalink
Merge pull request #95 from poteat/poteat/more-reified-utilities
Browse files Browse the repository at this point in the history
More reified utilities
  • Loading branch information
poteat authored Oct 10, 2024
2 parents f2c251f + 4365f2e commit ecefc8c
Show file tree
Hide file tree
Showing 35 changed files with 1,160 additions and 77 deletions.
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

0 comments on commit ecefc8c

Please sign in to comment.