View Source Matcha.Context behaviour (Matcha v0.1.10)

Different types of match spec are intended to be used for different purposes, and support different instructions in their bodies for different use-cases.

The modules implementing the Matcha.Context behaviour define the different types of Matcha.Spec, provide documentation for what specialized instructions that type supports, and are used during Elixir-to-match spec conversion as a concrete function definition to use when expanding instructions (since most of these specialized instructions do not exist anywhere as an actual functions, this lets the Elixir compiler complain about invalid instructions as UndefinedFunctionErrors).

predefined-contexts

Predefined contexts

Currently there are three applications of match specs supported:

  • :filter_map:

    Matchspecs intended to be used to filter/map over an in-memory list in an optimized fashion. These types of match spec reference the Matcha.Context.FilterMap module.

  • :match:

    Matchspecs intended to be used to match over an in-memory list in an optimized fashion. These types of match spec reference the Matcha.Context.Match module.

  • :table:

    Matchspecs intended to be used to efficiently select data from BEAM VM "table" tools, such as :ets, :dets, and :mnesia, and massage the values returned. These types of match spec reference the Matcha.Context.Table module.

  • :trace:

    Matchspecs intended to be used to instruct tracing utilities such as :dbg and :recon_trace exactly what function calls with what arguments to trace, and allows invoking special trace command instructions in response. These types of match spec reference the Matcha.Context.Trace module.

custom-contexts

Custom contexts

The context mechanism is technically extensible: any module can implement the Matcha.Context behaviour, define the callbacks, and list public no-op functions to allow their usage in specs compiled with that context (via Matcha.spec(CustomContext) do...).

In practice there is little point in defining a custom context: the supported use-cases for match specs are tightly coupled to the Erlang language, and Matcha covers all of them with its provided contexts, which should be sufficient for any application. The module+behaviour+callback implementation used in Matcha is less about offering extensibility, but instead used to simplify special-casing in Matcha.Spec function implementations, raise Elixir-flavored errors when an invalid instruction is used in the different types of spec, and provide a place to document what they do when invoked.

Link to this section Summary

Callbacks

A default value to use when executing match specs in this context.

Decides if the result of a spec match should be part of the result set.

Which primitive Erlang context this context module wraps.

Describes an issue with a test target.

Allows this context module to modify match specs before their execution.

Transforms the result of a spec match just after calling :ets.match_spec_run/2.

Transforms the result of a spec match just after calling :erlang.match_spec_test/3.

A validator that runs before executing a match spec against a target in this context.

Functions

Maps the shortcut references to the core Matcha context modules.

Resolves shortcut references to the core Matcha context modules.

Runs a spec against an enumerable.

Creates a lazy Stream that yields the results of running the spec against the provided enumberable.

Determines whether or not specs in this context can be compiled.

Determines whether or not specs in this context can used in tracing.

Tests that the provided spec in its Matcha.Context is valid.

Tests that the provided spec in its Matcha.Context correctly matches a provided match_target.

Link to this section Types

Link to this section Callbacks

Link to this callback

__default_match_target__()

View Source
@callback __default_match_target__() :: any()

A default value to use when executing match specs in this context.

This function is used to provide Matcha.Source.test/3 with a target value to test against, in situations where it is being used to simply validate the match spec itself, but we do not acutally care if the input matches the spec.

This value, when passed to this context's Matcha.Context.__valid_match_target__/1 callback, must produce a true value.

Link to this callback

__emit_erl_test_result__(result)

View Source
@callback __emit_erl_test_result__(result :: any()) ::
  {:emit, new_result :: any()} | :no_emit

Decides if the result of a spec match should be part of the result set.

This callback runs just after calls to __transform_erl_test_result__/1 or __transform_erl_test_result__/1.

Must return {:emit, result} to include the transformed result of a spec match, when executing it against in-memory data (as opposed to tracing or :ets) for validation or debugging purposes. Otherwise, returning :no_emit will hide the result.

@callback __erl_spec_type__() :: Matcha.Source.type()

Which primitive Erlang context this context module wraps.

Link to this callback

__invalid_match_target_error_message__(match_target)

View Source
@callback __invalid_match_target_error_message__(match_target :: any()) :: binary()

Describes an issue with a test target.

Invoked to generate a t:Matcha.Error.message when Matcha.Context.__valid_match_target__/1 fails.

Link to this callback

__prepare_source__(source)

