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

v0.46.0 #155

Merged
merged 2 commits into from
Jun 22, 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
84 changes: 84 additions & 0 deletions .changeset/grumpy-countries-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
"any-ts": minor
---

### new features
- added `match`, a namespace for advanced pattern matching
- added `any.functions` to describe any array of functions

### breaking changes

a few members of the `some` namespace behave differently than before:

- `some.keyOf`: this change was made to support a homomorphic `object.map` function
that operates on both arrays and objects, preserves structure in either case.

An example implementation:

```typescript
/**
* {@link map `map [overload 1/2]`} ("data-last")
*
* [TypeScript playground](https://tsplay.dev/weA2Yw)
*
* {@link map `map`} takes two arguments:
* 1. a function
* 2. a composite data structure that contains one or more targets to apply the function to
*
* A unique feature of this implementation is its polymorphism: it doesn't care whether the
* composite data structure is an array, or whether it's an object. It will apply the argument
* to each of the children, and will preserve the structure of the original shape.
*
* **Trade-off:** the data-last overload of {@link map `map`} is optimized for function composition.
* It works best when used inside a call to {@link fn.pipe `fn.pipe`} or {@link fn.flow `fn.flow`}.
* It comes with greater potential for code re-use, at the cost of slightly slower performance.
*
* **Ergonomics:** if you'd prefer to provide both arguments at the same time, see overload #2.
*/
export function map<const xs, target>
(fn: (x: xs[some.keyof<xs>], ix: some.keyof<xs>, xs: xs) => target): (xs: xs) => { [ix in keyof xs]: target }
/**
* {@link map `map [overload 2/2]`} ("data-first")
*
* [TypeScript playground](https://tsplay.dev/weA2Yw)
*
* {@link map `map`} is a polymorphic function that accepts a function and a data structure (such
* as an array or object) to apply the function to.
*
* A unique feature of this implementation is its ability to abstract away the type of the data
* structure it maps the function over; whether you pass it an object or an array, it will handle
* applying the function to the data strucuture's values and returning a data structure whose type
* corresponds 1-1 with the type of input.
*
* **Trade-off:** the data-first overload of {@link map `map`} evaluates eagerly. It comes with
* slightly better performance than the data-last overload, at the cost of reusability.
*
* **Ergonomics:** if you'd prefer to use {@link map `map`} in a pipeline, see overload #1.
*/
export function map<const xs, target>
(xs: xs, fn: (x: xs[some.keyof<xs>], xs: xs) => target): { [ix in keyof xs]: target }
// impl.
export function map<const xs, target>(
...args:
| [fn: (x: xs[some.keyof<xs>], ix: some.keyof<xs>, xs: xs) => target]
| [xs: xs, fn: (x: xs[some.keyof<xs>], ix: some.keyof<xs>, xs: xs) => target]
) {
if(args.length === 1) return (xs: xs) => map(xs, args[0])
else {
const [xs, fn] = args
if(globalThis.Array.isArray(xs)) return xs.map(fn as never)
else {
let out: any.struct = {}
for(const k in xs)
out[k] = fn(xs[k] as never, k as never, xs)
return out
}
}
}
```

- `some.entryOf`
slightly different semantics to support a polymorphic `object.entries` function

- `some.valueOf`
slightly different semantics to support a polymorphic `object.values` function
12 changes: 11 additions & 1 deletion src/any/any.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
export type { any }
export type { ANY_TS_VERSION } from "../version.js"

import type { some } from "../some.js"
import type { some } from "../some/some.js"
import type { to } from "../to.js"
import type { pathsof } from "../paths/paths.js"
import type { ANY_TS_VERSION } from "../version.js"
import type { _, id } from "../util.js"

