I'm leaving this here to help those who come barking up the same tree.
In building this tool, I realized I was trying to implement a compiler (parser & generator) for erlang terms.
The original use case for this project is: when accepting input from unknown clients, before performing any resource-intensive tasks, the input needs to be checked for well-formedness (e.g. no atoms when strings are expected, no lists of integers where utf8 binary strings are expected, etc.). If anything is wrong, all syntactical problems with the input term should be reported (not just the first to fail). If the input is well-formed, it would then be translated into an internal format.
This is essentially a compiler from one erlang format to another, based on a model-specific grammar.
Instead of building a parser for a subset of erlang terms, I've chosen
to implement basic parsing and compilation as simple erlang functions
for each model: parse
and create
, respectively. It seems the most
pragmatic solution, but the code isn't terribly succinct, nor is it a
solution that lends itself immediately to much reuse.
Please ping me if you implement an efficient match-spec-like parser, or find a better solution!
- Erlang Match Specs implement almost exactly what I need, but (evidently) cannot be used efficiently for matching arbitrary terms.
- One way to use Match Specs for matching arbitrary terms.
- Daniel Luna's Erlang-type-checker operates on
-spec
s, which are available indebug_info
builds (here's a mailing list thread about it). - This thread gets into metaprogramming and a bit about Match Spec reuse.
- Neotoma and Yecc for parsing strings based on PEGs (Parsing Expression Grammars).
type_check
is a generic run-time type checker for erlang terms.
{ok, all_valid} = type_check:validate(
[<<"hi">>, 1, { 42, <<"the answer">>, [blah]}]
, [string, number, {integer, string, [atom]}]).
{bad_types, [{<<"there">>, number}]} = type_check:validate(
[<<"hi">>, <<"there">>, [1,2,3.0]],
[ string, number, {each, number}]).
See test/type_check_tests.erl
for more examples, or better yet, just
read the code. There's not much of it!
The one use-case in which I've really missed type checking is when validating external client input. My worry isn't so much that arguments of the wrong types will crash the system; my worry is that they won't crash the system, and instead result in unexpected behavior. At the very least, having the option to ensure arguments are of the correct type or crash if not, offers a little peace of mind.
type_check:validate/2
takes the Value
to be checked, and a
declaration of the expected Type
signature. It returns either
{ok, all_valid}
, or{bad_types, [{GivenValue, ExpectedType}]}
The returned list of bad_types
can come from anywhere in the given
Value
being checked, so in complex data structures, a bit of a hunt
for bad values may be required.
MIT (see LICENSE)