Matcha (Matcha v0.1.3) View Source

First-class match specification tooling for Elixir.

Matcha offers tight integration with Elixir and match specifications.

Match specifications are a BEAM VM feature that executes simple pattern matching operations very close-to-the-metal, often several thousand times more performant than a comparable Enum operation. They can be used to efficiently:

However, they are notoriously difficult to compose and use. Matcha makes this intuitive with ergonomic macros and a fluent API with which to manipulate them.

Examples

require Matcha

# Turns Elixir code into a match specification
iex> spec = Matcha.spec do
...>   {x, y, z} -> x + y + z
...> end
...> spec.source
[{{:"$1", :"$2", :"$3"}, [], [{:+, {:+, :"$1", :"$2"}, :"$3"}]}]

For more information, check out the interactive usage guides, including using Matcha for:

Known Limitations

Currently, it is not possible to:

  • Use the Kernel.in/2 macro in guards. (see: open issue)
  • Use the Kernel.tuple_size/1 or :erlang.tuple_size/1 guards. (see: documentation)
    • This is a fundamental limitation of match specs.
  • Use any is_record guards (neither Elixir's implementation because of the Kernel.tuple_size/1 limitation above, nor erlang's implementation for other reasons). (see: documentation)
  • Both destructure values from a data structure into bindings, and assign the datastructure to a variable, except at the top-level of a clause.
    • This is how match specs work by design; though there may be a work-around using :erlang.map_get/2 for maps, but at this time introducing an inconsistency doesn't seem worth it.

Link to this section Summary

Functions

Builds a Matcha.Pattern that represents a "filter" operation on a given input.

Builds a Matcha.Spec that represents a "filter+map" operation on a given input.

Traces function calls to module, executing a spec on matching arguments.

Link to this section Functions

Link to this macro

pattern(pattern)

View Source (macro)

Specs

pattern(Macro.t()) :: Macro.t()

Builds a Matcha.Pattern that represents a "filter" operation on a given input.

Match patterns represent a "filter" operation on a given input, ignoring anything that does not fit the match "shape" specified.

For more information on match patterns, consult the Matcha.Pattern docs.

Examples

iex> require Matcha
...> pattern = Matcha.pattern({x, y, x})
#Matcha.Pattern<{:"$1", :"$2", :"$1"}>
iex> Matcha.Pattern.match?(pattern, {1, 2, 3})
false
iex> Matcha.Pattern.match?(pattern, {1, 2, 1})
true
Link to this macro

spec(context \\ :filter_map, spec)

View Source (macro)

Specs

spec(Matcha.Context.t(), Macro.t()) :: Macro.t()

Builds a Matcha.Spec that represents a "filter+map" operation on a given input.

The context may be :filter_map, :table, :trace, or a Matcha.Context module. This is detailed in the Matcha.Context docs.

For more information on match specs, consult the Matcha.Spec docs.

Examples

iex> require Matcha
...> Matcha.spec do
...>   {x, y, x}
...>     when x > y and y > 0
...>       -> x
...>   {x, y, y}
...>     when x < y and y < 0
...>       -> y
...> end
#Matcha.Spec<[{{:"$1", :"$2", :"$1"}, [{:andalso, {:>, :"$1", :"$2"}, {:>, :"$2", 0}}], [:"$1"]}, {{:"$1", :"$2", :"$2"}, [{:andalso, {:<, :"$1", :"$2"}, {:<, :"$2", 0}}], [:"$2"]}], context: :filter_map>
Link to this macro

trace_calls(module, function, opts \\ [], spec)

View Source (macro)

Traces function calls to module, executing a spec on matching arguments.

Tracing is a powerful feature of the BEAM VM, allowing for near zero-cost monitoring of what is happening in running systems. The functions in Matcha.Trace provide utilities for accessing this functionality.

One of the most powerful forms of tracing uses match specifications: rather that just print information on when a certain function signature with some number of arguments is invoked, they let you:

  • dissect the arguments in question with pattern-matching and guards
  • take special actions in response (documented in Matcha.Context.Trace)

This macro is a shortcut for constructing a spec with the :trace context via Matcha.spec/2, and tracing the specified module and function with it via Matcha.Trace.calls/4.

For more information on tracing in general, consult the Matcha.Trace docs.

Examples

iex> require Matcha
...> Matcha.trace_calls(Enum, :join, limit: 3) do
...>   [_enumerable] -> message("using default joiner")
...>   [_enumerable, ""] -> message("using default joiner (but explicitly)")
...>   [_enumerable, _custom] -> message("using custom joiner")
...> end
...> Enum.join(1..3)
# Prints a trace message with "using default joiner" appended
"123"
iex> Enum.join(1..3, "")
# Prints a trace message with "using default joiner (but explicitly)" appended
"123"
iex> Enum.join(1..3, ", ")
# Prints a trace message with "using custom joiner" appended
"1, 2, 3"