initial commit
This commit is contained in:
commit
d9daed0d09
14 changed files with 470 additions and 0 deletions
15
jsconfig.json
Normal file
15
jsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "es2020",
|
||||||
|
"target": "es6",
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"checkJs": true,
|
||||||
|
"paths": {
|
||||||
|
"/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
13
package.json
Normal file
13
package.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "izuna",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Rowan <rowan@kitsu.cafe> (https://kitsu.cafe)",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"description": ""
|
||||||
|
}
|
83
src/curry.js
Normal file
83
src/curry.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
* @template {(...args: any[]) => any} F
|
||||||
|
* @typedef {F extends ((...args: infer A) => any) ? A : never} Params
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {T extends [any, ...any[]] ? T[0] : never} Head
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []} Tail
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {T extends ([] | [any]) ? false : true} HasTail
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {{ 0: Last<Tail<T>>; 1: Head<T> }[HasTail<T> extends true ? 0 : 1]} Last
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {T['length']} Length
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template E
|
||||||
|
* @template {any[]} T
|
||||||
|
* @typedef {((head: E, ...args: T) => any) extends ((...args: infer U) => any) ? U : T} Prepend
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {number} N
|
||||||
|
* @template {any[]} T
|
||||||
|
* @template {any[]} [I = []]
|
||||||
|
* @typedef {{ 0: Drop<N, Tail<T>, Prepend<any, I>>; 1: T }[Length<I> extends N ? 1 : 0]} Drop
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template X, Y
|
||||||
|
* @typedef {X extends Y ? X : Y} Cast
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} P, R
|
||||||
|
* @typedef {<T extends any[]>(...args: Cast<T, Partial<P>>) => Drop<Length<T>, P> extends [any, ...any[]] ? Curried<Cast<Drop<Length<T>, P>, any[]>, R> : R} Curried
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} P, R
|
||||||
|
* @param {number} arity
|
||||||
|
* @param {(...args: P) => R} func
|
||||||
|
* @returns {Curried<P, R>}
|
||||||
|
*/
|
||||||
|
export function curryN(arity, func) {
|
||||||
|
return function curried(...args) {
|
||||||
|
if (args.length >= arity) {
|
||||||
|
return func.apply(this, args)
|
||||||
|
} else {
|
||||||
|
/** @type {Curried<P, R>} */
|
||||||
|
return function(...args2) {
|
||||||
|
return curried.apply(this, args.concat(args2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {any[]} P, R
|
||||||
|
* @param {(...args: P) => R} func
|
||||||
|
* @returns {Curried<P, R>}
|
||||||
|
*/
|
||||||
|
export function curry(func) {
|
||||||
|
return curryN(func.length, func)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
26
src/fantasy-land.js
Normal file
26
src/fantasy-land.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* @param {PropertyKey[]} methods
|
||||||
|
* @param {Fn} f
|
||||||
|
*/
|
||||||
|
export function dispatch(methods, f) {
|
||||||
|
return function() {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = Array.from(arguments)
|
||||||
|
const obj = last(args)
|
||||||
|
|
||||||
|
if (!isArray(obj)) {
|
||||||
|
for (let i = 0; i < obj.length; i++) {
|
||||||
|
const fn = obj[methods[i]]
|
||||||
|
if (isFn(fn)) {
|
||||||
|
return fn.apply(obj, args.slice(0, -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.apply(this, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
138
src/function.js
Normal file
138
src/function.js
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import { curry, curryN } from './curry.js'
|
||||||
|
import { concat, iter } from './list.js'
|
||||||
|
|
||||||
|
/** @import { Fn, Morphism, InferredMorphism, Predicate } from './types.js' */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T} x
|
||||||
|
* @returns {x}
|
||||||
|
*/
|
||||||
|
export const id = x => x
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {...Fn} fns
|
||||||
|
* @returns {(x: any) => any}
|
||||||
|
*/
|
||||||
|
export const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
|
||||||
|
|
||||||
|
export const compose = curry(
|
||||||
|
/**
|
||||||
|
* @template T, U, V
|
||||||
|
* @param {Morphism<U, V>} f
|
||||||
|
* @param {Morphism<T, U>} g
|
||||||
|
* @param {T} x
|
||||||
|
* @returns V
|
||||||
|
*/
|
||||||
|
(f, g, x) => chain(g, chain(f, x))
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T | T[]} a
|
||||||
|
* @Returns {T[]}
|
||||||
|
*/
|
||||||
|
export const liftA = a => Array.isArray(a) ? a : [a]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T, U, R
|
||||||
|
* @param {(x: T, y: U) => R} binary
|
||||||
|
* @param {T} x
|
||||||
|
* @param {U} y
|
||||||
|
* @returns {R}
|
||||||
|
*/
|
||||||
|
export const liftF = curry((binary, x, y) => binary(x, y))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} a
|
||||||
|
* @param {number} b
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const add = (a, b) => a + b
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T} x
|
||||||
|
* @returns {() => T}
|
||||||
|
*/
|
||||||
|
export const thunk = x => () => x
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T, U
|
||||||
|
* @template {(a: T, b: U, ...rest: any[]) => any} F
|
||||||
|
* @param {F} fn
|
||||||
|
* @returns {(b: U, a: T, ...rest: any[]) => any}
|
||||||
|
*/
|
||||||
|
export function flip(fn) {
|
||||||
|
return curryN(fn.length, function(a, b) {
|
||||||
|
const args = Array.prototype.slice.call(arguments, 0)
|
||||||
|
args[0] = b
|
||||||
|
args[1] = a
|
||||||
|
return fn.apply(this, args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ifElse = curry(
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {Predicate<T>} pred
|
||||||
|
* @param {InferredMorphism<T>} pass
|
||||||
|
* @param {InferredMorphism<T>} fail
|
||||||
|
* @param {T} x
|
||||||
|
*/
|
||||||
|
(pred, pass, fail, x) => (
|
||||||
|
pred(x) ? pass(x) : fail(x)))
|
||||||
|
|
||||||
|
export const when = curry(
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {Predicate<T>} pred
|
||||||
|
* @param {InferredMorphism<T>} pass
|
||||||
|
* @param {T} x
|
||||||
|
*/
|
||||||
|
(pred, pass, x) => ifElse(pred, pass, id, x))
|
||||||
|
|
||||||
|
export const unless = curry(
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {Predicate<T>} pred
|
||||||
|
* @param {InferredMorphism<T>} fail
|
||||||
|
* @param {T} x
|
||||||
|
*/
|
||||||
|
(pred, fail, x) => ifElse(pred, id, fail, x))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template A, B
|
||||||
|
* @template {Morphism<A, B>} [F=Morphism<A, B>]
|
||||||
|
* @typedef {(f: F, a: A) => B} StaticMorphism
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const ap = curry(
|
||||||
|
/**
|
||||||
|
* @template A, B
|
||||||
|
* @template {Morphism<A, B>} Ap
|
||||||
|
* @param {Morphism<A, B>} f
|
||||||
|
* @param {{ ap: Ap } | Ap} a
|
||||||
|
*/
|
||||||
|
(f, a) => {
|
||||||
|
const fs = liftA(dispatchF('ap', f))
|
||||||
|
const args = liftA(a)
|
||||||
|
|
||||||
|
const xs = fs.reduce((acc, f) => (
|
||||||
|
concat(acc, iter(map(f, args)))
|
||||||
|
), [])
|
||||||
|
|
||||||
|
return [...xs]
|
||||||
|
})
|
||||||
|
|
||||||
|
export const chain = curry(
|
||||||
|
function chain(f, a) {
|
||||||
|
})
|
||||||
|
|
||||||
|
export const map = curry((f, a) => {
|
||||||
|
})
|
||||||
|
|
||||||
|
export const reduce = curry((f, acc, xs) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
10
src/index.js
Normal file
10
src/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export * from './curry.js'
|
||||||
|
export * from './function.js'
|
||||||
|
export * from './list.js'
|
||||||
|
export * from './logic.js'
|
||||||
|
export * from './math.js'
|
||||||
|
export * from './object.js'
|
||||||
|
export * from './relation.js'
|
||||||
|
export * from './string.js'
|
||||||
|
export * from './type.js'
|
||||||
|
|
123
src/list.js
Normal file
123
src/list.js
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import { curry } from './curry.js'
|
||||||
|
import { inc } from './math.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T | Iterable<T> | Iterator<T>} value
|
||||||
|
* @returns {value is Iterable<T>}
|
||||||
|
*/
|
||||||
|
export function isIterable(value) {
|
||||||
|
return value[Symbol.iterator] != null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T | Iterable<T> | Iterator<T>} value
|
||||||
|
* @yields {T}
|
||||||
|
*/
|
||||||
|
export function* iter(value) {
|
||||||
|
if (isIterable(value)) {
|
||||||
|
yield* Iterator.from(value)
|
||||||
|
} else {
|
||||||
|
yield value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {...(Iterable<T> | Iterator<T>)} iterators
|
||||||
|
* @yields {T}
|
||||||
|
*/
|
||||||
|
export const concat = function*(...iterators) {
|
||||||
|
for (const iter of iterators) {
|
||||||
|
for (const item of Iterator.from(iter)) {
|
||||||
|
yield item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {number} i
|
||||||
|
* @param {T[] | Iterable<T>} a
|
||||||
|
* @returns {T}
|
||||||
|
*/
|
||||||
|
export const nth = (i, a) => {
|
||||||
|
if (Array.isArray(a)) {
|
||||||
|
return a[i]
|
||||||
|
} else {
|
||||||
|
return /** @type {T} */ (iter(a).find((_, n) => i === n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {number} n
|
||||||
|
* @param {T[] | Iterable<T>} a
|
||||||
|
* @returns {a}
|
||||||
|
*/
|
||||||
|
export const take = (n, a) => {
|
||||||
|
if ('slice' in a) {
|
||||||
|
return a.slice(0, n)
|
||||||
|
} else {
|
||||||
|
iter(a).take(n)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {number} n
|
||||||
|
* @param {T[] | Iterable<T>} a
|
||||||
|
* @returns {a}
|
||||||
|
*/
|
||||||
|
export const drop = (n, a) => {
|
||||||
|
if ('slice' in a) {
|
||||||
|
return a.slice(n)
|
||||||
|
} else {
|
||||||
|
iter(a).drop(n)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayLike} a
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const length = a => {
|
||||||
|
if ('length' in a) {
|
||||||
|
return a.length
|
||||||
|
} else {
|
||||||
|
return iter(a).reduce(inc, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T[] | Iterable<T>} a
|
||||||
|
* @returns {T}
|
||||||
|
*/
|
||||||
|
export const head = a => nth(0, a)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T[] | Iterable<T>} a
|
||||||
|
* @returns {a}
|
||||||
|
*/
|
||||||
|
export const tail = a => drop(1, a)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T[]} a
|
||||||
|
*/
|
||||||
|
export const last = a => nth(length(a) - 1, a)
|
||||||
|
|
||||||
|
export const prepend = curry(
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T} x
|
||||||
|
* @param {Iterable<T>} xs
|
||||||
|
*/
|
||||||
|
(x, xs) => concat([x], xs))
|
||||||
|
|
||||||
|
|
0
src/logic.js
Normal file
0
src/logic.js
Normal file
12
src/math.js
Normal file
12
src/math.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* @param {number} a
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const inc = a => a + 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} a
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const dec = a => a - 1
|
||||||
|
|
0
src/object.js
Normal file
0
src/object.js
Normal file
0
src/relation.js
Normal file
0
src/relation.js
Normal file
0
src/string.js
Normal file
0
src/string.js
Normal file
27
src/type.js
Normal file
27
src/type.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* @typedef {"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"} TypeOfValue
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T} x
|
||||||
|
* @returns {TypeOfValue}
|
||||||
|
*/
|
||||||
|
export const type = x => typeof x
|
||||||
|
|
||||||
|
export const is = curry(
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {FunctionConstructor} ctr
|
||||||
|
* @param {T} x
|
||||||
|
*/
|
||||||
|
(ctr, x) => x.constructor === ctr)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T} x
|
||||||
|
*/
|
||||||
|
export const isFn = x => type(x) === 'function'
|
||||||
|
|
||||||
|
export const isArray = Array.isArray
|
||||||
|
|
23
src/types.js
Normal file
23
src/types.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
export default {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {(...v: any) => any} Fn
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T, U
|
||||||
|
* @typedef {(value:T) => U} Morphism
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {<U>(value:T) => U} InferredMorphism
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @typedef {(value: T) => boolean} Predicate
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue