ericelliott / rtype

Intuitive structural type notation for JavaScript.
MIT License
1.13k stars 38 forks source link

rtype --> predicate parser/compiler #62

Open ericelliott opened 8 years ago

ericelliott commented 8 years ago

Write a function that takes an rtype interface description as a string and returns an object that can be used for runtime type checking.

interface TypeChecker {
  checkInputs: Predicate,
  checkOutput: Predicate,
  checkError: Predicate
}

parseSignature(signature: String) => TypeChecker

The target function will get wrapped by a utility such as rfx. When the wrapper function gets called, it will pass inputs to checkInputs(). If it returns true, only then does the original function get called. When the function returns, its output will be similarly checked by checkOutput() before it gets returned to the original caller. If the function throws, the error will also be checked, by checkError().

Useful background:

If you're curious about types, see the introduction to types from the Stanford Compiler course.

Contributing

Start with unit tests, please. See Why I Use Tape Instead of Mocha & So Should You & Five Questions Every Unit Test Must Answer for guidance on how to write a good unit test suite.

Related issues:

tomek-he-him commented 8 years ago

No time at the moment. We have the big open issue for runtime type checking in our project at work. But I can’t predict when we get down to it.

maiermic commented 8 years ago

Is this example correct?

parseSignature('Number => String') => {
  checkInputs: function isNumber(value) {
    return typeof value === 'number';
  },
  checkOutput: function isString(value) {
    return typeof value === 'string';
  },
  checkError: () => true // no errors defined in signature
}
ericelliott commented 8 years ago

@maiermic What does that look like with multiple arguments? =)

I think there are still some open questions here:

  1. How do we communicate which input to check / which input is the wrong type in case of error? A predicate only returns true or false.
  2. How does this work for generator functions?
maiermic commented 8 years ago

Good questions.

  1. What should happen if a check fails?
  2. We could wrap the generator object to check the type of each yielded value.
maiermic commented 8 years ago

What does that look like with multiple arguments?

How about this example with two parameters?

function isNumber(value) {
  return typeof value === 'number';
}

function isString(value) {
  return typeof value === 'string';
}

parseSignature('(Number, String) => String') => {
  checkInputs(...inputs) {
    return isNumber(inputs[0]) && isString(inputs[1]);
  },
  checkOutput: isString,
  checkError: () => true // no errors defined in signature
}
maiermic commented 8 years ago

The current suggestion for parseSignature looks like this:

parseSignature(signature: String) => TypeChecker

Regarding separation of concerns: How about splitting the functionality (signature parsing and generation of TypeChecker) into two functions?

parseSignature(signature: String) => RTypeAST
generateTypeChecker(signature: RTypeAST) => TypeChecker
ericelliott commented 8 years ago

Good suggestion. We should do that. We should also make the type checking functions return an array of TypeErrors (or emit in the generator case).