[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