Last active
November 4, 2019 08:16
-
-
Save mackwic/301e0145b079465e95341c4bdd2ab1f2 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 { expect } from 'chai' | |
import { curry, isArity0, castToCurry20, castToCurry21 } from '../../src/domain/FunctionalProgramming' | |
describe("Domain | FunctionalProgramming", () => { | |
describe("#isArity0", () => { | |
it("should be true when function takes zero arguments", () => { | |
expect(isArity0(() => void 0)).to.be.true | |
}) | |
}) | |
describe("#curry", () => { | |
describe("arity 2", () => { | |
const add = (a, b) => `${a} + ${b}` | |
it("should be callable with all arguments at once", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const result = f1(1, 2) | |
// assert | |
expect(result).to.equal("1 + 2") | |
}) | |
it("should be callable with argument used one at once", () => { | |
// arrange | |
let f1 = curry(add) | |
// act | |
f1 = castToCurry21(f1) | |
const f2 = f1(12) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
const result = f2(2) | |
// assert | |
expect(result).to.equal("12 + 2") | |
}) | |
}) | |
describe("arity 3", () => { | |
const add = (a, b, c) => `${a} + ${b} - ${c}` | |
it("should be callable with all arguments at once", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const result = f1(1, 2, 3) | |
// assert | |
expect(result).to.equal("1 + 2 - 3") | |
}) | |
it("should be callable with 2 arguments used, then one", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const f2 = f1(12, 25) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
// if () { throw new Error("failure !") } | |
const result = f2(2) | |
// assert | |
expect(result).to.equal("12 + 25 - 2") | |
}) | |
it("should be callable with 1 arguments used, then 2", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const f2 = f1(12) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
const result = f2(23, 2) | |
// assert | |
expect(result).to.equal("12 + 23 - 2") | |
}) | |
it("should be callable with one argument used at once", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const f2 = f1(7) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
const f3 = f2(8) | |
if (typeof f3 == "string") { throw new Error("failure !") } | |
const result = f3(9) | |
// assert | |
expect(result).to.equal("7 + 8 - 9") | |
}) | |
}) | |
describe("arity 4", () => { | |
const add = (a, b, c, d) => `${a} + ${b} - ${c} * ${d}` | |
it("should be callable with all arguments at once", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const result = f1(1, 2, 3, 4) | |
// assert | |
expect(result).to.equal("1 + 2 - 3 * 4") | |
}) | |
it("should be callable with arguments used two by two", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const f2 = f1(12, 25) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
const result = f2(3, 45) | |
// assert | |
expect(result).to.equal("12 + 25 - 3 * 45") | |
}) | |
it("should be callable with arguments used one at one", () => { | |
// arrange | |
const f1 = curry(add) | |
// act | |
const f2 = f1(7) | |
if (typeof f2 == "string") { throw new Error("failure !") } | |
const f3 = f2(8) | |
if (typeof f3 == "string") { throw new Error("failure !") } | |
const result = f3(9) | |
// assert | |
expect(result).to.equal("7 + 8 - 9") | |
}) | |
}) | |
}) | |
}) |
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
export function isArityN(n: number, f: Function): boolean { | |
return f.length == n | |
} | |
export const isArityNC = (curry(isArityN) as Curry21<number, Function, boolean>) | |
export const isArity0 = isArityNC(0) | |
export const isArity1 = isArityNC(1) | |
export const isArity2 = isArityNC(2) | |
export const isArity3 = isArityNC(3) | |
export const isArity4 = isArityNC(4) | |
export type Curry20<A, B, Ret> = (a: A, b: B) => Ret | |
export type Curry21<A, B, Ret> = (a: A) => (b: B) => Ret | |
export type Curry2<A, B, Ret> = Curry20<A, B, Ret> | Curry21<A, B, Ret> | |
export type Curry30<A, B, C, Ret> = (a: A, b: B, c: C) => Ret | |
export type Curry31<A, B, C, Ret> = (a: A, b: B) => (c: C) => Ret | |
export type Curry32<A, B, C, Ret> = (a: A) => (b: B, c: C) => Ret | |
export type Curry33<A, B, C, Ret> = (a: A) => (b: B) => (c: C) => Ret | |
export type Curry40<A, B, C, D, Ret> = (a: A, b: B, c: C, d: D) => Ret | |
export type Curry41<A, B, C, D, Ret> = (a: A, b: B, c: C) => (d: D) => Ret | |
export type Curry42<A, B, C, D, Ret> = (a: A, b: B) => (c: C) => (d: D) => Ret | |
export type Curry43<A, B, C, D, Ret> = (a: A) => (b: B) => (c: C) => (d: D) => Ret | |
export function curry<A, Ret>(f: (a: A) => Ret): (a: A) => Ret | |
export function curry<A, B, Ret>(fn: (a: A, b?: B) => Ret): (Curry21<A, B, Ret> | Curry20<A, B, Ret>) | |
export function curry<A, B, C, Ret>(fn: (a: A, b?: B, c?: C) => Ret): Curry30<A, B, C, Ret> | Curry31<A, B, C, Ret> | Curry32<A, B, C, Ret> | Curry33<A, B, C, Ret> | |
export function curry<A, B, C, D, Ret>(fn: (a: A, b?: B, c?: C, d?: D) => Ret): Curry40<A, B, C, D, Ret> | Curry41<A, B, C, D, Ret> | Curry42<A, B, C, D, Ret> | Curry43<A, B, C, D, Ret> | |
export function curry(fn) { | |
const argN = fn.length; | |
return function (...args) { | |
if (args.length < argN) { | |
return curry(fn.bind(this, ...args)); | |
} else { | |
return fn.call(this, ...args); | |
} | |
} | |
} | |
export function tagCurry20<A, B, Ret>(f: Curry2<A, B, Ret>): f is Curry20<A, B, Ret> { | |
if (!isArity2(f)) { throw new Error("type tagging error !") } | |
return true | |
} | |
export function tagCurry21<A, B, Ret>(f: Curry2<A, B, Ret>): f is Curry21<A, B, Ret> { | |
if (!isArity1(f)) { throw new Error("type tagging error !") } | |
return true | |
} | |
export function castToCurry20<A, B, Ret>(f: Curry2<A, B, Ret>): Curry20<A, B, Ret> { | |
if (tagCurry20(f)) { | |
return f as Curry20<A, B, Ret> | |
} | |
} | |
export function castToCurry21<A, B, Ret>(f: Curry2<A, B, Ret>): Curry21<A, B, Ret> { | |
if (tagCurry21(f)) { | |
return f as Curry21<A, B, Ret> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment