Matcha
======
> ***First-class match specification and match patterns for Elixir.***
[![Version][hex-version-badge]][hex]
[![Downloads][hex-downloads-badge]][hex]
[![Documentation][docs-badge]][docs]
[![Dependencies][deps-badge]][deps]
[![License][hex-license-badge]][hex]
| :thumbsup: | [Test Suite][suite] | [Test Coverage][coverage] |
|:------------------:|:------------------------------------------------------:|:------------------------------------------:|
| [Release][release] | [![Build Status][release-suite-badge]][release-suite] | ![Coverage Status][release-coverage-badge] |
| [Latest][latest] | [![Build Status][latest-suite-badge]][latest-suite] | ![Coverage Status][latest-coverage-badge] |
Synopsis
--------
Matcha offers tight integration with Elixir and match patterns/specifications.
### Features
- Supports compiling Elixir code into match patterns/specs, similar to [`ex2ms`](#ex2ms)
- Represents patterns/specs as structs, allowing for protocol support
- Offers higher-level APIs for validation and utilization of patterns/specs
- Supports table, tracing, and custom spec contexts in both compiler and APIs
For more information about the BEAM VM's match patterns and specifications in general, consult [this guide][docs-patterns-and-specs].
### Motivation
Erlang's match pattern and spec *capabilities* are pretty powerful. However, the syntax for ***composing*** them is an unintuitive DSL that loosely mirrors a non-existent erlang AST, and built-in ***support*** for using them is limited to a few functions.
#### Composition
The tuple-and-atom spec DSL is very informally specified<span id="footnote-ref-1">[¹](#footnote-1)</span> and hard to debug. It can be frustrating for erlang programmers to write, and is often even harder for Elixir developers who can be disconnected from the erlang-oriented syntax.
There is a parse transform for turning erlang functions into matchspecs, but that is not readily accessible from the Elixir ecosystem. The `Ex2ms` library offers similar functionality for Elixir, but has [certain limitations](#ex2ms) of its own.
This sort of compile-time transformation is exactly what Elixir's macro system was built to do well, and `Matcha` offers powerful macros for composing match patterns and specs.
#### Support
The erlang standard library only offers a few functions scattered across several modules for working with the spec DSL.
I'd love to enable more ways to tap into this cool feature, so `Matcha` offers a more user-friendly API for working with match patterns and specs in different contexts.
Installation
------------
- Add `matcha` to your Mix project's dependencies specification, ie:
```ex
# mix.exs
def deps do
[
#...
{:matcha, "~> 0.1"},
#...
]
end
```
- Install your updated dependencies, ie:
```sh
$ mix deps.get
```
Usage
-----
For in-depth usage instructions, refer to [the online documentation][docs].
Alternatives
------------
### Ex2ms
[`Ex2ms`](https://github.com/ericmj/ex2ms) is a great project, and served as the seed for this one! `Matcha` extends it by match patterns as well as specs, wrapping them in structs, and offering an extensive API for validating and working with them.
Furthermore, the `Ex2ms` matchspec compiler has a few limitations that this project seeks to overcome:
- It doesn't tap into Elixir compiler internals, so can't quite translate as many Elixir code constructs. `Matcha` invokes `:elixir_expand.expand/2` to cover a little more ground.
- It doesn't tap into erlang compiler internals, so must hard-code what functions are allowed in spec bodies. `Matcha` defers to `:erl_internal` to be a little more future-proof.
- It doesn't support compiling specs for tracing contexts. Tracing is very fertile unexplored ground in the Elixir ecosystem, so `Matcha` introduces the concept of different contexts to support investigation in this domain.
Footnotes
---------
---
<span id="footnote-1">¹</span> per the [Erlang Matchspec Docs][erlang-matchspec-grammar-docs]:
> A match specification used in *tracing*/*ets* can be described in the following ***informal*** grammar...
[⏎](#footnote-ref-1)
---
<!-- Links -->
[hex]: https://hex.pm/packages/matcha
[hex-version-badge]: https://img.shields.io/hexpm/v/matcha.svg?maxAge=86400&style=flat-square
[hex-downloads-badge]: https://img.shields.io/hexpm/dt/matcha.svg?maxAge=86400&style=flat-square
[hex-license-badge]: https://img.shields.io/badge/license-MIT-7D26CD.svg?maxAge=86400&style=flat-square
[docs]: https://hexdocs.pm/matcha
<!-- [docs-badge]: https://inch-ci.org/github/christhekeele/matcha.svg?branch=release&style=flat-square -->
[docs-badge]: https://img.shields.io/badge/documentation-online-purple?maxAge=86400&style=flat-square
[docs-patterns-and-specs]: https://hexdocs.pm/matcha/patterns-and-specs.html#content
[deps]: https://hex.pm/packages/matcha
[deps-badge]: https://img.shields.io/badge/dependencies-none-blue?maxAge=86400&style=flat-square
[suite]: https://github.com/christhekeele/matcha/actions?query=workflow%3A%22Test+Suite%22
[coverage]: https://coveralls.io/github/christhekeele/matcha
[release]: https://github.com/christhekeele/matcha/tree/release
[release-suite]: https://github.com/christhekeele/matcha/actions?query=workflow%3A%22Test+Suite%22+branch%3Arelease
[release-suite-badge]: https://img.shields.io/github/checks-status/christhekeele/matcha/release.svg?maxAge=86400&style=flat-square
[release-coverage-badge]: https://img.shields.io/coveralls/christhekeele/matcha/release.svg?maxAge=86400&style=flat-square
[latest]: https://github.com/christhekeele/matcha/tree/latest
[latest-suite]: https://github.com/christhekeele/matcha/actions?query=workflow%3A%22Test+Suite%22+branch%3Alatest
[latest-suite-badge]: https://img.shields.io/github/checks-status/christhekeele/matcha/latest.svg?maxAge=86400&style=flat-square
[latest-coverage-badge]: https://img.shields.io/coveralls/christhekeele/matcha/latest.svg?maxAge=86400&style=flat-square
[erlang-matchspec-grammar-docs]: https://erlang.org/doc/apps/erts/match_spec.html#:~:text=A%20match%20specification%20used%20in%20tracing,the%20following%20informal%20grammar