Skip to content

Instantly share code, notes, and snippets.

@benbenbenbenbenben
Created March 18, 2022 12:17
Show Gist options
  • Save benbenbenbenbenben/de024f86b0b3366f8d0d53416fdcd448 to your computer and use it in GitHub Desktop.
Save benbenbenbenbenben/de024f86b0b3366f8d0d53416fdcd448 to your computer and use it in GitHub Desktop.
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