Skip to content

Commit

Permalink
Merge pull request #99 from poteat/poteat/object-utils
Browse files Browse the repository at this point in the history
Add additional object utilities + natural number reifications + `Iso` module.
  • Loading branch information
poteat authored Jan 12, 2025
2 parents 59a9e73 + 203a337 commit 361bde2
Show file tree
Hide file tree
Showing 62 changed files with 1,351 additions and 54 deletions.
21 changes: 21 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [0.26.0]

- Introduce the `Iso` module for isomorphic bijections.
- Add `Iso.String.Chars` to wrap the process of iterating over chars.
- Add `Iso.String.Words` to wrap the process of iterating over words.
- Add `Iso.NaturalNumber.Increment` to wrap the process of incrementing a number.
- Add `Iso.NaturalNumber.Decrement` to wrap the process of decrementing a number.
- Add `Iso.NaturalNumber.Digits` to wrap the process of converting a natural number to a list of digits.
- Add `Object.DeepEntries` to get all deep paths and values in an object.
- Add `Object.FromEntries` to create an object from a list of key-value pairs.
- Add `Object.IsObject` to check if a value is an object.
- Reify `NaturalNumber.DivideBy` to a value-level function.
- Reify `NaturalNumber.Divide` to a value-level function.
- Reify `NaturalNumber.IsEven` to a value-level function.
- Reify `NaturalNumber.IsOdd` to a value-level function.
- Add `NaturalNumber.Undigits` to convert a list of digits to a natural number.
- Reify `String.First` and `String.Last` to value-level functions.
- Reify `String.Init` and `String.Tail` to value-level functions.
- Add `String.Unwords` to join a list of strings with a space delimiter.
- Add `String.RepeatBy` to repeat a string by a given number (argument swapped).

## [0.25.2]

