0.0.2 • Published 6 years ago

@lindem/overseer v0.0.2

Weekly downloads
-
License
CC0-1.0
Repository
github
Last release
6 years ago

Overseer

Control flow structures and runtime check helpers for untyped JavaScript.

Predicates

  • Predicates are functions that return boolean.
  • A special form used throughout this code is the unaryPredicate, which is a predicate that accepts only one value to check some assumption about that value.
  • A predicateGenerator is a function that creates unaryPredicate functions from some input.
  • multiple unaryPredicates can be combined with and and or, or negated with not
  • the lodash library is used throughout this library. It comes with multiple predicates already.

Example of how to combine predicates:

import {isInteger} from 'lodash';
import {makeGreaterThanPredicate} from '@lindem/overseer/generators/comparisons';

const isPositiveInteger = and(isInteger, makeGreaterThanPredicate(0));

Invocation Contracts

  • A higher-order-function which decorates another function to include invocation checks is called an invocationContractWrapper. This includes checks for each of the declared parameters, checks for arity (the number of parameters the function consumes), and checks for the return value.
  • Checks for parameters, arity and return values are optional.
  • Contracts will raise Errors (throw) if the contract is breached.

Example of an invocationContract:

import {isInteger} from 'lodash';
import {InvocationContractBuilder} from '@lindem/overseer/constructs/invocation-contract';
import {
  makeGreaterThanPredicate,
  makeStrictEqualityPredicate
} from '@lindem/overseer/generators/comparisons';

const isNonNegativeInteger = and(isInteger, makeGreaterThanOrEqualPredicate(0));
const isPositiveInteger = and(isInteger, makeGreaterThanPredicate(0));

const decorateUnaryIntegerToIntegerFunction = new InvocationContractBuilder()
  .setParameterPredicates([isNonNegativeInteger])
  .setParameterLength(1)
  .setReturnValuePredicate(isPositiveInteger)
  .build();

// ... elsewhere

function faculty(n) {
  return n <= 1 ? 1 : n * n - 1;
}

const checkedFaculty = decorateUnaryIntegerToIntegerFunction(faculty);

// ... elsewhere

checkedFaculty(3); // yields 6.
checkedFaculty('hi'); // error, "Contract Breached: Malformed Parameter"
checkedFaculty(2, 3); // error, "Contract Breached: This function requires 1 parameter..."
checkedFaculty(); // same as above

The advantage of doing it that way is that the code in the actual function can rely on all checking already being done. Depending on whether the contract is specified to that extent, all of the following can be reasonably assumed:

  • Assurances made to the decorated function:
    • Parameters passed specification tests.
    • No parameters are missing.
  • Assurances made to the caller:
    • The function return value passed its specification.

There is no checking in the actual code, because we just decorated a function with the contract and are done.

Of course, you do not save keystrokes. The contract can be created elsewhere and be applied to many functions, though.

If there are both arity checks and parameter predicates while trailing undefineds occur (which may either be missing parameters or parameters actually having the value undefined), parameter predicates will run first.

Considerations for using these constructs

  • This library will not make your application run faster. The constructs herein may introduce overhead because of all the extra checking that is going on.