/**
* ## {@link any `any 🧩`}
* `=================`
*
* {@link any `any`} is a namespace for constraints, least upper bounds,
* and type constructors that double as pattern matchers.
*
* It is the main export of the `any-ts` library, and provides its namesake.
*/
declare namespace any {
export {
type ANY_TS_VERSION as VERSION,
Expand Down Expand Up @@ -127,6 +136,7 @@ declare namespace any {
export type strings<type extends any.array<string> = any.array<string>> = type
export type numbers<type extends any.array<number> = any.array<number>> = type
export type booleans<type extends any.array<boolean> = any.array<boolean>> = type
export type functions<type extends any.array<any.function> = any.array<any.function>> = type
export type indices<type extends any.array<any.index> = any.array<any.index>> = type
export type keys<type extends any.array<any.key> = any.array<any.key>> = type
export type paths<type extends any.array<any.path> = any.array<any.path>> = type
Expand Down
2 changes: 1 addition & 1 deletion src/array/array.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { any } from "../any/exports.js"
import type { check, typecheck } from "../check/exports.js"
import type { TypeError } from "../exports.js"
import type { TypeError } from "../type-error/type-error.js"
import type { nonempty } from "../nonempty/nonempty.js"
import type { queue } from "./queue.js"
import type { tuple } from "./tuple.js"
Expand Down
3 changes: 2 additions & 1 deletion src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export {
export type { empty } from "./empty/exports.js"
export type { nonempty } from "./nonempty/exports.js"
export type { array, nonemptyArray, queue, tuple } from "./array/exports.js"
export type { some } from "./some.js"
export type { some } from "./some/some.js"
export type { match } from "./match/exports.js"
export type { object } from "./object/exports.js"
export type { boolean } from "./boolean/exports.js"
export type { cache } from "./cache/exports.js"
Expand Down
2 changes: 1 addition & 1 deletion src/lens/monocle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type { named }

import type { some } from "../some.js"
import type { some } from "../some/some.js"
import type { any } from "../any/exports.js"
import type { nonempty } from "../nonempty/nonempty.js"
import type { never } from "../exports.js"
Expand Down
1 change: 1 addition & 0 deletions src/match/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type * as match from "./match.js"
106 changes: 106 additions & 0 deletions src/match/match.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type { any } from "../any/any.js"
import type { never } from "../never/never.js"

export type {
match_finiteArray as finiteArray,
match_finiteBoolean as finiteBoolean,
match_finiteKey as finiteKey,
match_finiteIndex as finiteIndex,
match_finiteLiteral as finiteLiteral,
match_finiteNumber as finiteNumber,
match_finiteString as finiteString,

}

export type {
match_record as record,
match_emptyObject as emptyObject,
match_nonfiniteArray as nonfiniteArray,
match_strict as strict,
}

declare namespace match_strict {
export {
match_finiteBooleanStrict as finiteBoolean,
match_strictSubsetOf as subsetOf,
}
}

type match_finiteArray<t>
= [
t extends any.array
? number extends t["length"] ? never
: t
: never
] extends [infer out extends any.array] ? out : never
;

type match_nonfiniteArray<t>
= [
t extends any.array
? number extends t["length"] ? t
: never
: never
] extends [infer out] ? out : never
;

type match_record<t>
= [t] extends [any.struct]
? [t] extends [any.array]
? never
: any.struct
: never
;

type match_emptyObject<t>
= [t] extends [any.struct]
? [keyof t] extends [never] ? {}
: never
: never
;

type match_finiteString<t>
= [t] extends [string]
? [string] extends [t] ? never
: string
: never
;

type match_finiteNumber<t>
= [t] extends [number]
? [number] extends [t] ? never
: number
: never
;

type match_finiteBoolean<t>
= [globalThis.Extract<t, boolean>] extends [infer out]
? [boolean] extends [out] ? never
: out
: never
;

type match_finiteBooleanStrict<t>
= [[t] extends [boolean] ? [boolean] extends [t] ? never : t : never] extends [infer out]
? out
: never
;

type match_finiteKey<t>
= [t] extends [any.key]
? any.key extends t ? never
: any.key
: never
;

type match_finiteIndex<t> = match_strict.subsetOf<t, any.index>

type match_strictSubsetOf<t, set>
= set extends set
? [set extends t ? never : globalThis.Extract<t, set>] extends [infer out]
? out
: never.close.inline_var<"out">
: never.close.distributive<"set">
;

type match_finiteLiteral<t> = match_finiteBoolean<t> | match_strict.subsetOf<t, any.key>
2 changes: 2 additions & 0 deletions src/number/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type {

import type { any } from "../any/exports.js"

export type parseInt<t> = never | t extends `${infer x extends number}` ? x : t

type isNumber<x> = [x] extends [number] ? true : false
type isNegative<x> = [x] extends [any.showable] ? [`${x}`] extends [`-${number}`] ? true : false : false
type isPositive<x> = [x] extends [any.showable] ? [`${x}`] extends [`-${number}`] ? false : true : false
Expand Down
2 changes: 1 addition & 1 deletion src/object/object.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { any } from "../any/exports.js"
import type { some } from "../some.js"
import type { some } from "../some/some.js"
import type { evaluate } from "../evaluate/exports.js"

export declare namespace object {
Expand Down
Loading