- Fix `List.Sort` to work with 2-ary comparators.
Expand Down
2 changes: 1 addition & 1 deletion pkg/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hkt-toolbelt",
"version": "0.25.2",
"version": "0.26.0",
"description": "Functional and composable type utilities",
"types": "./index.d.ts",
"main": "./index.js",
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * as Digit from './digit'
export * as DigitList from './digit-list'
export * as Function from './function'
export * as Integer from './integer'
export * as Iso from './iso'
export * as Kind from './kind'
export * as List from './list'
export * as Loop from './loop'
Expand All @@ -29,6 +30,7 @@ import * as Digit from './digit'
import * as DigitList from './digit-list'
import * as Function from './function'
import * as Integer from './integer'
import * as Iso from './iso'
import * as Kind from './kind'
import * as List from './list'
import * as Loop from './loop'
Expand All @@ -53,6 +55,7 @@ const _ = {
Digit,
Function,
Integer,
Iso,
Kind,
List,
Loop,
Expand Down
24 changes: 24 additions & 0 deletions src/iso.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export * from './iso/'

/**
* The `Iso` module contains various isomorphisms. These isomorphisms are used
* to transform between different representations of the same type.
*
* Generally, isomorphisms consist of a mapping and an inverse mapping.
* The isomorphism takes in a kind, and returns a new kind where the input is
* transformed via the primary mapping, and the output is transformed via the
* inverse mapping.
*
* A very common operation is to perform a .split(""), do some operations, and
* then .join("") the result. This can be combined into a single step using
* the `Iso` module.
*
* @example
* ```ts
* import { String, Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.String.Words, $<List.Map, String.Capitalize>>;
* type T1 = $<T0, 'foo bar baz'>; // 'Foo Bar Baz'
* ```
*/
declare module './iso' {}
14 changes: 14 additions & 0 deletions src/iso/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export * as NaturalNumber from './natural-number'
export * as String from './string'

import * as NaturalNumber from './natural-number'
import * as String from './string'

const _ = {
NaturalNumber: NaturalNumber,
String: String
}

type _ = typeof _

export default _
16 changes: 16 additions & 0 deletions src/iso/natural-number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export * from './natural-number/'

/**
* The `Iso.NaturalNumber` module contains various number-related isomorphisms.
* These first compute some transformation on the input, and then apply the
* inverse transformation to the output.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.NaturalNumber.Increment, $<NaturalNumber.Multiply, 2>>;
* type T1 = $<T0, 10>; // (N + 1) * 2 - 1 = 21
* ```
*/
declare module './natural-number' {}
15 changes: 15 additions & 0 deletions src/iso/natural-number/decrement.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { $, Iso, NaturalNumber, Test } from '../..'

type Decrement_Spec = [
/**
* Can wrap a kind i.e. f(n - 1) + 1, for f(x) = x * 2
*/
Test.Expect<
$<$<Iso.NaturalNumber.Decrement, $<NaturalNumber.Multiply, 2>>, 10>,
19
>
]

it('should wrap a kind i.e. f(n - 1) + 1, for f(x) = x * 2', () => {
expect(Iso.NaturalNumber.decrement(NaturalNumber.multiply(2))(10)).toBe(19)
})
58 changes: 58 additions & 0 deletions src/iso/natural-number/decrement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { $, Type, Kind, NaturalNumber } from '../..'

/**
* Given a kind, return an isomorphism such that the input is decremented by
* one, and the output is incremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = Iso.NaturalNumber._$decrement<$<NaturalNumber.Multiply, 2>>;
* type T1 = $<T0, 10>; // (N - 1) * 2 + 1 = 19
* ```
*/
export type _$decrement<K extends Kind.Kind> = $<
Kind.Pipe,
[NaturalNumber.Decrement, K, NaturalNumber.Increment]
>

/**
* Given a kind, return an isomorphism such that the input is decremented by
* one, and the output is incremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.NaturalNumber.Decrement, $<NaturalNumber.Multiply, 2>>;
* type T1 = $<T0, 10>; // (N - 1) * 2 + 1 = 19
* ```
*/
export interface Decrement extends Kind.Kind {
f(x: Type._$cast<this[Kind._], Kind.Kind>): _$decrement<typeof x>
}

/**
* Given a kind, return an isomorphism such that the input is decremented by
* one, and the output is incremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* const T0 = Iso.NaturalNumber.decrement(NaturalNumber.multiply(2));
* const T1 = T0(10); // (N - 1) * 2 + 1 = 19
* ```
*/
export const decrement = ((f: Kind._$reify<Kind.Kind<(x: number) => number>>) =>
(x: number) =>
NaturalNumber.increment(
f(NaturalNumber.decrement(x))
)) as Kind._$reify<Decrement>
22 changes: 22 additions & 0 deletions src/iso/natural-number/digits.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { $, Iso, NaturalNumber, Test, List } from '../..'

type Digits_Spec = [
/**
* Can convert a natural number to a list of digits.
*/
Test.Expect<
$<
$<Iso.NaturalNumber.Digits, $<List.Map, $<NaturalNumber.Multiply, 2>>>,
99
>,
1818
>
]

it('should convert a natural number to a list of digits', () => {
const result = Iso.NaturalNumber.digits(List.map(NaturalNumber.multiply(2)))(
99
)

expect(result).toBe(1818)
})
65 changes: 65 additions & 0 deletions src/iso/natural-number/digits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { $, Type, Kind, NaturalNumber, Number } from '../..'

/**
* Given a kind, return an isomorphism such that the input is converted to a
* list of digits, and the output is converted from a list of digits back to a
* natural number.
*
* @param {Kind.Kind} K - The kind to convert to a natural number.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = Iso.NaturalNumber._$digits<$<List.Map<$<NaturalNumber.Multiply, 2>>>;
* type T1 = $<T0, 99>; // 1818
* ```
*/
export type _$digits<
K extends Kind.Kind<(x: Number.Number[]) => Number.Number[]>
> = $<Kind.Pipe, [NaturalNumber.Digits, K, NaturalNumber.Undigits]>

/**
* Given a kind, return an isomorphism such that the input is converted to a
* list of digits, and the output is converted from a list of digits back to a
* natural number.
*
* @param {Kind.Kind} K - The kind to convert to a natural number.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.NaturalNumber.Digits, $<List.Map, $<NaturalNumber.Multiply, 2>>>
* type T1 = $<T0, 99>; // 1818
* ```
*/
export interface Digits extends Kind.Kind {
f(
x: Type._$cast<
this[Kind._],
Kind.Kind<(x: Number.Number[]) => Number.Number[]>
>
): _$digits<typeof x>
}

/**
* Given a kind, return an isomorphism such that the input is converted to a
* list of digits, and the output is converted from a list of digits back to a
* natural number.
*
* @param {Kind.Kind} K - The kind to convert to a natural number.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* const T0 = Iso.NaturalNumber.digits(List.map(NaturalNumber.multiply(2)));
* const T1 = T0(99); // 1818
* ```
*/
export const digits = ((
f: Kind._$reify<Kind.Kind<(x: Number.Number[]) => number[]>>
) =>
(x: number) =>
NaturalNumber.undigits(f(NaturalNumber.digits(x)))) as Kind._$reify<Digits>
15 changes: 15 additions & 0 deletions src/iso/natural-number/increment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { $, Iso, NaturalNumber, Test } from '../..'

type Increment_Spec = [
/**
* Can wrap a kind i.e. f(n + 1) - 1, for f(x) = x * 2
*/
Test.Expect<
$<$<Iso.NaturalNumber.Increment, $<NaturalNumber.Multiply, 2>>, 10>,
21
>
]

it('should wrap a kind i.e. f(n + 1) - 1, for f(x) = x * 2', () => {
expect(Iso.NaturalNumber.increment(NaturalNumber.multiply(2))(10)).toBe(21)
})
58 changes: 58 additions & 0 deletions src/iso/natural-number/increment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { $, Type, Kind, NaturalNumber } from '../..'

/**
* Given a kind, return an isomorphism such that the input is incremented by
* one, and the output is decremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = Iso.NaturalNumber._$increment<$<NaturalNumber.Multiply, 2>>;
* type T1 = $<T0, 10>; // (N + 1) * 2 - 1 = 21
* ```
*/
export type _$increment<K extends Kind.Kind> = $<
Kind.Pipe,
[NaturalNumber.Increment, K, NaturalNumber.Decrement]
>

/**
* Given a kind, return an isomorphism such that the input is incremented by
* one, and the output is decremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.NaturalNumber.Increment, $<NaturalNumber.Multiply, 2>>;
* type T1 = $<T0, 10>; // (N + 1) * 2 - 1 = 21
* ```
*/
export interface Increment extends Kind.Kind {
f(x: Type._$cast<this[Kind._], Kind.Kind>): _$increment<typeof x>
}

/**
* Given a kind, return an isomorphism such that the input is incremented by
* one, and the output is decremented by one.
*
* @param {Kind.Kind} K - The kind to wrap with an increment/decrement.
*
* @example
* ```ts
* import { Iso } from "hkt-toolbelt";
*
* const T0 = Iso.NaturalNumber.increment(NaturalNumber.multiply(2));
* const T1 = T0(10); // (N + 1) * 2 - 1 = 21
* ```
*/
export const increment = ((f: Kind._$reify<Kind.Kind<(x: number) => number>>) =>
(x: number) =>
NaturalNumber.decrement(
f(NaturalNumber.increment(x))
)) as Kind._$reify<Increment>
3 changes: 3 additions & 0 deletions src/iso/natural-number/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './decrement'
export * from './digits'
export * from './increment'
16 changes: 16 additions & 0 deletions src/iso/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export * from './string/'

/**
* The `Iso.String` module contains various string-related isomorphisms. These
* isomorphisms are used to transform between different representations of
* strings.
*
* @example
* ```ts
* import { String, Iso } from "hkt-toolbelt";
*
* type T0 = $<Iso.String.Words, $<List.Map, String.Capitalize>>;
* type T1 = $<T0, 'foo bar baz'>; // 'Foo Bar Baz'
* ```
*/
declare module './string' {}
15 changes: 15 additions & 0 deletions src/iso/string/chars.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { $, Iso, String, Test, List } from '../..'

type Chars_Spec = [
/**
* Can repeat each character of a string by two.
*/
Test.Expect<
$<$<Iso.String.Chars, $<List.Map, $<String.RepeatBy, 2>>>, 'bar'>,
'bbaarr'
>
]

it('should repeat each character of a string by two', () => {
expect(Iso.String.chars(List.map(String.repeatBy(2)))('bar')).toBe('bbaarr')
})
Loading

0 comments on commit 361bde2

Please sign in to comment.