add dispatch methods

This commit is contained in:
Rowan 2025-04-17 12:52:29 -05:00
parent e11a0870c2
commit f20cf6841a
7 changed files with 77 additions and 18 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules/

22
package-lock.json generated Normal file
View file

@ -0,0 +1,22 @@
{
"name": "izuna",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "izuna",
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"devDependencies": {
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git"
}
},
"node_modules/folktest": {
"version": "1.0.0",
"resolved": "git+https://git.kitsu.cafe/rowan/folktest.git#708d44f1215be33fcceba426029f44b4f963dbe5",
"dev": true,
"license": "GPL-3.0-or-later"
}
}
}

View file

@ -5,9 +5,12 @@
"type": "module", "type": "module",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "./test/index.js"
}, },
"keywords": [], "keywords": [],
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"description": "" "description": "",
"devDependencies": {
"folktest": "git+https://git.kitsu.cafe/rowan/folktest.git"
}
} }

View file

@ -1,6 +1,8 @@
import { isArray, isFn } from './type.js'
import { last } from './list.js'
/** /**
* @param {PropertyKey[]} methods * @param {PropertyKey[]} methods
* @param {Fn} f * @param {import('./types.js').Fn} f
*/ */
export function dispatch(methods, f) { export function dispatch(methods, f) {
return function() { return function() {
@ -12,7 +14,8 @@ export function dispatch(methods, f) {
const obj = last(args) const obj = last(args)
if (!isArray(obj)) { if (!isArray(obj)) {
for (let i = 0; i < obj.length; i++) { const len = methods.length
for (let i = 0; i < len; i++) {
const fn = obj[methods[i]] const fn = obj[methods[i]]
if (isFn(fn)) { if (isFn(fn)) {
return fn.apply(obj, args.slice(0, -1)) return fn.apply(obj, args.slice(0, -1))

View file

@ -1,5 +1,6 @@
import { curry, curryN } from './curry.js' import { curry, curryN } from './curry.js'
import { concat, iter } from './list.js' import { dispatch } from './fantasy-land.js'
import { concat as iconcat, iter } from './list.js'
/** @import { Fn, Morphism, InferredMorphism, Predicate } from './types.js' */ /** @import { Fn, Morphism, InferredMorphism, Predicate } from './types.js' */
@ -16,7 +17,11 @@ export const id = x => x
*/ */
export const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x) export const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
export const compose = curry( export const concat = curry(dispatch(['fantasy-land/concat', 'concat'],
(b, a) => iconcat(iter(a), iter(b))
))
export const compose = curry(dispatch(['fantasy-land/compose', 'compose'],
/** /**
* @template T, U, V * @template T, U, V
* @param {Morphism<U, V>} f * @param {Morphism<U, V>} f
@ -25,7 +30,7 @@ export const compose = curry(
* @returns V * @returns V
*/ */
(f, g, x) => chain(g, chain(f, x)) (f, g, x) => chain(g, chain(f, x))
) ))
/** /**
* @template T * @template T
@ -107,7 +112,7 @@ export const unless = curry(
* @typedef {(f: F, a: A) => B} StaticMorphism * @typedef {(f: F, a: A) => B} StaticMorphism
*/ */
export const ap = curry( export const ap = curry(dispatch(['fantasy-land/ap', 'ap'],
/** /**
* @template A, B * @template A, B
* @template {Morphism<A, B>} Ap * @template {Morphism<A, B>} Ap
@ -115,7 +120,7 @@ export const ap = curry(
* @param {{ ap: Ap } | Ap} a * @param {{ ap: Ap } | Ap} a
*/ */
(f, a) => { (f, a) => {
const fs = liftA(dispatchF('ap', f)) const fs = liftA(f)
const args = liftA(a) const args = liftA(a)
const xs = fs.reduce((acc, f) => ( const xs = fs.reduce((acc, f) => (
@ -123,16 +128,17 @@ export const ap = curry(
), []) ), [])
return [...xs] return [...xs]
}) }))
export const chain = curry( export const chain = curry(dispatch(['fantasy-land/chain', 'chain'],
function chain(f, a) { (f, a) => a.map(f)
}) ))
export const map = curry((f, a) => { export const map = curry(dispatch(['fantasy-land/map', 'map'],
}) (f, a) => a.map(f)
))
export const reduce = curry((f, acc, xs) => { export const reduce = curry(dispatch(['fantasy-land/reduce', 'reduce'],
(f, acc, xs) => xs.reduce(f, acc)
}) ))

7
test/index.js Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env node
import { TerminalRunner } from 'folktest'
import * as Tests from './units/index.js'
console.log(TerminalRunner(Tests).toString())

16
test/units/index.js Normal file
View file

@ -0,0 +1,16 @@
import { it, assert } from 'folktest'
import { dispatch } from '../../src/fantasy-land.js'
const obj = {
'test': a => console.log('test', a)
}
export const Tests = [
it('whatever', () => {
const f = dispatch(['fantasy-land/test', 'test'], (f, a) => {
console.log(f, a)
})
console.log(f(1, obj))
})
]