[erlang-questions] clarify: Erlang syntax, unbound variables, and parse transforms

Matthew Dempsky matthew@REDACTED
Thu Nov 22 00:11:11 CET 2007


According to section 6.2 of the Erlang Reference Manual, ``[u]nbound
variables are only allowed in patterns.''  Whether this restriction is
part of Erlang's syntax or its semantics is unclear to me; in
particular, I would like to know how it interacts with parse
transforms.

Right now, the Erlang compiler accepts .erl files with unbound or
universal variables in non-pattern contexts, as long as they are
removed by applying parse transforms.  Is it safe to rely on this
behavior?

For example, I posted a module that transforms '~'(Pattern = Expr) to
case Expr of Pattern -> true; _ -> false end.  I chose the '~'(Pattern
= Expr) syntax after reading section 4 of the ERTS User's Guide and
finding that an abstract format for universal variables is only
defined in pattern contexts (such as the left-hand side of a match
expression).  Later, I rewrote the module to allow '~'(Expr, Pattern1,
Pattern2, ..., PatternN), but I am unsure whether this is valid.

Another example (motivated by [1] and [2]), the module below
transforms cut(ExprList) to fun(X_1, X_2, ..., X_N) -> ExprList end
where each universal variable in ExprList in a non-pattern context is
replaced with X_i; e.g., cut(_ + 3 * _) transforms to fun(X_1, X_2) ->
X_1 + 3 * X_2 end.

Thanks.

[1] http://debasishg.blogspot.com/2007/11/erlang-string-lambdas.html
[2] http://srfi.schemers.org/srfi-26/srfi-26.html


%% @copyright 2007 Mochi Media, Inc.
%% @author Matthew Dempsky <matthew@REDACTED>
%%
%% @doc Module implementing a parse transform adding support for a
%% <code>cut(ExprList)</code> syntax.

-module(cut_pt).
-author(matthew@REDACTED).

-export([parse_transform/2]).

-define(KEY, 'cut_pt:gensym').

parse_transform(Forms, _Options) ->
    NewForms = erl_syntax:revert_forms(
                 [erl_syntax_lib:map(fun transform/1, F) || F <- Forms]),
    erase(?KEY),
    NewForms.

transform(Form) ->
    case erl_syntax:type(Form) of
        application ->
            Operator = erl_syntax:application_operator(Form),
            case erl_syntax:type(Operator) of
                atom ->
                    case erl_syntax:atom_value(Operator) of
                        cut ->
                            ExprList = erl_syntax:application_arguments(Form),
                            doit(ExprList);
                        _ ->
                            Form
                    end;
                _ ->
                    Form
            end;
        _ ->
            Form
    end.

doit(ExprList) ->
    {NewExprList, Acc} =
        lists:mapfoldl(fun doit_1/2, [], ExprList),
    erl_syntax:fun_expr(
      [erl_syntax:clause(lists:reverse(Acc), none, NewExprList)]).

doit_1(Expr, Acc) ->
    case erl_syntax:type(Expr) of
        underscore ->
            Variable = erl_syntax:variable(gensym()),
            {Variable, [Variable | Acc]};
        match_expr ->
            Pattern = erl_syntax:match_expr_pattern(Expr),
            Body = erl_syntax:match_expr_body(Expr),
            {NewBody, NewAcc} =
                erl_syntax_lib:mapfold_subtrees(fun doit_1/2, Acc, Body),
            {erl_syntax:match_expr(Pattern, NewBody), NewAcc};
        clause ->
            Patterns = erl_syntax:clause_patterns(Expr),
            Guard = erl_syntax:clause_guard(Expr),
            Body = erl_syntax:clause_body(Expr),
            {NewGuard, NewAcc1} = doit_1(Guard, Acc),
            {NewBody, NewAcc2} =
                lists:mapfoldl(fun doit_1/2, NewAcc1, Body),
            {erl_syntax:clause(Patterns, NewGuard, NewBody), NewAcc2};
        _ ->
            erl_syntax_lib:mapfold_subtrees(fun doit_1/2, Acc, Expr)
    end.

gensym() ->
    N = case get(?KEY) of undefined -> 1; V -> V end,
    put(?KEY, N + 1),
    "cut_pt:var_" ++ integer_to_list(N).



More information about the erlang-questions mailing list