tags:

views:

354

answers:

6

Is there an equivalent to the C preprocessor directive ## in Erlang?

Let's say I want to concatenate two atom() using a -define preprocessor directive, how would I do this without having run-time side-effects?

A: 

i think there are not. And i think because of that you can do any this stuff at runtime without side-effects

JLarky
I understand that the compiler is said to be really good but how can there be no side-effects (e.g. cycles consumed)?
jldupont
Can you provide example what you mean?
Hynek -Pichi- Vychodil
A: 

I'm not sure I understand what you are asking but I'll take a stab at it.

-define(CATATOM(A, B), list_to_atom(list:concat(atom_to_list(A), atom_to_list(B)))).
AtomA = atom1.
AtomB = atom2.
NewAtom = ?CATATOM(AtomA, AtomB). % NewAtom is atom1atom2

or perhaps you meant this?

-define(CATATOM(A, B), AB).
NewAtom = ?CATATOM(atom1, atom2). % NewAtom is atom1atom2

although I'm not sure what use the new second would actually be. Since it would be simpler to just write atom1atom2 instead of the macro.

The second one will incur no runtime side-effects. The first will incur runtime side-effects since the result of the macro is 3 functions to run at runtime.

Jeremy Wall
Second one would most probably yield "variable 'AB' is unbound".
Zed
Your proposal, assuming it is working, would result in runtime evaluation: I am seeking *compile-time* evaluation.
jldupont
A: 

There is the concept of having parse transforms, that would allow you to concatenate atoms at compile time.

Christian
Doesn't help much if you don't explain how to do it with a parse transform.
Adam Lindberg
+2  A: 

You can get a close enough result using parse transforms. The following parse_transform looks for "atom1 ++ atom2" and converts it to "atom1atom2" at compile time.

example module

-module(z).

-export([z/0]).

-compile({parse_transform, zt}).

z() -> concat ++ enate.

compiling with 'S' proves that it is indeed concatenated at compile time:

{function, z, 0, 2}.
  {label,1}.
    {func_info,{atom,z},{atom,z},0}.
  {label,2}.
    {move,{atom,concatenate},{x,0}}.
    return.

works as expected:

1> z:z().
concatenate

the module containing the parse transform:

-module(zt).

-export([parse_transform/2]).

parse_transform(AST, _Options) ->
  [parse(T) || T <- AST].

parse({function, _, _, _, _} = T) ->
  erl_syntax_lib:map(fun hashhash/1, T);
parse(T) -> T.

hashhash(Tree) ->
  erl_syntax:revert(
    case erl_syntax:type(Tree) of
      infix_expr ->
        Op = erl_syntax:infix_expr_operator(Tree),
        Left = erl_syntax:infix_expr_left(Tree),
        Right = erl_syntax:infix_expr_right(Tree),
        case {erl_syntax:operator_name(Op), erl_syntax:type(Left), erl_syntax:type(Right)} of
          {'++', atom, atom} ->
            erl_syntax:atom(erl_syntax:atom_literal(Left) ++ erl_syntax:atom_literal(Right));
          _ ->
            Tree
        end;
      _ ->
        Tree
    end
  ).

EDIT: edited to "overload" the infix ++ operator. Previous version used '##' function.

Zed
This looks great! I can't get it to compile through erlIDE: do I need to do something special?
jldupont
erlc needs access to zt.beam when compiling z.erl, so you have to add its path to erlc using the -pa flag, most probably: "erlc -pa . z.erl".Not sure how to do this with erlIDE.
Zed
The erlIDE isn't fully featured yet... it does not allow to specify additional paths (such as with the -pa switch).Is there a way of doing this with a -compile directive?
jldupont
how about -compile({parse_transform, zt}) ? :)
Zed
@zed: cute... I am referring to an equivalent to '-pa' option for erlc. Of course the -compile({parse_transform ...) is required ;-)
jldupont
+1  A: 

You can't actually do anything in a macro, it is just pure textual, well token level, substitution. N.B. you are working on the source code and not evaluating it. If you need more complex types of substitution then you need to use a parse transform.

If you write a concatenation macro you can always the ??Arg form to get the argument as a string. Look the preprocessor section of the on-line reference manual.

Of course the really interesting question is why you would want to concatenate two atoms at compile-time? I assume it is the output of another macro, otherwise there would be no point in doing it.

rvirding
I want to be able to "parameterize" a module e.g.-define(APP, app).-define(EXT, ext).-define(NAME, ?APP++?EXT).where the operator '++' would concatenate the two atom() at compile time.
jldupont
A: 

You mention in one of your answers that you want to parameterize modules - this can be done at run time...

The academic paper on it is here.

Mochiweb uses it.

Gordon Guthrie
Not quite what I am looking for but thanks.
jldupont