update concat; add unit tests for it
This commit is contained in:
parent
b853fe8476
commit
4dabfd8a43
6 changed files with 92 additions and 71 deletions
|
@ -1,12 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "es2020",
|
||||
"module": "node",
|
||||
"target": "es6",
|
||||
"lib": ["esnext", "dom"],
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { curry, curryN } from './curry.js'
|
||||
import { dispatch } from './fantasy-land.js'
|
||||
import { head, isIterable, iterConcat, iter, tail } from './list.js'
|
||||
import { head, isIterable, concatIter, iter, tail } from './list.js'
|
||||
|
||||
/** @import { Fn, Morphism, InferredMorphism, Predicate } from './types.js' */
|
||||
|
||||
|
@ -13,10 +13,12 @@ export const id = x => x
|
|||
|
||||
export const concat = dispatch(['fantasy-land/concat', 'concat'],
|
||||
(b, a) => {
|
||||
if (isIterable(a) || isIterable(b)) {
|
||||
return iterConcat(iter(a), iter(b))
|
||||
} else {
|
||||
if (Array.isArray(a) && (Array.isArray(b) || !isIterable(b))) {
|
||||
return a.concat(b)
|
||||
} else if (Array.isArray(b) && (Array.isArray(a) || !isIterable(a))) {
|
||||
return b.concat(a)
|
||||
} else {
|
||||
return concatIter(iter(a), iter(b))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -122,7 +124,7 @@ export const ap = dispatch(['fantasy-land/ap', 'ap'],
|
|||
const args = liftA(a)
|
||||
|
||||
const xs = fs.reduce((acc, f) => (
|
||||
concat(acc, iter(map(f, args)))
|
||||
concat(acc, map(f, args))
|
||||
), [])
|
||||
|
||||
return [...xs]
|
||||
|
|
|
@ -29,7 +29,7 @@ export function* iter(value) {
|
|||
* @param {...(Iterable<T> | Iterator<T>)} iterators
|
||||
* @yields {T}
|
||||
*/
|
||||
export const iterConcat = function*(...iterators) {
|
||||
export const concatIter = function*(...iterators) {
|
||||
for (const iter of iterators) {
|
||||
for (const item of Iterator.from(iter)) {
|
||||
yield item
|
||||
|
|
62
test/units/dispatch.js
Normal file
62
test/units/dispatch.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { it, assert, assertEq } from 'folktest'
|
||||
import { dispatch } from '../../src/fantasy-land.js'
|
||||
|
||||
const test = (re, str = '') => re instanceof RegExp ? re.test(str) : new RegExp(re).test(str)
|
||||
|
||||
const assertErr = (f, err) => {
|
||||
try {
|
||||
f()
|
||||
} catch (e) {
|
||||
if (typeof err === 'string' || err instanceof RegExp) {
|
||||
assert(test(err, e.message), `${err} did not match ${e.message}`)
|
||||
} else if (typeof err === 'function') {
|
||||
assert(e.constructor === err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const expected = err ? `"${err}" ` : ""
|
||||
assert(false, `expecting error ${expected}but function did not throw`)
|
||||
}
|
||||
|
||||
const wrong = msg => assert(false, msg)
|
||||
|
||||
export const Dispatch = [
|
||||
it('should dispatch to any listed method or fallback', () => {
|
||||
const a1 = {
|
||||
['fantasy-land/test']: x => assertEq(x, 1),
|
||||
test: () => wrong(`a1['test'] should not have been called`)
|
||||
}
|
||||
|
||||
const f1 = dispatch(['fantasy-land/test', 'test'], (v, x) => {
|
||||
wrong(`f1 fallback should not have been called for ${JSON.stringify(x)}`)
|
||||
})
|
||||
|
||||
f1(1, a1)
|
||||
|
||||
delete a1['fantasy-land/test']
|
||||
|
||||
assertErr(() => {
|
||||
f1(1, a1)
|
||||
})
|
||||
|
||||
const f2 = dispatch(['test', 'fantasy-land/test'], (v, x) => {
|
||||
wrong(`f2 fallback should not have been called for ${JSON.stringify(x)}`)
|
||||
})
|
||||
|
||||
const a2 = {
|
||||
['fantasy-land/test']: () => wrong(`a2['fantasy-land/test'] should not have been called`),
|
||||
test: x => assertEq(x, 2)
|
||||
}
|
||||
|
||||
f2(2, a2)
|
||||
|
||||
const f3 = dispatch(['fantasy-land/test', 'test'], x => {
|
||||
assertEq(x, 3)
|
||||
})
|
||||
|
||||
f3(3, {})
|
||||
})
|
||||
]
|
||||
|
19
test/units/function.js
Normal file
19
test/units/function.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { it, assert, assertEq } from 'folktest'
|
||||
import { concat } from '../..//src/function.js'
|
||||
import { isIterable, iter } from '../..//src/list.js'
|
||||
|
||||
export const Functions = [
|
||||
it('concat', () => {
|
||||
assertEq(concat(1, []), [1])
|
||||
assertEq(concat([], 1), [1])
|
||||
assertEq(concat([2], [1]), [1, 2])
|
||||
const a = concat([2], iter([1]))
|
||||
assert(isIterable(a), 'concat([2], iter([1])) returned a non-iterable')
|
||||
assertEq([...a], [1, 2])
|
||||
|
||||
const b = concat(iter([2]), [1])
|
||||
assert(isIterable(b), 'concat(iter([2]), [1]) returned a non-iterable')
|
||||
assertEq([...b], [1, 2])
|
||||
})
|
||||
]
|
||||
|
|
@ -1,62 +1,3 @@
|
|||
import { it, assert, assertEq } from 'folktest'
|
||||
import { dispatch } from '../../src/fantasy-land.js'
|
||||
|
||||
const test = (re, str = '') => re instanceof RegExp ? re.test(str) : new RegExp(re).test(str)
|
||||
|
||||
const assertErr = (f, err) => {
|
||||
try {
|
||||
f()
|
||||
} catch (e) {
|
||||
if (typeof err === 'string' || err instanceof RegExp) {
|
||||
assert(test(err, e.message), `${err} did not match ${e.message}`)
|
||||
} else if (typeof err === 'function') {
|
||||
assert(e.constructor === err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const expected = err ? `"${err}" ` : ""
|
||||
assert(false, `expecting error ${expected}but function did not throw`)
|
||||
}
|
||||
|
||||
const wrong = msg => assert(false, msg)
|
||||
|
||||
export const Dispatch = [
|
||||
it('should dispatch to any listed method or fallback', () => {
|
||||
const a1 = {
|
||||
['fantasy-land/test']: x => assertEq(x, 1),
|
||||
test: () => wrong(`a1['test'] should not have been called`)
|
||||
}
|
||||
|
||||
const f1 = dispatch(['fantasy-land/test', 'test'], (v, x) => {
|
||||
wrong(`f1 fallback should not have been called for ${JSON.stringify(x)}`)
|
||||
})
|
||||
|
||||
f1(1, a1)
|
||||
|
||||
delete a1['fantasy-land/test']
|
||||
|
||||
assertErr(() => {
|
||||
f1(1, a1)
|
||||
})
|
||||
|
||||
const f2 = dispatch(['test', 'fantasy-land/test'], (v, x) => {
|
||||
wrong(`f2 fallback should not have been called for ${JSON.stringify(x)}`)
|
||||
})
|
||||
|
||||
const a2 = {
|
||||
['fantasy-land/test']: () => wrong(`a2['fantasy-land/test'] should not have been called`),
|
||||
test: x => assertEq(x, 2)
|
||||
}
|
||||
|
||||
f2(2, a2)
|
||||
|
||||
const f3 = dispatch(['fantasy-land/test', 'test'], x => {
|
||||
assertEq(x, 3)
|
||||
})
|
||||
|
||||
f3(3, {})
|
||||
})
|
||||
]
|
||||
export * from './dispatch.js'
|
||||
export * from './function.js'
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue