View Source merlin_quote_transform (merlin v3.0.1)

Extends merl to allow patterns with ?Q/1 macro arbitrarily nested, as well as use those patterns directly in function heads.

It works by transforming the function clauses to a case, which then merl turns into a call to merl:switch/2. This allows you to use the power of merl matching much more conveniently.

Here's an example, say you have a function func:
   func(enter, ?Q("_@var"), #{module := State}) ->
       success.
It becomes something like:
   func(Arg1, Arg2, Arg3) ->
       case merlin_quote_transform:switch(
           [Arg1, Arg2, Arg3],
           [
               fun (enter, Var1, #{module := State}) ->
                       case Var1 of
                           ?Q("_@var") ->
                               {ok, success};
                           _ ->
                               continue
                       end
                   (_, _, _) ->
                       continue
               end
           ]
       ) of
           {ok, ValueVar1} -> ValueVar1;
           _ ->
               error(function_clause)
       end.

Which then merl expands to match ?Q("_@Var") on `Arg2/Var1.

Another example:
   case Foo of
       #state{form=?Q("_@Var = _@Expr") = Form, bindings=Bindings} when
           erl_syntax:type(Var) =:= variable
       ->
           erl_eval:expr(Form, Bindings);
       #state{form=Form} = State when
           erl_syntax:type(Form) =:= variable
       ->
           matches;
       #state{} = State ->
           State
   end
   case merlin_quote_transform:switch(
       [Foo],
       [
           fun (#state{form=Form, bindings=Bindings}) ->
                   case Form of
                       ?Q("_@Var = _@Expr") when
                           erl_syntax:type(Var) =:= variable
                       ->
                           erl_eval:expr(Form, Bindings);
                       _ ->
                           continue
                   end;
               (_) ->
                   continue
           end,
           fun (#state{form=Form}) ->
                   case
                       try
                           erl_syntax:type(Form) =:= variable
                       catch _:_ ->
                           false
                       end
                   of
                       true ->
                           matches;
                       false ->
                           continue
                   end;
               (_) ->
                   continue
           end,
           fun (#state{} = State) ->
                   State;
               (_) ->
                   continue
           end
       ]
   ) of
       {ok, __ValueVar1} -> __ValueVar1;
       _ ->
           error(function_clause)
   end

Here you can see that we allow extended guards even if the clause does not have any merl patterns. But we can't just use it as it, so we wrap it in a try/catch.

You might also have noticed that each clause becomes its own fun expression. This is to allow matching in stages, first vanilla Erlang and then merl, while at the same time avoiding unsafe use of variables between clauses.

That design took a long time to settle on, but now it works.

Summary

Functions

Link to this function

parse_transform(Forms, Options)

View Source