Metaprogramming

Vlad Dumitrescu vladdu55@REDACTED
Thu Aug 24 10:55:33 CEST 2006


On 8/23/06, David Hopwood <david.nospam.hopwood@REDACTED> wrote:
> Do you have any design notes or examples of what this might look like?

> Will it be possible to write a quasipattern anywhere that a pattern
> can currently occur in Erlang?

Yes, I'd like it to be possible. My implementation is what Richard
described above as Lisp's backquote - when the module is parsed, the
new stuff is converted to Erlang terms and from then on it's just
regular code.

Provisional syntax is:
  <| ... |> encloses code and its value is simply the parse tree
  @Var is a "unquote" application, replacing the variable value in the code
  @@Var is a "splicing-unquote" application, where Var must be a list
and its content is spliced in the containing list (possibly even
tuple)

  <|name| ... |> will use module "name" to retrieve lexical scanner
and parser, thus allowing even

  <|xml|     <html> @Header @Content @Footer </html> |>
which looks like a yaws file inside out :-)

  Fields = [id, name, age],
  <|sql| SELECT @@Fields FROM @Table |>

  <| fun(X) -> X + 1. |> is shorthand for <|erl| ... |>

and also opens for using small domain specific languages instead of
handling the corresponding Erlang terms.

The advantage is that code that handles parse trees becomes less
dependent on the parser used (i.e. if I change the way the parse tree
looks like, there will be less cases where code using it will have to
be adjusted). Of course, one would still need to know how the
underlying data looks like, but hopefully it will be easier to write
and read it.

More examples:

* compare

  Func = <|
    bar(X) when is_integer(X) ->
      true;
    bar(_) ->
      false.
  |>,

with

   Func = {function,13,bar,1,
                  [{clause,13,[{var,13,'X'}],

[[{call,13,{atom,13,is_integer},[{var,13,'X'}]}]],
                                   [{atom,14,true}]},
                   {clause,15,[{var,15,'_'}],
                                   [],
                                   [{atom,16,false}]}]}

* extracting data from a parse tree (warning: function definitions are tricky!)

  FuncTree  = smerl:get_function(Mod, Fun),
  <| @FunctionName(@@Arguments) -> @@Body. |> = FuncTree, ...

would expand to something like

  {function, 3, FunctionName, _, [{clause, 3, Arguments, _, Body}]}

[[ the above is wrong, as it assumes there's one single clause, but
it's just an example ]]

* as a more advanced use, I can think of something like

-macro([repeat/2]).

repeat(N, Fun, Args) ->
  Expr = <| @Fun(@N, @@Args)  |>,
  repeat_1(N, Expr, <| |>).

repeat_1(0, Expr, Result) ->
  Result;
repeat_1(N, Expr, Result) ->
  Expr ++ Result.

The macro would be evaluated at parse/compile time and calling
    repeat(4, AFun, [2,x])
would give
    AFun(4, 2, x),
    AFun(3, 2, x),
    AFun(2, 2, x),
    AFun(1, 2, x)

So now you could add 'for' loops! :-)

There's along way to go yet, and it still remains to see if it would
become an academic feature or if it will be used in real applications.
My plan is to have it as an add-on to Erlide (but of course also
useful by itself) and use it as a separate language (source files will
have an extension of "mrl" instead). I had in mind to have this (more
or less) ready for the User Conference and submit it there.

best regards,
Vlad



More information about the erlang-questions mailing list