View Source
@callback __prepare_source__(source :: Matcha.Source.uncompiled()) ::
  {:ok, new_source :: Matcha.Source.uncompiled()}
  | {:error, Matcha.Error.problems()}

Allows this context module to modify match specs before their execution.

This hook is the main entrypoint for creating custom contexts, allowing them to augment the match spec with new behaviour when executed in this context.

Care must be taken to handle the results of the modified match spec after execution correctly, before they are returned to the caller. This should be implemented in the callbacks:

Link to this callback

__transform_erl_run_results__(results)

View Source
@callback __transform_erl_run_results__(results :: [any()]) ::
  {:ok, results :: [any()]} | {:error, Matcha.Error.problems()}

Transforms the result of a spec match just after calling :ets.match_spec_run/2.

You can think of this as an opportunity to "undo" any modifications to the user's provided matchspec made in __prepare_source__/1.

Must return {:ok, result} to indicate that the returned value is valid; otherwise return {:error, problems} to raise an exception.

Link to this callback

__transform_erl_test_result__(result)

View Source
@callback __transform_erl_test_result__(result :: any()) ::
  {:ok, result :: any()} | {:error, Matcha.Error.problems()}

Transforms the result of a spec match just after calling :erlang.match_spec_test/3.

You can think of this as an opportunity to "undo" any modifications to the user's provided matchspec made in __prepare_source__/1.

Must return {:ok, result} to indicate that the returned value is valid; otherwise return {:error, problems} to raise an exception.

Link to this callback

__valid_match_target__(match_target)

View Source
@callback __valid_match_target__(match_target :: any()) :: boolean()

A validator that runs before executing a match spec against a target in this context.

This validator is run before any match specs are executed on inputs to Matcha.Source.test/3, and all elements of the enumerable input to Matcha.Source.run/2.

If this function returns false, the match spec will not be executed, instead returning a t:Matcha.Error.error_problem with a t:Matcha.Error.message generated by the Matcha.Context.__invalid_match_target_error_message__/1 callback.

Link to this section Functions

Link to this function

__core_context_aliases__()

View Source
@spec __core_context_aliases__() :: Keyword.t()

Maps the shortcut references to the core Matcha context modules.

This describes which shortcuts users may write, for example in Matcha.spec(:some_shortcut) instead of the fully qualified module Matcha.spec(Matcha.Context.SomeContext).

@spec resolve(atom() | t()) :: t() | no_return()

Resolves shortcut references to the core Matcha context modules.

This allows users to write, for example, Matcha.spec(:trace) instead of the fully qualified module Matcha.spec(Matcha.Context.Trace).

@spec run(Matcha.Spec.t(), Enumerable.t()) ::
  {:ok, [any()]} | {:error, Matcha.Error.problems()}

Runs a spec against an enumerable.

This is a key function that ensures the input spec and results are passed through the callbacks of a Matcha.Context.

Returns either {:ok, results} or {:error, problems} (that other ! APIs may use to raise an exception).

Link to this function

stream(spec, enumerable)

View Source
@spec stream(Matcha.Spec.t(), Enumerable.t()) ::
  {:ok, Enumerable.t()} | {:error, Matcha.Error.problems()}

Creates a lazy Stream that yields the results of running the spec against the provided enumberable.

This is a key function that ensures the input spec and results are passed through the callbacks of a Matcha.Context.

Returns either {:ok, stream} or {:error, problems} (that other ! APIs may use to raise an exception).

Link to this function

supports_compilation?(context)

View Source
@spec supports_compilation?(t()) :: boolean()

Determines whether or not specs in this context can be compiled.

Link to this function

supports_tracing?(context)

View Source
@spec supports_tracing?(t()) :: boolean()

Determines whether or not specs in this context can used in tracing.

@spec test(Matcha.Spec.t()) :: {:ok, any()} | {:error, Matcha.Error.problems()}

Tests that the provided spec in its Matcha.Context is valid.

Invokes __default_match_target__/0 and passes it into :erlang.match_spec_test/3.

Returns either {:ok, stream} or {:error, problems} (that other ! APIs may use to raise an exception).

Link to this function

test(spec, match_target)

View Source
@spec test(Matcha.Spec.t(), Matcha.Source.match_target()) ::
  {:ok, any()} | {:error, Matcha.Error.problems()}

Tests that the provided spec in its Matcha.Context correctly matches a provided match_target.

Passes the provided match_target into :erlang.match_spec_test/3.

Returns either {:ok, stream} or {:error, problems} (that other ! APIs may use to raise an exception).