View Source MatchSpec (match_spec v0.3.3)
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/1
macro as its form, or usedefmatchspec/2
ordefmatchspecp/2
, where the matchspecs form is similar to theKernel.fn/1
form - 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/1
modifier 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
when
section.- for
defmatchspec/2
,defmatchspecp/2
, orfun2msfun/4
: aKernel.in/2
guard 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 end
Top-level tuple match
fn {_, _, value} -> value end
When with guard
fn {_, _, value} when is_integer(value) -> value end
Limitations 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/2
outside of a function body (inside the module body)fun2msfun/4
outside of function body (:lambda
,:def
, or:defp
)The following use cases are currently supported:
fun2ms/2
inside a function bodyfun2msfun/4
inside of a function body (:lambda
only)defmatchspec/2
alwaysdefmatchspecp/2
always
Structure matching inside a tuple
fn {%{key: a}} -> a end
Binary matching inside a tuple
fn {<<"foo" :: binary, 42, a :: binary>>} -> a end
Result expression modification by guards
fn {a, b} -> a + b end
examples-disallowed
Examples (disallowed)
Arity not 1
fn foo, bar -> foo + bar end
Multiple tuple matches
fn {_, :foo} = {:bar, value} -> value end
Non-tuple top level match
fn "foo" <> bar -> bar end
fn %{foo: bar} -> bar end
Disallowed type in binary match
fn {<<foo :: integer-big-endian>>} -> foo end
Non-guard function in match
fn {a} when String.starts_with?(a, "foo") -> a end
Non-guard function in result
fn {a, b} -> a ++ b end
Note
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
end
This 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:
:ast
emits elixir ast to write a lambda.
iex> MatchSpec.ms2fun([{{:"$1", :"$2"}, [], [:"$2"]}], :ast)
{:fn, [],
[{:->, [], [[{:{}, [], [{:v1, [], nil}, {:v2, [], nil}]}], {:v2, [], nil}]}]}
:code
outputs formatted elixir code.
iex> MatchSpec.ms2fun([{{:"$1", :"$2"}, [], [:"$2"]}, {{:"$1"}, [], [:"$_"]}], :code)
"""
fn
{v1, v2} -> v2
tuple = {v1} -> tuple
end
"""