Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Commit

Permalink
Add unary variants
Browse files Browse the repository at this point in the history
  • Loading branch information
KSXGitHub committed Sep 23, 2019
1 parent 5edfb39 commit a00acf9
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 4 deletions.
48 changes: 48 additions & 0 deletions codegen/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ function composeValParams (quantity: number) {
return [begin, last].filter(Boolean).join(',\n')
}

function composeUnaryValParams (quantity: number) {
const sig = (arg: number, res: number) =>
`(x: T${arg}) => T${res}`

return Array
.from({ length: quantity - 1 })
.map((_, i) => ` f${i}: ${sig(i + 1, i)}`)
.join(',\n')
}

function pipeRetVal (quantity: number) {
return `T${quantity - 1}`
}
Expand All @@ -39,6 +49,14 @@ function pipeRetFunc (quantity: number) {
return `(...args: Args) => T${quantity - 1}`
}

function pipeRetUnaryFunc (quantity: number) {
return `(x: T0) => T${quantity - 1}`
}

function composeUnaryRetFunc (quantity: number) {
return `(x: T${quantity - 1}) => T0`
}

interface Gen {
(quantity: number, name: string): string
}
Expand Down Expand Up @@ -97,3 +115,33 @@ export function genComposeFuncOverload (quantity: number, name: string) {
}

export const genComposeFunc = mkgen(genComposeFuncOverload)

export function genPipeUnaryFuncOverload (quantity: number, name: string) {
const types = typeParams(quantity + 1)
const vals = pipeValParams(quantity + 1)
const rets = pipeRetUnaryFunc(quantity + 1)
return [
`export declare function ${name} <`,
types,
'> (',
vals,
`): ${rets};`
].join('\n')
}

export const genPipeUnaryFunc = mkgen(genPipeUnaryFuncOverload)

export function genComposeUnaryFuncOverload (quantity: number, name: string) {
const types = typeParams(quantity + 1)
const vals = composeUnaryValParams(quantity + 1)
const rets = composeUnaryRetFunc(quantity + 1)
return [
`export declare function ${name} <`,
types + ',',
'> (',
vals,
`): ${rets};`
].join('\n')
}

export const genComposeUnaryFunc = mkgen(genComposeUnaryFuncOverload)
14 changes: 12 additions & 2 deletions codegen/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import path from 'path'
import { writeFile } from 'fs-extra'
import { genPipeVal, genPipeFunc, genComposeFunc } from './gen'

import {
genPipeVal,
genPipeFunc,
genComposeFunc,
genPipeUnaryFunc,
genComposeUnaryFunc
} from './gen'

export async function main () {
const filename = path.resolve(__dirname, '../lib/index.d.ts')
Expand All @@ -14,7 +21,10 @@ export async function main () {
genPipeVal(quantity, 'pipe'),
genPipeFunc(quantity, 'pipeline'),
genComposeFunc(quantity, 'compose'),
'export { pipeline as composeRight }'
genPipeUnaryFunc(quantity, 'pipelineUnary'),
genComposeUnaryFunc(quantity, 'composeUnary'),
'export { pipeline as composeRight }',
'export { pipelineUnary as composeUnaryRight }'
].join('\n\n')

await writeFile(filename, content)
Expand Down
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@ export const pipeline = (...fns) =>
export const compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)))

export const pipelineUnary = (...fns) =>
fns.reduce((f, g) => x => g(f(x)))

export const composeUnary = (...fns) =>
fns.reduce((f, g) => x => f(g(x)))

export { pipeline as composeRight }
export { pipelineUnary as composeUnaryRight }
262 changes: 261 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { pipe, pipeline, compose, composeRight } from '..'
import {
pipe,
pipeline,
compose,
pipelineUnary,
composeUnary,
composeRight,
composeUnaryRight
} from '..'

describe('pipe', () => {
it('without function', () => {
Expand Down Expand Up @@ -333,6 +341,258 @@ describe('compose', () => {
})
})

describe('pipelineUnary', () => {
describe('with one function', () => {
const setup = (times = 1) => {
const x0 = Symbol('x0')
const x1 = Symbol('x1')
const f0 = jest.fn(() => x1)
const fn = pipelineUnary(f0)
const ys = Array.from({ length: times }).map(() => fn(x0))
return { x0, x1, f0, fn, ys }
}

it('calls provided function once', () => {
const { f0 } = setup()
expect(f0).toBeCalledTimes(1)
})

it('calls provided function every time the resulting function is called', () => {
const times = 5
const { f0 } = setup(times)
expect(f0).toBeCalledTimes(times)
})

it('passes arguments to provided function', () => {
const { x0, f0 } = setup()
expect(f0).toBeCalledWith(x0)
})

it('returns result of provided function', () => {
const { x1, ys: [y] } = setup()
expect(y).toBe(x1)
})
})

describe('with multiple functions', () => {
const setup = (times = 1) => {
const x0 = Symbol('x0')
const x1 = Symbol('x1')
const x2 = Symbol('x2')
const x3 = Symbol('x3')
const x4 = Symbol('x4')
const f0 = jest.fn(() => x1)
const f1 = jest.fn(() => x2)
const f2 = jest.fn(() => x3)
const f3 = jest.fn(() => x4)
const fn = pipelineUnary(f0, f1, f2, f3)
const ys = Array.from({ length: times }).map(() => fn(x0))
return { x0, x1, x2, x3, x4, f0, f1, f2, f3, fn, ys }
}

describe('calls each function once', () => {
it('f0', () => {
const { f0 } = setup()
expect(f0).toBeCalledTimes(1)
})

it('f1', () => {
const { f1 } = setup()
expect(f1).toBeCalledTimes(1)
})

it('f2', () => {
const { f2 } = setup()
expect(f2).toBeCalledTimes(1)
})

it('f3', () => {
const { f3 } = setup()
expect(f3).toBeCalledTimes(1)
})
})

describe('calls each function every time the resulting function is called', () => {
const times = 5

it('f0', () => {
const { f0 } = setup(times)
expect(f0).toBeCalledTimes(times)
})

it('f1', () => {
const { f1 } = setup(times)
expect(f1).toBeCalledTimes(times)
})

it('f2', () => {
const { f2 } = setup(times)
expect(f2).toBeCalledTimes(times)
})

it('f3', () => {
const { f3 } = setup(times)
expect(f3).toBeCalledTimes(times)
})
})

describe('passes result of previous function to next function', () => {
it('x0 → f0', () => {
const { x0, f0 } = setup()
expect(f0).toBeCalledWith(x0)
})

it('x1 → f1', () => {
const { x1, f1 } = setup()
expect(f1).toBeCalledWith(x1)
})

it('x2 → f2', () => {
const { x2, f2 } = setup()
expect(f2).toBeCalledWith(x2)
})

it('x3 → f3', () => {
const { x3, f3 } = setup()
expect(f3).toBeCalledWith(x3)
})
})

it('returns result of the last function', () => {
const { x4, ys: [y] } = setup()
expect(y).toBe(x4)
})
})
})

describe('composeUnary', () => {
describe('with one function', () => {
const setup = (times = 1) => {
const x0 = Symbol('x0')
const x1 = Symbol('x1')
const f0 = jest.fn(() => x1)
const fn = composeUnary(f0)
const ys = Array.from({ length: times }).map(() => fn(x0))
return { x0, x1, f0, fn, ys }
}

it('calls provided function once', () => {
const { f0 } = setup()
expect(f0).toBeCalledTimes(1)
})

it('calls provided function every time the resulting function is called', () => {
const times = 5
const { f0 } = setup(times)
expect(f0).toBeCalledTimes(times)
})

it('passes arguments to provided function', () => {
const { x0, f0 } = setup()
expect(f0).toBeCalledWith(x0)
})

it('returns result of provided function', () => {
const { x1, ys: [y] } = setup()
expect(y).toBe(x1)
})
})

describe('with multiple functions', () => {
const setup = (times = 1) => {
const x0 = Symbol('x0')
const x1 = Symbol('x1')
const x2 = Symbol('x2')
const x3 = Symbol('x3')
const x4 = Symbol('x4')
const f0 = jest.fn(() => x0)
const f1 = jest.fn(() => x1)
const f2 = jest.fn(() => x2)
const f3 = jest.fn(() => x3)
const fn = composeUnary(f0, f1, f2, f3)
const ys = Array.from({ length: times }).map(() => fn(x4))
return { x0, x1, x2, x3, x4, f0, f1, f2, f3, fn, ys }
}

describe('calls each function once', () => {
it('f0', () => {
const { f0 } = setup()
expect(f0).toBeCalledTimes(1)
})

it('f1', () => {
const { f1 } = setup()
expect(f1).toBeCalledTimes(1)
})

it('f2', () => {
const { f2 } = setup()
expect(f2).toBeCalledTimes(1)
})

it('f3', () => {
const { f3 } = setup()
expect(f3).toBeCalledTimes(1)
})
})

describe('calls each function every time the resulting function is called', () => {
const times = 5

it('f0', () => {
const { f0 } = setup(times)
expect(f0).toBeCalledTimes(times)
})

it('f1', () => {
const { f1 } = setup(times)
expect(f1).toBeCalledTimes(times)
})

it('f2', () => {
const { f2 } = setup(times)
expect(f2).toBeCalledTimes(times)
})

it('f3', () => {
const { f3 } = setup(times)
expect(f3).toBeCalledTimes(times)
})
})

describe('passes result of previous function to next function', () => {
it('x1 → f0', () => {
const { x1, f0 } = setup()
expect(f0).toBeCalledWith(x1)
})

it('x2 → f1', () => {
const { x2, f1 } = setup()
expect(f1).toBeCalledWith(x2)
})

it('x3 → f2', () => {
const { x3, f2 } = setup()
expect(f2).toBeCalledWith(x3)
})

it('x4 → f3', () => {
const { x4, f3 } = setup()
expect(f3).toBeCalledWith(x4)
})
})

it('returns result of the last function', () => {
const { x0, ys: [y] } = setup()
expect(y).toBe(x0)
})
})
})

it('composeRight is pipeline', () => {
expect(composeRight).toBe(pipeline)
})

it('composeUnaryRight is pipelineUnary', () => {
expect(composeUnaryRight).toBe(pipelineUnary)
})
Loading

0 comments on commit a00acf9

Please sign in to comment.