View Source MatchSpec (match_spec v0.3.1)
Elixir module to help you write matchspecs.
contains functions which transform elixir-style functions into erlang matchspecs, and vice versa.
functions-to-matchspecs
Functions to matchspecs
For transforming elixir-style functions into matchspecs, the following restrictions apply:
function-form
Function form
- The function must use the
Kernel.fn/1macro as its form, or usedefmatchspec/2ordefmatchspecp/2, where the matchspecs form is similar to theKernel.fn/1form - The function must have arity 1.
function-argument-matching
Function argument matching
- The function may only match whole variables or tuple patterns.
- Only one tuple pattern may be matched.
- if your tuple contains a binary pattern match the binary pattern may only
consist of bytes and strings, and only the
size/1modifier is allowed.- bitstrings matching is not supported
- conversions such as float are not supported.
guards-and-return-expression
Guards and return expression
- The function may only use guards in its
whensection.- for
defmatchspec/2,defmatchspecp/2, orfun2msfun/4: aKernel.in/2guard may take a bound variable as its second parameter.
- for
- The function may only return a single expression that (optionally) uses guard functions to transform matches.
examples-allowed
Examples (allowed)
Binding a top-level variable
fn tuple -> tuple endTop-level tuple match
fn {_, _, value} -> value endWhen with guard
fn {_, _, value} when is_integer(value) -> value endLimitations on local guards
local (
defguardp) guards are only supported when the macro is inside of a function body, due to limitations on macro resolution timing.The following use cases are currently NOT supported:
fun2ms/2outside of a function body (inside the module body)fun2msfun/4outside of function body (:lambda,:def, or:defp)The following use cases are currently supported:
fun2ms/2inside a function bodyfun2msfun/4inside of a function body (:lambdaonly)defmatchspec/2alwaysdefmatchspecp/2always
Structure matching inside a tuple
fn {%{key: a}} -> a endBinary matching inside a tuple
fn {<<"foo" :: binary, 42, a :: binary>>} -> a endResult expression modification by guards
fn {a, b} -> a + b end
examples-disallowed
Examples (disallowed)
Arity not 1
fn foo, bar -> foo + bar endMultiple tuple matches
fn {_, :foo} = {:bar, value} -> value endNon-tuple top level match
fn "foo" <> bar -> bar end
fn %{foo: bar} -> bar endDisallowed type in binary match
fn {<<foo :: integer-big-endian>>} -> foo endNon-guard function in match
fn {a} when String.starts_with?(a, "foo") -> a endNon-guard function in result
fn {a, b} -> a ++ b endNote
The restrictions on binary matching exist due to limitations on the BIFs available to ets and may change in the future if OTP comes to support these conversions in its kernel.
Link to this section Summary
Functions
Writes a matchspec-generating function based on a body.
Writes a matchspec-generating function based on a body.
converts a function ast into an ets matchspec.
converts a function into a function that generates a match spec based on bindings.
converts a matchspec into elixir AST for functions. Unfortunately, the ast generator cannot guess names for variables, so variable names are set by the numerical value of the matchspec token
Link to this section Functions
Writes a matchspec-generating function based on a body.
You may provide multiple function bodies.
This macro uses the same backend as fun2msfun/4 and will generate
identical code to that macro.
Example:
use MatchSpec
defmatchspec my_matchspec(value) do
{key, ^value} when key !== :foo -> key
endThis generates the equivalent to the following function:
def my_matchspec(value) do
[{{:"$1", :"$2"}, [{:"=:=", :"$2", {:const, value}}, {:"=/=", :"$1", {:const, :foo}}], [:"$1"]]
end
Writes a matchspec-generating function based on a body.
You may provide multiple function bodies.
This macro uses the same backend as fun2msfun/4 and will generate
identical code.
see defmatchspec/2 for details
converts a function ast into an ets matchspec.
The function must also be "fn ast"; you can't pass a shorthand lambda or
a lambda to an existing lambda.
The function lambda form is only used as a scaffolding to represent ets matching and filtering operations, by default it will not be instantiated into bytecode of the resulting module.
iex> require MatchSpec
iex> MatchSpec.fun2ms(fn tuple = {k, v} when v > 1 and v < 10 -> tuple end)
[{{:"$1", :"$2"}, [{:andalso, {:>, :"$2", 1}, {:<, :"$2", 10}}], [:"$_"]}]You can use variables from the calling scope in the filters.
iex> require MatchSpec
iex> my_atom = :foo
iex> MatchSpec.fun2ms(fn tuple = {k, _} when k === my_atom -> tuple end)
[{{:"$1", :_}, [{:"=:=", :"$1", {:const, :foo}}], [:"$_"]}]If you would also like the equivalent lambda, pass with_fun: true as an
option and the fun2ms/2 macro will emit a tuple of the matchspec and the
lambda.
iex> {ms, fun} = MatchSpec.fun2ms(fn {:key, value} -> value end, with_fun: true)
iex> :ets.test_ms({:key, "value"}, ms)
{:ok, "value"}
iex> fun.({:key, "value"})
"value"This macro uses the same backend as fun2msfun/4 and will emit the same
matchspec as if you passed no parameters to fun2msfun/4
converts a function into a function that generates a match spec based on bindings.
This can be used to either create a named function or an anonymous function. If you would like to use one of the free variables in your function as a part of the head of the match, you must pin it.
if you omit the first parameter, it will create an anonymous function.
basic-example-with-lambda-default
Basic example with :lambda (default):
iex> require MatchSpec
# using a variable in the match
iex> lambda = MatchSpec.fun2msfun(:lambda, fn {key, value} when key === target -> value end, [target])
iex> lambda.(:key)
[{{:"$1", :"$2"}, [{:"=:=", :"$1", {:const, :key}}], [:"$2"]}]
#pinning a variable
iex> lambda2 = MatchSpec.fun2msfun(fn {^key, value} -> value end, [key])
iex> lambda2.(:key)
[{{:"$1", :"$2"}, [{:"=:=", :"$1", {:const, :key}}], [:"$2"]}]Note that the bindings parameter acts like pattern matching on function
arguments: They may use complex matches and there can be more than one, the
arity of the anonymous (or def/defp) function matches the length of the
bindings argument.
iex> require MatchSpec
iex> lambda = MatchSpec.fun2msfun(:lambda, fn {^key, ^value} -> true end, [%{key: key}, value])
iex> lambda.(%{key: :key}, :value)
[{{:"$1", :"$2"}, [{:"=:=", :"$1", {:const, :key}}, {:"=:=", :"$2", {:const, :value}}], [true]}]
example-with-def-defp
Example with (:def/:defp):
require MatchSpec
MatchSpec.fun2msfun(:def, :matchspec, fn {key, value} when key == target -> value end, [target])
converts a matchspec into elixir AST for functions. Unfortunately, the ast generator cannot guess names for variables, so variable names are set by the numerical value of the matchspec token
The second parameter takes two modes:
:astemits elixir ast to write a lambda.
iex> MatchSpec.ms2fun([{{:"$1", :"$2"}, [], [:"$2"]}], :ast)
{:fn, [],
[{:->, [], [[{:{}, [], [{:v1, [], nil}, {:v2, [], nil}]}], {:v2, [], nil}]}]}:codeoutputs formatted elixir code.
iex> MatchSpec.ms2fun([{{:"$1", :"$2"}, [], [:"$2"]}, {{:"$1"}, [], [:"$_"]}], :code)
"""
fn
{v1, v2} -> v2
tuple = {v1} -> tuple
end
"""