Created
March 18, 2022 12:17
-
-
Save benbenbenbenbenben/de024f86b0b3366f8d0d53416fdcd448 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
type Contract<V, A extends any[]> = (value: V) => (...args: A) => V; | |
const argsStack: any[] = []; | |
const Barrier = Symbol(); | |
export const contract = <V, A extends any[]>( | |
f: Contract<V, A> | |
): ((...args: A) => V) => { | |
const handler = (...contractArgs: A) => { | |
const value = argsStack.shift(); | |
if (value === Barrier) { | |
return f(undefined as unknown as V)(...contractArgs); | |
} | |
return f(value)(...contractArgs); | |
}; | |
return handler as (...args: A) => V; | |
}; | |
type EnsuredFunction<R> = () => R; | |
export const fun = <R, F extends EnsuredFunction<R>>(f: F) => { | |
const wrapper = (...args: any[]) => { | |
argsStack.push(...[...args, Barrier]); | |
try { | |
return f(); | |
} catch (e) { | |
throw e; | |
} finally { | |
argsStack.shift(); | |
} | |
}; | |
return wrapper as (...args: Parameters<F>) => R; | |
}; | |
describe("contract", () => { | |
const between = contract( | |
(value: number) => | |
(min: number, max: number): number => { | |
if (value < min || value > max) { | |
throw "input out of range"; | |
} | |
return value; | |
} | |
); | |
test("throw when x not between a-b", () => { | |
const addTwo0to100numbers = fun( | |
(a = between(0, 100), b = between(0, 100)) => { | |
return a + b; | |
} | |
); | |
expect(addTwo0to100numbers(100, 100)).toBe(200); | |
expect(addTwo0to100numbers(10, 100)).toBe(110); | |
expect(() => addTwo0to100numbers(-1, -1)).toThrow("input out of range"); | |
}); | |
const required = contract((value: number) => () => { | |
if (typeof value === "undefined") { | |
throw "value required"; | |
} | |
return value; | |
}); | |
test("required", () => { | |
const needs = fun((a = required(), x = required()) => { | |
return true; | |
}); | |
expect(() => needs()).toThrow("value required"); | |
const needsAndAdds = fun( | |
(a = between(0, 10), x = required(), b = between(0, 10)) => { | |
return "something"; | |
} | |
); | |
expect(needsAndAdds(10, 0, 10)).toBe("something"); | |
expect(() => needsAndAdds(2)).toThrow("value required"); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment