Last active
January 19, 2023 13:59
-
-
Save ElectricCoffee/915817349cca27aebe3e3a2a71da5549 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _ from 'lodash'; | |
import Fn from './functools'; | |
it('ignores all inputs', () => { | |
const actual = Fn.nop(800, 'hello there', []); | |
expect(actual).toBeUndefined(); | |
}); | |
it('only returns the constant', () => { | |
const fn = Fn.constant(8); | |
const expected = [8, 8, 8, 8]; | |
const actual = [1, 2, 3, 4].map(fn); | |
expect(actual).toEqual(expected); | |
}); | |
it('flips the inputs correctly', () => { | |
const fn = Fn.flip(Fn.num.sub); | |
const expected = 2 - 5; | |
const actual = fn(5, 2); | |
expect(actual).toBe(expected); | |
}); | |
it('applies left arg correctly', () => { | |
const fn = Fn.overLeft(Fn.str.cat, x => x.toUpperCase()); | |
const expected = 'BATman'; | |
const actual = fn('bat', 'man'); | |
expect(actual).toEqual(expected); | |
}); | |
it('applies right arg correctly', () => { | |
const fn = Fn.overRight(Fn.str.cat, JSON.stringify); | |
const expected = 'foo: ["bar"]'; | |
const actual = fn('foo: ', ['bar']); | |
expect(actual).toEqual(expected); | |
}); | |
it('applies both args correctly', () => { | |
const fn = Fn.over(Fn.num.add, Number); | |
const expected = 24; | |
const actual = fn('6', '18'); | |
expect(actual).toBe(expected); | |
}); | |
it('correctly applies two different functions to the same argument', () => { | |
const avg = Fn.fork1(Fn.num.div, _.sum, _.size); | |
const expected = (1 + 2 + 3) / 3; // in case of rounding errors; | |
const actual = avg([1, 2, 3]); | |
expect(actual).toBe(expected); | |
}); | |
it('correctly applies two different functions to both arguments', () => { | |
const fn = Fn.fork2(Fn.num.div, Fn.num.add, Fn.num.mul); | |
const expected = (2 + 3) / (2 * 3); | |
const actual = fn(2, 3); | |
expect(actual).toBe(expected); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _ from 'lodash'; | |
/* eslint-disable no-bitwise */ | |
namespace Fn { | |
/** No operation. A function that does nothing */ | |
export const nop = (..._ignore: any) => {}; | |
/** Identity function. Returns its own input */ | |
export const id = <T>(x: T) => x; | |
/** creates a function that returns a constant value `x` regardless of input */ | |
export const constant = | |
<T>(x: T) => | |
(..._ignore: any) => | |
x; | |
/** Constructs a new binary function with its arguments flipped. `f(a, b)` becomes `f'(b, a)` */ | |
export const flip = | |
<A, B, C>(f: (a: A, b: B) => C) => | |
(b: B, a: A) => | |
f(a, b); | |
/** | |
* Constructs a new function `f'(a, b)` which does the same as calling `f(g(a), b)`. | |
* It applies `g` to its left argument before running `f` | |
*/ | |
export const overLeft = | |
<A, B, C, D>(f: (c: C, b: B) => D, g: (a: A) => C) => | |
(a: A, b: B) => | |
f(g(a), b); | |
/** | |
* Constructs a new function `f'(a, b)` which does the same as calling `f(a, g(b))`. | |
* It applies `g` to its right argument before running `f` | |
*/ | |
export const overRight = | |
<A, B, C, D>(f: (a: A, c: C) => D, g: (b: B) => C) => | |
(a: A, b: B) => | |
f(a, g(b)); | |
/** | |
* Constructs a new function `f'(a, b)` which does the same as calling `f(g(a), g(b))`. | |
* It applies `g` to both arguments before running `f`. | |
* Essentially the opposite of `atop`. | |
*/ | |
export const over = | |
<AB, C, D>(f: (c1: C, c2: C) => D, g: (ab: AB) => C) => | |
(a: AB, b: AB) => | |
f(g(a), g(b)); | |
/** | |
* Constructs a new function `f'(a, b)` which does the same as calling `g(f(a, b))`. | |
* It applies `a` and `b` to `f` before calling `g`. | |
* Essentially the opposite of `over`. | |
*/ | |
export const atop = | |
<A, B, C, D>(f: (a: A, b: B) => C, g: (c: C) => D) => | |
(a: A, b: B) => | |
g(f(a, b)); | |
/** | |
* Creates a new function `f'(a)` which does the same as running `f(g(a), h(a))`. | |
* It runs `a` through `g` and `h` before running the results through `f`. | |
*/ | |
export const fork1 = | |
<A, B, C, D>(f: (b: B, c: C) => D, g: (a: A) => B, h: (a: A) => C) => | |
(a: A) => | |
f(g(a), h(a)); | |
export const compose = _.flow; | |
/** | |
* Creates a new function `f'(a, b)` which does the same as running `f(g(a, b), h(a, b))`. | |
* It runs `a` and `b` through `g` and `h` and feeds the results to `f`. | |
*/ | |
export const fork2 = | |
<A, B, C, D, E>( | |
f: (c: C, d: D) => E, | |
g: (a: A, b: B) => C, | |
h: (a: A, b: B) => D, | |
) => | |
(a: A, b: B) => | |
f(g(a, b), h(a, b)); | |
// a lot of the more advanced functions require binary functions of the form foo(a, b). | |
// None of the built-in binary operators follow this scheme. | |
export namespace num { | |
export const neg = (a: number) => -a; | |
export const add = (a: number, b: number) => a + b; | |
export const sub = (a: number, b: number) => a - b; | |
export const mul = (a: number, b: number) => a * b; | |
export const div = (a: number, b: number) => a / b; | |
} | |
export namespace bool { | |
export const not = (a: boolean) => !a; | |
export const and = (a: boolean, b: boolean) => a && b; | |
export const or = (a: boolean, b: boolean) => a || b; | |
} | |
export namespace bin { | |
export const not = (a: number) => ~a; | |
export const and = (a: number, b: number) => a & b; | |
export const or = (a: number, b: number) => a | b; | |
} | |
export namespace str { | |
export const cat = (a: string, b: string) => a + b; | |
} | |
} | |
export default Fn; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment