├── .npmignore ├── .nvmrc ├── .huskyrc ├── .lintstagedrc ├── tsconfig.json ├── mocharc.json ├── src ├── Typeclass │ ├── Bounded.ts │ ├── ForEach │ │ ├── index.ts │ │ └── ForEach.test.ts │ ├── Commutative.ts │ ├── Inverse.ts │ ├── Concat.ts │ ├── Eq.test.ts │ ├── Identity.test.ts │ ├── Associative.test.ts │ ├── Concat.test.ts │ ├── Inverse.test.ts │ ├── index.ts │ ├── Associative.ts │ ├── CommutativeBoth.ts │ ├── CommutativeEither.ts │ ├── Compactable.ts │ ├── Identity.ts │ ├── IdentityFlatten.ts │ ├── IdentityEither.ts │ └── Debug.ts ├── Json.ts ├── Law │ ├── Inverse.ts │ ├── Associative.ts │ ├── Commutative.ts │ ├── index.ts │ ├── Identity.ts │ ├── AssociativeBoth.ts │ ├── Eq.ts │ ├── Ord.ts │ └── ForEach.ts ├── logical.ts ├── string.test.ts ├── boolean.test.ts ├── Progress.test.ts ├── Id.ts ├── Match.ts ├── Endomorphism.ts ├── number.test.ts ├── index.ts ├── common.ts ├── struct.ts ├── Predicate.test.ts ├── Progress.ts ├── Refinement.ts ├── Unary.ts ├── Predicate.ts ├── Either.test.ts ├── function.test.ts ├── boolean.ts ├── DataEither.test.ts └── string.ts ├── tools ├── overloads │ ├── tsconfig.json │ ├── definitions │ │ ├── CommutativeBoth.ts │ │ ├── Compactable.ts │ │ ├── CommutativeEither.ts │ │ ├── IdentityBoth.ts │ │ ├── IdentityFlatten.ts │ │ ├── IdentityEither.ts │ │ ├── Identity.ts │ │ ├── FoldMap.size.ts │ │ ├── FoldMap.isEmpty.ts │ │ ├── FoldMap.isNonEmpty.ts │ │ ├── Top.ts │ │ ├── Bottom.ts │ │ ├── FoldMap.addIndex.ts │ │ ├── Top.top.ts │ │ ├── FilterMap.map.ts │ │ ├── FoldMap.reverse.ts │ │ ├── Compact.ts │ │ ├── FoldMap.toArray.ts │ │ ├── PartitionMap.separate.ts │ │ ├── Covariant.tupled.ts │ │ ├── Bottom.bottom.ts │ │ ├── AssociativeFlatten.ts │ │ ├── AssociativeEither.eventually.ts │ │ ├── FoldMap.reduceIdentity.ts │ │ ├── AssociativeFlatten.makeAssociativeBoth.ts │ │ ├── FoldMap.foldLeft.ts │ │ ├── Covariant.mapTo.ts │ │ ├── FoldMap.reduceAssociative.ts │ │ ├── FoldMap.reduceCommutative.ts │ │ ├── FilterMap.compact.ts │ │ ├── Top.makeFromValue.ts │ │ ├── FoldMapWithIndex.foldMap.ts │ │ ├── AssociativeBoth.zipLeft.ts │ │ ├── AssociativeBoth.zipRight.ts │ │ ├── FoldMap.some.ts │ │ ├── Contravariant.ts │ │ ├── FoldMap.every.ts │ │ ├── Law │ │ │ ├── testAllHKTLaws.ts │ │ │ ├── testCovariant.ts │ │ │ ├── testFilterMap.ts │ │ │ ├── testCovariantIdentityFlatten.ts │ │ │ ├── testCovariantIdentityBoth.ts │ │ │ ├── testCovariantAssociativeEither.ts │ │ │ ├── testCovariantAssociativeFlatten.ts │ │ │ ├── testCovariantIdentityEither.ts │ │ │ └── testContravariant.ts │ │ ├── FoldMap.exists.ts │ │ ├── FoldMap.intercalate.ts │ │ ├── Top.makeFromLazy.ts │ │ ├── Covariant.flap.ts │ │ ├── FoldMap.count.ts │ │ ├── FilterMap.ts │ │ ├── FoldMap.find.ts │ │ ├── Compact.compact.ts │ │ ├── Reduce.ts │ │ ├── Separate.ts │ │ ├── Identity.fromIdentityEitherCovariant.ts │ │ ├── FoldMap.reduce.ts │ │ ├── Covariant.ts │ │ ├── ReduceRight.ts │ │ ├── ForEach.foldMap.ts │ │ ├── FilterMapWithIndex.filterMap.ts │ │ ├── AssociativeFlatten.flatMap.ts │ │ ├── Divariant.map.ts │ │ ├── FoldMap.contains.ts │ │ ├── Trivariant.map.ts │ │ ├── ReduceWithIndex.reduce.ts │ │ ├── AssociativeCompose.ts │ │ ├── Bicovariant.map.ts │ │ ├── FoldMap.groupBy.ts │ │ ├── AssociativeBoth.ts │ │ ├── Divariant.contramap.ts │ │ ├── FoldMapWithIndex.toMap.ts │ │ ├── Bicovariant.mapLeft.ts │ │ ├── Covariant.bindTo.ts │ │ ├── FoldMap.ts │ │ ├── NaturalTransformation.ts │ │ ├── Invariant.ts │ │ ├── FoldMap.reduceRight.ts │ │ ├── Reduce.reduce.ts │ │ ├── AssociativeFlatten.flatten.ts │ │ ├── FoldMap.foldMap.ts │ │ ├── ReduceRightWithIndex.reduceRight.ts │ │ ├── AssociativeEither.ts │ │ ├── ReduceRight.reduceRight.ts │ │ ├── Contravariant.contramap.ts │ │ ├── AssociativeEither.orElse.ts │ │ ├── Covariant.map.ts │ │ ├── Invariant.imap.ts │ │ ├── Contravariant.contramapC.ts │ │ ├── ForEach.map.ts │ │ ├── Trivariant.contramap.ts │ │ ├── AssociativeEither.tuple.ts │ │ ├── IdentityBoth.tuple.ts │ │ ├── AssociativeBoth.both.ts │ │ ├── FilterMap.filterMap.ts │ │ ├── Trivariant.bimap.ts │ │ ├── AssociativeBoth.tuple.ts │ │ ├── Divariant.ts │ │ ├── ForEach.sequence.ts │ │ ├── Bicovariant.ts │ │ ├── ForEach.mapAccum.ts │ │ ├── CovariantWithIndex.map.ts │ │ ├── FoldMapWithIndex.ts │ │ ├── AssociativeCompose.compose.ts │ │ ├── FoldMap.partitionMap.ts │ │ ├── Divariant.dimap.ts │ │ ├── PartitionMap.ts │ │ ├── ReduceWithIndex.ts │ │ ├── AssociativeEither.either.ts │ │ ├── FilterMapWithIndex.ts │ │ ├── ReduceRightWithIndex.ts │ │ ├── AssociativeEither.eitherWith.ts │ │ ├── ForEach.forEach.ts │ │ ├── CovariantWithIndex.ts │ │ ├── Bicovariant.bimap.ts │ │ ├── AssociativeBoth.bothWith.ts │ │ ├── PartitionMapWithIndex.ts │ │ ├── IdentityBoth.struct.ts │ │ ├── FilterMap.filter.ts │ │ ├── PartitionMapWithIndex.partitionMap.ts │ │ ├── AssociativeFlatten.bind.ts │ │ ├── ForEach.compacted.ts │ │ ├── ForEach.ts │ │ ├── ForEachWithIndex.forEach.ts │ │ ├── Trivariant.ts │ │ ├── ForEach.separated.ts │ │ └── ForEachWithIndex.ts │ ├── findHKTParams.ts │ ├── common.ts │ ├── cli.ts │ └── PrintManagerContext.ts ├── common.ts └── generatePackageExports.ts ├── .prettierrc.cjs ├── .editorconfig ├── vite.config.ts ├── tsconfig.build.json ├── .github └── dependabot.yml ├── tsconfig.base.json ├── .gitignore ├── .circleci └── config.yml ├── .eslintrc.cjs └── readme.md /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/gallium 2 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{ts, tsx}": [ 3 | "eslint --fix" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": [ 4 | "src", 5 | "tools" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "node-option": [ 3 | "experimental-specifier-resolution=node", 4 | "loader=ts-node/esm" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/Typeclass/Bounded.ts: -------------------------------------------------------------------------------- 1 | import { Ord } from './Ord.js' 2 | 3 | export interface Bounded extends Ord { 4 | readonly top: A 5 | readonly bottom: A 6 | } 7 | -------------------------------------------------------------------------------- /tools/overloads/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "../../tsconfig.base.json", 4 | "include": [ 5 | "." 6 | , "../generatePackageExports.ts" ] 7 | } 8 | -------------------------------------------------------------------------------- /src/Json.ts: -------------------------------------------------------------------------------- 1 | export type Json = JsonPrimitive | JsonArray | JsonRecord 2 | 3 | export type JsonPrimitive = string | number | boolean | null 4 | 5 | export type JsonArray = readonly Json[] 6 | 7 | export type JsonRecord = { readonly [key: string]: Json } 8 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | bracketSpacing: true, 4 | jsxBracketSameLine: false, 5 | parser: 'typescript', 6 | semi: false, 7 | singleQuote: true, 8 | tabWidth: 2, 9 | trailingComma: 'all', 10 | useTabs: false 11 | } 12 | -------------------------------------------------------------------------------- /tools/overloads/definitions/CommutativeBoth.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeBoth } from './AssociativeBoth.js' 2 | import { interface_ } from './common.js' 3 | 4 | export const CommutativeBoth = interface_('CommutativeBoth', [], [AssociativeBoth]) 5 | 6 | export const node = CommutativeBoth 7 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Compactable.ts: -------------------------------------------------------------------------------- 1 | import { Compact } from './Compact.js' 2 | import { Separate } from './Separate.js' 3 | import { interface_ } from './common.js' 4 | 5 | export const node = interface_('Compactable', [], [Compact, Separate]) 6 | 7 | export const Compactable = node 8 | -------------------------------------------------------------------------------- /src/Typeclass/ForEach/index.ts: -------------------------------------------------------------------------------- 1 | export * from './compacted.js' 2 | export * from './composition.js' 3 | export * from './foldMap.js' 4 | export * from './ForEach.js' 5 | export * from './map.js' 6 | export * from './mapAccum.js' 7 | export * from './separated.js' 8 | export * from './sequence.js' 9 | -------------------------------------------------------------------------------- /tools/overloads/definitions/CommutativeEither.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeEither } from './AssociativeEither.js' 2 | import { interface_ } from './common.js' 3 | 4 | export const CommutativeEither = interface_('CommutativeEither', [], [AssociativeEither]) 5 | 6 | export const node = CommutativeEither 7 | -------------------------------------------------------------------------------- /tools/overloads/definitions/IdentityBoth.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeBoth } from './AssociativeBoth.js' 2 | import { Top } from './Top.js' 3 | import { interface_ } from './common.js' 4 | 5 | export const IdentityBoth = interface_('IdentityBoth', [], [AssociativeBoth, Top]) 6 | 7 | export const node = IdentityBoth 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /tools/overloads/definitions/IdentityFlatten.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeFlatten } from './AssociativeFlatten.js' 2 | import { Top } from './Top.js' 3 | import { interface_ } from './common.js' 4 | 5 | export const IdentityFlatten = interface_('IdentityFlatten', [], [AssociativeFlatten, Top]) 6 | 7 | export const node = IdentityFlatten 8 | -------------------------------------------------------------------------------- /tools/overloads/definitions/IdentityEither.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeEither } from './AssociativeEither.js' 2 | import { Bottom } from './Bottom.js' 3 | import { interface_ } from './common.js' 4 | 5 | export const IdentityEither = interface_('IdentityEither', [], [AssociativeEither, Bottom]) 6 | 7 | export const node = IdentityEither 8 | -------------------------------------------------------------------------------- /tools/overloads/findHKTParams.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, ParentNode, TypeParam } from './AST.js' 2 | 3 | export function findHKTParams(node: ParentNode) { 4 | return node.typeParams.filter(isHKTParam) 5 | } 6 | 7 | export function isHKTParam(param: TypeParam): param is HKTParam { 8 | return param.tag === HKTParam.tag 9 | } 10 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Identity.ts: -------------------------------------------------------------------------------- 1 | import { Interface } from '../AST.js' 2 | 3 | import { aTypeParam } from './common.js' 4 | 5 | export const node = new Interface( 6 | 'Identity', 7 | [aTypeParam], 8 | [aTypeParam.labeled('id')], 9 | [new Interface('Associative', [aTypeParam], [])], 10 | ) 11 | 12 | export const Identity = node 13 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.size.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { aTypeParam, derived_, fn_, kind_, number_, placeholder } from './common.js' 3 | 4 | export const size = derived_( 5 | 'size', 6 | [FoldMap], 7 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled()], number_), 8 | ) 9 | 10 | export const node = size 11 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.isEmpty.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { aTypeParam, boolean_, derived_, fn_, kind_, placeholder } from './common.js' 3 | 4 | export const isEmpty = derived_( 5 | 'isEmpty', 6 | [FoldMap], 7 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled()], boolean_), 8 | ) 9 | 10 | export const node = isEmpty 11 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.isNonEmpty.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { aTypeParam, boolean_, derived_, fn_, kind_, placeholder } from './common.js' 3 | 4 | export const isNonEmpty = derived_( 5 | 'isNonEmpty', 6 | [FoldMap], 7 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled()], boolean_), 8 | ) 9 | 10 | export const node = isNonEmpty 11 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Top.ts: -------------------------------------------------------------------------------- 1 | import { Kind, Static } from '../AST.js' 2 | 3 | import { hkt, interface_, placeholderWithDefaults } from './common.js' 4 | 5 | const unknownType = new Static('unknown') 6 | 7 | export const Top = interface_( 8 | 'Top', 9 | [new Kind(hkt, [placeholderWithDefaults, unknownType]).labeled('top')], 10 | [], 11 | ) 12 | 13 | export const node = Top 14 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bottom.ts: -------------------------------------------------------------------------------- 1 | import { Kind, Static } from '../AST.js' 2 | 3 | import { hkt, interface_, placeholderWithDefaults } from './common.js' 4 | 5 | const neverType = new Static('never') 6 | 7 | export const Bottom = interface_( 8 | 'Bottom', 9 | [new Kind(hkt, [placeholderWithDefaults, neverType]).labeled('bottom')], 10 | [], 11 | ) 12 | 13 | export const node = Bottom 14 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.addIndex.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { FoldMapWithIndex } from './FoldMapWithIndex.js' 3 | import { curriedPlaceholder_, derived_, hkt, number_ } from './common.js' 4 | 5 | export const addIndex = derived_( 6 | 'addIndex', 7 | [FoldMap], 8 | FoldMapWithIndex.toTypeClass(hkt).setParams([number_, curriedPlaceholder_(hkt)]), 9 | ) 10 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Top.top.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { Top } from './Top.js' 5 | import { composed_, kindFWithDefaults_, kindGWithDefaults_ } from './common.js' 6 | 7 | export const top = composed_( 8 | 'top', 9 | [Top, Covariant], 10 | [Top], 11 | kindFWithDefaults_([kindGWithDefaults_([new Static(`unknown`)])]), 12 | ) 13 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMap.map.ts: -------------------------------------------------------------------------------- 1 | import { FilterMap } from './FilterMap.js' 2 | import { aTypeParam, bTypeParam, derived_, fab_, fn_, kind_, placeholder } from './common.js' 3 | 4 | export const map = derived_( 5 | 'map', 6 | [FilterMap], 7 | fn_( 8 | '', 9 | [aTypeParam, bTypeParam], 10 | [fab_], 11 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], kind_([bTypeParam])), 12 | ), 13 | ) 14 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reverse.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { ForEach } from './ForEach.js' 3 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const reverse = derived_( 6 | 'reverse', 7 | [FoldMap, ForEach], 8 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled()], kind_([aTypeParam])), 9 | ) 10 | 11 | export const node = reverse 12 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Compact.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { aTypeParam, fnLabeled_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const node = interface_('Compact', [ 6 | fnLabeled_( 7 | 'compact', 8 | [placeholder, aTypeParam], 9 | [kind_([new Static(`Maybe`)]).labeled('kind')], 10 | kind_([aTypeParam]), 11 | ), 12 | ]) 13 | 14 | export const Compact = node 15 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.toArray.ts: -------------------------------------------------------------------------------- 1 | import { ArrayNode } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const toArray = derived_( 7 | 'toArray', 8 | [FoldMap], 9 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled()], new ArrayNode(aTypeParam)), 10 | ) 11 | 12 | export const node = toArray 13 | -------------------------------------------------------------------------------- /tools/overloads/definitions/PartitionMap.separate.ts: -------------------------------------------------------------------------------- 1 | import { KindParam, Labeled } from '../AST.js' 2 | 3 | import { PartitionMap } from './PartitionMap.js' 4 | import { Separate } from './Separate.js' 5 | import { derived_ } from './common.js' 6 | 7 | export const separate = derived_( 8 | 'separate', 9 | [PartitionMap], 10 | (Separate.properties as readonly Labeled[])[0].param, 11 | ) 12 | 13 | export const node = separate 14 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.tupled.ts: -------------------------------------------------------------------------------- 1 | import { Tuple } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const node = derived_( 7 | 'tupled', 8 | [Covariant], 9 | fn_( 10 | '', 11 | [placeholder, aTypeParam], 12 | [kind_([aTypeParam]).labeled('kind')], 13 | kind_([new Tuple([aTypeParam])]), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bottom.bottom.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Bottom } from './Bottom.js' 4 | import { Covariant } from './Covariant.js' 5 | import { Top } from './Top.js' 6 | import { composed_, kindFWithDefaults_, kindGWithDefaults_ } from './common.js' 7 | 8 | export const bottom = composed_( 9 | 'bottom', 10 | [Top, Covariant], 11 | [Bottom], 12 | kindFWithDefaults_([kindGWithDefaults_([new Static(`never`)])]), 13 | ) 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | 3 | import { defineConfig } from 'vite' 4 | import tsconfigPaths from 'vite-tsconfig-paths' 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | tsconfigPaths({ 9 | root: __dirname, 10 | }), 11 | ], 12 | build: { 13 | outDir: join(__dirname, 'build'), 14 | sourcemap: true, 15 | manifest: true, 16 | emptyOutDir: true, 17 | }, 18 | server: { 19 | port: 7777, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeFlatten.ts: -------------------------------------------------------------------------------- 1 | import { aTypeParam, fnLabeled_, interface_, kind_, placeholder } from './common.js' 2 | 3 | export const AssociativeFlatten = interface_( 4 | 'AssociativeFlatten', 5 | [ 6 | fnLabeled_( 7 | 'flatten', 8 | [placeholder, aTypeParam], 9 | [kind_([kind_([aTypeParam])]).labeled('kind')], 10 | kind_([aTypeParam]), 11 | ), 12 | ], 13 | [], 14 | ) 15 | 16 | export const node = AssociativeFlatten 17 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.base.json", 4 | "compilerOptions": { 5 | "skipLibCheck": true, 6 | "plugins": [ 7 | { 8 | "transform": "@zoltu/typescript-transformer-append-js-extension/output/index.js", 9 | "after": true 10 | } 11 | ] 12 | }, 13 | "include": [ 14 | "src" 15 | ], 16 | "exclude": [ 17 | "src/**/*.test.ts", 18 | "tools" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.eventually.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeEither } from './AssociativeEither.js' 2 | import { Covariant } from './Covariant.js' 3 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const eventually = derived_( 6 | 'eventually', 7 | [AssociativeEither, Covariant], 8 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled('kind')], kind_([aTypeParam])), 9 | ) 10 | 11 | export const node = eventually 12 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reduceIdentity.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const reduceIdentity = derived_( 7 | 'reduceIdentity', 8 | [FoldMap], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static(`Identity`).labeled('I')], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], aTypeParam), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeFlatten.makeAssociativeBoth.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeBoth } from './AssociativeBoth.js' 2 | import { AssociativeFlatten } from './AssociativeFlatten.js' 3 | import { Covariant } from './Covariant.js' 4 | import { derived_, hkt } from './common.js' 5 | 6 | export const makeAssociativeBoth = derived_( 7 | 'makeAssociativeBoth', 8 | [AssociativeFlatten, Covariant], 9 | AssociativeBoth.toTypeClass(hkt), 10 | ) 11 | 12 | export const node = makeAssociativeBoth 13 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.foldLeft.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const foldLeft = derived_( 7 | 'foldLeft', 8 | [FoldMap], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static(`Identity<${aTypeParam.type}>`).labeled('I')], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], aTypeParam), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.mapTo.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { aTypeParam, bTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const node = derived_( 7 | 'mapTo', 8 | [Covariant], 9 | fn_( 10 | '', 11 | [bTypeParam], 12 | [new Static('B').labeled('b')], 13 | fn_('', [placeholder, aTypeParam], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reduceAssociative.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const reduceAssociative = derived_( 7 | 'reduceAssociative', 8 | [FoldMap], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static(`Associative`).labeled('A')], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], new Static(`Maybe`)), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reduceCommutative.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const reduceCommutative = derived_( 7 | 'reduceCommutative', 8 | [FoldMap], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static(`Commutative`).labeled('C')], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], new Static(`Maybe`)), 14 | ), 15 | ) 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMap.compact.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FilterMap } from './FilterMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const makeCompact = derived_( 7 | 'makeCompact', 8 | [FilterMap], 9 | fn_( 10 | '', 11 | [placeholder, aTypeParam], 12 | [kind_([new Static(`Maybe<${aTypeParam.type}>`)]).labeled('kind')], 13 | kind_([aTypeParam]), 14 | ), 15 | ) 16 | 17 | export const node = makeCompact 18 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Top.makeFromValue.ts: -------------------------------------------------------------------------------- 1 | import { Covariant } from './Covariant.js' 2 | import { Top } from './Top.js' 3 | import { aTypeParam, derived_, fn_, kindWithDefaults_, placeholderWithDefaults } from './common.js' 4 | 5 | export const makeFromValue = derived_( 6 | 'makeFromValue', 7 | [Top, Covariant], 8 | fn_( 9 | '', 10 | [aTypeParam, placeholderWithDefaults], 11 | [aTypeParam.labeled('a')], 12 | kindWithDefaults_([aTypeParam]), 13 | ), 14 | ) 15 | 16 | export const node = makeFromValue 17 | -------------------------------------------------------------------------------- /src/Law/Inverse.ts: -------------------------------------------------------------------------------- 1 | import { DeepEquals, Eq } from '../Typeclass/Eq.js' 2 | import { Inverse } from '../Typeclass/Inverse.js' 3 | import { pipe } from '../function.js' 4 | 5 | import * as Arbitrary from './Arbitrary.js' 6 | 7 | export function testInverse(I: Inverse, E: Eq = DeepEquals) { 8 | return (Arb: Arbitrary.Arbitrary) => 9 | pipe( 10 | Arb, 11 | Arbitrary.toProperty( 12 | (a) => E.equals(I.inverse(a, a), I.id) && E.equals(I.concat(I.inverse(I.id, a), a), I.id), 13 | ), 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMapWithIndex.foldMap.ts: -------------------------------------------------------------------------------- 1 | import { KindParam, Labeled } from '../AST.js' 2 | 3 | import { kTypeParam } from './CovariantWithIndex.js' 4 | import { FoldMap } from './FoldMap.js' 5 | import { FoldMapWithIndex } from './FoldMapWithIndex.js' 6 | import { derived_ } from './common.js' 7 | 8 | export const foldMapWithIndex = derived_( 9 | 'foldMap', 10 | [FoldMapWithIndex.setParams([kTypeParam])], 11 | (FoldMap.properties as readonly Labeled[])[0].param, 12 | undefined, 13 | [kTypeParam], 14 | ) 15 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.zipLeft.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeBoth } from './AssociativeBoth.js' 2 | import { Covariant } from './Covariant.js' 3 | import { aTypeParam, bTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const zipLeft = derived_( 6 | 'zipLeft', 7 | [AssociativeBoth, Covariant], 8 | fn_( 9 | '', 10 | [placeholder, bTypeParam], 11 | [kind_([bTypeParam]).labeled('second')], 12 | fn_('', [aTypeParam], [kind_([aTypeParam]).labeled('first')], kind_([aTypeParam])), 13 | ), 14 | ) 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.zipRight.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeBoth } from './AssociativeBoth.js' 2 | import { Covariant } from './Covariant.js' 3 | import { aTypeParam, bTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const zipRight = derived_( 6 | 'zipRight', 7 | [AssociativeBoth, Covariant], 8 | fn_( 9 | '', 10 | [placeholder, bTypeParam], 11 | [kind_([bTypeParam]).labeled('second')], 12 | fn_('', [aTypeParam], [kind_([aTypeParam]).labeled('first')], kind_([bTypeParam])), 13 | ), 14 | ) 15 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.some.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const some = derived_( 9 | 'some', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [fnLabeled_('predicate', [], [aTypeParam.labeled('a')], bool)], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], bool), 16 | ), 17 | ) 18 | 19 | export const node = some 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Contravariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const node = interface_('Contravariant', [ 6 | fnLabeled_( 7 | 'contramap', 8 | [bTypeParam, aTypeParam], 9 | [new Dynamic([bTypeParam, aTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f')], 10 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 11 | ), 12 | ]) 13 | 14 | export const Contravariant = node 15 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.every.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const every = derived_( 9 | 'every', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [fnLabeled_('predicate', [], [aTypeParam.labeled('a')], bool)], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], bool), 16 | ), 17 | ) 18 | 19 | export const node = every 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testAllHKTLaws.ts: -------------------------------------------------------------------------------- 1 | import { ObjectNode, Static } from '../../AST.js' 2 | import { curriedPlaceholder_, fn_, hkt } from '../common.js' 3 | 4 | export const testAllHKTLaws = fn_( 5 | 'testAllHKTLaws', 6 | [hkt, curriedPlaceholder_(hkt)], 7 | [ 8 | new ObjectNode([ 9 | new Static(`typeof import('fast-check')`).labeled('fc'), 10 | new Static(`typeof import('fast-check')`).labeled('fc'), 11 | new Static(`typeof import('fast-check')`).labeled('fc'), 12 | ]).labeled('params'), 13 | ], 14 | new Static(`void`), 15 | ) 16 | -------------------------------------------------------------------------------- /src/Law/Associative.ts: -------------------------------------------------------------------------------- 1 | import { Associative } from '../Typeclass/Associative.js' 2 | import { DeepEquals, Eq } from '../Typeclass/Eq.js' 3 | import { pipe } from '../function.js' 4 | 5 | import * as Arbitrary from './Arbitrary.js' 6 | 7 | export function testAssociativity(A: Associative, Eq: Eq = DeepEquals) { 8 | return (Arb: Arbitrary.Arbitrary) => 9 | pipe( 10 | Arbitrary.tuple(Arb, Arb, Arb), 11 | Arbitrary.toProperty(([a, b, c]) => 12 | Eq.equals(A.concat(a, A.concat(b, c)), A.concat(A.concat(a, b), c)), 13 | ), 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /src/Law/Commutative.ts: -------------------------------------------------------------------------------- 1 | import { Commutative } from '../Typeclass/Commutative.js' 2 | import { DeepEquals, Eq } from '../Typeclass/Eq.js' 3 | import { pipe } from '../function.js' 4 | 5 | import * as Arbitrary from './Arbitrary.js' 6 | 7 | export function testCommutativity(C: Commutative, Eq: Eq = DeepEquals) { 8 | return (Arb: Arbitrary.Arbitrary) => 9 | pipe( 10 | Arbitrary.tuple(Arb, Arb, Arb), 11 | Arbitrary.toProperty(([a, b, c]) => 12 | Eq.equals(C.concat(a, C.concat(b, c)), C.concat(C.concat(b, c), a)), 13 | ), 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.exists.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const exists = derived_( 9 | 'exists', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bool)], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bool), 16 | ), 17 | ) 18 | 19 | export const node = exists 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.intercalate.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const intercalate = derived_( 7 | 'intercalate', 8 | [FoldMap], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static(`Identity`).labeled('I')], 13 | fn_( 14 | '', 15 | [], 16 | [aTypeParam.labeled('a')], 17 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], aTypeParam), 18 | ), 19 | ), 20 | ) 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Top.makeFromLazy.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { Top } from './Top.js' 5 | import { aTypeParam, derived_, fn_, kindWithDefaults_, placeholderWithDefaults } from './common.js' 6 | 7 | export const makeFromLazy = derived_( 8 | 'makeFromLazy', 9 | [Top, Covariant], 10 | fn_( 11 | '', 12 | [aTypeParam, placeholderWithDefaults], 13 | [new Static(`Lazy`).labeled('f')], 14 | kindWithDefaults_([aTypeParam]), 15 | ), 16 | ) 17 | 18 | export const node = makeFromLazy 19 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.flap.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { aTypeParam, bTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const node = derived_( 7 | 'flap', 8 | [Covariant], 9 | fn_( 10 | '', 11 | [aTypeParam], 12 | [new Static('A').labeled('a')], 13 | fn_( 14 | '', 15 | [placeholder, bTypeParam], 16 | [kind_([new Dynamic([], () => `(a: A) => B`)]).labeled('kind')], 17 | kind_([bTypeParam]), 18 | ), 19 | ), 20 | ) 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.count.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const count = derived_( 9 | 'count', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bool)], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], new Static(`number`)), 16 | ), 17 | ) 18 | 19 | export const node = count 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const FilterMap = interface_( 6 | 'FilterMap', 7 | [ 8 | fnLabeled_( 9 | 'filterMap', 10 | [aTypeParam, bTypeParam], 11 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], new Static(`Maybe`))], 12 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 13 | ), 14 | ], 15 | [], 16 | ) 17 | 18 | export const node = FilterMap 19 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.find.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const find = derived_( 9 | 'find', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [fnLabeled_('predicate', [], [aTypeParam.labeled('a')], bool)], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], new Static(`Maybe`)), 16 | ), 17 | ) 18 | 19 | export const node = find 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Compact.compact.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { Compact } from './Compact.js' 4 | import { Covariant } from './Covariant.js' 5 | import { aTypeParam, composed_, fn_, kindF_, kindG_, placeholderF, placeholderG } from './common.js' 6 | 7 | export const compact = composed_( 8 | 'compact', 9 | [Covariant], 10 | [Compact], 11 | fn_( 12 | '', 13 | [placeholderF, placeholderG, aTypeParam], 14 | [kindF_([kindG_([new Dynamic([aTypeParam], ([a]) => `Maybe<${a}>`)])]).labeled()], 15 | kindF_([kindG_([aTypeParam])]), 16 | ), 17 | ) 18 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Reduce.ts: -------------------------------------------------------------------------------- 1 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 2 | 3 | export const Reduce = interface_( 4 | 'Reduce', 5 | [ 6 | fnLabeled_( 7 | 'reduce', 8 | [bTypeParam, aTypeParam], 9 | [ 10 | bTypeParam.labeled('b'), 11 | fnLabeled_('f', [], [bTypeParam.labeled('b'), aTypeParam.labeled('a')], bTypeParam), 12 | ], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 14 | ), 15 | ], 16 | [], 17 | ) 18 | 19 | export const node = Reduce 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Separate.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Tuple } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const node = interface_('Separate', [ 6 | fnLabeled_( 7 | 'separate', 8 | [placeholder, aTypeParam, bTypeParam], 9 | [ 10 | kind_([new Dynamic([aTypeParam, bTypeParam], ([A, B]) => `Either<${A}, ${B}>`)]).labeled( 11 | 'kind', 12 | ), 13 | ], 14 | new Tuple([kind_([aTypeParam]), kind_([bTypeParam])]), 15 | ), 16 | ]) 17 | 18 | export const Separate = node 19 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Identity.fromIdentityEitherCovariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { IdentityEither } from './IdentityEither.js' 5 | import { aTypeParam, derived_, fn_, kindWithDefaults_, placeholder } from './common.js' 6 | 7 | export const fromIdentityEitherCovariant = derived_( 8 | 'fromIdentityEitherCovariant', 9 | [IdentityEither, Covariant], 10 | fn_( 11 | '', 12 | [placeholder, aTypeParam], 13 | [], 14 | new Dynamic([kindWithDefaults_([aTypeParam])], ([k]) => `Identity<${k}>`), 15 | ), 16 | ) 17 | -------------------------------------------------------------------------------- /src/Typeclass/Commutative.ts: -------------------------------------------------------------------------------- 1 | import * as A from './Associative.js' 2 | 3 | export interface Commutative extends A.Associative {} 4 | 5 | export const struct = A.struct as (commutatives: { 6 | readonly [K in keyof A]: Commutative 7 | }) => Commutative<{ readonly [K in keyof A]: A[K] }> 8 | 9 | export const tuple = A.tuple as ( 10 | ...commutatives: { readonly [K in keyof A]: Commutative } 11 | ) => Commutative> 12 | 13 | export const concatAll = A.concatAll as ( 14 | C: Commutative, 15 | ) => (startWith: A) => (as: readonly A[]) => A 16 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reduce.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 3 | 4 | export const reduce = derived_( 5 | 'reduce', 6 | [FoldMap], 7 | fn_( 8 | '', 9 | [bTypeParam, aTypeParam], 10 | [ 11 | bTypeParam.labeled('seed'), 12 | fnLabeled_('f', [], [bTypeParam.labeled('b'), aTypeParam.labeled('a')], bTypeParam), 13 | ], 14 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 15 | ), 16 | ) 17 | 18 | export const node = reduce 19 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | fnLabeled_, 7 | fn_, 8 | interface_, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const node = interface_('Covariant', [ 14 | fnLabeled_( 15 | 'map', 16 | [aTypeParam, bTypeParam], 17 | [new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f')], 18 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 19 | ), 20 | ]) 21 | 22 | export const Covariant = node 23 | -------------------------------------------------------------------------------- /src/logical.ts: -------------------------------------------------------------------------------- 1 | import { Predicate } from './Predicate.js' 2 | import { Refinement } from './Refinement.js' 3 | 4 | export function ifElse( 5 | refinement: Refinement, 6 | ifF: (a: A1) => B, 7 | elseF: (a: A) => C, 8 | ): (value: A) => B | C 9 | 10 | export function ifElse( 11 | predicate: Predicate, 12 | ifF: (a: A) => B, 13 | elseF: (a: A) => C, 14 | ): (value: A) => B | C 15 | 16 | export function ifElse(predicate: Predicate, ifF: (a: A) => B, elseF: (a: A) => C) { 17 | return (value: A): B | C => (predicate(value) ? ifF(value) : elseF(value)) 18 | } 19 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceRight.ts: -------------------------------------------------------------------------------- 1 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 2 | 3 | export const ReduceRight = interface_( 4 | 'ReduceRight', 5 | [ 6 | fnLabeled_( 7 | 'reduceRight', 8 | [bTypeParam, aTypeParam], 9 | [ 10 | bTypeParam.labeled('b'), 11 | fnLabeled_('f', [], [aTypeParam.labeled('a'), bTypeParam.labeled('b')], bTypeParam), 12 | ], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 14 | ), 15 | ], 16 | [], 17 | ) 18 | 19 | export const node = ReduceRight 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.foldMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { ForEach } from './ForEach.js' 4 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const foldMap = derived_( 7 | 'foldMap', 8 | [ForEach], 9 | fn_( 10 | '', 11 | [bTypeParam], 12 | [new Static(`Identity`).labeled('I')], 13 | fn_( 14 | '', 15 | [aTypeParam], 16 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 17 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], bTypeParam), 18 | ), 19 | ), 20 | ) 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMapWithIndex.filterMap.ts: -------------------------------------------------------------------------------- 1 | import { KindParam, Labeled, Static } from '../AST.js' 2 | 3 | import { FilterMap } from './FilterMap.js' 4 | import { FilterMapWithIndex } from './FilterMapWithIndex.js' 5 | import { curriedPlaceholder_, fn_, hkt } from './common.js' 6 | 7 | const k = new Static(`K`) 8 | 9 | export const filterMap = fn_( 10 | 'filterMap', 11 | [hkt, k, curriedPlaceholder_(hkt)], 12 | [ 13 | FilterMapWithIndex.toTypeClass(hkt) 14 | .setParams([k, curriedPlaceholder_(hkt)]) 15 | .labeled(), 16 | ], 17 | (FilterMap.properties as readonly Labeled[])[0].param, 18 | ) 19 | -------------------------------------------------------------------------------- /src/Law/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Arbitrary.js' 2 | export * as Associative from './Associative.js' 3 | export * as AssociativeBoth from './AssociativeBoth.js' 4 | export * as AssociativeFlatten from './AssociativeFlatten.js' 5 | export * as Commutative from './Commutative.js' 6 | export * as Contravariant from './Contravariant.js' 7 | export * as Covariant from './Covariant.js' 8 | export * as Eq from './Eq.js' 9 | export * as Identity from './Identity.js' 10 | export * as IdentityBoth from './IdentityBoth.js' 11 | export * as IdentityFlatten from './IdentityFlatten.js' 12 | export * as Inverse from './Inverse.js' 13 | export * as Ord from './Ord.js' 14 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeFlatten.flatMap.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeFlatten } from './AssociativeFlatten.js' 2 | import { Covariant } from './Covariant.js' 3 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const flatMap = derived_( 6 | 'flatMap', 7 | [AssociativeFlatten, Covariant], 8 | fn_( 9 | '', 10 | [aTypeParam, placeholder, bTypeParam], 11 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], kind_([bTypeParam]))], 12 | fn_('', [], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 13 | ), 14 | ) 15 | 16 | export const node = flatMap 17 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Divariant.map.ts: -------------------------------------------------------------------------------- 1 | import { Divariant } from './Divariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | cTypeParam, 6 | derived_, 7 | fnLabeled_, 8 | fn_, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const map = derived_( 14 | 'map', 15 | [Divariant], 16 | fn_( 17 | '', 18 | [bTypeParam, cTypeParam], 19 | [fnLabeled_('f', [], [bTypeParam.labeled('b')], cTypeParam)], 20 | fn_( 21 | '', 22 | [placeholder, aTypeParam], 23 | [kind_([aTypeParam, bTypeParam]).labeled()], 24 | kind_([aTypeParam, cTypeParam]), 25 | ), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.contains.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const bool = new Static(`boolean`) 7 | 8 | export const contains = derived_( 9 | 'contains', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [aTypeParam], 14 | [new Static(`Eq`).labeled('E')], 15 | fn_( 16 | '', 17 | [], 18 | [aTypeParam.labeled('a')], 19 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bool), 20 | ), 21 | ), 22 | ) 23 | 24 | export const node = contains 25 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Trivariant.map.ts: -------------------------------------------------------------------------------- 1 | import { Trivariant } from './Trivariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | cTypeParam, 6 | dTypeParam, 7 | derived_, 8 | fab_, 9 | fn_, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | export const map = derived_( 15 | 'map', 16 | [Trivariant], 17 | fn_( 18 | '', 19 | [aTypeParam, bTypeParam], 20 | [fab_], 21 | fn_( 22 | '', 23 | [placeholder, cTypeParam, dTypeParam], 24 | [kind_([cTypeParam, dTypeParam, aTypeParam]).labeled()], 25 | kind_([cTypeParam, dTypeParam, bTypeParam]), 26 | ), 27 | ), 28 | ) 29 | -------------------------------------------------------------------------------- /src/string.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as L from './Law/index.js' 5 | import { testAllDataLaws } from './Law/internal-test-all-laws.js' 6 | import * as S from './string.js' 7 | 8 | describe(import.meta.url, () => { 9 | testAllDataLaws({ 10 | name: 'String Instances', 11 | fc, 12 | Arbitrary: L.string(), 13 | Eq: { 14 | string: S.Eq, 15 | }, 16 | Ord: { 17 | string: S.Ord, 18 | }, 19 | Associative: { 20 | string: [S.Associative, S.Eq], 21 | }, 22 | Identity: { 23 | string: [S.Identity, S.Eq], 24 | }, 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceWithIndex.reduce.ts: -------------------------------------------------------------------------------- 1 | import { KindParam, Labeled, Static } from '../AST.js' 2 | 3 | import { Reduce } from './Reduce.js' 4 | import { ReduceWithIndex } from './ReduceWithIndex.js' 5 | import { curriedPlaceholder_, fn_, hkt } from './common.js' 6 | 7 | const k = new Static(`K`) 8 | 9 | export const reduce = fn_( 10 | 'reduce', 11 | [hkt, k, curriedPlaceholder_(hkt)], 12 | [ 13 | ReduceWithIndex.toTypeClass(hkt) 14 | .setParams([k, curriedPlaceholder_(hkt)]) 15 | .labeled('RI'), 16 | ], 17 | (Reduce.properties as readonly Labeled[])[0].param, 18 | ) 19 | 20 | export const node = reduce 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeCompose.ts: -------------------------------------------------------------------------------- 1 | import { 2 | aTypeParam, 3 | bTypeParam, 4 | cTypeParam, 5 | fnLabeled_, 6 | fn_, 7 | interface_, 8 | kind_, 9 | placeholder, 10 | } from './common.js' 11 | 12 | export const node = interface_('AssociativeCompose', [ 13 | fnLabeled_( 14 | 'compose', 15 | [placeholder, bTypeParam, cTypeParam], 16 | [kind_([bTypeParam, cTypeParam]).labeled('second')], 17 | fn_( 18 | '', 19 | [aTypeParam], 20 | [kind_([aTypeParam, bTypeParam]).labeled('first')], 21 | kind_([aTypeParam, cTypeParam]), 22 | ), 23 | ), 24 | ]) 25 | 26 | export const AssociativeCompose = node 27 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bicovariant.map.ts: -------------------------------------------------------------------------------- 1 | import { Bicovariant } from './Bicovariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | cTypeParam, 6 | derived_, 7 | fnLabeled_, 8 | fn_, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const node = derived_( 14 | 'map', 15 | [Bicovariant], 16 | fn_( 17 | '', 18 | [aTypeParam, bTypeParam], 19 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 20 | fn_( 21 | '', 22 | [placeholder, cTypeParam], 23 | [kind_([cTypeParam, aTypeParam]).labeled('kind')], 24 | kind_([cTypeParam, bTypeParam]), 25 | ), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.groupBy.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const k = new Static(`K`) 7 | 8 | export const groupBy = derived_( 9 | 'groupBy', 10 | [FoldMap], 11 | fn_( 12 | '', 13 | [k, aTypeParam], 14 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], k)], 15 | fn_( 16 | '', 17 | [placeholder], 18 | [kind_([aTypeParam]).labeled()], 19 | new Static(`ReadonlyMap>`), 20 | ), 21 | ), 22 | ) 23 | 24 | export const node = groupBy 25 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.ts: -------------------------------------------------------------------------------- 1 | import { Tuple } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const AssociativeBoth = interface_( 6 | 'AssociativeBoth', 7 | [ 8 | fnLabeled_( 9 | 'both', 10 | [placeholder, bTypeParam], 11 | [kind_([bTypeParam]).labeled('second')], 12 | fn_( 13 | '', 14 | [aTypeParam], 15 | [kind_([aTypeParam]).labeled('first')], 16 | kind_([new Tuple([aTypeParam, bTypeParam])]), 17 | ), 18 | ), 19 | ], 20 | [], 21 | ) 22 | 23 | export const node = AssociativeBoth 24 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Divariant.contramap.ts: -------------------------------------------------------------------------------- 1 | import { Divariant } from './Divariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | cTypeParam, 6 | derived_, 7 | fnLabeled_, 8 | fn_, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const contramap = derived_( 14 | 'contramap', 15 | [Divariant], 16 | fn_( 17 | '', 18 | [bTypeParam, cTypeParam], 19 | [fnLabeled_('f', [], [bTypeParam.labeled('b')], cTypeParam)], 20 | fn_( 21 | '', 22 | [placeholder, aTypeParam], 23 | [kind_([cTypeParam, aTypeParam]).labeled()], 24 | kind_([bTypeParam, aTypeParam]), 25 | ), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMapWithIndex.toMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMapWithIndex } from './FoldMapWithIndex.js' 4 | import { aTypeParam, curriedPlaceholder_, fn_, hkt, kind_, placeholder } from './common.js' 5 | 6 | const k = new Static(`K`) 7 | 8 | export const toMap = fn_( 9 | 'toMap', 10 | [hkt, k, curriedPlaceholder_(hkt)], 11 | [ 12 | FoldMapWithIndex.toTypeClass(hkt) 13 | .setParams([k, curriedPlaceholder_(hkt)]) 14 | .labeled(), 15 | ], 16 | fn_( 17 | '', 18 | [placeholder, aTypeParam], 19 | [kind_([aTypeParam]).labeled()], 20 | new Static(`ReadonlyMap`), 21 | ), 22 | ) 23 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bicovariant.mapLeft.ts: -------------------------------------------------------------------------------- 1 | import { Bicovariant } from './Bicovariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | cTypeParam, 6 | derived_, 7 | fnLabeled_, 8 | fn_, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const node = derived_( 14 | 'mapLeft', 15 | [Bicovariant], 16 | fn_( 17 | '', 18 | [aTypeParam, bTypeParam], 19 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 20 | fn_( 21 | '', 22 | [placeholder, cTypeParam], 23 | [kind_([aTypeParam, cTypeParam]).labeled('kind')], 24 | kind_([bTypeParam, cTypeParam]), 25 | ), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.bindTo.ts: -------------------------------------------------------------------------------- 1 | import { ObjectNode, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | export const nameTypeParam = new Static('N extends string') 7 | 8 | export const node = derived_( 9 | 'bindTo', 10 | [Covariant], 11 | fn_( 12 | '', 13 | [nameTypeParam], 14 | [nameTypeParam.labeled('name')], 15 | fn_( 16 | '', 17 | [placeholder, aTypeParam], 18 | [kind_([aTypeParam]).labeled('kind')], 19 | kind_([new ObjectNode([new Static('A').labeled(`[K in N]`)])]), 20 | ), 21 | ), 22 | ) 23 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const FoldMap = interface_( 6 | 'FoldMap', 7 | [ 8 | fnLabeled_( 9 | 'foldMap', 10 | [bTypeParam], 11 | [new Static(`Identity`).labeled('ID')], 12 | fn_( 13 | '', 14 | [aTypeParam], 15 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 16 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 17 | ), 18 | ), 19 | ], 20 | [], 21 | ) 22 | 23 | export const node = FoldMap 24 | -------------------------------------------------------------------------------- /tools/overloads/definitions/NaturalTransformation.ts: -------------------------------------------------------------------------------- 1 | import { Interface } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | curriedPlaceholder_, 6 | fnLabeled_, 7 | hktF, 8 | hktG, 9 | kindF_, 10 | kindG_, 11 | placeholderF, 12 | placeholderG, 13 | } from './common.js' 14 | 15 | export const NaturalTransformation = new Interface( 16 | 'NaturalTransformation', 17 | [hktF, curriedPlaceholder_(hktF), hktG, curriedPlaceholder_(hktG)], 18 | [ 19 | fnLabeled_( 20 | 'naturalTransformation', 21 | [placeholderF, aTypeParam, placeholderG], 22 | [kindF_([aTypeParam]).labeled()], 23 | kindG_([aTypeParam]), 24 | ), 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /src/Typeclass/Inverse.ts: -------------------------------------------------------------------------------- 1 | import { Identity } from './Identity.js' 2 | 3 | export interface Inverse extends Identity { 4 | readonly inverse: (a: A, b: A) => A 5 | } 6 | 7 | export function multiply(I: Inverse) { 8 | return (a: A, amount: number) => { 9 | if (amount === 0) { 10 | return I.id 11 | } 12 | 13 | let i = amount 14 | let r = I.id 15 | 16 | const multiply_ = amount > 0 ? () => (r = I.concat(r, a)) : () => (r = I.inverse(r, a)) 17 | const updateIndex = amount > 0 ? () => (i -= 1) : () => (i += 1) 18 | 19 | while (i !== 0) { 20 | multiply_() 21 | updateIndex() 22 | } 23 | 24 | return r 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Invariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const node = interface_('Invariant', [ 6 | fnLabeled_( 7 | 'imap', 8 | [aTypeParam, bTypeParam], 9 | [ 10 | new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f'), 11 | new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${b}, ${a}>`).labeled('g'), 12 | ], 13 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 14 | ), 15 | ]) 16 | 17 | export const Invariant = node 18 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.reduceRight.ts: -------------------------------------------------------------------------------- 1 | import { FoldMap } from './FoldMap.js' 2 | import { ForEach } from './ForEach.js' 3 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 4 | 5 | export const reduceRight = derived_( 6 | 'reduceRight', 7 | [FoldMap, ForEach], 8 | fn_( 9 | 'reduceRight', 10 | [bTypeParam, aTypeParam], 11 | [ 12 | bTypeParam.labeled('b'), 13 | fnLabeled_('f', [], [aTypeParam.labeled('a'), bTypeParam.labeled('b')], bTypeParam), 14 | ], 15 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 16 | ), 17 | ) 18 | 19 | export const node = reduceRight 20 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Reduce.reduce.ts: -------------------------------------------------------------------------------- 1 | import { Reduce } from './Reduce.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | composed_, 6 | fnLabeled_, 7 | fn_, 8 | kindF_, 9 | kindG_, 10 | placeholderF, 11 | placeholderG, 12 | } from './common.js' 13 | 14 | export const reduce = composed_( 15 | 'reduce', 16 | [Reduce], 17 | [Reduce], 18 | fn_( 19 | '', 20 | [bTypeParam, aTypeParam], 21 | [ 22 | bTypeParam.labeled('seed'), 23 | fnLabeled_('f', [], [bTypeParam.labeled('b'), aTypeParam.labeled('a')], bTypeParam), 24 | ], 25 | fn_('', [placeholderF, placeholderG], [kindF_([kindG_([aTypeParam])]).labeled()], bTypeParam), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationMap": true, 5 | "downlevelIteration": true, 6 | "esModuleInterop": true, 7 | "importHelpers": true, 8 | "module": "ES2015", 9 | "moduleResolution": "NodeNext", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strict": true, 15 | "strictFunctionTypes": true, 16 | "strictNullChecks": true, 17 | "strictPropertyInitialization": true, 18 | "sourceMap": true, 19 | "target": "es2019", 20 | "lib": [ 21 | "DOM", 22 | "es2019" 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeFlatten.flatten.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeFlatten } from './AssociativeFlatten.js' 2 | import { Covariant } from './Covariant.js' 3 | import { ForEach } from './ForEach.js' 4 | import { IdentityBoth } from './IdentityBoth.js' 5 | import { aTypeParam, composed_, fn_, kindF_, kindG_, placeholderF, placeholderG } from './common.js' 6 | 7 | export const flatten = composed_( 8 | 'flatten', 9 | [IdentityBoth, AssociativeFlatten, Covariant], 10 | [AssociativeFlatten, ForEach], 11 | fn_( 12 | '', 13 | [placeholderF, placeholderG, aTypeParam], 14 | [kindF_([kindG_([kindF_([kindG_([aTypeParam])])])]).labeled()], 15 | kindF_([kindG_([aTypeParam])]), 16 | ), 17 | ) 18 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.foldMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { FoldMap } from './FoldMap.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | composed_, 8 | fab_, 9 | fn_, 10 | kindF_, 11 | kindG_, 12 | placeholderF, 13 | placeholderG, 14 | } from './common.js' 15 | 16 | export const foldMap = composed_( 17 | 'foldMap', 18 | [FoldMap], 19 | [FoldMap], 20 | fn_( 21 | '', 22 | [bTypeParam], 23 | [new Static(`Identity`).labeled('I')], 24 | fn_( 25 | '', 26 | [aTypeParam], 27 | [fab_], 28 | fn_('', [placeholderF, placeholderG], [kindF_([kindG_([aTypeParam])]).labeled()], bTypeParam), 29 | ), 30 | ), 31 | ) 32 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceRightWithIndex.reduceRight.ts: -------------------------------------------------------------------------------- 1 | import { KindParam, Labeled, Static } from '../AST.js' 2 | 3 | import { ReduceRight } from './ReduceRight.js' 4 | import { ReduceRightWithIndex } from './ReduceRightWithIndex.js' 5 | import { curriedPlaceholder_, fn_, hkt } from './common.js' 6 | 7 | const k = new Static(`K`) 8 | 9 | export const reduceRight = fn_( 10 | 'reduceRight', 11 | [hkt, k, curriedPlaceholder_(hkt)], 12 | [ 13 | ReduceRightWithIndex.toTypeClass(hkt) 14 | .setParams([k, curriedPlaceholder_(hkt)]) 15 | .labeled('RRI'), 16 | ], 17 | (ReduceRight.properties as readonly Labeled[])[0].param, 18 | ) 19 | 20 | export const node = reduceRight 21 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { aTypeParam, bTypeParam, fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | export const AssociativeEither = interface_( 6 | 'AssociativeEither', 7 | [ 8 | fnLabeled_( 9 | 'either', 10 | [placeholder, bTypeParam], 11 | [kind_([bTypeParam]).labeled('second')], 12 | fn_( 13 | '', 14 | [aTypeParam], 15 | [kind_([aTypeParam]).labeled('first')], 16 | kind_([new Dynamic([aTypeParam, bTypeParam], ([a, b]) => `Either<${a}, ${b}>`)]), 17 | ), 18 | ), 19 | ], 20 | [], 21 | ) 22 | 23 | export const node = AssociativeEither 24 | -------------------------------------------------------------------------------- /src/boolean.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as L from './Law/index.js' 5 | import { testAllDataLaws } from './Law/internal-test-all-laws.js' 6 | import * as B from './boolean.js' 7 | 8 | describe(import.meta.url, () => { 9 | testAllDataLaws({ 10 | name: 'Boolean Instances', 11 | fc, 12 | Arbitrary: L.boolean, 13 | Eq: { 14 | boolean: B.Eq, 15 | }, 16 | Ord: { 17 | boolean: B.Ord, 18 | }, 19 | Associative: { 20 | All: [B.AssociativeAll, B.Eq], 21 | Any: [B.AssociativeAny, B.Eq], 22 | }, 23 | Identity: { 24 | All: [B.All, B.Eq], 25 | Any: [B.Any, B.Eq], 26 | }, 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceRight.reduceRight.ts: -------------------------------------------------------------------------------- 1 | import { ReduceRight } from './ReduceRight.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | composed_, 6 | fnLabeled_, 7 | fn_, 8 | kindF_, 9 | kindG_, 10 | placeholderF, 11 | placeholderG, 12 | } from './common.js' 13 | 14 | export const reduceRight = composed_( 15 | 'reduceRight', 16 | [ReduceRight], 17 | [ReduceRight], 18 | fn_( 19 | '', 20 | [bTypeParam, aTypeParam], 21 | [ 22 | bTypeParam.labeled('b'), 23 | fnLabeled_('f', [], [aTypeParam.labeled('a'), bTypeParam.labeled('b')], bTypeParam), 24 | ], 25 | fn_('', [placeholderF, placeholderG], [kindF_([kindG_([aTypeParam])]).labeled()], bTypeParam), 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /src/Typeclass/Concat.ts: -------------------------------------------------------------------------------- 1 | import { Lazy, identity, second } from '../function.js' 2 | 3 | // Concat represents a concatenation that does NOT have any associated laws. 4 | export interface Concat { 5 | readonly concat: (first: A, second: A) => A 6 | } 7 | 8 | export const First: Concat = { concat: identity } 9 | export const Second: Concat = { concat: second } 10 | 11 | export const concatAll = 12 | (M: Concat) => 13 | (startWith: A) => 14 | (as: ReadonlyArray): A => 15 | as.reduce(M.concat, startWith) 16 | 17 | export const reverse = (M: Concat): Concat => ({ 18 | concat: (f, s) => M.concat(s, f), 19 | }) 20 | 21 | export const fromLazy = (f: Lazy): Concat => ({ 22 | concat: f, 23 | }) 24 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Contravariant.contramap.ts: -------------------------------------------------------------------------------- 1 | import { Contravariant } from './Contravariant.js' 2 | import { 3 | aTypeParam, 4 | bTypeParam, 5 | composed_, 6 | fnLabeled_, 7 | fn_, 8 | kindF_, 9 | kindG_, 10 | placeholderF, 11 | placeholderG, 12 | } from './common.js' 13 | 14 | export const contramap = composed_( 15 | 'contramap', 16 | [Contravariant], 17 | [Contravariant], 18 | fn_( 19 | '', 20 | [bTypeParam, aTypeParam], 21 | [fnLabeled_('f', [], [bTypeParam.labeled('b')], aTypeParam)], 22 | fn_( 23 | '', 24 | [placeholderF, placeholderG, aTypeParam], 25 | [kindF_([kindG_([aTypeParam])]).labeled()], 26 | kindF_([kindG_([bTypeParam])]), 27 | ), 28 | ), 29 | ) 30 | -------------------------------------------------------------------------------- /src/Progress.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as L from './Law/index.js' 5 | import { testAllDataLaws } from './Law/internal-test-all-laws.js' 6 | import * as P from './Progress.js' 7 | 8 | describe(import.meta.url, () => { 9 | testAllDataLaws({ 10 | name: `Progress Instances`, 11 | fc, 12 | Arbitrary: L.Progress, 13 | Associative: { 14 | Progress: [P.Associative, P.Eq], 15 | }, 16 | Commutative: { 17 | Progress: [P.Commutative, P.Eq], 18 | }, 19 | Identity: { 20 | Progress: [P.Identity, P.Eq], 21 | }, 22 | Eq: { 23 | Progress: P.Eq, 24 | }, 25 | Ord: { 26 | Progress: P.Ord, 27 | }, 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.orElse.ts: -------------------------------------------------------------------------------- 1 | import { UnionNode } from '../AST.js' 2 | 3 | import { AssociativeEither } from './AssociativeEither.js' 4 | import { Covariant } from './Covariant.js' 5 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 6 | 7 | export const orElse = derived_( 8 | 'orElse', 9 | [AssociativeEither, Covariant], 10 | fn_( 11 | '', 12 | [placeholder, bTypeParam], 13 | [fnLabeled_('f', [], [], kind_([bTypeParam]))], 14 | fn_( 15 | '', 16 | [aTypeParam], 17 | [kind_([aTypeParam]).labeled('kind')], 18 | kind_([new UnionNode(aTypeParam, bTypeParam)]), 19 | ), 20 | ), 21 | ) 22 | 23 | export const node = orElse 24 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Covariant.map.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | composed_, 8 | fn_, 9 | kindF_, 10 | kindG_, 11 | placeholderF, 12 | placeholderG, 13 | } from './common.js' 14 | 15 | export const map = composed_( 16 | 'map', 17 | [Covariant], 18 | [Covariant], 19 | fn_( 20 | '', 21 | [aTypeParam, bTypeParam], 22 | [new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f')], 23 | fn_( 24 | '', 25 | [placeholderF, placeholderG], 26 | [kindF_([kindG_([aTypeParam])]).labeled()], 27 | kindF_([kindG_([bTypeParam])]), 28 | ), 29 | ), 30 | ) 31 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Invariant.imap.ts: -------------------------------------------------------------------------------- 1 | import { Covariant } from './Covariant.js' 2 | import { Invariant } from './Invariant.js' 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | composed_, 7 | fab_, 8 | fnLabeled_, 9 | fn_, 10 | kindF_, 11 | kindG_, 12 | placeholderF, 13 | placeholderG, 14 | } from './common.js' 15 | 16 | export const imap = composed_( 17 | 'imap', 18 | [Covariant], 19 | [Invariant], 20 | fn_( 21 | '', 22 | [aTypeParam, bTypeParam], 23 | [fab_, fnLabeled_('g', [], [bTypeParam.labeled('b')], aTypeParam)], 24 | fn_( 25 | '', 26 | [placeholderF, placeholderG], 27 | [kindF_([kindG_([aTypeParam])]).labeled()], 28 | kindF_([kindG_([bTypeParam])]), 29 | ), 30 | ), 31 | ) 32 | -------------------------------------------------------------------------------- /src/Id.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Params } from './HKT.js' 2 | import { AssociativeBoth1 } from './Typeclass/AssociativeBoth.js' 3 | import { Covariant1 } from './Typeclass/Covariant.js' 4 | import { IdentityBoth1 } from './Typeclass/IdentityBoth.js' 5 | import { Top1 } from './Typeclass/Top.js' 6 | 7 | export type Id = A 8 | 9 | export interface IdHKT extends HKT { 10 | readonly type: Id 11 | } 12 | 13 | export const AssociativeBoth: AssociativeBoth1 = { 14 | both: (s) => (f) => [f, s], 15 | } 16 | 17 | export const Covariant: Covariant1 = { 18 | map: (f) => (a) => f(a), 19 | } 20 | 21 | export const Top: Top1 = { 22 | top: [], 23 | } 24 | 25 | export const IdentityBoth: IdentityBoth1 = { 26 | ...AssociativeBoth, 27 | ...Top, 28 | } 29 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Contravariant.contramapC.ts: -------------------------------------------------------------------------------- 1 | import { Contravariant } from './Contravariant.js' 2 | import { Covariant } from './Covariant.js' 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | composed_, 7 | fnLabeled_, 8 | fn_, 9 | kindF_, 10 | kindG_, 11 | placeholderF, 12 | placeholderG, 13 | } from './common.js' 14 | 15 | export const contramap = composed_( 16 | 'contramapC', 17 | [Covariant], 18 | [Contravariant], 19 | fn_( 20 | '', 21 | [bTypeParam, aTypeParam], 22 | [fnLabeled_('f', [], [bTypeParam.labeled('b')], aTypeParam)], 23 | fn_( 24 | '', 25 | [placeholderF, placeholderG, aTypeParam], 26 | [kindF_([kindG_([aTypeParam])]).labeled()], 27 | kindF_([kindG_([bTypeParam])]), 28 | ), 29 | ), 30 | ) 31 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.map.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { ForEach } from './ForEach.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from './common.js' 14 | 15 | export const map = fn_( 16 | 'map', 17 | [hkt, curriedPlaceholder_(hkt)], 18 | [ 19 | new Dynamic( 20 | [ForEach.toTypeClass(hkt).setParams([curriedPlaceholder_(hkt)])], 21 | (f) => `${f}['forEach']`, 22 | ).labeled('forEach'), 23 | ], 24 | fn_( 25 | '', 26 | [aTypeParam, bTypeParam], 27 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 28 | fn_('', [placeholder], [kind_([aTypeParam]).labeled()], kind_([bTypeParam])), 29 | ), 30 | ) 31 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Trivariant.contramap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Trivariant } from './Trivariant.js' 4 | import { aTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const eTypeParam = new Static(`E`) 7 | const r1TypeParam = new Static(`R1`) 8 | const r2TypeParam = new Static(`R2`) 9 | 10 | export const contramap = derived_( 11 | 'contramap', 12 | [Trivariant], 13 | fn_( 14 | '', 15 | [r1TypeParam, r2TypeParam], 16 | [fnLabeled_('f', [], [r1TypeParam.labeled('r1')], r2TypeParam)], 17 | fn_( 18 | '', 19 | [placeholder, eTypeParam, aTypeParam], 20 | [kind_([r2TypeParam, eTypeParam, aTypeParam]).labeled()], 21 | kind_([r1TypeParam, eTypeParam, aTypeParam]), 22 | ), 23 | ), 24 | ) 25 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.tuple.ts: -------------------------------------------------------------------------------- 1 | import { ArrayNode, Dynamic, Kind, Static } from '../AST.js' 2 | 3 | import { AssociativeEither } from './AssociativeEither.js' 4 | import { Covariant } from './Covariant.js' 5 | import { derived_, fn_, hkt, kindWithDefaults_, placeholder } from './common.js' 6 | 7 | export const tuple = derived_( 8 | 'tuple', 9 | [AssociativeEither, Covariant], 10 | fn_( 11 | '', 12 | [new ArrayNode(kindWithDefaults_([new Static('any')])).nonEmpty().labeled('AS')], 13 | [new Static(`AS`).labeled('...values')], 14 | new Kind(hkt, [ 15 | placeholder.extract(`AS[number]`), 16 | new Dynamic( 17 | [hkt], 18 | ([hkt]) => `ParamOf<${hkt.split('extends')[0].trim()}, AS[number], Params.A>`, 19 | ), 20 | ]), 21 | ), 22 | ) 23 | -------------------------------------------------------------------------------- /tools/overloads/definitions/IdentityBoth.tuple.ts: -------------------------------------------------------------------------------- 1 | import { ArrayNode, Dynamic, Kind, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { IdentityBoth } from './IdentityBoth.js' 5 | import { derived_, fn_, hkt, kindWithDefaults_, placeholder } from './common.js' 6 | 7 | export const tuple = derived_( 8 | 'tuple', 9 | [IdentityBoth, Covariant], 10 | fn_( 11 | '', 12 | [new ArrayNode(kindWithDefaults_([new Static('any')])).labeled('AS')], 13 | [new Static(`AS`).labeled('...values')], 14 | new Kind(hkt, [ 15 | placeholder.extract(`AS[number]`), 16 | new Dynamic( 17 | [hkt], 18 | ([hkt]) => `{ readonly [K in keyof AS]: ParamOf<${hkt}, AS[K], Params.A> }`, 19 | ), 20 | ]), 21 | ), 22 | ) 23 | 24 | export const node = tuple 25 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.both.ts: -------------------------------------------------------------------------------- 1 | import { Tuple } from '../AST.js' 2 | 3 | import { AssociativeBoth } from './AssociativeBoth.js' 4 | import { Covariant } from './Covariant.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | composed_, 9 | fn_, 10 | kindF_, 11 | kindG_, 12 | placeholderF, 13 | placeholderG, 14 | } from './common.js' 15 | 16 | export const both = composed_( 17 | 'both', 18 | [AssociativeBoth, Covariant], 19 | [AssociativeBoth], 20 | fn_( 21 | '', 22 | [placeholderF, placeholderG, bTypeParam], 23 | [kindF_([kindG_([bTypeParam])]).labeled('second')], 24 | fn_( 25 | '', 26 | [aTypeParam], 27 | [kindF_([kindG_([aTypeParam])]).labeled('first')], 28 | kindF_([kindG_([new Tuple([aTypeParam, bTypeParam])])]), 29 | ), 30 | ), 31 | ) 32 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMap.filterMap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { FilterMap } from './FilterMap.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | composed_, 9 | fnLabeled_, 10 | fn_, 11 | kindF_, 12 | kindG_, 13 | placeholderF, 14 | placeholderG, 15 | } from './common.js' 16 | 17 | export const filterMap = composed_( 18 | 'filterMap', 19 | [Covariant], 20 | [FilterMap], 21 | fn_( 22 | 'filterMap', 23 | [aTypeParam, bTypeParam], 24 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], new Static(`Maybe`))], 25 | fn_( 26 | '', 27 | [placeholderF, placeholderG], 28 | [kindF_([kindG_([aTypeParam])]).labeled('kind')], 29 | kindF_([kindG_([bTypeParam])]), 30 | ), 31 | ), 32 | ) 33 | -------------------------------------------------------------------------------- /src/Match.ts: -------------------------------------------------------------------------------- 1 | import * as M from './Maybe.js' 2 | import { flow, pipe } from './function.js' 3 | 4 | export interface Match { 5 | (a: A): M.Maybe 6 | } 7 | 8 | export const map = 9 | (f: (a: A) => B) => 10 | (match: Match): Match => 11 | flow(match, M.map(f)) 12 | 13 | export const mapMaybe = 14 | (f: (a: A) => M.Maybe) => 15 | (match: Match): Match => 16 | flow(match, M.flatMap(f)) 17 | 18 | export const flatMap = 19 | (f: (a: A) => Match) => 20 | (match: Match): Match => 21 | (i) => 22 | pipe( 23 | i, 24 | match, 25 | M.flatMap((a) => f(a)(i)), 26 | ) 27 | 28 | export const getOrElse = 29 | (orElse: () => B) => 30 | (match: Match) => 31 | flow(match, M.getOrElse(orElse)) 32 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Trivariant.bimap.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { Trivariant } from './Trivariant.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | cTypeParam, 8 | dTypeParam, 9 | derived_, 10 | fab_, 11 | fnLabeled_, 12 | fn_, 13 | kind_, 14 | placeholder, 15 | } from './common.js' 16 | 17 | const rTypeParam = new Static(`R`) 18 | 19 | export const bimap = derived_( 20 | 'bimap', 21 | [Trivariant], 22 | fn_( 23 | '', 24 | [aTypeParam, bTypeParam, cTypeParam, dTypeParam], 25 | [fab_, fnLabeled_('g', [], [cTypeParam.labeled('c')], dTypeParam)], 26 | fn_( 27 | '', 28 | [placeholder, rTypeParam], 29 | [kind_([rTypeParam, aTypeParam, cTypeParam]).labeled()], 30 | kind_([rTypeParam, bTypeParam, dTypeParam]), 31 | ), 32 | ), 33 | ) 34 | -------------------------------------------------------------------------------- /src/Endomorphism.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Params } from './HKT.js' 2 | import type { Associative } from './Typeclass/Associative.js' 3 | import type { Identity } from './Typeclass/Identity.js' 4 | import type * as I from './Typeclass/Invariant.js' 5 | import { flow, identity } from './function.js' 6 | 7 | export interface Endomorphism { 8 | (a: A): A 9 | } 10 | 11 | export const makeAssociative = (): Associative> => ({ 12 | concat: flow, 13 | }) 14 | 15 | export const makeIdentity = (): Identity> => ({ 16 | ...makeAssociative(), 17 | id: identity, 18 | }) 19 | 20 | export interface EndomorphismHKT extends HKT { 21 | readonly type: Endomorphism 22 | } 23 | 24 | export const Invariant: I.Invariant = { 25 | imap: (fo, fi) => (f) => flow(fi, f, fo), 26 | } 27 | -------------------------------------------------------------------------------- /src/Typeclass/Eq.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe, it } from 'vitest' 3 | 4 | import * as L from '../Law/index.js' 5 | import * as B from '../boolean.js' 6 | import { pipe } from '../function.js' 7 | import * as N from '../number.js' 8 | 9 | import * as E from './Eq.js' 10 | 11 | describe(import.meta.url, () => { 12 | describe('Contravariant', () => { 13 | const { identity, associativity } = pipe( 14 | L.number(), 15 | L.Contravariant.testContravariant()( 16 | E.Contravariant, 17 | (a: number) => String(a), 18 | (s: string) => s.length % 2 === 0, 19 | N.Eq, 20 | B.Eq, 21 | (eq: E.Eq, a: A, b: A) => eq.equals(a, b), 22 | B.Eq, 23 | ), 24 | )(fc) 25 | 26 | it('Identity', identity) 27 | it('Associativity', associativity) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.tuple.ts: -------------------------------------------------------------------------------- 1 | import { ArrayNode, Dynamic, Kind, Static } from '../AST.js' 2 | 3 | import { AssociativeBoth } from './AssociativeBoth.js' 4 | import { Covariant } from './Covariant.js' 5 | import { derived_, fn_, hkt, kindWithDefaults_, placeholder } from './common.js' 6 | 7 | export const tuple = derived_( 8 | 'tuple', 9 | [AssociativeBoth, Covariant], 10 | fn_( 11 | '', 12 | [new ArrayNode(kindWithDefaults_([new Static('any')])).nonEmpty().labeled('AS')], 13 | [new Static(`AS`).labeled('...values')], 14 | new Kind(hkt, [ 15 | placeholder.extract(`AS[number]`), 16 | new Dynamic( 17 | [hkt], 18 | ([hkt]) => 19 | `{ readonly [K in keyof AS]: ParamOf<${hkt 20 | .split('extends')[0] 21 | .trim()}, AS[K], Params.A> }`, 22 | ), 23 | ]), 24 | ), 25 | ) 26 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Divariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | dTypeParam, 8 | fnLabeled_, 9 | fn_, 10 | interface_, 11 | kind_, 12 | placeholder, 13 | } from './common.js' 14 | 15 | export const node = interface_('Divariant', [ 16 | fnLabeled_( 17 | 'dimap', 18 | [aTypeParam, bTypeParam, cTypeParam, dTypeParam], 19 | [ 20 | new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f'), 21 | new Dynamic([cTypeParam, dTypeParam] as const, ([c, d]) => `Unary<${c}, ${d}>`).labeled('g'), 22 | ], 23 | fn_( 24 | '', 25 | [placeholder], 26 | [kind_([bTypeParam, cTypeParam]).labeled('kind')], 27 | kind_([aTypeParam, dTypeParam]), 28 | ), 29 | ), 30 | ]) 31 | 32 | export const Divariant = node 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.sequence.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Kind } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { ForEach } from './ForEach.js' 5 | import { IdentityBoth } from './IdentityBoth.js' 6 | import { aTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 7 | 8 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 9 | const hkt2Placeholder = hkt2.toPlaceholder() 10 | 11 | export const sequence = derived_( 12 | 'sequence', 13 | [ForEach], 14 | derived_( 15 | '', 16 | [IdentityBoth, Covariant], 17 | fn_( 18 | '', 19 | [placeholder, hkt2Placeholder, aTypeParam], 20 | [kind_([new Kind(hkt2, [hkt2Placeholder, aTypeParam])]).labeled('kind')], 21 | new Kind(hkt2, [hkt2Placeholder, kind_([aTypeParam])]), 22 | ), 23 | hkt2, 24 | ), 25 | ) 26 | 27 | export const node = sequence 28 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bicovariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | dTypeParam, 8 | fnLabeled_, 9 | fn_, 10 | interface_, 11 | kind_, 12 | placeholder, 13 | } from './common.js' 14 | 15 | export const Bicovariant = interface_('Bicovariant', [ 16 | fnLabeled_( 17 | 'bimap', 18 | [aTypeParam, bTypeParam, cTypeParam, dTypeParam], 19 | [ 20 | new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f'), 21 | new Dynamic([cTypeParam, dTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('g'), 22 | ], 23 | fn_( 24 | '', 25 | [placeholder], 26 | [kind_([aTypeParam, cTypeParam]).labeled('kind')], 27 | kind_([bTypeParam, dTypeParam]), 28 | ), 29 | ), 30 | ]) 31 | 32 | export const node = Bicovariant 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.mapAccum.ts: -------------------------------------------------------------------------------- 1 | import { Static, Tuple } from '../AST.js' 2 | 3 | import { ForEach } from './ForEach.js' 4 | import { aTypeParam, bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const sTypeParam = new Static(`S`) 7 | 8 | export const mapAccum = derived_( 9 | 'mapAccum', 10 | [ForEach], 11 | fn_( 12 | '', 13 | [sTypeParam, aTypeParam, bTypeParam], 14 | [ 15 | sTypeParam.labeled('s'), 16 | fnLabeled_( 17 | 'f', 18 | [], 19 | [sTypeParam.labeled('s'), aTypeParam.labeled('a')], 20 | new Tuple([sTypeParam, bTypeParam]), 21 | ), 22 | ], 23 | fn_( 24 | '', 25 | [placeholder], 26 | [kind_([aTypeParam]).labeled()], 27 | new Tuple([sTypeParam, kind_([bTypeParam])]), 28 | ), 29 | ), 30 | ) 31 | 32 | export const node = mapAccum 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/CovariantWithIndex.map.ts: -------------------------------------------------------------------------------- 1 | import { Static } from '../AST.js' 2 | 3 | import { CovariantWithIndex } from './CovariantWithIndex.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from './common.js' 14 | 15 | const kTypeParam = new Static('K') 16 | 17 | export const map = fn_( 18 | 'map', 19 | [hkt, kTypeParam, curriedPlaceholder_(hkt)], 20 | [ 21 | CovariantWithIndex.toTypeClass(hkt) 22 | .setParams([kTypeParam, curriedPlaceholder_(hkt)]) 23 | .labeled('CI'), 24 | ], 25 | fn_( 26 | '', 27 | [aTypeParam, bTypeParam], 28 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam)], 29 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 30 | ), 31 | ) 32 | 33 | export const node = map 34 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMapWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Interface, Static } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | fn_, 9 | hkt, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | export const FoldMapWithIndex = new Interface( 15 | 'FoldMapWithIndex', 16 | [hkt, new Static(`K`), curriedPlaceholder_(hkt)], 17 | [ 18 | fnLabeled_( 19 | 'foldMapWithIndex', 20 | [bTypeParam], 21 | [new Static(`Identity`).labeled('ID')], 22 | fn_( 23 | '', 24 | [aTypeParam], 25 | [fnLabeled_('f', [], [new Static(`K`).labeled('k'), aTypeParam.labeled('a')], bTypeParam)], 26 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 27 | ), 28 | ), 29 | ], 30 | [], 31 | ) 32 | 33 | export const node = FoldMapWithIndex 34 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeCompose.compose.ts: -------------------------------------------------------------------------------- 1 | import { AssociativeCompose } from './AssociativeCompose.js' 2 | import { AssociativeFlatten } from './AssociativeFlatten.js' 3 | import { Covariant } from './Covariant.js' 4 | import { 5 | bTypeParam, 6 | cTypeParam, 7 | composed_, 8 | dTypeParam, 9 | fn_, 10 | kindF_, 11 | kindG_, 12 | placeholderF, 13 | placeholderG, 14 | } from './common.js' 15 | 16 | export const compose = composed_( 17 | 'compose', 18 | [AssociativeFlatten, Covariant], 19 | [AssociativeCompose], 20 | fn_( 21 | '', 22 | [placeholderF, placeholderG, cTypeParam, dTypeParam], 23 | [kindF_([kindG_([cTypeParam, dTypeParam])]).labeled('second')], 24 | fn_( 25 | '', 26 | [bTypeParam], 27 | [kindF_([kindG_([bTypeParam, cTypeParam])]).labeled('first')], 28 | kindF_([kindG_([bTypeParam, dTypeParam])]), 29 | ), 30 | ), 31 | ) 32 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FoldMap.partitionMap.ts: -------------------------------------------------------------------------------- 1 | import { Static, Tuple } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { FoldMap } from './FoldMap.js' 5 | import { IdentityEither } from './IdentityEither.js' 6 | import { Top } from './Top.js' 7 | import { 8 | aTypeParam, 9 | bTypeParam, 10 | cTypeParam, 11 | derived_, 12 | fnLabeled_, 13 | fn_, 14 | kind_, 15 | placeholder, 16 | } from './common.js' 17 | 18 | export const partitionMap = derived_( 19 | 'partitionMap', 20 | [FoldMap, IdentityEither, Top, Covariant], 21 | fn_( 22 | '', 23 | [aTypeParam, bTypeParam, cTypeParam], 24 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], new Static(`Either`))], 25 | fn_( 26 | '', 27 | [placeholder], 28 | [kind_([aTypeParam]).labeled()], 29 | new Tuple([kind_([bTypeParam]), kind_([cTypeParam])]), 30 | ), 31 | ), 32 | ) 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Divariant.dimap.ts: -------------------------------------------------------------------------------- 1 | import { Covariant } from './Covariant.js' 2 | import { Divariant } from './Divariant.js' 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | composed_, 8 | dTypeParam, 9 | fnLabeled_, 10 | fn_, 11 | kindF_, 12 | kindG_, 13 | placeholderF, 14 | placeholderG, 15 | } from './common.js' 16 | 17 | export const dimap = composed_( 18 | 'dimap', 19 | [Divariant], 20 | [Covariant], 21 | fn_( 22 | '', 23 | [aTypeParam, bTypeParam, cTypeParam, dTypeParam], 24 | [ 25 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 26 | fnLabeled_('g', [], [cTypeParam.labeled('c')], dTypeParam), 27 | ], 28 | fn_( 29 | '', 30 | [placeholderF, placeholderG], 31 | [kindF_([bTypeParam, kindG_([cTypeParam])]).labeled()], 32 | kindF_([aTypeParam, kindG_([dTypeParam])]), 33 | ), 34 | ), 35 | ) 36 | -------------------------------------------------------------------------------- /tools/overloads/definitions/PartitionMap.ts: -------------------------------------------------------------------------------- 1 | import { Static, Tuple } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | fnLabeled_, 8 | fn_, 9 | interface_, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | export const PartitionMap = interface_( 15 | 'PartitionMap', 16 | [ 17 | fnLabeled_( 18 | 'partitionMap', 19 | [aTypeParam, bTypeParam, cTypeParam], 20 | [ 21 | fnLabeled_( 22 | 'f', 23 | [], 24 | [aTypeParam.labeled('a')], 25 | new Static(`Either<${bTypeParam.type}, ${cTypeParam.type}>`), 26 | ), 27 | ], 28 | fn_( 29 | '', 30 | [placeholder], 31 | [kind_([aTypeParam]).labeled('kind')], 32 | new Tuple([kind_([bTypeParam]), kind_([cTypeParam])]), 33 | ), 34 | ), 35 | ], 36 | [], 37 | ) 38 | 39 | export const node = PartitionMap 40 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Interface, Static } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | fn_, 9 | hkt, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | export const ReduceWithIndex = new Interface( 15 | 'ReduceWithIndex', 16 | [hkt, new Static(`K`), curriedPlaceholder_(hkt)], 17 | [ 18 | fnLabeled_( 19 | 'reduceWithIndex', 20 | [bTypeParam, aTypeParam], 21 | [ 22 | bTypeParam.labeled('b'), 23 | fnLabeled_( 24 | 'f', 25 | [], 26 | [new Static(`K`).labeled('k'), bTypeParam.labeled('b'), aTypeParam.labeled('a')], 27 | bTypeParam, 28 | ), 29 | ], 30 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 31 | ), 32 | ], 33 | [], 34 | ) 35 | 36 | export const node = ReduceWithIndex 37 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.either.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { AssociativeEither } from './AssociativeEither.js' 4 | import { AssociativeFlatten } from './AssociativeFlatten.js' 5 | import { Covariant } from './Covariant.js' 6 | import { 7 | aTypeParam, 8 | bTypeParam, 9 | composed_, 10 | fn_, 11 | kindF_, 12 | kindG_, 13 | placeholderF, 14 | placeholderG, 15 | } from './common.js' 16 | 17 | export const either = composed_( 18 | 'either', 19 | [AssociativeFlatten, Covariant], 20 | [AssociativeEither], 21 | fn_( 22 | '', 23 | [placeholderF, placeholderG, bTypeParam], 24 | [kindF_([kindG_([bTypeParam])]).labeled('second')], 25 | fn_( 26 | '', 27 | [aTypeParam], 28 | [kindF_([kindG_([aTypeParam])]).labeled('first')], 29 | kindF_([kindG_([new Dynamic([aTypeParam, bTypeParam], ([a, b]) => `Either<${a}, ${b}>`)])]), 30 | ), 31 | ), 32 | ) 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMapWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Interface, Static } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | fn_, 9 | hkt, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | const kTypeParam = new Static(`K`) 15 | 16 | export const FilterMapWithIndex = new Interface( 17 | 'FilterMapWithIndex', 18 | [hkt, kTypeParam, curriedPlaceholder_(hkt)], 19 | [ 20 | fnLabeled_( 21 | 'filterMapWithIndex', 22 | [aTypeParam, bTypeParam], 23 | [ 24 | fnLabeled_( 25 | 'f', 26 | [], 27 | [kTypeParam.labeled('k'), aTypeParam.labeled('a')], 28 | new Static(`Maybe`), 29 | ), 30 | ], 31 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 32 | ), 33 | ], 34 | [], 35 | ) 36 | 37 | export const node = FilterMapWithIndex 38 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ReduceRightWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Interface, Static } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | fn_, 9 | hkt, 10 | kind_, 11 | placeholder, 12 | } from './common.js' 13 | 14 | export const ReduceRightWithIndex = new Interface( 15 | 'ReduceRightWithIndex', 16 | [hkt, new Static(`K`), curriedPlaceholder_(hkt)], 17 | [ 18 | fnLabeled_( 19 | 'reduceRightWithIndex', 20 | [bTypeParam, aTypeParam], 21 | [ 22 | bTypeParam.labeled('b'), 23 | fnLabeled_( 24 | 'f', 25 | [], 26 | [new Static(`K`).labeled('k'), aTypeParam.labeled('a'), bTypeParam.labeled('b')], 27 | bTypeParam, 28 | ), 29 | ], 30 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], bTypeParam), 31 | ), 32 | ], 33 | [], 34 | ) 35 | 36 | export const node = ReduceRightWithIndex 37 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeEither.eitherWith.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { AssociativeEither } from './AssociativeEither.js' 4 | import { Contravariant } from './Contravariant.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | cTypeParam, 9 | derived_, 10 | fnLabeled_, 11 | fn_, 12 | kind_, 13 | placeholder, 14 | } from './common.js' 15 | 16 | export const eitherWith = derived_( 17 | 'eitherWith', 18 | [AssociativeEither, Contravariant], 19 | fn_( 20 | '', 21 | [placeholder, bTypeParam, cTypeParam, aTypeParam], 22 | [ 23 | kind_([bTypeParam]).labeled('b'), 24 | fnLabeled_( 25 | 'f', 26 | [], 27 | [cTypeParam.labeled('c')], 28 | new Dynamic([aTypeParam, bTypeParam], ([a, b]) => `Either<${a}, ${b}>`), 29 | ), 30 | ], 31 | fn_('', [], [kind_([aTypeParam]).labeled('a')], kind_([cTypeParam])), 32 | ), 33 | ) 34 | 35 | export const node = eitherWith 36 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.forEach.ts: -------------------------------------------------------------------------------- 1 | import { Covariant } from './Covariant.js' 2 | import { ForEach } from './ForEach.js' 3 | import { IdentityBoth } from './IdentityBoth.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | composed_, 8 | derived_, 9 | fnLabeled_, 10 | fn_, 11 | kindF_, 12 | kindG_, 13 | kind_, 14 | placeholder, 15 | placeholderF, 16 | placeholderG, 17 | } from './common.js' 18 | 19 | export const forEach = composed_( 20 | 'forEach', 21 | [ForEach], 22 | [ForEach], 23 | derived_( 24 | '', 25 | [IdentityBoth, Covariant], 26 | fn_( 27 | '', 28 | [aTypeParam, placeholder, bTypeParam], 29 | [fnLabeled_('f', [], [aTypeParam.labeled('a')], kind_([bTypeParam]))], 30 | fn_( 31 | '', 32 | [placeholderF, placeholderG], 33 | [kindF_([kindG_([aTypeParam])]).labeled('kind')], 34 | kind_([kindF_([kindG_([bTypeParam])])]), 35 | ), 36 | ), 37 | ), 38 | ) 39 | -------------------------------------------------------------------------------- /src/Typeclass/Identity.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe, it } from 'vitest' 3 | 4 | import * as L from '../Law/index.js' 5 | import * as M from '../Maybe.js' 6 | import * as B from '../boolean.js' 7 | import * as N from '../number.js' 8 | import * as S from '../string.js' 9 | 10 | import { fromIdentityEitherCovariant } from './Identity.js' 11 | 12 | describe(import.meta.url, () => { 13 | describe(fromIdentityEitherCovariant.name, () => { 14 | it('creates an Identity instance', () => { 15 | const makeIdentity = fromIdentityEitherCovariant({ 16 | ...M.IdentityEither, 17 | ...M.Covariant, 18 | }) 19 | 20 | L.Identity.testIdentity(makeIdentity(), M.makeEq(N.Eq))(L.Maybe(L.number()))(fc) 21 | L.Identity.testIdentity(makeIdentity(), M.makeEq(S.Eq))(L.Maybe(L.string()))(fc) 22 | L.Identity.testIdentity(makeIdentity(), M.makeEq(B.Eq))(L.Maybe(L.boolean))(fc) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /tools/overloads/definitions/CovariantWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, FunctionSignature, Interface, Static } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | hkt, 9 | kind_, 10 | placeholder, 11 | } from './common.js' 12 | 13 | export const kTypeParam = new Static('K') 14 | 15 | export const node = new Interface( 16 | 'CovariantWithIndex', 17 | [hkt, kTypeParam, curriedPlaceholder_(hkt)], 18 | [ 19 | fnLabeled_( 20 | 'mapWithIndex', 21 | [aTypeParam, bTypeParam], 22 | [ 23 | new Dynamic( 24 | [aTypeParam, bTypeParam, kTypeParam] as const, 25 | ([a, b, k]) => `(index: ${k}, a: ${a}) => ${b}`, 26 | ).labeled('f'), 27 | ], 28 | new FunctionSignature( 29 | '', 30 | [placeholder], 31 | [kind_([aTypeParam]).labeled('kind')], 32 | kind_([bTypeParam]), 33 | ), 34 | ), 35 | ], 36 | ) 37 | 38 | export const CovariantWithIndex = node 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # generated files 40 | build 41 | dist 42 | esm 43 | cjs 44 | umd 45 | lib 46 | .tmp 47 | .cache-loader 48 | 49 | # other stuff 50 | src/test.ts 51 | .DS_STORE 52 | .vscode 53 | .fusebox 54 | .history 55 | tsconfig*.tsbuildinfo 56 | .rpt2_cache 57 | .eslintcache 58 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Bicovariant.bimap.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic } from '../AST.js' 2 | 3 | import { Bicovariant } from './Bicovariant.js' 4 | import { Covariant } from './Covariant.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | cTypeParam, 9 | composed_, 10 | dTypeParam, 11 | fn_, 12 | kindF_, 13 | kindG_, 14 | placeholderF, 15 | placeholderG, 16 | } from './common.js' 17 | 18 | export const bimap = composed_( 19 | 'bimap', 20 | [Covariant], 21 | [Bicovariant], 22 | fn_( 23 | '', 24 | [aTypeParam, bTypeParam, cTypeParam, dTypeParam], 25 | [ 26 | new Dynamic([aTypeParam, bTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('f'), 27 | new Dynamic([cTypeParam, dTypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled('g'), 28 | ], 29 | fn_( 30 | '', 31 | [placeholderF, placeholderG], 32 | [kindF_([kindG_([aTypeParam, cTypeParam])]).labeled()], 33 | kindF_([kindG_([bTypeParam, dTypeParam])]), 34 | ), 35 | ), 36 | ) 37 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Static } from '../../AST.js' 2 | import { Covariant } from '../Covariant.js' 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from '../common.js' 14 | 15 | export const testCovariant = fn_( 16 | 'testCovariant', 17 | [hkt, curriedPlaceholder_(hkt), aTypeParam, bTypeParam, cTypeParam, placeholder], 18 | [ 19 | Covariant.toTypeClass(hkt).labeled('C'), 20 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 21 | fnLabeled_('g', [], [bTypeParam.labeled('b')], cTypeParam), 22 | new Dynamic([kind_([cTypeParam])], ([kind]) => `Eq<${kind}>`).labeled(`Eq?`), 23 | ], 24 | fn_( 25 | '', 26 | [], 27 | [new Dynamic([kind_([aTypeParam])], ([k]) => `Arbitrary.Arbitrary<${k}>`).labeled(`Arb`)], 28 | fn_('', [], [new Static(`typeof import('fast-check')`).labeled('fc')], new Static(`void`)), 29 | ), 30 | ) 31 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeBoth.bothWith.ts: -------------------------------------------------------------------------------- 1 | import { IntersectionNode, Tuple } from '../AST.js' 2 | 3 | import { AssociativeBoth } from './AssociativeBoth.js' 4 | import { Contravariant } from './Contravariant.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | cTypeParam, 9 | curriedPlaceholder_, 10 | fnLabeled_, 11 | fn_, 12 | hkt, 13 | kind_, 14 | placeholder, 15 | } from './common.js' 16 | 17 | export const bothWith = fn_( 18 | 'bothWith', 19 | [hkt, curriedPlaceholder_(hkt)], 20 | [ 21 | new IntersectionNode(AssociativeBoth.toTypeClass(hkt), Contravariant.toTypeClass(hkt)).labeled( 22 | 'AB', 23 | ), 24 | ], 25 | fn_( 26 | '', 27 | [placeholder, bTypeParam, cTypeParam, aTypeParam], 28 | [ 29 | kind_([bTypeParam]).labeled('b'), 30 | fnLabeled_('f', [], [cTypeParam.labeled('c')], new Tuple([aTypeParam, bTypeParam])), 31 | ], 32 | fn_('', [], [kind_([aTypeParam]).labeled('a')], kind_([cTypeParam])), 33 | ), 34 | ) 35 | 36 | export const node = bothWith 37 | -------------------------------------------------------------------------------- /src/number.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as L from './Law/index.js' 5 | import { testAllDataLaws } from './Law/internal-test-all-laws.js' 6 | import * as N from './number.js' 7 | 8 | describe(import.meta.url, () => { 9 | testAllDataLaws({ 10 | name: 'Number Instances', 11 | fc, 12 | // Only constrained because really large numbers used in multiplication w/ JavaScript aren't exactly deterministic 13 | Arbitrary: L.number({ min: -10000, max: 10000 }), 14 | Eq: { 15 | number: N.Eq, 16 | }, 17 | Ord: { 18 | number: N.Ord, 19 | }, 20 | Associative: { 21 | Sum: [N.AssociativeSum, N.Eq], 22 | Product: [N.AssociativeProduct, N.Eq], 23 | }, 24 | Commutative: { 25 | Sum: [N.CommutativeSum, N.Eq], 26 | Product: [N.CommutativeProduct, N.Eq], 27 | }, 28 | Identity: { 29 | Sum: [N.IdentitySum, N.Eq], 30 | Product: [N.IdentityProduct, N.Eq], 31 | }, 32 | Inverse: { 33 | Sum: [N.Inverse, N.Eq], 34 | }, 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/Law/Identity.ts: -------------------------------------------------------------------------------- 1 | import { DeepEquals, Eq } from '../Typeclass/Eq.js' 2 | import { Identity } from '../Typeclass/Identity.js' 3 | import { pipe } from '../function.js' 4 | 5 | import * as Arbitrary from './Arbitrary.js' 6 | 7 | export function testIdentity(I: Identity, Eq: Eq = DeepEquals) { 8 | const left = testLeftIdentity(I, Eq) 9 | const right = testRightIdentity(I, Eq) 10 | 11 | return (Arb: Arbitrary.Arbitrary) => (fc: typeof import('fast-check')) => ({ 12 | left: () => left(Arb).property(fc), 13 | right: () => right(Arb).property(fc), 14 | }) 15 | } 16 | 17 | export function testLeftIdentity(I: Identity, Eq: Eq = DeepEquals) { 18 | return (Arb: Arbitrary.Arbitrary) => 19 | pipe( 20 | Arb, 21 | Arbitrary.toProperty((a) => Eq.equals(I.concat(a, I.id), a)), 22 | ) 23 | } 24 | 25 | export function testRightIdentity(I: Identity, Eq: Eq = DeepEquals) { 26 | return (Arb: Arbitrary.Arbitrary) => 27 | pipe( 28 | Arb, 29 | Arbitrary.toProperty((a) => Eq.equals(I.concat(I.id, a), a)), 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /tools/overloads/definitions/PartitionMapWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { Interface, Static, Tuple } from '../AST.js' 2 | 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from './common.js' 14 | 15 | export const PartitionMapWithIndex = new Interface( 16 | 'PartitionMapWithIndex', 17 | [hkt, new Static(`K`), curriedPlaceholder_(hkt)], 18 | [ 19 | fnLabeled_( 20 | 'partitionMapWithIndex', 21 | [aTypeParam, bTypeParam, cTypeParam], 22 | [ 23 | fnLabeled_( 24 | 'f', 25 | [], 26 | [new Static(`K`).labeled('k'), aTypeParam.labeled('a')], 27 | new Static(`Either<${bTypeParam.type}, ${cTypeParam.type}>`), 28 | ), 29 | ], 30 | fn_( 31 | '', 32 | [placeholder], 33 | [kind_([aTypeParam]).labeled('kind')], 34 | new Tuple([kind_([bTypeParam]), kind_([cTypeParam])]), 35 | ), 36 | ), 37 | ], 38 | [], 39 | ) 40 | 41 | export const node = PartitionMapWithIndex 42 | -------------------------------------------------------------------------------- /tools/overloads/definitions/IdentityBoth.struct.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Kind, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { IdentityBoth } from './IdentityBoth.js' 5 | import { derived_, fn_, hkt, placeholder, placeholderWithDefaults } from './common.js' 6 | 7 | export const struct = derived_( 8 | 'struct', 9 | [IdentityBoth, Covariant], 10 | fn_( 11 | '', 12 | [ 13 | new Dynamic( 14 | [placeholderWithDefaults], 15 | ([...params]) => 16 | `A extends Readonly 1 ? params.length : '' 18 | } s.split('=')[1]) 21 | .join(', ')}${params.length > 1 ? ', ' : ''}any>>>`, 22 | ), 23 | ], 24 | [new Static(`A`).labeled('values')], 25 | new Kind(hkt, [ 26 | placeholder.extract(`A[string]`), 27 | new Dynamic([hkt], ([hkt]) => `{ readonly [K in keyof A]: ParamOf<${hkt}, A[K], Params.A> }`), 28 | ]), 29 | ), 30 | ) 31 | 32 | export const node = struct 33 | -------------------------------------------------------------------------------- /tools/overloads/definitions/FilterMap.filter.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Static } from '../AST.js' 2 | 3 | import { FilterMap } from './FilterMap.js' 4 | import { aTypeParam, bTypeParam, derived_, fn_, kind_, placeholder } from './common.js' 5 | 6 | const pred = fn_( 7 | '', 8 | [aTypeParam], 9 | [new Static(`Predicate`).labeled('predicate')], 10 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([aTypeParam])), 11 | ) 12 | 13 | const refine = fn_( 14 | '', 15 | [aTypeParam, new Static(`B extends A`)], 16 | [new Static(`Refinement`).labeled('refinement')], 17 | fn_('', [placeholder], [kind_([aTypeParam]).labeled('kind')], kind_([bTypeParam])), 18 | ) 19 | 20 | export const filter = derived_( 21 | 'filter', 22 | [FilterMap], 23 | new Dynamic( 24 | [pred, refine], 25 | ([p, r]) => `{\n ${printOverloadFunction(r)}\n ${printOverloadFunction(p)}}`, 26 | ), 27 | ) 28 | 29 | function printOverloadFunction(f: string) { 30 | const i = f.indexOf('=>') 31 | 32 | return f.slice(0, i).trim() + `: ` + f.slice(i + 2).trim() 33 | } 34 | 35 | export const node = filter 36 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * as Array from './Array.js' 2 | export * as boolean from './boolean.js' 3 | export * as Branded from './Branded.js' 4 | export * as Data from './Data.js' 5 | export * as Either from './Either.js' 6 | export * as Endomorphism from './Endomorphism.js' 7 | export * as Id from './Id.js' 8 | export * as Map from './Map.js' 9 | export * as Match from './Match.js' 10 | export * as Maybe from './Maybe.js' 11 | export * as NonEmptyArray from './NonEmptyArray.js' 12 | export * as number from './number.js' 13 | export * as Predicate from './Predicate.js' 14 | export * as Progress from './Progress.js' 15 | export * as Record from './Record.js' 16 | export * as Refinement from './Refinement.js' 17 | export * as RoseTree from './RoseTree.js' 18 | export * as Set from './Set.js' 19 | export * as string from './string.js' 20 | export * as struct from './struct.js' 21 | export * as These from './These.js' 22 | export * as Tree from './Tree.js' 23 | export * as Tuple from './Tuple.js' 24 | export * as Typeclass from './Typeclass/index.js' 25 | export * as Unary from './Unary.js' 26 | export * from './function.js' 27 | export * from './HKT.js' 28 | export * from './logical.js' 29 | -------------------------------------------------------------------------------- /src/Law/AssociativeBoth.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Kind } from '../HKT.js' 2 | import { AssociativeBoth } from '../Typeclass/AssociativeBoth.js' 3 | import { Covariant } from '../Typeclass/Covariant.js' 4 | import { DeepEquals, Eq } from '../Typeclass/Eq.js' 5 | import { pipe } from '../function.js' 6 | 7 | import * as Arbitrary from './Arbitrary.js' 8 | 9 | export function testCovariantAssociativeBoth( 10 | ABC: AssociativeBoth & Covariant, 11 | fab: Kind B>, 12 | fbc: Kind C>, 13 | E: Eq> = DeepEquals, 14 | ) { 15 | return (A: Arbitrary.Arbitrary>) => 16 | pipe( 17 | A, 18 | Arbitrary.map((k: Kind) => { 19 | return [ 20 | pipe( 21 | k, 22 | ABC.both(fab), 23 | ABC.both(fbc), 24 | ABC.map(([[a, ab], bc]) => bc(ab(a))), 25 | ), 26 | pipe( 27 | k, 28 | ABC.both(fbc), 29 | ABC.both(fab), 30 | ABC.map(([[a, bc], ab]) => bc(ab(a))), 31 | ), 32 | ] as const 33 | }), 34 | Arbitrary.toProperty(([a, b]) => E.equals(a, b)), 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /tools/overloads/definitions/PartitionMapWithIndex.partitionMap.ts: -------------------------------------------------------------------------------- 1 | import { Static, Tuple } from '../AST.js' 2 | 3 | import { PartitionMapWithIndex } from './PartitionMapWithIndex.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | cTypeParam, 8 | curriedPlaceholder_, 9 | fnLabeled_, 10 | fn_, 11 | hkt, 12 | kind_, 13 | placeholder, 14 | } from './common.js' 15 | 16 | const kTypeParam = new Static('K') 17 | 18 | export const partitionMap = fn_( 19 | 'partitionMap', 20 | [hkt, kTypeParam, curriedPlaceholder_(hkt)], 21 | [ 22 | PartitionMapWithIndex.toTypeClass(hkt) 23 | .setParams([kTypeParam, curriedPlaceholder_(hkt)]) 24 | .labeled('PMI'), 25 | ], 26 | fn_( 27 | '', 28 | [aTypeParam, bTypeParam, cTypeParam], 29 | [ 30 | fnLabeled_( 31 | 'f', 32 | [], 33 | [aTypeParam.labeled('a')], 34 | new Static(`Either<${bTypeParam.type}, ${cTypeParam.type}>`), 35 | ), 36 | ], 37 | fn_( 38 | '', 39 | [placeholder], 40 | [kind_([aTypeParam]).labeled('kind')], 41 | new Tuple([kind_([bTypeParam]), kind_([cTypeParam])]), 42 | ), 43 | ), 44 | ) 45 | 46 | export const node = partitionMap 47 | -------------------------------------------------------------------------------- /src/Typeclass/Associative.test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from 'node:assert' 2 | 3 | import { describe, it } from 'vitest' 4 | 5 | import * as A from './Associative.js' 6 | 7 | describe(import.meta.url, () => { 8 | describe('constant', () => { 9 | it('creates an Associative instance which always returns a paraticular value', () => { 10 | deepStrictEqual(A.constant(3).concat(1, 2), 3) 11 | }) 12 | }) 13 | 14 | describe('struct', () => { 15 | it('creates an Associative for a structure', () => { 16 | const { concat } = A.struct({ 17 | a: A.First, 18 | b: A.Second, 19 | }) 20 | 21 | deepStrictEqual(concat({ a: 1, b: 2 }, { a: 3, b: 4 }), { a: 1, b: 4 }) 22 | }) 23 | }) 24 | 25 | describe('tuple', () => { 26 | it('creates an Associative for a tuple', () => { 27 | const { concat } = A.tuple(A.First, A.Second) 28 | 29 | deepStrictEqual(concat([1, 2], [3, 4]), [1, 4]) 30 | }) 31 | }) 32 | 33 | describe('intercalate', () => { 34 | it('creates an Associative instance ', () => { 35 | const { concat } = A.intercalate(3)({ concat: (x, y) => x + y }) 36 | 37 | deepStrictEqual(concat(1, 2), 6) 38 | }) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /tools/overloads/definitions/AssociativeFlatten.bind.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Static } from '../AST.js' 2 | 3 | import { AssociativeFlatten } from './AssociativeFlatten.js' 4 | import { Covariant } from './Covariant.js' 5 | import { bTypeParam, derived_, fnLabeled_, fn_, kind_, placeholder } from './common.js' 6 | 7 | const aTypeParam = new Static(`A extends Readonly>`) 8 | 9 | export const nameTypeParam = new Static('N extends string') 10 | 11 | export const node = derived_( 12 | 'bind', 13 | [AssociativeFlatten, Covariant], 14 | fn_( 15 | '', 16 | [nameTypeParam, aTypeParam, placeholder, bTypeParam], 17 | [ 18 | new Dynamic( 19 | [nameTypeParam, aTypeParam], 20 | ([name, a]) => `Exclude<${name}, keyof ${a}>`, 21 | ).labeled('name'), 22 | fnLabeled_('f', [], [aTypeParam.labeled('a')], kind_([bTypeParam])), 23 | ], 24 | fn_( 25 | '', 26 | [], 27 | [kind_([aTypeParam]).labeled('kind')], 28 | kind_([ 29 | new Dynamic( 30 | [aTypeParam, bTypeParam], 31 | ([a, b]) => `{ readonly [K in keyof ${a} | N]: K extends keyof ${a} ? ${a}[K] : ${b} }`, 32 | ), 33 | ]), 34 | ), 35 | ), 36 | ) 37 | -------------------------------------------------------------------------------- /src/common.ts: -------------------------------------------------------------------------------- 1 | import type { HKT2, Params } from './HKT.js' 2 | import type { Covariant2 } from './Typeclass/Covariant.js' 3 | import type { Identity } from './Typeclass/Identity.js' 4 | import type { IdentityBoth2 } from './Typeclass/IdentityBoth.js' 5 | 6 | export type Include = A extends B ? A : never 7 | 8 | export type State = (s: S) => readonly [S, A] 9 | 10 | export interface StateHKT extends HKT2 { 11 | readonly type: State 12 | } 13 | 14 | export const makeStateIdentity = (ID: Identity): Identity> => ({ 15 | id: (s) => [s, ID.id], 16 | concat: (first, second) => (s) => { 17 | const [s2, a1] = first(s) 18 | const [s3, a2] = second(s2) 19 | 20 | return [s3, ID.concat(a1, a2)] 21 | }, 22 | }) 23 | 24 | export const IdentityBothState: IdentityBoth2 = { 25 | top: (s) => [s, []], 26 | both: (second) => (first) => (s) => { 27 | const [s2, b1] = first(s) 28 | const [s3, b2] = second(s2) 29 | 30 | return [s3, [b1, b2]] as const 31 | }, 32 | } 33 | 34 | export const CovariantState: Covariant2 = { 35 | map: (f) => (state) => (s) => { 36 | const [s2, a] = state(s) 37 | 38 | return [s2, f(a)] 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.compacted.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Kind, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { ForEach } from './ForEach.js' 5 | import { IdentityBoth } from './IdentityBoth.js' 6 | import { Compact } from './Compact.js' 7 | import { 8 | aTypeParam, 9 | bTypeParam, 10 | derived_, 11 | fnLabeled_, 12 | fn_, 13 | kind_, 14 | placeholder, 15 | } from './common.js' 16 | 17 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 18 | 19 | export const compacted = derived_( 20 | 'compacted', 21 | [ForEach, Compact], 22 | derived_( 23 | '', 24 | [IdentityBoth, Covariant], 25 | fn_( 26 | '', 27 | [aTypeParam,hkt2.toPlaceholder(), bTypeParam], 28 | [ 29 | fnLabeled_( 30 | 'f', 31 | [], 32 | [aTypeParam.labeled('a')], 33 | new Kind(hkt2, [hkt2.toPlaceholder(), new Static(`Maybe`)]), 34 | ), 35 | ], 36 | fn_( 37 | '', 38 | [placeholder], 39 | [kind_([aTypeParam]).labeled()], 40 | new Kind(hkt2, [ 41 | hkt2.toPlaceholder(), 42 | kind_([bTypeParam]), 43 | ]), 44 | ), 45 | ), 46 | hkt2, 47 | ), 48 | ) 49 | 50 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testFilterMap.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, ObjectNode, Static } from '../../AST.js' 2 | import { FilterMap } from '../FilterMap.js' 3 | import { 4 | aTypeParam, 5 | boolean_, 6 | curriedPlaceholder_, 7 | fnLabeled_, 8 | fn_, 9 | hkt, 10 | kind_, 11 | placeholder, 12 | } from '../common.js' 13 | 14 | export const testFilterMap = fn_( 15 | 'testFilterMap', 16 | [hkt, curriedPlaceholder_(hkt), aTypeParam, placeholder], 17 | [ 18 | FilterMap.toTypeClass(hkt).labeled('FM'), 19 | fnLabeled_('f', [], [aTypeParam.labeled('a')], boolean_), 20 | fnLabeled_('g', [], [aTypeParam.labeled('b')], boolean_), 21 | new Dynamic([kind_([aTypeParam])], ([kind]) => `Eq<${kind}>`).labeled(`Eq?`), 22 | ], 23 | fn_( 24 | '', 25 | [], 26 | [new Dynamic([kind_([aTypeParam])], ([k]) => `Arbitrary.Arbitrary<${k}>`).labeled(`Arb`)], 27 | fn_( 28 | '', 29 | [], 30 | [new Static(`typeof import('fast-check')`).labeled('fc')], 31 | new ObjectNode([ 32 | new Static(`() => void`).labeled('identity'), 33 | new Static(`() => void`).labeled('distributivity'), 34 | new Static(`() => void`).labeled('annihilation'), 35 | ]), 36 | ), 37 | ), 38 | ) 39 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Kind } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { IdentityBoth } from './IdentityBoth.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | derived_, 9 | fnLabeled_, 10 | fn_, 11 | interface_, 12 | kind_, 13 | placeholder, 14 | } from './common.js' 15 | 16 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 17 | const hkt2Placeholder = hkt2.toPlaceholder() 18 | 19 | export const ForEach = interface_( 20 | 'ForEach', 21 | [ 22 | derived_( 23 | '', 24 | [IdentityBoth, Covariant], 25 | fn_( 26 | '', 27 | [aTypeParam, hkt2Placeholder, bTypeParam], 28 | [ 29 | fnLabeled_( 30 | 'f', 31 | [], 32 | [aTypeParam.labeled('a')], 33 | new Kind(hkt2, [hkt2Placeholder, bTypeParam]), 34 | ), 35 | ], 36 | fn_( 37 | '', 38 | [placeholder], 39 | [kind_([aTypeParam]).labeled('kind')], 40 | new Kind(hkt2, [hkt2Placeholder, kind_([bTypeParam])]), 41 | ), 42 | ), 43 | hkt2, 44 | ).labeled('forEach'), 45 | ], 46 | [Covariant], 47 | ) 48 | 49 | export const node = ForEach 50 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEachWithIndex.forEach.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Kind, Static } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { ForEachWithIndex } from './ForEachWithIndex.js' 5 | import { IdentityBoth } from './IdentityBoth.js' 6 | import { 7 | aTypeParam, 8 | bTypeParam, 9 | derived_, 10 | fnLabeled_, 11 | fn_, 12 | hkt, 13 | kind_, 14 | placeholder, 15 | } from './common.js' 16 | 17 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 18 | const hkt2Placeholder = hkt2.toPlaceholder() 19 | 20 | export const forEach = derived_( 21 | 'forEach', 22 | [ForEachWithIndex], 23 | derived_( 24 | '', 25 | [IdentityBoth, Covariant], 26 | fn_( 27 | '', 28 | [aTypeParam, hkt2Placeholder, bTypeParam], 29 | [ 30 | fnLabeled_( 31 | 'f', 32 | [], 33 | [aTypeParam.labeled('a')], 34 | new Kind(hkt2, [hkt2Placeholder, bTypeParam]), 35 | ), 36 | ], 37 | fn_( 38 | '', 39 | [placeholder], 40 | [kind_([aTypeParam]).labeled('kind')], 41 | new Kind(hkt2, [hkt2Placeholder, kind_([bTypeParam])]), 42 | ), 43 | ), 44 | hkt2, 45 | ), 46 | hkt, 47 | [new Static('K')], 48 | ) 49 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Trivariant.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, Static } from '../AST.js' 2 | 3 | import { fnLabeled_, fn_, interface_, kind_, placeholder } from './common.js' 4 | 5 | const r1TypeParam = new Static(`R1`) 6 | const r2TypeParam = new Static(`R2`) 7 | 8 | const e1TypeParam = new Static(`E1`) 9 | const e2TypeParam = new Static(`E2`) 10 | 11 | const a1TypeParam = new Static(`A1`) 12 | const a2TypeParam = new Static(`A2`) 13 | 14 | export const Trivariant = interface_('Trivariant', [ 15 | fnLabeled_( 16 | 'trimap', 17 | [r1TypeParam, r2TypeParam, e1TypeParam, e2TypeParam, a1TypeParam, a2TypeParam], 18 | [ 19 | new Dynamic([r1TypeParam, r2TypeParam] as const, ([a, b]) => `Unary<${a}, ${b}>`).labeled( 20 | 'f', 21 | ), 22 | new Dynamic([e1TypeParam, e2TypeParam] as const, ([c, d]) => `Unary<${c}, ${d}>`).labeled( 23 | 'g', 24 | ), 25 | new Dynamic([a1TypeParam, a2TypeParam] as const, ([e, f]) => `Unary<${e}, ${f}>`).labeled( 26 | 'h', 27 | ), 28 | ], 29 | fn_( 30 | '', 31 | [placeholder], 32 | [kind_([r2TypeParam, e1TypeParam, a1TypeParam]).labeled('kind')], 33 | kind_([r1TypeParam, e2TypeParam, a2TypeParam]), 34 | ), 35 | ), 36 | ]) 37 | -------------------------------------------------------------------------------- /src/Law/Eq.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from '../Typeclass/Eq.js' 2 | import { pipe } from '../function.js' 3 | 4 | import * as Arbitrary from './Arbitrary.js' 5 | 6 | export function testEq(E: Eq) { 7 | return (Arb: Arbitrary.Arbitrary) => (fc: typeof import('fast-check')) => ({ 8 | reflexivity: () => testEqReflexivity(E)(Arb).property(fc), 9 | symmetry: () => testEqSymmetry(E)(Arb).property(fc), 10 | transitivity: () => testEqTransitivity(E)(Arb).property(fc), 11 | }) 12 | } 13 | 14 | export function testEqReflexivity(E: Eq) { 15 | return (Arb: Arbitrary.Arbitrary) => 16 | pipe( 17 | Arb, 18 | Arbitrary.toProperty((a) => E.equals(a, a)), 19 | ) 20 | } 21 | 22 | export function testEqSymmetry(E: Eq) { 23 | return (Arb: Arbitrary.Arbitrary) => 24 | pipe( 25 | Arbitrary.tuple(Arb, Arb), 26 | Arbitrary.toProperty(([a, b]) => E.equals(a, b) === E.equals(b, a)), 27 | ) 28 | } 29 | 30 | export function testEqTransitivity(E: Eq) { 31 | return (Arb: Arbitrary.Arbitrary) => 32 | pipe( 33 | Arbitrary.tuple(Arb, Arb, Arb), 34 | Arbitrary.toProperty( 35 | ([a, b, c]) => (E.equals(a, b) && E.equals(b, c)) === (E.equals(a, b) && E.equals(a, c)), 36 | ), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/Typeclass/Concat.test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from 'node:assert' 2 | 3 | import { describe, it } from 'vitest' 4 | 5 | import * as C from './Concat.js' 6 | 7 | describe(import.meta.url, () => { 8 | describe('First', () => { 9 | it('is a Concat instance that always returns the first value', () => { 10 | deepStrictEqual(C.First.concat(1, 2), 1) 11 | }) 12 | }) 13 | describe('Second', () => { 14 | it('is a Concat instance that always returns the second value', () => { 15 | deepStrictEqual(C.Second.concat(1, 2), 2) 16 | }) 17 | }) 18 | 19 | describe('concatAll', () => { 20 | it('uses a Concat instance to concatenate multiple values', () => { 21 | deepStrictEqual(C.concatAll({ concat: (x: number, y) => x + y })(0)([1, 2, 3, 4, 5]), 15) 22 | }) 23 | }) 24 | 25 | describe('reverse', () => { 26 | it('uses a Concat instance to concatenate multiple values', () => { 27 | deepStrictEqual(C.reverse(C.First).concat(1, 2), 2) 28 | deepStrictEqual(C.reverse(C.Second).concat(1, 2), 1) 29 | }) 30 | }) 31 | 32 | describe('fromLazy', () => { 33 | it('uses a Concat instance that always returns the lazily applied function', () => { 34 | deepStrictEqual(C.fromLazy(() => 3).concat(1, 2), 3) 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEach.separated.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Kind, Static, Tuple } from '../AST.js' 2 | 3 | import { Covariant } from './Covariant.js' 4 | import { ForEach } from './ForEach.js' 5 | import { IdentityBoth } from './IdentityBoth.js' 6 | import { Separate } from './Separate.js' 7 | import { 8 | aTypeParam, 9 | bTypeParam, 10 | cTypeParam, 11 | derived_, 12 | fnLabeled_, 13 | fn_, 14 | kind_, 15 | placeholder, 16 | } from './common.js' 17 | 18 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 19 | 20 | export const separated = derived_( 21 | 'separated', 22 | [ForEach, Separate], 23 | derived_( 24 | '', 25 | [IdentityBoth, Covariant], 26 | fn_( 27 | '', 28 | [aTypeParam,hkt2.toPlaceholder(), bTypeParam, cTypeParam], 29 | [ 30 | fnLabeled_( 31 | 'f', 32 | [], 33 | [aTypeParam.labeled('a')], 34 | new Kind(hkt2, [hkt2.toPlaceholder(), new Static(`Either`)]), 35 | ), 36 | ], 37 | fn_( 38 | '', 39 | [placeholder], 40 | [kind_([aTypeParam]).labeled()], 41 | new Kind(hkt2, [ 42 | hkt2.toPlaceholder(), 43 | new Tuple([kind_([bTypeParam]), kind_([cTypeParam])]), 44 | ]), 45 | ), 46 | ), 47 | hkt2, 48 | ), 49 | ) 50 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariantIdentityFlatten.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, IntersectionNode, ObjectNode, Static } from '../../AST.js' 2 | import { Covariant } from '../Covariant.js' 3 | import { IdentityFlatten } from '../IdentityFlatten.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | cTypeParam, 8 | curriedPlaceholder_, 9 | fnLabeled_, 10 | fn_, 11 | hkt, 12 | kind_, 13 | placeholder, 14 | } from '../common.js' 15 | 16 | export const testCovariantIdentityFlatten = fn_( 17 | 'testCovariantIdentityFlatten', 18 | [hkt, curriedPlaceholder_(hkt), placeholder, aTypeParam, bTypeParam, cTypeParam], 19 | [ 20 | new IntersectionNode(Covariant.toTypeClass(hkt), IdentityFlatten.toTypeClass(hkt)).labeled( 21 | 'AFC', 22 | ), 23 | fnLabeled_('f', [], [aTypeParam.labeled('a')], kind_([bTypeParam])), 24 | new Dynamic([kind_([cTypeParam])], ([k]) => `Eq<${k}>`).labeled('E'), 25 | ], 26 | fn_( 27 | '', 28 | [], 29 | [new Static(`Arbitrary.Arbitrary`).labeled('A')], 30 | fn_( 31 | '', 32 | [], 33 | [new Static(`typeof import('fast-check')`).labeled('fc')], 34 | new ObjectNode([ 35 | new Static(`() => void`).labeled('leftIdentity'), 36 | new Static(`() => void`).labeled('rightIdentity'), 37 | ]), 38 | ), 39 | ), 40 | ) 41 | -------------------------------------------------------------------------------- /src/Typeclass/ForEach/ForEach.test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from 'node:assert' 2 | 3 | import { describe, it } from 'vitest' 4 | 5 | import * as Array from '../../Array.js' 6 | import * as Data from '../../Data.js' 7 | import * as Either from '../../Either.js' 8 | import { pipe } from '../../function.js' 9 | import * as N from '../../number.js' 10 | 11 | import * as ForEach from './index.js' 12 | 13 | describe(import.meta.url, () => { 14 | describe(ForEach.foldMap.name, () => { 15 | it('derives foldMap from ForEach instance', () => { 16 | const foldMapArray = ForEach.foldMap(Array.ForEach)(N.IdentitySum) 17 | const foldMapData = ForEach.foldMap(Data.ForEach)(N.IdentitySum) 18 | const foldMapEither = ForEach.foldMap(Either.ForEach)(N.IdentitySum) 19 | 20 | deepStrictEqual( 21 | pipe( 22 | [1, 2, 3], 23 | foldMapArray((x: number) => x + 1), 24 | ), 25 | 9, 26 | ) 27 | 28 | deepStrictEqual( 29 | pipe( 30 | Data.Replete(1), 31 | foldMapData((x: number) => x + 1), 32 | ), 33 | 2, 34 | ) 35 | 36 | deepStrictEqual( 37 | pipe( 38 | Either.Right(1), 39 | foldMapEither((a: number) => a + 1), 40 | ), 41 | 2, 42 | ) 43 | }) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/struct.ts: -------------------------------------------------------------------------------- 1 | import { Cast } from 'ts-toolbelt/out/Any/Cast.js' 2 | 3 | import { Associative } from './Typeclass/Associative.js' 4 | import { Identity } from './Typeclass/Identity.js' 5 | 6 | export type StructEntries = ReadonlyArray 7 | 8 | export const fromEntries = ( 9 | ...entries: Entries 10 | ): FromEntries => Object.fromEntries(entries) as FromEntries 11 | 12 | export type FromEntries = Entries extends readonly [ 13 | readonly [infer K, infer V], 14 | ...infer Rest, 15 | ] 16 | ? FromEntries, Intersect, V>>> 17 | : { readonly [K in keyof R]: R[K] } 18 | 19 | export type KV = { 20 | readonly [_ in K]: V 21 | } 22 | 23 | export type Intersect = { 24 | readonly [K in keyof A | keyof B]: K extends keyof B ? B[K] : A[Cast] 25 | } 26 | 27 | export const makeAssignAssociative = (): Associative => ({ 28 | concat: (first, second) => ({ ...first, ...second }), 29 | }) 30 | 31 | export const empty: Readonly> = Object.create(null) 32 | 33 | export const makeAssignIdentity = (): Identity => ({ 34 | ...makeAssignAssociative(), 35 | id: empty, 36 | }) 37 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariantIdentityBoth.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, IntersectionNode, ObjectNode, Static } from '../../AST.js' 2 | import { Covariant } from '../Covariant.js' 3 | import { IdentityBoth } from '../IdentityBoth.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from '../common.js' 14 | 15 | export const testCovariantIdentityBoth = fn_( 16 | 'testCovariantIdentityBoth', 17 | [hkt, curriedPlaceholder_(hkt), placeholder, aTypeParam, bTypeParam], 18 | [ 19 | new IntersectionNode(Covariant.toTypeClass(hkt), IdentityBoth.toTypeClass(hkt)).labeled('IBC'), 20 | new Dynamic([kind_([aTypeParam])], ([k]) => `Eq<${k}>`).labeled('EA'), 21 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 22 | new Dynamic([kind_([bTypeParam])], ([k]) => `Eq<${k}>`).labeled('EB'), 23 | ], 24 | fn_( 25 | '', 26 | [], 27 | [new Static(`Arbitrary.Arbitrary`).labeled('A')], 28 | fn_( 29 | '', 30 | [], 31 | [new Static(`typeof import('fast-check')`).labeled('fc')], 32 | new ObjectNode([ 33 | new Static(`() => void`).labeled('identity'), 34 | new Static(`() => void`).labeled('homomorphism'), 35 | new Static(`() => void`).labeled('interchange'), 36 | ]), 37 | ), 38 | ), 39 | ) 40 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariantAssociativeEither.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, IntersectionNode, ObjectNode, Static } from '../../AST.js' 2 | import { AssociativeEither } from '../AssociativeEither.js' 3 | import { Covariant } from '../Covariant.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from '../common.js' 14 | 15 | export const testCovariantAssociativeEither = fn_( 16 | 'testCovariantAssociativeEither', 17 | [hkt, curriedPlaceholder_(hkt), placeholder, aTypeParam, bTypeParam], 18 | [ 19 | new IntersectionNode(Covariant.toTypeClass(hkt), AssociativeEither.toTypeClass(hkt)).labeled( 20 | 'IBC', 21 | ), 22 | new Dynamic([kind_([aTypeParam])], ([k]) => `Eq<${k}>`).labeled('EA'), 23 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 24 | new Dynamic([kind_([bTypeParam])], ([k]) => `Eq<${k}>`).labeled('EB'), 25 | ], 26 | fn_( 27 | '', 28 | [], 29 | [new Dynamic([kind_([aTypeParam])], ([k]) => `Arbitrary.Arbitrary<${k}>`).labeled('A')], 30 | fn_( 31 | '', 32 | [], 33 | [new Static(`typeof import('fast-check')`).labeled('fc')], 34 | new ObjectNode([ 35 | new Static(`() => void`).labeled('associativity'), 36 | new Static(`() => void`).labeled('distributivity'), 37 | ]), 38 | ), 39 | ), 40 | ) 41 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariantAssociativeFlatten.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, IntersectionNode, ObjectNode, Static } from '../../AST.js' 2 | import { AssociativeFlatten } from '../AssociativeFlatten.js' 3 | import { Covariant } from '../Covariant.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | cTypeParam, 8 | curriedPlaceholder_, 9 | fnLabeled_, 10 | fn_, 11 | hkt, 12 | kind_, 13 | placeholder, 14 | } from '../common.js' 15 | 16 | export const testCovariantAssociativeFlatten = fn_( 17 | 'testCovariantAssociativeFlatten', 18 | [hkt, curriedPlaceholder_(hkt), placeholder, aTypeParam, bTypeParam, cTypeParam], 19 | [ 20 | new IntersectionNode(Covariant.toTypeClass(hkt), AssociativeFlatten.toTypeClass(hkt)).labeled( 21 | 'AFC', 22 | ), 23 | fnLabeled_('f', [], [aTypeParam.labeled('a')], kind_([bTypeParam])), 24 | fnLabeled_('g', [], [bTypeParam.labeled('b')], kind_([cTypeParam])), 25 | new Dynamic([kind_([bTypeParam])], ([k]) => `Eq<${k}>`).labeled('E'), 26 | ], 27 | fn_( 28 | '', 29 | [], 30 | [new Dynamic([kind_([aTypeParam])], ([k]) => `Arbitrary.Arbitrary<${k}>`).labeled('A')], 31 | fn_( 32 | '', 33 | [], 34 | [new Static(`typeof import('fast-check')`).labeled('fc')], 35 | new ObjectNode([ 36 | new Static(`() => void`).labeled('associativity'), 37 | new Static(`() => void`).labeled('derivedAssociativeBoth'), 38 | ]), 39 | ), 40 | ), 41 | ) 42 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testCovariantIdentityEither.ts: -------------------------------------------------------------------------------- 1 | import { Dynamic, IntersectionNode, ObjectNode, Static } from '../../AST.js' 2 | import { Covariant } from '../Covariant.js' 3 | import { IdentityEither } from '../IdentityEither.js' 4 | import { 5 | aTypeParam, 6 | bTypeParam, 7 | curriedPlaceholder_, 8 | fnLabeled_, 9 | fn_, 10 | hkt, 11 | kind_, 12 | placeholder, 13 | } from '../common.js' 14 | 15 | export const testCovariantIdentityEither = fn_( 16 | 'testCovariantIdentityEither', 17 | [hkt, curriedPlaceholder_(hkt), placeholder, aTypeParam, bTypeParam], 18 | [ 19 | new IntersectionNode(Covariant.toTypeClass(hkt), IdentityEither.toTypeClass(hkt)).labeled( 20 | 'IBC', 21 | ), 22 | new Dynamic([kind_([aTypeParam])], ([k]) => `Eq<${k}>`).labeled('EA'), 23 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 24 | new Dynamic([kind_([bTypeParam])], ([k]) => `Eq<${k}>`).labeled('EB'), 25 | ], 26 | fn_( 27 | '', 28 | [], 29 | [new Dynamic([kind_([aTypeParam])], ([k]) => `Arbitrary.Arbitrary<${k}>`).labeled('A')], 30 | fn_( 31 | '', 32 | [], 33 | [new Static(`typeof import('fast-check')`).labeled('fc')], 34 | new ObjectNode([ 35 | new Static(`() => void`).labeled('distributivity'), 36 | new Static(`() => void`).labeled('leftIdentity'), 37 | new Static(`() => void`).labeled('rightIdentity'), 38 | new Static(`() => void`).labeled('annihilation'), 39 | ]), 40 | ), 41 | ), 42 | ) 43 | -------------------------------------------------------------------------------- /src/Typeclass/Inverse.test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from 'node:assert' 2 | 3 | import { describe, it } from 'vitest' 4 | 5 | import { InverseAll, InverseAny } from '../boolean.js' 6 | import { Inverse } from '../number.js' 7 | 8 | import { multiply } from './Inverse.js' 9 | 10 | describe(import.meta.url, () => { 11 | describe(multiply.name, () => { 12 | it('allows multiplying a value', () => { 13 | deepStrictEqual(multiply(Inverse)(2, 2), 4) 14 | deepStrictEqual(multiply(Inverse)(2, -2), -4) 15 | 16 | deepStrictEqual(multiply(InverseAll)(true, 2), true, 'InverseAll :: true * 2 = true') 17 | deepStrictEqual(multiply(InverseAll)(true, -1), false, 'InverseAll :: true * -1 = false') 18 | deepStrictEqual(multiply(InverseAll)(false, 2), false, 'InverseAll :: false * 2 = false') 19 | deepStrictEqual(multiply(InverseAll)(false, 1), false, 'InverseAll :: false * 1 = false') 20 | deepStrictEqual(multiply(InverseAll)(false, -1), true, 'InverseAll :: false * -1 = true') 21 | 22 | deepStrictEqual(multiply(InverseAny)(true, 2), true, 'InverseAny :: true * 2 = true') 23 | deepStrictEqual(multiply(InverseAny)(true, -1), false, 'InverseAny :: true * -1 = false') 24 | deepStrictEqual(multiply(InverseAny)(false, 2), false, 'InverseAny :: false * 2 = false') 25 | deepStrictEqual(multiply(InverseAny)(false, 1), false, 'InverseAny :: false * 1 = false') 26 | deepStrictEqual(multiply(InverseAny)(false, -1), true, 'InverseAny :: false * -1 = true') 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /tools/overloads/definitions/Law/testContravariant.ts: -------------------------------------------------------------------------------- 1 | import { ObjectNode, Static } from '../../AST.js' 2 | import { Contravariant } from '../Contravariant.js' 3 | import { 4 | aTypeParam, 5 | bTypeParam, 6 | cTypeParam, 7 | curriedPlaceholder_, 8 | dTypeParam, 9 | fnLabeled_, 10 | fn_, 11 | hkt, 12 | kind_, 13 | placeholder, 14 | } from '../common.js' 15 | 16 | export const testContravariant = fn_( 17 | 'testContravariant', 18 | [hkt, curriedPlaceholder_(hkt), aTypeParam, bTypeParam, cTypeParam, placeholder, dTypeParam], 19 | [ 20 | Contravariant.toTypeClass(hkt).labeled('C'), 21 | fnLabeled_('f', [], [aTypeParam.labeled('a')], bTypeParam), 22 | fnLabeled_('g', [], [bTypeParam.labeled('b')], cTypeParam), 23 | kind_([aTypeParam]).labeled('kindA'), 24 | kind_([cTypeParam]).labeled('kindC'), 25 | fnLabeled_( 26 | 'run', 27 | [aTypeParam], 28 | [ 29 | kind_([aTypeParam]).labeled(), 30 | aTypeParam.labeled('a'), 31 | aTypeParam.labeled('b'), 32 | aTypeParam.labeled('c'), 33 | ], 34 | dTypeParam, 35 | ), 36 | new Static(`Eq`).labeled(`Eq?`), 37 | ], 38 | fn_( 39 | '', 40 | [], 41 | [new Static(`Arbitrary.Arbitrary`).labeled('AA')], 42 | fn_( 43 | '', 44 | [], 45 | [new Static(`typeof import('fast-check')`).labeled('fc')], 46 | new ObjectNode([ 47 | new Static(`() => void`).labeled('identity'), 48 | new Static(`() => void`).labeled('associativity'), 49 | ]), 50 | ), 51 | ), 52 | ) 53 | -------------------------------------------------------------------------------- /src/Typeclass/index.ts: -------------------------------------------------------------------------------- 1 | export * as Associative from './Associative.js' 2 | export * as AssociativeBoth from './AssociativeBoth.js' 3 | export * as AssociativeCompose from './AssociativeCompose.js' 4 | export * as AssociativeEither from './AssociativeEither.js' 5 | export * as AssociativeFlatten from './AssociativeFlatten.js' 6 | export * as Bicovariant from './Bicovariant.js' 7 | export * as Bottom from './Bottom.js' 8 | export * as Bounded from './Bounded.js' 9 | export * as Commutative from './Commutative.js' 10 | export * as CommutativeBoth from './CommutativeBoth.js' 11 | export * as CommutativeEither from './CommutativeEither.js' 12 | export * as Compact from './Compact.js' 13 | export * as Compactable from './Compactable.js' 14 | export * as Concat from './Concat.js' 15 | export * as Contravariant from './Contravariant.js' 16 | export * as Covariant from './Covariant.js' 17 | export * as CovariantWithIndex from './CovariantWithIndex.js' 18 | export * as Debug from './Debug.js' 19 | export * as Divariant from './Divariant.js' 20 | export * as ForEach from './ForEach/index.js' 21 | export * as IdentityBoth from './IdentityBoth.js' 22 | export * as IdentityEither from './IdentityEither.js' 23 | export * as IdentityFlatten from './IdentityFlatten.js' 24 | export * as Invariant from './Invariant.js' 25 | export * as Inverse from './Inverse.js' 26 | export * as NaturalTransformation from './NaturalTransformation.js' 27 | export * as Ord from './Ord.js' 28 | export * as Separate from './Separate.js' 29 | export * as Top from './Top.js' 30 | export * as Trivariant from './Trivariant.js' 31 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2.1 6 | 7 | executors: 8 | lts: 9 | docker: 10 | - image: cimg/node:lts 11 | 12 | working_directory: ~/repo 13 | resource_class: large 14 | 15 | latest: 16 | docker: 17 | - image: cimg/node:current 18 | 19 | working_directory: ~/repo 20 | resource_class: large 21 | 22 | jobs: 23 | test-lts: 24 | executor: lts 25 | steps: 26 | - checkout 27 | 28 | - restore_cache: 29 | keys: 30 | - v1-dependencies-{{ checksum "pnpm-lock.yaml" }} 31 | 32 | - run: corepack enable 33 | - run: pnpm add -g pnpm 34 | - run: pnpm install 35 | 36 | - save_cache: 37 | paths: 38 | - node_modules 39 | key: v1-dependencies-{{ checksum "pnpm-lock.yaml" }} 40 | 41 | - run: npm run test 42 | 43 | test-latest: 44 | executor: latest 45 | steps: 46 | - checkout 47 | 48 | - restore_cache: 49 | keys: 50 | - v1-dependencies-{{ checksum "pnpm-lock.yaml" }} 51 | 52 | - run: corepack enable 53 | - run: pnpm add -g pnpm 54 | - run: pnpm install 55 | 56 | - save_cache: 57 | paths: 58 | - node_modules 59 | key: v1-dependencies-{{ checksum "pnpm-lock.yaml" }} 60 | 61 | - run: npm run test 62 | 63 | workflows: 64 | version: 2 65 | test-node: 66 | jobs: 67 | - test-lts 68 | - test-latest 69 | -------------------------------------------------------------------------------- /src/Law/Ord.ts: -------------------------------------------------------------------------------- 1 | import { Ord } from '../Typeclass/Ord.js' 2 | import { pipe } from '../function.js' 3 | 4 | import * as Arbitrary from './Arbitrary.js' 5 | 6 | export function testOrd(O: Ord) { 7 | return (Arb: Arbitrary.Arbitrary) => (fc: typeof import('fast-check')) => ({ 8 | totality: () => testOrdTotality(O)(Arb).property(fc), 9 | reflexivity: () => testOrdReflexivity(O)(Arb).property(fc), 10 | antisymmetry: () => testOrdAntisymmetry(O)(Arb).property(fc), 11 | transitivity: () => testOrdTransitivity(O)(Arb).property(fc), 12 | }) 13 | } 14 | 15 | export function testOrdTotality(O: Ord) { 16 | return (Arb: Arbitrary.Arbitrary) => 17 | pipe( 18 | Arbitrary.tuple(Arb, Arb), 19 | Arbitrary.toProperty(([a, b]) => O.compare(a, b) <= 0 || O.compare(b, a) <= 0), 20 | ) 21 | } 22 | 23 | export function testOrdReflexivity(O: Ord) { 24 | return (Arb: Arbitrary.Arbitrary) => 25 | pipe( 26 | Arb, 27 | Arbitrary.toProperty((a) => O.compare(a, a) <= 0), 28 | ) 29 | } 30 | 31 | export function testOrdAntisymmetry(O: Ord) { 32 | return (Arb: Arbitrary.Arbitrary) => 33 | pipe( 34 | Arbitrary.tuple(Arb, Arb), 35 | Arbitrary.toProperty( 36 | ([a, b]) => (O.compare(a, b) <= 0 && O.compare(b, a) <= 0) === O.equals(a, b), 37 | ), 38 | ) 39 | } 40 | 41 | export function testOrdTransitivity(O: Ord) { 42 | return (Arb: Arbitrary.Arbitrary) => 43 | pipe( 44 | Arbitrary.tuple(Arb, Arb, Arb), 45 | Arbitrary.toProperty( 46 | ([a, b, c]) => !(O.compare(a, b) <= 0 && O.compare(b, c) <= 0) || O.compare(a, c) <= 0, 47 | ), 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /tools/common.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, readdirSync } from 'node:fs' 2 | import { dirname, isAbsolute, join } from 'node:path' 3 | 4 | import fastGlob from 'fast-glob' 5 | 6 | export const ROOT_DIR = dirname(dirname(new URL(import.meta.url).pathname)) 7 | export const SOURCE_DIR = join(ROOT_DIR, 'src') 8 | 9 | export const ROOT_FILES = ['exports'] 10 | 11 | export function compiledFiles(name: string): readonly string[] { 12 | const fileName = parseName(name) 13 | 14 | return [`/${fileName}.d.ts`, `/${fileName}.d.ts.map`, `/${fileName}.js`, `/${fileName}.js.map`] 15 | } 16 | 17 | export function parseName(name: string): string { 18 | return name.replace(/\.tsx?/, '') 19 | } 20 | 21 | export const MODULES: ReadonlyArray = readdirSync(SOURCE_DIR) 22 | .sort() 23 | .filter( 24 | (x) => x !== 'internal.ts' && x !== 'index.ts' && !x.endsWith('.test.ts') && !x.startsWith('.'), 25 | ) 26 | 27 | export function getRelativeFile(directory: string, fileName: string) { 28 | return join(directory, fileName) 29 | } 30 | 31 | export function readRelativeFile(directory: string, fileName: string) { 32 | return readFileSync(getRelativeFile(directory, fileName)).toString() 33 | } 34 | 35 | export function findFilePaths(directory: string, fileGlobs: readonly string[]): string[] { 36 | // eslint-disable-next-line import/no-named-as-default-member 37 | return fastGlob 38 | .sync(Array.from(fileGlobs), { cwd: directory, onlyFiles: true }) 39 | .map((x) => makeAbsolute(directory, x.toString())) 40 | } 41 | 42 | export function makeAbsolute(basePath: string, absoluteOrRelative: string): string { 43 | return isAbsolute(absoluteOrRelative) ? absoluteOrRelative : join(basePath, absoluteOrRelative) 44 | } 45 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/eslint-recommended', 7 | 'plugin:@typescript-eslint/recommended', 8 | 'prettier', 9 | 'plugin:prettier/recommended', 10 | 'plugin:import/errors', 11 | 'plugin:import/warnings', 12 | 'plugin:import/typescript', 13 | ], 14 | ignorePatterns: ['docs/**'], 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module', 18 | }, 19 | env: { 20 | node: true, 21 | }, 22 | rules: { 23 | '@typescript-eslint/no-empty-interface': 'off', 24 | '@typescript-eslint/no-explicit-any': 'off', 25 | '@typescript-eslint/no-use-before-define': 'off', 26 | 'import/order': [ 27 | 'error', 28 | { 29 | 'newlines-between': 'always', 30 | alphabetize: { 31 | order: 'asc', 32 | caseInsensitive: false, 33 | }, 34 | }, 35 | ], 36 | // Enable sort-imports to sort named imports within a single import 37 | // statement, but *disable* its declaration sort, and let 38 | // import/order's alphabetize feature handle sorting declarations 39 | // based on import path. 40 | 'sort-imports': [ 41 | 'error', 42 | { 43 | ignoreDeclarationSort: true, 44 | }, 45 | ], 46 | 'import/no-unresolved': 'off', // Allow TS to do this checking, 47 | 'import/no-cycle': ['error', { maxDepth: Infinity }], 48 | }, 49 | settings: { 50 | 'import/parsers': { 51 | '@typescript-eslint/parser': ['.ts', '.tsx'], 52 | }, 53 | 'import/resolver': { 54 | typescript: { 55 | project: '.', 56 | }, 57 | }, 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /src/Predicate.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe, it } from 'vitest' 3 | 4 | import * as L from './Law/index.js' 5 | import { testAllDataLaws } from './Law/internal-test-all-laws.js' 6 | import * as P from './Predicate.js' 7 | import { fromEquals } from './Typeclass/Eq.js' 8 | import { pipe } from './function.js' 9 | 10 | describe(import.meta.url, () => { 11 | testAllDataLaws({ 12 | name: `Predicate`, 13 | fc, 14 | Arbitrary: pipe( 15 | L.boolean, 16 | L.flatMap((b) => L.constant(() => b)), 17 | ), 18 | Associative: { 19 | All: [ 20 | P.makeAssociativeAll(), 21 | fromEquals((f: P.Predicate, s) => f(1) === s(1) && f(2) === s(2)), 22 | ], 23 | Any: [ 24 | P.makeAssociativeAny(), 25 | fromEquals((f: P.Predicate, s) => f(1) === s(1) && f(2) === s(2)), 26 | ], 27 | }, 28 | Identity: { 29 | All: [ 30 | P.makeIdentityAll(), 31 | fromEquals((f: P.Predicate, s) => f(1) === s(1) && f(2) === s(2)), 32 | ], 33 | Any: [ 34 | P.makeIdentityAny(), 35 | fromEquals((f: P.Predicate, s) => f(1) === s(1) && f(2) === s(2)), 36 | ], 37 | }, 38 | }) 39 | 40 | describe('Contravariant', () => { 41 | const { identity, associativity } = L.Contravariant.testContravariant()( 42 | P.Contravariant, 43 | (s: string) => s.length, 44 | (n: number) => n + 2, 45 | (a: string) => a.length % 2 === 0, 46 | (a: number) => a % 2 === 0, 47 | (predicate, n) => predicate(n), 48 | )(L.string())(fc) 49 | 50 | it('Identity', identity) 51 | it('Associativity', associativity) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /src/Progress.ts: -------------------------------------------------------------------------------- 1 | import * as M from './Maybe.js' 2 | import * as A from './Typeclass/Associative.js' 3 | import * as C from './Typeclass/Commutative.js' 4 | import * as D from './Typeclass/Debug.js' 5 | import * as EQ from './Typeclass/Eq.js' 6 | import * as I from './Typeclass/Identity.js' 7 | import * as O from './Typeclass/Ord.js' 8 | import { pipe } from './function.js' 9 | import * as N from './number.js' 10 | 11 | export interface Progress { 12 | readonly loaded: N.NonNegativeFloat 13 | readonly total: M.Maybe 14 | } 15 | 16 | export function Progress( 17 | loaded: N.NonNegativeFloat, 18 | total: M.Maybe = M.Nothing, 19 | ): Progress { 20 | return { 21 | loaded, 22 | total, 23 | } 24 | } 25 | 26 | export const Associative: A.Associative = A.struct({ 27 | loaded: N.NonNegativeFloatAssociativeSum, 28 | total: M.makeFailFastAssociative(N.NonNegativeFloatAssociativeSum), 29 | }) 30 | 31 | export const Identity: I.Identity = { 32 | ...Associative, 33 | id: Progress(N.NonNegativeIdentitySum.id, M.Just(N.NonNegativeIdentitySum.id)), 34 | } 35 | 36 | export const Commutative: C.Commutative = Associative 37 | 38 | export const Debug: D.Debug = { 39 | debug: (progress) => 40 | `Progress(${pipe( 41 | progress.total, 42 | M.match( 43 | () => progress.loaded, 44 | (t) => `${progress.loaded}/${t}`, 45 | ), 46 | )})`, 47 | } 48 | 49 | const maybeN = M.makeEq(N.Eq) 50 | 51 | export const Eq: EQ.Eq = { 52 | equals: (a, b) => a.loaded === b.loaded && maybeN.equals(a.total, b.total), 53 | } 54 | 55 | export const Ord: O.Ord = pipe( 56 | N.Ord, 57 | M.makeOrd, 58 | O.bothWith(N.Ord, (p: Progress) => [p.total, p.loaded]), 59 | ) 60 | -------------------------------------------------------------------------------- /tools/overloads/definitions/ForEachWithIndex.ts: -------------------------------------------------------------------------------- 1 | import { HKTParam, Interface, IntersectionNode, Kind, Static } from '../AST.js' 2 | 3 | import { CovariantWithIndex } from './CovariantWithIndex.js' 4 | import { IdentityBoth } from './IdentityBoth.js' 5 | import { 6 | aTypeParam, 7 | bTypeParam, 8 | curriedPlaceholder_, 9 | fnLabeled_, 10 | fn_, 11 | hkt, 12 | kind_, 13 | placeholder, 14 | } from './common.js' 15 | 16 | const hkt2 = new HKTParam(Symbol('T2'), 'T2') 17 | const hkt2Placeholder = hkt2.toPlaceholder() 18 | 19 | export const ForEachWithIndex = new Interface( 20 | 'ForEachWithIndex', 21 | [hkt, new Static(`K`), curriedPlaceholder_(hkt)], 22 | [ 23 | fnLabeled_( 24 | 'forEachWithIndex', 25 | [hkt2, new Static(`K2`), curriedPlaceholder_(hkt2)], 26 | [ 27 | new IntersectionNode( 28 | IdentityBoth.toTypeClass(hkt2).setParams([curriedPlaceholder_(hkt2)]), 29 | CovariantWithIndex.toTypeClass(hkt2).setParams([ 30 | new Static(`K2`), 31 | curriedPlaceholder_(hkt2), 32 | ]), 33 | ).labeled('IB'), 34 | ], 35 | fn_( 36 | '', 37 | [aTypeParam, hkt2Placeholder, bTypeParam], 38 | [ 39 | fnLabeled_( 40 | 'f', 41 | [], 42 | [new Static(`K`).labeled('k'), aTypeParam.labeled('a')], 43 | new Kind(hkt2, [hkt2Placeholder, bTypeParam]), 44 | ), 45 | ], 46 | fn_( 47 | '', 48 | [placeholder], 49 | [kind_([aTypeParam]).labeled('kind')], 50 | new Kind(hkt2, [hkt2Placeholder, kind_([bTypeParam])]), 51 | ), 52 | ), 53 | ), 54 | ], 55 | [CovariantWithIndex.setParams([hkt, new Static('K'), curriedPlaceholder_(hkt)])], 56 | ) 57 | 58 | export const node = ForEachWithIndex 59 | -------------------------------------------------------------------------------- /src/Refinement.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { Cast } from 'ts-toolbelt/out/Any/Cast.js' 3 | 4 | import type { Branded, Combine } from './Branded.js' 5 | import type { Either } from './Either.js' 6 | import type { Maybe } from './Maybe.js' 7 | 8 | export interface Refinement { 9 | (a: A): a is B 10 | } 11 | 12 | export type InputOf = [T] extends [Refinement] ? I : never 13 | export type OutputOf = [T] extends [Refinement] ? O : never 14 | 15 | export const fromMaybeK = (getOption: (a: A) => Maybe): Refinement => { 16 | return (a: A): a is B => getOption(a).tag === 'Just' 17 | } 18 | 19 | export const fromEitherK = ( 20 | getEither: (a: A) => Either, 21 | ): Refinement => { 22 | return (a: A): a is B => getEither(a).tag === 'Right' 23 | } 24 | 25 | export const id = (): Refinement => { 26 | return (_): _ is A => true 27 | } 28 | 29 | export const not = 30 | (refinement: Refinement): Refinement> => 31 | (a): a is Exclude => 32 | !refinement(a) 33 | 34 | export const or = 35 | (second: Refinement) => 36 | (first: Refinement): Refinement => 37 | (a): a is B | C => 38 | first(a) || second(a) 39 | 40 | export const and = 41 | (second: Refinement) => 42 | (first: Refinement): Refinement, A>> => 43 | (a): a is Cast, A> => 44 | first(a) && second(a) 45 | 46 | type CombineRefinment = A extends Branded 47 | ? B extends Branded 48 | ? Combine 49 | : A & B 50 | : A & B 51 | 52 | export const zero = (): Refinement => { 53 | return (_): _ is B => false 54 | } 55 | 56 | export const compose = 57 | (bc: Refinement) => 58 | (ab: Refinement): Refinement => { 59 | return (i): i is C => ab(i) && bc(i) 60 | } 61 | -------------------------------------------------------------------------------- /tools/overloads/common.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-const */ 2 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 3 | 4 | import { dirname } from 'node:path' 5 | 6 | import { Params } from '../../src/HKT.js' 7 | import { DeepEquals } from '../../src/Typeclass/Eq.js' 8 | 9 | export const possibleLengths: ReadonlyArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 10 | export type PossibleLength = typeof possibleLengths[number] 11 | 12 | export function getDirname(x: string) { 13 | return dirname(new URL(x).pathname) 14 | } 15 | 16 | export const hktParamNames = [ 17 | Params.A, 18 | Params.E, 19 | Params.R, 20 | Params.S, 21 | Params.U, 22 | Params.V, 23 | Params.W, 24 | Params.X, 25 | Params.Y, 26 | Params.Z, 27 | ] as const 28 | 29 | export function combinations( 30 | options: ReadonlyArray>, 31 | ): ReadonlyArray> { 32 | const inputs = options.slice(0) 33 | 34 | if (inputs.length === 1) { 35 | return inputs[0].map((x) => [x] as const) 36 | } 37 | 38 | const possiblilties: Array> = [] 39 | 40 | while (inputs.length > 1) { 41 | const current = inputs.shift()! 42 | const next = inputs.shift()! 43 | 44 | const combined = next.reduce((acc: ReadonlyArray>, x) => { 45 | return acc.concat(current.map((h) => [h, x])) 46 | }, []) 47 | 48 | possiblilties.push(...combined) 49 | } 50 | 51 | return possiblilties 52 | } 53 | 54 | export function uniq(array: ReadonlyArray): ReadonlyArray { 55 | const seen: A[] = [] 56 | const unique: A[] = [] 57 | 58 | for (const value of array) { 59 | if (seen.find((x) => DeepEquals.equals(value, x))) { 60 | continue 61 | } 62 | 63 | seen.push(value) 64 | unique.push(value) 65 | } 66 | 67 | return unique 68 | } 69 | 70 | export function uniqBy(array: ReadonlyArray, by: (a: A) => B): ReadonlyArray { 71 | const seen: B[] = [] 72 | const unique: A[] = [] 73 | 74 | for (const value of array) { 75 | const y = by(value) 76 | if (seen.find((x) => DeepEquals.equals(y, x))) { 77 | continue 78 | } 79 | 80 | seen.push(y) 81 | unique.push(value) 82 | } 83 | 84 | return unique 85 | } 86 | -------------------------------------------------------------------------------- /src/Unary.ts: -------------------------------------------------------------------------------- 1 | import type { HKT2, Params } from './HKT.js' 2 | import * as AB from './Typeclass/AssociativeBoth.js' 3 | import * as AC from './Typeclass/AssociativeCompose.js' 4 | import * as AF from './Typeclass/AssociativeFlatten.js' 5 | import * as C from './Typeclass/Covariant.js' 6 | import * as D from './Typeclass/Divariant.js' 7 | import * as IB from './Typeclass/IdentityBoth.js' 8 | import * as T from './Typeclass/Top.js' 9 | import { flow, identity } from './function.js' 10 | 11 | export type Unary = (a: A) => B 12 | 13 | export interface UnaryHKT extends HKT2 { 14 | readonly type: Unary 15 | } 16 | 17 | export const Covariant: C.Covariant2 = { 18 | map: flow, 19 | } 20 | 21 | export const bindTo = C.bindTo(Covariant) 22 | export const flap = C.flap(Covariant) 23 | export const mapTo = C.mapTo(Covariant) 24 | export const tupled = C.tupled(Covariant) 25 | 26 | export const Top: T.Top2 = { 27 | top: identity, 28 | } 29 | 30 | export const top = Top.top 31 | export const fromLazy = T.makeFromLazy({ ...Top, ...Covariant }) 32 | export const fromValue = T.makeFromValue({ ...Top, ...Covariant }) 33 | 34 | export const AssociativeBoth: AB.AssociativeBoth2 = { 35 | both: (s) => (f) => (e) => [f(e), s(e)], 36 | } 37 | 38 | export const both = AssociativeBoth.both 39 | export const zipLeft = AB.zipLeft({ ...AssociativeBoth, ...Covariant }) 40 | export const zipRight = AB.zipRight({ ...AssociativeBoth, ...Covariant }) 41 | 42 | export const AssociativeCompose: AC.AssociativeCompose2 = { 43 | compose: flow, 44 | } 45 | 46 | export const compose = AC.compose 47 | 48 | export const Flatten: AF.AssociativeFlatten2 = { 49 | flatten: (f) => (e) => f(e)(e), 50 | } 51 | 52 | export const flatten = AF.flatten 53 | export const flatMap = AF.flatMap({ ...Flatten, ...Covariant }) 54 | export const bind = AF.bind({ ...Flatten, ...Covariant }) 55 | 56 | export const Divariant: D.Divariant2 = { 57 | dimap: (fi, fo) => (f) => flow(fi, f, fo), 58 | } 59 | 60 | export const dimap = D.dimap 61 | 62 | export const IdentityBoth: IB.IdentityBoth2 = { 63 | ...AssociativeBoth, 64 | ...Top, 65 | } 66 | -------------------------------------------------------------------------------- /src/Predicate.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Params } from './HKT.js' 2 | import type { Associative } from './Typeclass/Associative.js' 3 | import * as AB from './Typeclass/AssociativeBoth.js' 4 | import * as AE from './Typeclass/AssociativeEither.js' 5 | import * as Contra from './Typeclass/Contravariant.js' 6 | import type { Identity } from './Typeclass/Identity.js' 7 | import { constFalse, constTrue, flow, pipe } from './function.js' 8 | 9 | export interface Predicate { 10 | (a: A): boolean 11 | } 12 | 13 | export const not = 14 | (predicate: Predicate): Predicate => 15 | (a) => 16 | !predicate(a) 17 | 18 | export const or = 19 | (second: Predicate) => 20 | (first: Predicate): Predicate => 21 | (a) => 22 | first(a) || second(a) 23 | 24 | export const and = 25 | (second: Predicate) => 26 | (first: Predicate): Predicate => 27 | (a) => 28 | first(a) && second(a) 29 | 30 | export const makeAssociativeAny = (): Associative> => ({ 31 | concat: (first, second) => pipe(first, or(second)), 32 | }) 33 | 34 | export const makeIdentityAny = (): Identity> => ({ 35 | ...makeAssociativeAny(), 36 | id: constFalse, 37 | }) 38 | 39 | export const makeAssociativeAll = (): Associative> => ({ 40 | concat: (first, second) => pipe(first, and(second)), 41 | }) 42 | 43 | export const makeIdentityAll = (): Identity> => ({ 44 | ...makeAssociativeAll(), 45 | id: constTrue, 46 | }) 47 | 48 | export const contramap = 49 | (f: (b: B) => A) => 50 | (predicate: Predicate): Predicate => 51 | flow(f, predicate) 52 | 53 | export interface PredicateHKT extends HKT { 54 | readonly type: Predicate 55 | } 56 | 57 | export const Contravariant: Contra.Contravariant1 = { 58 | contramap, 59 | } 60 | 61 | export const AssociativeBoth: AB.AssociativeBoth1 = { 62 | both: 63 | (s) => 64 | (f) => 65 | ([a, b]) => 66 | f(a) && s(b), 67 | } 68 | 69 | export const bothWith = AB.bothWith({ ...Contravariant, ...AssociativeBoth }) 70 | 71 | export const AssociativeEither: AE.AssociativeEither1 = { 72 | either: (s) => (f) => (either) => either.tag === 'Left' ? f(either.left) : s(either.right), 73 | } 74 | 75 | export const eitherWith = AE.eitherWith({ ...Contravariant, ...AssociativeEither }) 76 | -------------------------------------------------------------------------------- /src/Typeclass/Associative.ts: -------------------------------------------------------------------------------- 1 | import * as C from './Concat.js' 2 | 3 | /** 4 | * Associative is a typeclass for conrete values that support a concatenation that is associative. 5 | * 6 | * Laws: 7 | * Associativity :: concat(concat(a, b), c) === concat(a, concat(b, c)) 8 | */ 9 | export type Associative = C.Concat 10 | 11 | /** 12 | * Create an Associative instance that always return the same value. 13 | */ 14 | export const constant = (value: A): Associative => ({ 15 | concat: () => value, 16 | }) 17 | 18 | /** 19 | * Change the ordering of an Associative concatenation 20 | */ 21 | export const reverse: (A: Associative) => Associative = C.reverse 22 | 23 | /** 24 | * Construct an Associative for an object defined in terms of Associative's 25 | * for all of the properties. 26 | */ 27 | export const struct = (associatives: { 28 | readonly [K in keyof A]: Associative 29 | }): Associative<{ 30 | readonly [K in keyof A]: A[K] 31 | }> => ({ 32 | concat: (first, second) => { 33 | const r: A = {} as any 34 | for (const k in associatives) { 35 | r[k] = associatives[k].concat(first[k], second[k]) 36 | } 37 | return r 38 | }, 39 | }) 40 | 41 | /** 42 | * Construct an Associative for a tuple defined in terms of Associative's 43 | * for all each index. 44 | */ 45 | export const tuple = >( 46 | ...associatives: { readonly [K in keyof A]: Associative } 47 | ): Associative> => ({ 48 | concat: (first, second) => associatives.map((s, i) => s.concat(first[i], second[i])) as any, 49 | }) 50 | 51 | /** 52 | * Combinator to zip a value in between the other values during concatenation. 53 | * Similar to ['a', 'b'].join(', ') but for all types instead of just strings. 54 | */ 55 | export const intercalate = 56 | (middle: A) => 57 | (A: Associative): Associative => ({ 58 | concat: (x, y) => A.concat(x, A.concat(middle, y)), 59 | }) 60 | 61 | /** 62 | * Concatenate an array of values given a seed. 63 | */ 64 | export const concatAll: (A: Associative) => (startWith: A) => (as: readonly A[]) => A = 65 | C.concatAll 66 | 67 | /** 68 | * An Assocative instance which will always return the first value. 69 | */ 70 | export const First: Associative = C.First 71 | 72 | /** 73 | * An Assocative instance which will always return the second value. 74 | */ 75 | export const Second: Associative = C.Second 76 | -------------------------------------------------------------------------------- /tools/overloads/cli.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'node:module' 2 | import { join } from 'node:path' 3 | 4 | import { format } from 'prettier' 5 | import yargs from 'yargs' 6 | 7 | import { ParentNode } from './AST.js' 8 | import { getDirname } from './common.js' 9 | import { generateOverloads } from './generateOverloads.js' 10 | import { printOverload } from './printOverload.js' 11 | 12 | const dirname = getDirname(import.meta.url) 13 | const require = createRequire(import.meta.url) 14 | const prettierConfig = require('../../.prettierrc.cjs') 15 | 16 | const program = yargs(process.argv, process.cwd()) 17 | .option('definition', { 18 | alias: 'd', 19 | type: 'string', 20 | require: true, 21 | }) 22 | .option('noImports', { 23 | type: 'boolean', 24 | }) 25 | .option('printOutput', { 26 | type: 'boolean', 27 | }) 28 | .option('skipFormat', { 29 | type: 'boolean', 30 | }) 31 | 32 | const HKT_IMPORTS = `import { 33 | HKT, 34 | HKT2, 35 | HKT3, 36 | HKT4, 37 | HKT5, 38 | HKT6, 39 | HKT7, 40 | HKT8, 41 | HKT9, 42 | HKT10, 43 | Kind, 44 | Kind2, 45 | Kind3, 46 | Kind4, 47 | Kind5, 48 | Kind6, 49 | Kind7, 50 | Kind8, 51 | Kind9, 52 | Kind10, 53 | Params, 54 | DefaultOf, 55 | } from './HKT.js'` 56 | 57 | async function main() { 58 | const { definition, noImports, printOutput, skipFormat } = await program.argv 59 | const filePath = join( 60 | dirname, 61 | 'definitions', 62 | definition.endsWith('.ts') ? definition : definition + '.ts', 63 | ) 64 | // eslint-disable-next-line @typescript-eslint/no-var-requires 65 | const mod = await import(filePath) 66 | const modKeys = Object.keys(mod) 67 | const node = (modKeys.length === 1 ? mod[modKeys[0]] : mod['node']) as ParentNode 68 | 69 | console.log('Generating overloads...') 70 | 71 | const printed = generateOverloads(node).map(([overload, context]) => 72 | printOverload(overload, context), 73 | ) 74 | 75 | const input = (noImports ? '' : HKT_IMPORTS + '\n\n') + printed.join('\n\n') 76 | const output = (skipFormat ? input : format(input, prettierConfig)) + '\n' 77 | 78 | if (printOutput) { 79 | console.log(output) 80 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 81 | // @ts-ignore 82 | const { default: clipboard } = await import('clipboardy') 83 | 84 | await clipboard.write(output) 85 | 86 | console.log('Copied overload to your clipboard!') 87 | } 88 | } 89 | 90 | main().catch((error) => { 91 | console.error(error) 92 | 93 | process.exitCode = 1 94 | }) 95 | -------------------------------------------------------------------------------- /src/Law/ForEach.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Kind, Params } from '../HKT.js' 2 | import * as AB from '../Typeclass/AssociativeBoth.js' 3 | import { Covariant, map } from '../Typeclass/Covariant.js' 4 | import { Eq } from '../Typeclass/Eq.js' 5 | import * as FE from '../Typeclass/ForEach/index.js' 6 | import * as IB from '../Typeclass/IdentityBoth.js' 7 | import * as T from '../Typeclass/Top.js' 8 | import { pipe } from '../function.js' 9 | 10 | import * as Arbitrary from './Arbitrary.js' 11 | 12 | export function testForEach( 13 | FE: FE.ForEach, 14 | IBC1: IB.IdentityBoth & Covariant, 15 | IBC2: IB.IdentityBoth & Covariant, 16 | E: Eq>>, 17 | E2: Eq>>, 18 | ) { 19 | return (A: Arbitrary.Arbitrary>) => (fc: typeof import('fast-check')) => ({ 20 | identity: () => testForEachIdentity(FE, IBC1, E)(A).property(fc), 21 | associativity: () => testForEachAssociativity(FE, IBC1, IBC2, E2)(A).property(fc), 22 | }) 23 | } 24 | 25 | function testForEachIdentity( 26 | FE: FE.ForEach, 27 | IBC: IB.IdentityBoth & Covariant, 28 | E: Eq>>, 29 | ) { 30 | return (A: Arbitrary.Arbitrary>) => 31 | pipe( 32 | A, 33 | Arbitrary.toProperty((kind) => { 34 | const l = FE.forEach(IBC)(T.makeFromValue(IBC))(kind) 35 | const r = T.makeFromValue(IBC)(kind) 36 | 37 | return E.equals(l, r) 38 | }), 39 | ) 40 | } 41 | 42 | function testForEachAssociativity( 43 | FE: FE.ForEach, 44 | IBC: IB.IdentityBoth & Covariant, 45 | IBC2: IB.IdentityBoth & Covariant, 46 | E: Eq>>, 47 | ) { 48 | return (A: Arbitrary.Arbitrary>) => { 49 | const both_ = AB.both(IBC, IBC2) 50 | const top_ = T.top(IBC, IBC2) 51 | const map_ = map(IBC, IBC2) 52 | interface HKT_ extends HKT { 53 | readonly type: Kind> 54 | } 55 | const IBC_: IB.IdentityBoth & Covariant = { 56 | map: map_, 57 | both: both_, 58 | top: top_, 59 | } 60 | 61 | return pipe( 62 | A, 63 | Arbitrary.toProperty((kind) => 64 | E.equals( 65 | pipe(kind, FE.forEach(IBC_)(T.makeFromValue(IBC_))), 66 | pipe( 67 | kind, 68 | FE.forEach(IBC)(T.makeFromValue(IBC)), 69 | IBC.map(FE.forEach(IBC2)(T.makeFromValue(IBC2))), 70 | ), 71 | ), 72 | ), 73 | ) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Either.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as E from './Either.js' 5 | import * as L from './Law/index.js' 6 | import { testAllCovariantHKTLaws, testAllDataLaws } from './Law/internal-test-all-laws.js' 7 | import * as M from './Maybe.js' 8 | import { DeepEquals } from './Typeclass/Eq.js' 9 | import * as N from './number.js' 10 | import * as S from './string.js' 11 | 12 | describe(import.meta.url, () => { 13 | testAllDataLaws({ 14 | name: `Either Instances`, 15 | fc, 16 | Arbitrary: L.Either(L.string(), L.number()), 17 | Associative: { 18 | Either: [E.makeAssociative(S.Associative, N.AssociativeSum), E.makeEq(S.Eq, N.Eq)], 19 | }, 20 | Identity: { 21 | Either: [E.makeIdentity(S.Identity, N.IdentitySum), E.makeEq(S.Eq, N.Eq)], 22 | }, 23 | Eq: { 24 | Either: E.makeEq(S.Eq, N.Eq), 25 | }, 26 | Ord: { 27 | Either: E.makeOrd(S.Ord, N.Ord), 28 | }, 29 | }) 30 | 31 | testAllCovariantHKTLaws()({ 32 | name: `Data Instances`, 33 | fc, 34 | Arbitrary: L.Either(L.string(), L.number()), 35 | ArbitraryA: L.number(), 36 | Covariant: { 37 | Either: [E.Covariant, (x: number) => x + 1, (x: number) => x * 2, E.makeEq(S.Eq, N.Eq)], 38 | }, 39 | AssociativeBoth: { 40 | Either: [ 41 | { ...E.Covariant, ...E.AssociativeBoth }, 42 | E.Right((x: number) => x + 1), 43 | E.makeEq(S.Eq, N.Eq), 44 | ], 45 | }, 46 | IdentityBoth: { 47 | Either: [ 48 | { ...E.Covariant, ...E.IdentityBoth }, 49 | (x: number) => String(x), 50 | E.makeEq(DeepEquals, N.Eq), 51 | E.makeEq(DeepEquals, S.Eq), 52 | ], 53 | }, 54 | AssociativeFlatten: { 55 | Either: [ 56 | { ...E.Flatten, ...E.Covariant }, 57 | (x: number) => E.Right(String(x)), 58 | (s: string) => E.Right(s.length * 2), 59 | E.makeEq(DeepEquals, N.Eq), 60 | ], 61 | }, 62 | IdentityFlatten: { 63 | Either: [ 64 | { ...E.IdentityFlatten, ...E.Covariant }, 65 | (x: number) => E.Right(x + 2), 66 | E.makeEq(DeepEquals, N.Eq), 67 | ], 68 | }, 69 | AssociativeEither: { 70 | Either: [ 71 | { ...E.AssociativeEither, ...E.Covariant }, 72 | (x: number) => String(x), 73 | E.makeEq(S.Eq, N.Eq), 74 | E.makeEq(S.Eq, S.Eq), 75 | ], 76 | }, 77 | ForEach: { 78 | Either: [ 79 | E.ForEach, 80 | { ...E.IdentityBoth, ...E.Covariant }, 81 | { ...M.IdentityBoth, ...M.Covariant }, 82 | ], 83 | }, 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /src/Typeclass/CommutativeBoth.ts: -------------------------------------------------------------------------------- 1 | import { HKT, HKT10, HKT2, HKT3, HKT4, HKT5, HKT6, HKT7, HKT8, HKT9 } from '../HKT.js' 2 | 3 | import { 4 | AssociativeBoth, 5 | AssociativeBoth1, 6 | AssociativeBoth10, 7 | AssociativeBoth2, 8 | AssociativeBoth2EC, 9 | AssociativeBoth3, 10 | AssociativeBoth3EC, 11 | AssociativeBoth3RC, 12 | AssociativeBoth3REC, 13 | AssociativeBoth4, 14 | AssociativeBoth4EC, 15 | AssociativeBoth4RC, 16 | AssociativeBoth4REC, 17 | AssociativeBoth4SC, 18 | AssociativeBoth4SEC, 19 | AssociativeBoth4SRC, 20 | AssociativeBoth4SREC, 21 | AssociativeBoth5, 22 | AssociativeBoth6, 23 | AssociativeBoth7, 24 | AssociativeBoth8, 25 | AssociativeBoth9, 26 | } from './AssociativeBoth.js' 27 | 28 | export interface CommutativeBoth extends AssociativeBoth {} 29 | 30 | export interface CommutativeBoth1 extends AssociativeBoth1 {} 31 | 32 | export interface CommutativeBoth2 extends AssociativeBoth2 {} 33 | 34 | export interface CommutativeBoth2EC extends AssociativeBoth2EC {} 35 | 36 | export interface CommutativeBoth3 extends AssociativeBoth3 {} 37 | 38 | export interface CommutativeBoth3RC extends AssociativeBoth3RC {} 39 | 40 | export interface CommutativeBoth3REC extends AssociativeBoth3REC {} 41 | 42 | export interface CommutativeBoth3EC extends AssociativeBoth3EC {} 43 | 44 | export interface CommutativeBoth4 extends AssociativeBoth4 {} 45 | 46 | export interface CommutativeBoth4SREC 47 | extends AssociativeBoth4SREC {} 48 | 49 | export interface CommutativeBoth4SC extends AssociativeBoth4SC {} 50 | 51 | export interface CommutativeBoth4SRC extends AssociativeBoth4SRC {} 52 | 53 | export interface CommutativeBoth4RC extends AssociativeBoth4RC {} 54 | 55 | export interface CommutativeBoth4SEC extends AssociativeBoth4SEC {} 56 | 57 | export interface CommutativeBoth4REC extends AssociativeBoth4REC {} 58 | 59 | export interface CommutativeBoth4EC extends AssociativeBoth4EC {} 60 | 61 | export interface CommutativeBoth5 extends AssociativeBoth5 {} 62 | 63 | export interface CommutativeBoth6 extends AssociativeBoth6 {} 64 | 65 | export interface CommutativeBoth7 extends AssociativeBoth7 {} 66 | 67 | export interface CommutativeBoth8 extends AssociativeBoth8 {} 68 | 69 | export interface CommutativeBoth9 extends AssociativeBoth9 {} 70 | 71 | export interface CommutativeBoth10 extends AssociativeBoth10 {} 72 | -------------------------------------------------------------------------------- /src/function.test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual, throws } from 'node:assert' 2 | 3 | import * as fc from 'fast-check' 4 | import { describe, it } from 'vitest' 5 | 6 | import * as L from './Law/index.js' 7 | import { 8 | absurd, 9 | constFalse, 10 | constNull, 11 | constTrue, 12 | constUndefined, 13 | constVoid, 14 | constant, 15 | flow, 16 | identity, 17 | pipe, 18 | second, 19 | tupled, 20 | unsafeCoerce, 21 | } from './function.js' 22 | 23 | const add = (x: number) => (y: number) => x + y 24 | 25 | describe(import.meta.url, () => { 26 | describe(absurd.name, () => { 27 | it('always throws', () => throws(absurd)) 28 | }) 29 | 30 | describe('constant', () => { 31 | it('returns a function which always returns the provided value', () => { 32 | pipe( 33 | L.tuple(L.unknown, L.nonEmptyArray(L.unknown)), 34 | L.toProperty(([a, args]) => constant(a)(...args) === a), 35 | L.assert, 36 | )(fc) 37 | 38 | deepStrictEqual(constVoid(), undefined) 39 | deepStrictEqual(constUndefined(), undefined) 40 | deepStrictEqual(constTrue(), true) 41 | deepStrictEqual(constFalse(), false) 42 | deepStrictEqual(constNull(), null) 43 | }) 44 | }) 45 | 46 | describe('flow', () => { 47 | it('allows composing functions left-to-right', () => 48 | pipe( 49 | L.tuple( 50 | L.Arbitrary((fc) => fc.array(fc.constant(add(1)), { minLength: 1, maxLength: 9 })), 51 | L.number(), 52 | ), 53 | L.toProperty(([fns, n]) => flow(...(fns as [(n: number) => number]))(n) === n + fns.length), 54 | (x) => L.assert(x, { numRuns: 10000 }), 55 | )(fc)) 56 | }) 57 | 58 | describe('pipe', () => { 59 | it('allows applying a composition functions left-to-right with a value', () => 60 | pipe( 61 | L.tuple( 62 | L.Arbitrary((fc) => fc.array(fc.constant(add(1)), { minLength: 1, maxLength: 19 })), 63 | L.number(), 64 | ), 65 | L.toProperty(([fns, n]) => pipe(n, ...(fns as [(n: number) => number])) === n + fns.length), 66 | (x) => L.assert(x, { numRuns: 10000 }), 67 | )(fc)) 68 | }) 69 | 70 | describe('identity', () => { 71 | it('is a function which always returns the value provided', () => { 72 | deepStrictEqual(identity(1), 1) 73 | deepStrictEqual(identity(3), 3) 74 | }) 75 | }) 76 | 77 | describe('second', () => { 78 | it('always returns the second param', () => { 79 | deepStrictEqual(second(1, 2), 2) 80 | deepStrictEqual(second('foo', 'bar'), 'bar') 81 | }) 82 | }) 83 | 84 | describe('unsafeCoerce', () => { 85 | it('always reutrns the same value', () => { 86 | deepStrictEqual(unsafeCoerce(1), 1) 87 | }) 88 | }) 89 | 90 | describe('tupled', () => { 91 | it('returns the first value as a tuple', () => { 92 | deepStrictEqual(tupled(1), [1]) 93 | }) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /src/Typeclass/CommutativeEither.ts: -------------------------------------------------------------------------------- 1 | import { HKT, HKT10, HKT2, HKT3, HKT4, HKT5, HKT6, HKT7, HKT8, HKT9 } from '../HKT.js' 2 | 3 | import { 4 | AssociativeEither, 5 | AssociativeEither1, 6 | AssociativeEither10, 7 | AssociativeEither2, 8 | AssociativeEither2EC, 9 | AssociativeEither3, 10 | AssociativeEither3EC, 11 | AssociativeEither3RC, 12 | AssociativeEither3REC, 13 | AssociativeEither4, 14 | AssociativeEither4EC, 15 | AssociativeEither4RC, 16 | AssociativeEither4REC, 17 | AssociativeEither4SC, 18 | AssociativeEither4SEC, 19 | AssociativeEither4SRC, 20 | AssociativeEither4SREC, 21 | AssociativeEither5, 22 | AssociativeEither6, 23 | AssociativeEither7, 24 | AssociativeEither8, 25 | AssociativeEither9, 26 | } from './AssociativeEither.js' 27 | 28 | export interface CommutativeEither extends AssociativeEither {} 29 | 30 | export interface CommutativeEither1 extends AssociativeEither1 {} 31 | 32 | export interface CommutativeEither2 extends AssociativeEither2 {} 33 | 34 | export interface CommutativeEither2EC extends AssociativeEither2EC {} 35 | 36 | export interface CommutativeEither3 extends AssociativeEither3 {} 37 | 38 | export interface CommutativeEither3RC extends AssociativeEither3RC {} 39 | 40 | export interface CommutativeEither3REC 41 | extends AssociativeEither3REC {} 42 | 43 | export interface CommutativeEither3EC extends AssociativeEither3EC {} 44 | 45 | export interface CommutativeEither4 extends AssociativeEither4 {} 46 | 47 | export interface CommutativeEither4SREC 48 | extends AssociativeEither4SREC {} 49 | 50 | export interface CommutativeEither4SC extends AssociativeEither4SC {} 51 | 52 | export interface CommutativeEither4SRC 53 | extends AssociativeEither4SRC {} 54 | 55 | export interface CommutativeEither4RC extends AssociativeEither4RC {} 56 | 57 | export interface CommutativeEither4SEC 58 | extends AssociativeEither4SEC {} 59 | 60 | export interface CommutativeEither4REC 61 | extends AssociativeEither4REC {} 62 | 63 | export interface CommutativeEither4EC extends AssociativeEither4EC {} 64 | 65 | export interface CommutativeEither5 extends AssociativeEither5 {} 66 | 67 | export interface CommutativeEither6 extends AssociativeEither6 {} 68 | 69 | export interface CommutativeEither7 extends AssociativeEither7 {} 70 | 71 | export interface CommutativeEither8 extends AssociativeEither8 {} 72 | 73 | export interface CommutativeEither9 extends AssociativeEither9 {} 74 | 75 | export interface CommutativeEither10 extends AssociativeEither10 {} 76 | -------------------------------------------------------------------------------- /src/boolean.ts: -------------------------------------------------------------------------------- 1 | import type { Refinement } from './Refinement.js' 2 | import { Associative } from './Typeclass/Associative.js' 3 | import { Commutative } from './Typeclass/Commutative.js' 4 | import type * as D from './Typeclass/Debug.js' 5 | import type * as E from './Typeclass/Eq.js' 6 | import { Identity } from './Typeclass/Identity.js' 7 | import * as I from './Typeclass/Inverse.js' 8 | import type * as O from './Typeclass/Ord.js' 9 | import { Lazy } from './function.js' 10 | 11 | export const isBoolean: Refinement = (u: unknown): u is boolean => 12 | typeof u === 'boolean' 13 | 14 | /** 15 | * Defines the fold over a boolean value. 16 | * Takes two thunks `onTrue`, `onFalse` and a `boolean` value. 17 | * If `value` is false, `onFalse()` is returned, otherwise `onTrue()`. 18 | */ 19 | export const match = 20 | (onFalse: Lazy, onTrue: Lazy) => 21 | (value: boolean): A | B => 22 | value ? onTrue() : onFalse() 23 | 24 | export const Eq: E.Eq = { 25 | equals: (first, second) => first === second, 26 | } 27 | 28 | /** 29 | * `boolean` Associative under conjunction. 30 | * 31 | * @example 32 | * import { AssociativeAll } from 'hkt-ts/boolean.js' 33 | * 34 | * assert.deepStrictEqual(AssociativeAll.concat(true, true), true) 35 | * assert.deepStrictEqual(AssociativeAll.concat(true, false), false) 36 | * 37 | * @category instances 38 | */ 39 | export const AssociativeAll: Associative = { 40 | concat: (first, second) => first && second, 41 | } 42 | 43 | export const CommutativeAll: Commutative = AssociativeAll 44 | 45 | /** 46 | * `boolean` Associative under disjunction. 47 | * 48 | * @example 49 | * import { AssociativeAny } from 'hkt-ts/boolean.js' 50 | * 51 | * assert.deepStrictEqual(AssociativeAny.concat(true, true), true) 52 | * assert.deepStrictEqual(AssociativeAny.concat(true, false), true) 53 | * assert.deepStrictEqual(AssociativeAny.concat(false, false), false) 54 | * 55 | * @category instances 56 | */ 57 | export const AssociativeAny: Associative = { 58 | concat: (first, second) => first || second, 59 | } 60 | 61 | /** 62 | * `boolean` Identity 63 | * 64 | * The `id` value is `true`. 65 | * @category instances 66 | */ 67 | export const All: Identity = { 68 | ...AssociativeAll, 69 | id: true, 70 | } 71 | 72 | /** 73 | * `boolean` AssociativeIdentity under disjunction. 74 | * 75 | * The `id` value is `false`. 76 | * @category instances 77 | */ 78 | export const Any: Identity = { 79 | ...AssociativeAny, 80 | id: false, 81 | } 82 | 83 | export const Ord: O.Ord = { 84 | ...Eq, 85 | compare: (first, second) => (first < second ? -1 : first > second ? 1 : 0), 86 | } 87 | 88 | export const Debug: D.Debug = { 89 | debug: (b) => JSON.stringify(b), 90 | } 91 | 92 | export const InverseAny: I.Inverse = { 93 | ...Any, 94 | inverse: (a, b) => (a === Any.id ? !b : a), 95 | } 96 | 97 | export const InverseAll: I.Inverse = { 98 | ...All, 99 | inverse: (a, b) => (a === All.id ? !b : a), 100 | } 101 | -------------------------------------------------------------------------------- /src/DataEither.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from 'fast-check' 2 | import { describe } from 'vitest' 3 | 4 | import * as DE from './DataEither.js' 5 | import * as E from './Either.js' 6 | import * as L from './Law/index.js' 7 | import { testAllCovariantHKTLaws, testAllDataLaws } from './Law/internal-test-all-laws.js' 8 | import * as M from './Maybe.js' 9 | import { DeepEquals } from './Typeclass/Eq.js' 10 | import * as N from './number.js' 11 | import * as S from './string.js' 12 | 13 | describe(import.meta.url, () => { 14 | testAllDataLaws({ 15 | name: `DataEither Instances`, 16 | fc, 17 | Arbitrary: L.Data(L.Either(L.string(), L.number())), 18 | Associative: { 19 | Data: [DE.makeAssociative(S.Associative, N.AssociativeSum), DE.makeEq(S.Eq, N.Eq)], 20 | }, 21 | Identity: { 22 | Data: [DE.makeIdentity(S.Identity, N.IdentitySum), DE.makeEq(S.Eq, N.Eq)], 23 | }, 24 | Eq: { 25 | Data: DE.makeEq(S.Eq, N.Eq), 26 | }, 27 | Ord: { 28 | Data: DE.makeOrd(S.Ord, N.Ord), 29 | }, 30 | }) 31 | 32 | testAllCovariantHKTLaws()({ 33 | name: `Data Instances`, 34 | fc, 35 | Arbitrary: L.Data(L.Either(L.string(), L.number())), 36 | ArbitraryA: L.number(), 37 | Covariant: { 38 | Data: [DE.Covariant, (x: number) => x + 1, (x: number) => x * 2, DE.makeEq(S.Eq, N.Eq)], 39 | }, 40 | AssociativeBoth: { 41 | Data: [ 42 | { ...DE.Covariant, ...DE.AssociativeBoth }, 43 | DE.Success((x: number) => x + 1), 44 | DE.makeEq(S.Eq, N.Eq), 45 | ], 46 | }, 47 | IdentityBoth: { 48 | Data: [ 49 | { ...DE.Covariant, ...DE.IdentityBoth }, 50 | (x: number) => String(x), 51 | DE.makeEq(DeepEquals, N.Eq), 52 | DE.makeEq(DeepEquals, S.Eq), 53 | ], 54 | }, 55 | AssociativeFlatten: { 56 | Data: [ 57 | { ...DE.Flatten, ...DE.Covariant }, 58 | (x: number) => DE.Success(String(x)), 59 | (s: string) => DE.Success(s.length * 2), 60 | DE.makeEq(DeepEquals, N.Eq), 61 | ], 62 | }, 63 | IdentityFlatten: { 64 | Data: [ 65 | { ...DE.IdentityFlatten, ...DE.Covariant }, 66 | (x: number) => DE.Success(x + 2), 67 | DE.makeEq(DeepEquals, N.Eq), 68 | ], 69 | }, 70 | AssociativeEither: { 71 | Data: [ 72 | { ...DE.AssociativeEither, ...DE.Covariant }, 73 | (x: number) => String(x), 74 | DE.makeEq(S.Eq, N.Eq), 75 | DE.makeEq(S.Eq, S.Eq), 76 | ], 77 | }, 78 | IdentityEither: { 79 | Data: [ 80 | { ...DE.IdentityEither, ...DE.Covariant }, 81 | (x: number) => String(x), 82 | DE.makeEq(DeepEquals, N.Eq), 83 | DE.makeEq(DeepEquals, S.Eq), 84 | ], 85 | }, 86 | // FilterMap: { 87 | // Data: [DE.FilterMap, (x: number) => x > 2, (x: number) => x < 10, DE.makeEq(N.Eq)], 88 | // }, 89 | ForEach: { 90 | Data: [ 91 | DE.ForEach, 92 | { ...E.IdentityBoth, ...E.Covariant }, 93 | { ...M.IdentityBoth, ...M.Covariant }, 94 | ], 95 | }, 96 | }) 97 | }) 98 | -------------------------------------------------------------------------------- /tools/generatePackageExports.ts: -------------------------------------------------------------------------------- 1 | // TODO: update exports in package.json with all modules 2 | 3 | import fs from 'fs' 4 | import path, { extname } from 'path' 5 | 6 | import { MODULES, ROOT_DIR, SOURCE_DIR, findFilePaths } from './common.js' 7 | 8 | const TSX_REGEX = /.tsx?$/ 9 | const INDEX_REGEX = /\/?index$/ 10 | 11 | const packageJsonPath = path.join(ROOT_DIR, 'package.json') 12 | const packageJsonContents = fs.readFileSync(packageJsonPath).toString() 13 | const packageJson = JSON.parse(packageJsonContents) 14 | 15 | packageJson.exports = createExports() 16 | 17 | fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2).trim() + `\n`) 18 | 19 | type ExportMap = { 20 | require: { default: string; types: string } 21 | import: { default: string; types: string } 22 | } 23 | 24 | export function createExports() { 25 | const exports: Record = { 26 | '.': { 27 | require: { 28 | default: './cjs/index.js', 29 | types: './cjs/index.d.ts', 30 | }, 31 | import: { 32 | default: './esm/index.js', 33 | types: './esm/index.d.ts', 34 | }, 35 | }, 36 | } 37 | 38 | for (const module of MODULES) { 39 | const sourceDir = path.join(SOURCE_DIR, module) 40 | const isDirectory = fs.statSync(sourceDir).isDirectory() 41 | const name = module.replace(TSX_REGEX, '') 42 | 43 | exports[`./${name}`] = { 44 | require: { 45 | default: 46 | './' + 47 | (isDirectory ? path.join('cjs', name, 'index.js') : path.join('cjs', `${name}.js`)), 48 | types: 49 | './' + 50 | (isDirectory ? path.join('cjs', name, 'index.js') : path.join('cjs', `${name}.d.ts`)), 51 | }, 52 | import: { 53 | default: 54 | './' + 55 | (isDirectory ? path.join('esm', name, 'index.js') : path.join('esm', `${name}.js`)), 56 | types: 57 | './' + 58 | (isDirectory ? path.join('esm', name, 'index.js') : path.join('esm', `${name}.d.ts`)), 59 | }, 60 | } 61 | 62 | if (!isDirectory) { 63 | continue 64 | } 65 | 66 | const filePaths = findFilePaths(sourceDir, [ 67 | '!**/*.browser-test.ts', 68 | '!**/*.test.ts', 69 | '**/*.ts', 70 | ]) 71 | 72 | for (const filePath of filePaths) { 73 | const relativePath = path.relative(sourceDir, filePath) 74 | 75 | if (extname(relativePath) !== '.ts') { 76 | continue 77 | } 78 | 79 | const jsPath = relativePath.replace('.ts', '.js') 80 | const dtsPath = relativePath.replace('.ts', '.d.ts') 81 | const map: ExportMap = { 82 | require: { 83 | default: './' + path.join('cjs', name, jsPath), 84 | types: './' + path.join('cjs', name, dtsPath), 85 | }, 86 | import: { 87 | default: './' + path.join('esm', name, jsPath), 88 | types: './' + path.join('esm', name, dtsPath), 89 | }, 90 | } 91 | 92 | const relativeName = relativePath.replace(TSX_REGEX, '').replace(INDEX_REGEX, '') 93 | 94 | exports[`./${name}${relativeName ? `/${relativeName}` : ''}`] = map 95 | } 96 | } 97 | 98 | return exports 99 | } 100 | -------------------------------------------------------------------------------- /tools/overloads/PrintManagerContext.ts: -------------------------------------------------------------------------------- 1 | import { FunctionSignature, Interface, ParentNode, TypeAlias } from './AST.js' 2 | 3 | export type PrintContext = { 4 | parentNodes: ParentNode[] 5 | contextStack: CurrentContext[] 6 | } 7 | 8 | export type CurrentContext = 9 | | 'TypeParam' 10 | | 'Property' 11 | | 'FunctionParam' 12 | | 'Return' 13 | | 'Extension' 14 | | 'Kind' 15 | | 'Array' 16 | 17 | export class PrintContextManager { 18 | context: PrintContext 19 | recursive = false 20 | 21 | constructor() { 22 | this.context = { 23 | parentNodes: [], 24 | contextStack: [], 25 | } 26 | } 27 | 28 | clone() { 29 | const manager = new PrintContextManager() 30 | 31 | manager.recursive = true 32 | manager.context = { 33 | parentNodes: this.context.parentNodes.slice(0), 34 | contextStack: this.context.contextStack.slice(0), 35 | } 36 | 37 | return manager 38 | } 39 | 40 | addParentNode(node: ParentNode) { 41 | this.context.parentNodes.push(node) 42 | } 43 | 44 | popParentNode() { 45 | return this.context.parentNodes.pop() 46 | } 47 | 48 | addContext(currentContext: CurrentContext) { 49 | this.context.contextStack.push(currentContext) 50 | } 51 | 52 | popContext() { 53 | return this.context.contextStack.pop() 54 | } 55 | 56 | isWithinInterface(): boolean { 57 | return !!this.context.parentNodes.find((x) => x.tag === Interface.tag) 58 | } 59 | 60 | isWithinFunction(): boolean { 61 | return !!this.context.parentNodes.find((x) => x.tag === FunctionSignature.tag) 62 | } 63 | 64 | isWithinTypeAlias(): boolean { 65 | return !!this.context.parentNodes.find((x) => x.tag === TypeAlias.tag) 66 | } 67 | 68 | isWithinExtension(): boolean { 69 | return !!this.context.contextStack.find((x) => x === 'Extension') 70 | } 71 | 72 | isWithinReturn(): boolean { 73 | return !!this.context.contextStack.find((x) => x === 'Return') 74 | } 75 | 76 | isWithinProperty(): boolean { 77 | return !!this.context.contextStack.find((x) => x === 'Property') 78 | } 79 | 80 | isWithinTypeParam(): boolean { 81 | return !!this.context.contextStack.find((x) => x === 'TypeParam') 82 | } 83 | 84 | isWithinFunctionParam(): boolean { 85 | return !!this.context.contextStack.find((x) => x === 'FunctionParam') 86 | } 87 | 88 | isWithinKind(): boolean { 89 | return !!this.context.contextStack.find((x) => x === 'Kind') 90 | } 91 | 92 | isWithinArray(): boolean { 93 | return !!this.context.contextStack.find((x) => x === 'Array') 94 | } 95 | 96 | shouldPrintFunctionName(): boolean { 97 | return !this.isWithinReturn() && !this.isWithinProperty() && !this.isWithinFunctionParam() 98 | } 99 | 100 | shouldUseColon(): boolean { 101 | if (this.recursive) { 102 | this.recursive = false 103 | 104 | return true 105 | } 106 | 107 | return ( 108 | !this.isWithinProperty() && 109 | !this.isWithinReturn() && 110 | !this.isWithinExtension() && 111 | !this.isWithinFunctionParam() 112 | ) 113 | } 114 | 115 | getParentNode() { 116 | return this.context.parentNodes[this.context.parentNodes.length - 1] 117 | } 118 | 119 | getCurrentContext() { 120 | return this.context.contextStack[this.context.contextStack.length - 1] 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Typeclass/Compactable.ts: -------------------------------------------------------------------------------- 1 | import { HKT, HKT10, HKT2, HKT3, HKT4, HKT5, HKT6, HKT7, HKT8, HKT9 } from '../HKT.js' 2 | 3 | import { 4 | Compact, 5 | Compact1, 6 | Compact10, 7 | Compact2, 8 | Compact2EC, 9 | Compact3, 10 | Compact3EC, 11 | Compact3RC, 12 | Compact3REC, 13 | Compact4, 14 | Compact4EC, 15 | Compact4RC, 16 | Compact4REC, 17 | Compact4SC, 18 | Compact4SEC, 19 | Compact4SRC, 20 | Compact4SREC, 21 | Compact5, 22 | Compact6, 23 | Compact7, 24 | Compact8, 25 | Compact9, 26 | } from './Compact.js' 27 | import { 28 | Separate, 29 | Separate1, 30 | Separate10, 31 | Separate2, 32 | Separate2EC, 33 | Separate3, 34 | Separate3EC, 35 | Separate3RC, 36 | Separate3REC, 37 | Separate4, 38 | Separate4EC, 39 | Separate4RC, 40 | Separate4REC, 41 | Separate4SC, 42 | Separate4SEC, 43 | Separate4SRC, 44 | Separate4SREC, 45 | Separate5, 46 | Separate6, 47 | Separate7, 48 | Separate8, 49 | Separate9, 50 | } from './Separate.js' 51 | 52 | export interface Compactable extends Compact, Separate {} 53 | 54 | export interface Compactable1 extends Compact1, Separate1 {} 55 | 56 | export interface Compactable2 extends Compact2, Separate2 {} 57 | 58 | export interface Compactable2EC extends Compact2EC, Separate2EC {} 59 | 60 | export interface Compactable3 extends Compact3, Separate3 {} 61 | 62 | export interface Compactable3RC extends Compact3RC, Separate3RC {} 63 | 64 | export interface Compactable3REC 65 | extends Compact3REC, 66 | Separate3REC {} 67 | 68 | export interface Compactable3EC extends Compact3EC, Separate3EC {} 69 | 70 | export interface Compactable4 extends Compact4, Separate4 {} 71 | 72 | export interface Compactable4SREC 73 | extends Compact4SREC, 74 | Separate4SREC {} 75 | 76 | export interface Compactable4SC extends Compact4SC, Separate4SC {} 77 | 78 | export interface Compactable4SRC 79 | extends Compact4SRC, 80 | Separate4SRC {} 81 | 82 | export interface Compactable4RC extends Compact4RC, Separate4RC {} 83 | 84 | export interface Compactable4SEC 85 | extends Compact4SEC, 86 | Separate4SEC {} 87 | 88 | export interface Compactable4REC 89 | extends Compact4REC, 90 | Separate4REC {} 91 | 92 | export interface Compactable4EC extends Compact4EC, Separate4EC {} 93 | 94 | export interface Compactable5 extends Compact5, Separate5 {} 95 | 96 | export interface Compactable6 extends Compact6, Separate6 {} 97 | 98 | export interface Compactable7 extends Compact7, Separate7 {} 99 | 100 | export interface Compactable8 extends Compact8, Separate8 {} 101 | 102 | export interface Compactable9 extends Compact9, Separate9 {} 103 | 104 | export interface Compactable10 extends Compact10, Separate10 {} 105 | -------------------------------------------------------------------------------- /src/string.ts: -------------------------------------------------------------------------------- 1 | import { NonEmptyArray, isNonEmpty } from './NonEmptyArray.js' 2 | import { Refinement } from './Refinement.js' 3 | import * as A from './Typeclass/Associative.js' 4 | import * as Sh from './Typeclass/Debug.js' 5 | import * as E from './Typeclass/Eq.js' 6 | import * as I from './Typeclass/Identity.js' 7 | import * as O from './Typeclass/Ord.js' 8 | import { pipe } from './function.js' 9 | 10 | export const equals: E.Eq['equals'] = (x, y) => x === y 11 | 12 | export const Eq: E.Eq = { 13 | equals, 14 | } 15 | 16 | export const concat: A.Associative['concat'] = (x, y) => x + y 17 | 18 | export const Associative: A.Associative = { 19 | concat, 20 | } 21 | 22 | export const empty = '' as const 23 | 24 | export const Identity: I.Identity = { 25 | ...Associative, 26 | id: empty, 27 | } 28 | 29 | export const Ord: O.Ord = O.fromCompare((f, s) => (f < s ? -1 : f > s ? 1 : 0)) 30 | 31 | export const Debug: Sh.Debug = { 32 | debug: (s) => JSON.stringify(s), // Use JSON.stringify to ensure outputs valid JSON 33 | } 34 | 35 | export const isString: Refinement = (input: unknown): input is string => 36 | typeof input === 'string' 37 | 38 | export const isEmpty = (s: string): s is typeof empty => s.length === 0 39 | 40 | export const toUpperCase = (s: S): Uppercase => s.toUpperCase() as Uppercase 41 | export const toLowerCase = (s: S): Lowercase => s.toLowerCase() as Lowercase 42 | 43 | export const capitalize = (s: S): Capitalize => 44 | (s.length > 0 ? s[0].toUpperCase() + s.slice(1) : s) as Capitalize 45 | 46 | export const replace = 47 | (searchValue: string | RegExp, replaceValue: string) => 48 | (s: string): string => 49 | s.replace(searchValue, replaceValue) 50 | 51 | export const trim = (s: string): string => s.trim() 52 | export const trimStart = (s: string): string => s.trimStart() 53 | export const trimEnd = (s: string): string => s.trimEnd() 54 | 55 | export const slice = 56 | (start: number, end: number) => 57 | (s: string): string => 58 | s.slice(start, end) 59 | 60 | export const size = (s: string): number => s.length 61 | 62 | export const split = 63 | (separator: string | RegExp) => 64 | (s: string): NonEmptyArray => { 65 | const out = s.split(separator) 66 | 67 | return isNonEmpty(out) ? out : [s] 68 | } 69 | 70 | export const includes = 71 | (searchString: string, position?: number) => 72 | (s: string): boolean => 73 | s.includes(searchString, position) 74 | 75 | export const startsWith = 76 | (searchString: string, position?: number) => 77 | (s: string): boolean => 78 | s.startsWith(searchString, position) 79 | 80 | export const endsWith = 81 | (searchString: string, position?: number) => 82 | (s: string): boolean => 83 | s.endsWith(searchString, position) 84 | 85 | export const padStart = 86 | (maxLength: number, fillString?: string) => 87 | (s: string): string => 88 | s.padStart(maxLength, fillString) 89 | 90 | export const padEnd = 91 | (maxLength: number, fillString?: string) => 92 | (s: string): string => 93 | s.padEnd(maxLength, fillString) 94 | 95 | export const pad = 96 | (maxLength: number, fillString?: string) => 97 | (s: string): string => 98 | pipe( 99 | s, 100 | padStart(Math.round(maxLength / 2), fillString), 101 | padEnd(Math.floor(maxLength / 2), fillString), 102 | ) 103 | -------------------------------------------------------------------------------- /src/Typeclass/Identity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HKT, 3 | HKT10, 4 | HKT2, 5 | HKT3, 6 | HKT4, 7 | HKT5, 8 | HKT6, 9 | HKT7, 10 | HKT8, 11 | HKT9, 12 | Kind, 13 | Kind10, 14 | Kind2, 15 | Kind3, 16 | Kind4, 17 | Kind5, 18 | Kind6, 19 | Kind7, 20 | Kind8, 21 | Kind9, 22 | } from '../HKT.js' 23 | import { constant, pipe } from '../function.js' 24 | 25 | import { Associative } from './Associative.js' 26 | import { orElse } from './AssociativeEither.js' 27 | import { 28 | Covariant, 29 | Covariant1, 30 | Covariant10, 31 | Covariant2, 32 | Covariant3, 33 | Covariant4, 34 | Covariant5, 35 | Covariant6, 36 | Covariant7, 37 | Covariant8, 38 | Covariant9, 39 | } from './Covariant.js' 40 | import { 41 | IdentityEither, 42 | IdentityEither1, 43 | IdentityEither10, 44 | IdentityEither2, 45 | IdentityEither3, 46 | IdentityEither4, 47 | IdentityEither5, 48 | IdentityEither6, 49 | IdentityEither7, 50 | IdentityEither8, 51 | IdentityEither9, 52 | } from './IdentityEither.js' 53 | 54 | export interface Identity extends Associative { 55 | readonly id: A 56 | } 57 | 58 | export function fromIdentityEitherCovariant( 59 | IEC: IdentityEither10 & Covariant10, 60 | ): () => Identity> 61 | 62 | export function fromIdentityEitherCovariant( 63 | IEC: IdentityEither9 & Covariant9, 64 | ): () => Identity> 65 | 66 | export function fromIdentityEitherCovariant( 67 | IEC: IdentityEither8 & Covariant8, 68 | ): () => Identity> 69 | 70 | export function fromIdentityEitherCovariant( 71 | IEC: IdentityEither7 & Covariant7, 72 | ): () => Identity> 73 | 74 | export function fromIdentityEitherCovariant( 75 | IEC: IdentityEither6 & Covariant6, 76 | ): () => Identity> 77 | 78 | export function fromIdentityEitherCovariant( 79 | IEC: IdentityEither5 & Covariant5, 80 | ): () => Identity> 81 | 82 | export function fromIdentityEitherCovariant( 83 | IEC: IdentityEither4 & Covariant4, 84 | ): () => Identity> 85 | 86 | export function fromIdentityEitherCovariant( 87 | IEC: IdentityEither3 & Covariant3, 88 | ): () => Identity> 89 | 90 | export function fromIdentityEitherCovariant( 91 | IEC: IdentityEither2 & Covariant2, 92 | ): () => Identity> 93 | 94 | export function fromIdentityEitherCovariant( 95 | IEC: IdentityEither1 & Covariant1, 96 | ): () => Identity> 97 | 98 | export function fromIdentityEitherCovariant( 99 | IEC: IdentityEither & Covariant, 100 | ): () => Identity> 101 | 102 | export function fromIdentityEitherCovariant( 103 | IEC: IdentityEither & Covariant, 104 | ): () => Identity> { 105 | const orElse_ = orElse(IEC) 106 | const id = { 107 | id: IEC.bottom, 108 | concat: (f: Kind, s: Kind) => 109 | pipe( 110 | f, 111 | orElse_(() => s), 112 | ), 113 | } 114 | 115 | return constant(id) 116 | } 117 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # HKT-TS 2 | 3 | `HKT-TS` is an encoding for Higher-Kinded Types (HKT) for TypeScript, with composable Typclasses to describe the majority of functionality you'll be using to write your programs. 4 | 5 | It is principled, based primarily in [Type Theory](https://en.wikipedia.org/wiki/Type_theory) and [Alegbra](https://en.wikipedia.org/wiki/Algebra), but also with roots in [Category Theory](https://en.wikipedia.org/wiki/Category_theory). 6 | 7 | If you've ever used a compiler for a strictly-typed language like TypeScript before, the annotations you're writing already follow all of these rules, and you likely know more about 8 | it already than you may realize. If something is an Algebra, it can be solved, like how your type-checker solves for the correctness of your program. 9 | 10 | The laws required to build something like a type-checker are generally applicable to all programming -- this is where HKT-TS comes in. HKT-TS provides the highest level of abstractions possible 11 | within TypeScript and will allow you to place composition at the forefront of programs. It will allow you to rapidly compose small solutions into larger solutions with an emphasis on type safety. 12 | 13 | Right now there's not a lot to see besides the code itself, but it is my goal to make learning materials accessible on all of the topics above as well as the contents of this 14 | library -- check back soon for more. 15 | 16 | ## Acknowledgments 17 | 18 | HKT-TS has been greatly influenced by numerous other open-source projects, the biggest being the Scala library [ZIO-Prelude](https://zio.github.io/zio-prelude/) 19 | which is the pioneer of a brand new way to blend Category Theory with programming languages that aren't Haskell. 20 | 21 | Within the TypeScript space, I've been inspired and taken influence from [Effect-TS](https://github.com/Effect-TS/core) which offers a port of a larger portion of ZIO including ZIO-Prelude. 22 | If you're interested in powerful effects that are built upon abstractions like these, with a strong standard library, check them out. Also if you love FP, then check out the amazing things they're doing with [TS+](https://dev.to/matechs/the-case-for-ts-18b3) for functional programmers. I look forward to seeing it continue to mature! 23 | 24 | Last but certainly not least, I have to give a huge shoutout to [fp-ts](https://github.com/gcanti/fp-ts), another HKT encoding for TypeScript that provides a Haskell-like 25 | expression of Category Theory with a rich standard library and ecosystem, which I've been using for a few years now. It's helped me greatly to dive into the world of Category 26 | Theory as it is applied to programming, but after seeing [this talk](https://www.youtube.com/watch?v=OwmHgL9F_9Q) during the beginning months of COVID, I've constantly been questioning if it's the easiest approach to learn and teach. 27 | 28 | HKT-TS is the culmination of my attempts to learn this new set of TypeClasses and what they're capable of. 29 | 30 | ## Installation 31 | 32 | NOTE: For the best experience you'll want to use typescript >= 4.7 to be able to use the `NodeNext` moduleResolution mode to support the package exports defined for this project. All examples are written in this format. Otherwise `hkt-ts/cjs` or `hkt-ts/esm` will be needed to have type-safe imports with TypeScript. 33 | 34 | ```sh 35 | # NPM 36 | npm i --save hkt-ts 37 | 38 | # Yarn 39 | yarn add hkt-ts 40 | 41 | # PNPM 42 | pnpm add hkt-ts 43 | ``` 44 | 45 | ## Related Links 46 | 47 | - [zio-prelude](https://zio.github.io/zio-prelude/) 48 | - [Effect-TS](https://github.com/Effect-TS/core) 49 | - [fp-ts](https://github.com/gcanti/fp-ts) 50 | - 51 | 52 | -------------------------------------------------------------------------------- /src/Typeclass/IdentityFlatten.ts: -------------------------------------------------------------------------------- 1 | import { HKT, HKT10, HKT2, HKT3, HKT4, HKT5, HKT6, HKT7, HKT8, HKT9 } from '../HKT.js' 2 | 3 | import { 4 | AssociativeFlatten, 5 | AssociativeFlatten1, 6 | AssociativeFlatten10, 7 | AssociativeFlatten2, 8 | AssociativeFlatten2EC, 9 | AssociativeFlatten3, 10 | AssociativeFlatten3EC, 11 | AssociativeFlatten3RC, 12 | AssociativeFlatten3REC, 13 | AssociativeFlatten4, 14 | AssociativeFlatten4EC, 15 | AssociativeFlatten4RC, 16 | AssociativeFlatten4REC, 17 | AssociativeFlatten4SC, 18 | AssociativeFlatten4SEC, 19 | AssociativeFlatten4SRC, 20 | AssociativeFlatten4SREC, 21 | AssociativeFlatten5, 22 | AssociativeFlatten6, 23 | AssociativeFlatten7, 24 | AssociativeFlatten8, 25 | AssociativeFlatten9, 26 | } from './AssociativeFlatten.js' 27 | import { 28 | Top, 29 | Top1, 30 | Top10, 31 | Top2, 32 | Top2EC, 33 | Top3, 34 | Top3EC, 35 | Top3RC, 36 | Top3REC, 37 | Top4, 38 | Top4EC, 39 | Top4RC, 40 | Top4REC, 41 | Top4SC, 42 | Top4SEC, 43 | Top4SRC, 44 | Top4SREC, 45 | Top5, 46 | Top6, 47 | Top7, 48 | Top8, 49 | Top9, 50 | } from './Top.js' 51 | export interface IdentityFlatten extends AssociativeFlatten, Top {} 52 | 53 | export interface IdentityFlatten1 extends AssociativeFlatten1, Top1 {} 54 | 55 | export interface IdentityFlatten2 extends AssociativeFlatten2, Top2 {} 56 | 57 | export interface IdentityFlatten2EC 58 | extends AssociativeFlatten2EC, 59 | Top2EC {} 60 | 61 | export interface IdentityFlatten3 extends AssociativeFlatten3, Top3 {} 62 | 63 | export interface IdentityFlatten3RC 64 | extends AssociativeFlatten3RC, 65 | Top3RC {} 66 | 67 | export interface IdentityFlatten3REC 68 | extends AssociativeFlatten3REC, 69 | Top3REC {} 70 | 71 | export interface IdentityFlatten3EC 72 | extends AssociativeFlatten3EC, 73 | Top3EC {} 74 | 75 | export interface IdentityFlatten4 extends AssociativeFlatten4, Top4 {} 76 | 77 | export interface IdentityFlatten4SREC 78 | extends AssociativeFlatten4SREC, 79 | Top4SREC {} 80 | 81 | export interface IdentityFlatten4SC 82 | extends AssociativeFlatten4SC, 83 | Top4SC {} 84 | 85 | export interface IdentityFlatten4SRC 86 | extends AssociativeFlatten4SRC, 87 | Top4SRC {} 88 | 89 | export interface IdentityFlatten4RC 90 | extends AssociativeFlatten4RC, 91 | Top4RC {} 92 | 93 | export interface IdentityFlatten4SEC 94 | extends AssociativeFlatten4SEC, 95 | Top4SEC {} 96 | 97 | export interface IdentityFlatten4REC 98 | extends AssociativeFlatten4REC, 99 | Top4REC {} 100 | 101 | export interface IdentityFlatten4EC 102 | extends AssociativeFlatten4EC, 103 | Top4EC {} 104 | 105 | export interface IdentityFlatten5 extends AssociativeFlatten5, Top5 {} 106 | 107 | export interface IdentityFlatten6 extends AssociativeFlatten6, Top6 {} 108 | 109 | export interface IdentityFlatten7 extends AssociativeFlatten7, Top7 {} 110 | 111 | export interface IdentityFlatten8 extends AssociativeFlatten8, Top8 {} 112 | 113 | export interface IdentityFlatten9 extends AssociativeFlatten9, Top9 {} 114 | 115 | export interface IdentityFlatten10 extends AssociativeFlatten10, Top10 {} 116 | -------------------------------------------------------------------------------- /src/Typeclass/IdentityEither.ts: -------------------------------------------------------------------------------- 1 | import { HKT, HKT10, HKT2, HKT3, HKT4, HKT5, HKT6, HKT7, HKT8, HKT9 } from '../HKT.js' 2 | 3 | import { 4 | AssociativeEither, 5 | AssociativeEither1, 6 | AssociativeEither10, 7 | AssociativeEither2, 8 | AssociativeEither2EC, 9 | AssociativeEither3, 10 | AssociativeEither3EC, 11 | AssociativeEither3RC, 12 | AssociativeEither3REC, 13 | AssociativeEither4, 14 | AssociativeEither4EC, 15 | AssociativeEither4RC, 16 | AssociativeEither4REC, 17 | AssociativeEither4SC, 18 | AssociativeEither4SEC, 19 | AssociativeEither4SRC, 20 | AssociativeEither4SREC, 21 | AssociativeEither5, 22 | AssociativeEither6, 23 | AssociativeEither7, 24 | AssociativeEither8, 25 | AssociativeEither9, 26 | } from './AssociativeEither.js' 27 | import { 28 | Bottom, 29 | Bottom1, 30 | Bottom10, 31 | Bottom2, 32 | Bottom2EC, 33 | Bottom3, 34 | Bottom3EC, 35 | Bottom3RC, 36 | Bottom3REC, 37 | Bottom4, 38 | Bottom4EC, 39 | Bottom4RC, 40 | Bottom4REC, 41 | Bottom4SC, 42 | Bottom4SEC, 43 | Bottom4SRC, 44 | Bottom4SREC, 45 | Bottom5, 46 | Bottom6, 47 | Bottom7, 48 | Bottom8, 49 | Bottom9, 50 | } from './Bottom.js' 51 | export interface IdentityEither extends AssociativeEither, Bottom {} 52 | 53 | export interface IdentityEither1 extends AssociativeEither1, Bottom1 {} 54 | 55 | export interface IdentityEither2 extends AssociativeEither2, Bottom2 {} 56 | 57 | export interface IdentityEither2EC 58 | extends AssociativeEither2EC, 59 | Bottom2EC {} 60 | 61 | export interface IdentityEither3 extends AssociativeEither3, Bottom3 {} 62 | 63 | export interface IdentityEither3RC 64 | extends AssociativeEither3RC, 65 | Bottom3RC {} 66 | 67 | export interface IdentityEither3REC 68 | extends AssociativeEither3REC, 69 | Bottom3REC {} 70 | 71 | export interface IdentityEither3EC 72 | extends AssociativeEither3EC, 73 | Bottom3EC {} 74 | 75 | export interface IdentityEither4 extends AssociativeEither4, Bottom4 {} 76 | 77 | export interface IdentityEither4SREC 78 | extends AssociativeEither4SREC, 79 | Bottom4SREC {} 80 | 81 | export interface IdentityEither4SC 82 | extends AssociativeEither4SC, 83 | Bottom4SC {} 84 | 85 | export interface IdentityEither4SRC 86 | extends AssociativeEither4SRC, 87 | Bottom4SRC {} 88 | 89 | export interface IdentityEither4RC 90 | extends AssociativeEither4RC, 91 | Bottom4RC {} 92 | 93 | export interface IdentityEither4SEC 94 | extends AssociativeEither4SEC, 95 | Bottom4SEC {} 96 | 97 | export interface IdentityEither4REC 98 | extends AssociativeEither4REC, 99 | Bottom4REC {} 100 | 101 | export interface IdentityEither4EC 102 | extends AssociativeEither4EC, 103 | Bottom4EC {} 104 | 105 | export interface IdentityEither5 extends AssociativeEither5, Bottom5 {} 106 | 107 | export interface IdentityEither6 extends AssociativeEither6, Bottom6 {} 108 | 109 | export interface IdentityEither7 extends AssociativeEither7, Bottom7 {} 110 | 111 | export interface IdentityEither8 extends AssociativeEither8, Bottom8 {} 112 | 113 | export interface IdentityEither9 extends AssociativeEither9, Bottom9 {} 114 | 115 | export interface IdentityEither10 extends AssociativeEither10, Bottom10 {} 116 | -------------------------------------------------------------------------------- /src/Typeclass/Debug.ts: -------------------------------------------------------------------------------- 1 | import { HKT, Params } from '../HKT.js' 2 | import { Json } from '../Json.js' 3 | import { Include } from '../common.js' 4 | import { flow } from '../function.js' 5 | 6 | import * as AB from './AssociativeBoth.js' 7 | import * as AE from './AssociativeEither.js' 8 | import { Contravariant1 } from './Contravariant.js' 9 | import * as IB from './IdentityBoth.js' 10 | import { Top1 } from './Top.js' 11 | 12 | export interface Debug { 13 | readonly debug: (a: A) => string 14 | } 15 | 16 | export function Debug(debug: Debug['debug']): Debug { 17 | return { 18 | debug, 19 | } 20 | } 21 | 22 | export type InputOf = [T] extends [Debug] ? R : never 23 | 24 | export const struct = (shows: { readonly [K in keyof A]: Debug }): Debug<{ 25 | readonly [K in keyof A]: A[K] 26 | }> => 27 | Debug((a) => { 28 | let s = '{' 29 | for (const k in shows) { 30 | s += `"${k}":${shows[k].debug(a[k])},` 31 | } 32 | if (s.length > 1) { 33 | s = s.slice(0, -1) 34 | } 35 | s += '}' 36 | return s 37 | }) 38 | 39 | export const array = (debug: Debug): Debug> => 40 | Debug((t) => `[${t.map((a) => debug.debug(a)).join(', ')}]`) 41 | 42 | export const tuple = >( 43 | ...shows: { readonly [K in keyof A]: Debug } 44 | ): Debug> => Debug((t) => `[${t.map((a, i) => shows[i].debug(a)).join(', ')}]`) 45 | 46 | export const sum = 47 | >>() => 48 | (tag: T) => 49 | (eqs: SumDebugs): Debug => 50 | Debug((a) => (eqs as any)[a[tag]].debug(a)) 51 | 52 | export type SumDebugs>, T extends keyof A> = { 53 | readonly [K in KeysOf]: Debug> 54 | } 55 | 56 | type KeysOf = A[T] extends PropertyKey ? A[T] : never 57 | 58 | type FindType = Include 59 | 60 | export const lazy = (f: () => Debug): Debug => { 61 | let s: Debug 62 | 63 | return { 64 | debug: (a) => (s || (s = f())).debug(a), 65 | } 66 | } 67 | 68 | export const nullable = (debug: Debug): Debug => 69 | Debug((a) => (a === null ? 'null' : debug.debug(a))) 70 | 71 | export const optional = (debug: Debug): Debug => 72 | Debug((a) => (a === undefined ? 'undefined' : debug.debug(a))) 73 | 74 | export const Stringify: Debug = Debug((x) => JSON.stringify(x)) 75 | 76 | export const string: Debug = Stringify 77 | export const number: Debug = Stringify 78 | export const boolean: Debug = Stringify 79 | 80 | export interface DebugHKT extends HKT { 81 | readonly type: Debug 82 | } 83 | 84 | export const Contravariant: Contravariant1 = { 85 | contramap: (f) => (d) => ({ 86 | debug: flow(f, d.debug), 87 | }), 88 | } 89 | 90 | export const contramap = Contravariant.contramap 91 | 92 | export const AssociativeBoth: AB.AssociativeBoth1 = { 93 | both: (DB) => (DA) => tuple(DA, DB), 94 | } 95 | 96 | export const bothWith = AB.bothWith({ ...AssociativeBoth, ...Contravariant }) 97 | 98 | export const AssociativeEither: AE.AssociativeEither1 = { 99 | either: (DB) => (DA) => ({ 100 | debug: (either) => 101 | either.tag === 'Left' ? `Left<${DA.debug(either.left)}>` : `Right<${DB.debug(either.right)}>`, 102 | }), 103 | } 104 | 105 | export const eitherWith = AE.eitherWith({ ...AssociativeEither, ...Contravariant }) 106 | 107 | export const either = AssociativeEither.either 108 | 109 | export const Top: Top1 = { 110 | top: Stringify as Debug, 111 | } 112 | 113 | export const IdentityBoth: IB.IdentityBoth1 = { 114 | ...AssociativeBoth, 115 | ...Top, 116 | } 117 | --------------------------------------------------------------------------------