Skip to content

Commit acbca83

Browse files
committedDec 12, 2013
EEP 37: Funs with names
This adds optional names to fun expressions. A named fun expression is parsed as a tuple `{named_fun,Loc,Name,Clauses}` in erl_parse. If a fun expression has a name, it must be present and be the same in every of its clauses. The function name shadows the environment of the expression shadowing the environment and it is shadowed by the environment of the clauses' arguments. An unused function name triggers a warning unless it is prefixed by _, just as every variable. Variable _ is allowed as a function name. It is not an error to put a named function in a record field default value. When transforming to Core Erlang, the named fun Fun is changed into the following expression: letrec 'Fun'/Arity = fun (Args) -> let <Fun> = 'Fun'/Arity in Case in 'Fun'/Arity where Args is the list of arguments of 'Fun'/Arity and Case the Core Erlang expression corresponding to the clauses of Fun. This transformation allows us to entirely skip any k_var to k_local transformation in the fun's clauses bodies.
1 parent 6c5c398 commit acbca83

File tree

14 files changed

+268
-31
lines changed

14 files changed

+268
-31
lines changed
 

‎lib/compiler/src/sys_pre_expand.erl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ expr({'receive',Line,Cs0,To0,ToEs0}, St0) ->
344344
{{'receive',Line,Cs,To,ToEs},St3};
345345
expr({'fun',Line,Body}, St) ->
346346
fun_tq(Line, Body, St);
347+
expr({named_fun,Line,Name,Cs}, St) ->
348+
fun_tq(Line, Cs, St, Name);
347349
expr({call,Line,{atom,La,N}=Atom,As0}, St0) ->
348350
{As,St1} = expr_list(As0, St0),
349351
Ar = length(As),
@@ -475,6 +477,11 @@ fun_tq(Lf, {clauses,Cs0}, St0) ->
475477
Index = Uniq = 0,
476478
{{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},St2}.
477479

480+
fun_tq(Line, Cs0, St0, Name) ->
481+
{Cs1,St1} = fun_clauses(Cs0, St0),
482+
{Fname,St2} = new_fun_name(St1, Name),
483+
{{named_fun,Line,Name,Cs1,{0,0,Fname}},St2}.
484+
478485
fun_clauses([{clause,L,H0,G0,B0}|Cs0], St0) ->
479486
{H,St1} = head(H0, St0),
480487
{G,St2} = guard(G0, St1),
@@ -485,9 +492,12 @@ fun_clauses([], St) -> {[],St}.
485492

486493
%% new_fun_name(State) -> {FunName,State}.
487494

488-
new_fun_name(#expand{func=F,arity=A,fcount=I}=St) ->
495+
new_fun_name(St) ->
496+
new_fun_name(St, 'fun').
497+
498+
new_fun_name(#expand{func=F,arity=A,fcount=I}=St, FName) ->
489499
Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A)
490-
++ "-fun-" ++ integer_to_list(I) ++ "-",
500+
++ "-" ++ atom_to_list(FName) ++ "-" ++ integer_to_list(I) ++ "-",
491501
{list_to_atom(Name),St#expand{fcount=I+1}}.
492502

493503
%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.

‎lib/compiler/src/v3_core.erl

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
-record(icase, {anno=#a{},args,clauses,fc}).
9393
-record(icatch, {anno=#a{},body}).
9494
-record(iclause, {anno=#a{},pats,pguard=[],guard,body}).
95-
-record(ifun, {anno=#a{},id,vars,clauses,fc}).
95+
-record(ifun, {anno=#a{},id,vars,clauses,fc,name=unnamed}).
9696
-record(iletrec, {anno=#a{},defs,body}).
9797
-record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
9898
-record(iprimop, {anno=#a{},name,args}).
@@ -587,7 +587,11 @@ expr({'fun',L,{function,M,F,A}}, St0) ->
587587
name=#c_literal{val=make_fun},
588588
args=As},Aps,St1};
589589
expr({'fun',L,{clauses,Cs},Id}, St) ->
590-
fun_tq(Id, Cs, L, St);
590+
fun_tq(Id, Cs, L, St, unnamed);
591+
expr({named_fun,L,'_',Cs,Id}, St) ->
592+
fun_tq(Id, Cs, L, St, unnamed);
593+
expr({named_fun,L,Name,Cs,{Index,Uniq,_Fname}}, St) ->
594+
fun_tq({Index,Uniq,Name}, Cs, L, St, {named, Name});
591595
expr({call,L,{remote,_,M,F},As0}, #core{wanted=Wanted}=St0) ->
592596
{[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
593597
Lanno = lineno_anno(L, St1),
@@ -842,9 +846,9 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
842846
flags=#c_literal{val=Flags}},
843847
Eps ++ Eps2,St2}.
844848

845-
%% fun_tq(Id, [Clauses], Line, State) -> {Fun,[PreExp],State}.
849+
%% fun_tq(Id, [Clauses], Line, State, NameInfo) -> {Fun,[PreExp],State}.
846850

847-
fun_tq({_,_,Name}=Id, Cs0, L, St0) ->
851+
fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) ->
848852
Arity = clause_arity(hd(Cs0)),
849853
{Cs1,St1} = clauses(Cs0, St0),
850854
{Args,St2} = new_vars(Arity, St1),
@@ -853,7 +857,7 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0) ->
853857
Fc = function_clause(Ps, Anno, {Name,Arity}),
854858
Fun = #ifun{anno=#a{anno=Anno},
855859
id=[{id,Id}], %We KNOW!
856-
vars=Args,clauses=Cs1,fc=Fc},
860+
vars=Args,clauses=Cs1,fc=Fc,name=NameInfo},
857861
{Fun,[],St3}.
858862

859863
%% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}.
@@ -1711,13 +1715,18 @@ uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
17111715
Used = union(used_in_any(As1), used_in_any(Cs1)),
17121716
New = new_in_all(Cs1),
17131717
{#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
1714-
uexpr(#ifun{anno=A,id=Id,vars=As,clauses=Cs0,fc=Fc0}, Ks0, St0) ->
1718+
uexpr(#ifun{anno=A0,id=Id,vars=As,clauses=Cs0,fc=Fc0,name=Name}, Ks0, St0) ->
17151719
Avs = lit_list_vars(As),
1716-
Ks1 = union(Avs, Ks0),
1717-
{Cs1,St1} = ufun_clauses(Cs0, Ks1, St0),
1718-
{Fc1,St2} = ufun_clause(Fc0, Ks1, St1),
1719-
Used = subtract(intersection(used_in_any(Cs1), Ks0), Avs),
1720-
{#ifun{anno=A#a{us=Used,ns=[]},id=Id,vars=As,clauses=Cs1,fc=Fc1},St2};
1720+
Ks1 = case Name of
1721+
unnamed -> Ks0;
1722+
{named,FName} -> union(subtract([FName], Avs), Ks0)
1723+
end,
1724+
Ks2 = union(Avs, Ks1),
1725+
{Cs1,St1} = ufun_clauses(Cs0, Ks2, St0),
1726+
{Fc1,St2} = ufun_clause(Fc0, Ks2, St1),
1727+
Used = subtract(intersection(used_in_any(Cs1), Ks1), Avs),
1728+
A1 = A0#a{us=Used,ns=[]},
1729+
{#ifun{anno=A1,id=Id,vars=As,clauses=Cs1,fc=Fc1,name=Name},St2};
17211730
uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
17221731
Used = union(lit_vars(Op), lit_list_vars(As)),
17231732
{#iapply{anno=A#a{us=Used},op=Op,args=As},St};
@@ -2012,15 +2021,24 @@ cexpr(#itry{anno=A,args=La,vars=Vs,body=Lb,evars=Evs,handler=Lh}, As, St0) ->
20122021
cexpr(#icatch{anno=A,body=Les}, _As, St0) ->
20132022
{Ces,_Us1,St1} = cexprs(Les, [], St0), %Never export!
20142023
{#c_catch{body=Ces},[],A#a.us,St1};
2015-
cexpr(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
2016-
{Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export!
2017-
{Cfc,St2} = cclause(Lfc, [], St1),
2018-
Anno = A#a.anno,
2019-
{#c_fun{anno=Id++Anno,vars=Args,
2020-
body=#c_case{anno=Anno,
2021-
arg=set_anno(core_lib:make_values(Args), Anno),
2022-
clauses=Ccs ++ [Cfc]}},
2023-
[],A#a.us,St2};
2024+
cexpr(#ifun{name=unnamed}=Fun, As, St0) ->
2025+
cfun(Fun, As, St0);
2026+
cexpr(#ifun{anno=#a{us=Us0}=A0,name={named,Name},fc=#iclause{pats=Ps}}=Fun0,
2027+
As, St0) ->
2028+
case is_element(Name, Us0) of
2029+
false ->
2030+
cfun(Fun0, As, St0);
2031+
true ->
2032+
A1 = A0#a{us=del_element(Name, Us0)},
2033+
Fun1 = Fun0#ifun{anno=A1},
2034+
{#c_fun{body=Body}=CFun0,[],Us1,St1} = cfun(Fun1, As, St0),
2035+
RecVar = #c_var{name={Name,length(Ps)}},
2036+
Let = #c_let{vars=[#c_var{name=Name}],arg=RecVar,body=Body},
2037+
CFun1 = CFun0#c_fun{body=Let},
2038+
Letrec = #c_letrec{defs=[{RecVar,CFun1}],
2039+
body=RecVar},
2040+
{Letrec,[],Us1,St1}
2041+
end;
20242042
cexpr(#iapply{anno=A,op=Op,args=Args}, _As, St) ->
20252043
{#c_apply{anno=A#a.anno,op=Op,args=Args},[],A#a.us,St};
20262044
cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St) ->
@@ -2047,6 +2065,16 @@ cexpr(Lit, _As, St) ->
20472065
%%Vs = lit_vars(Lit),
20482066
{set_anno(Lit, Anno#a.anno),[],Vs,St}.
20492067

2068+
cfun(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
2069+
{Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export!
2070+
{Cfc,St2} = cclause(Lfc, [], St1),
2071+
Anno = A#a.anno,
2072+
{#c_fun{anno=Id++Anno,vars=Args,
2073+
body=#c_case{anno=Anno,
2074+
arg=set_anno(core_lib:make_values(Args), Anno),
2075+
clauses=Ccs ++ [Cfc]}},
2076+
[],A#a.us,St2}.
2077+
20502078
%% lit_vars(Literal) -> [Var].
20512079

20522080
lit_vars(Lit) -> lit_vars(Lit, []).

‎lib/dialyzer/test/options1_SUITE_data/src/compiler/sys_expand_pmod.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ expr({'fun',Line,Body,Info},St) ->
341341
{function,M,F,A} -> %This is an error in lint!
342342
{'fun',Line,{function,M,F,A},Info}
343343
end;
344+
expr({named_fun,Loc,Name,Cs,Info},St) ->
345+
{named_fun,Loc,Name,fun_clauses(Cs, St),Info};
344346
expr({call,Lc,{atom,_,new}=Name,As0},#pmod{parameters=Ps}=St)
345347
when length(As0) =:= length(Ps) ->
346348
%% The new() function does not take a 'THIS' argument (it's static).

‎lib/stdlib/examples/erl_id_trans.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ expr({'fun',Line,Body}) ->
419419
A = expr(A0),
420420
{'fun',Line,{function,M,F,A}}
421421
end;
422+
expr({named_fun,Loc,Name,Cs}) ->
423+
{named_fun,Loc,Name,fun_clauses(Cs)};
422424
expr({call,Line,F0,As0}) ->
423425
%% N.B. If F an atom then call to local function or BIF, if F a
424426
%% remote structure (see below) then call to other module,

‎lib/stdlib/src/epp.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,8 @@ macro_arg([{'case',Lc}|Toks], E, Arg) ->
12471247
macro_arg(Toks, ['end'|E], [{'case',Lc}|Arg]);
12481248
macro_arg([{'fun',Lc}|[{'(',_}|_]=Toks], E, Arg) ->
12491249
macro_arg(Toks, ['end'|E], [{'fun',Lc}|Arg]);
1250+
macro_arg([{'fun',_}=Fun,{var,_,_}=Name|[{'(',_}|_]=Toks], E, Arg) ->
1251+
macro_arg(Toks, ['end'|E], [Name,Fun|Arg]);
12501252
macro_arg([{'receive',Lr}|Toks], E, Arg) ->
12511253
macro_arg(Toks, ['end'|E], [{'receive',Lr}|Arg]);
12521254
macro_arg([{'try',Lr}|Toks], E, Arg) ->

‎lib/stdlib/src/erl_expand_records.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@ expr({'fun',_,{function,_M,_F,_A}}=Fun, St) ->
344344
expr({'fun',Line,{clauses,Cs0}}, St0) ->
345345
{Cs,St1} = clauses(Cs0, St0),
346346
{{'fun',Line,{clauses,Cs}},St1};
347+
expr({named_fun,Line,Name,Cs0}, St0) ->
348+
{Cs,St1} = clauses(Cs0, St0),
349+
{{named_fun,Line,Name,Cs},St1};
347350
expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, St) ->
348351
record_test(Line, A, Name, St);
349352
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},

‎lib/stdlib/src/erl_lint.erl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,15 @@ expr({'fun',Line,Body}, Vt, St) ->
20302030
{Bvt, St1} = expr_list([M,F,A], Vt, St),
20312031
{vtupdate(Bvt, Vt),St1}
20322032
end;
2033+
expr({named_fun,_,'_',Cs}, Vt, St) ->
2034+
fun_clauses(Cs, Vt, St);
2035+
expr({named_fun,Line,Name,Cs}, Vt, St0) ->
2036+
Nvt0 = [{Name,{bound,unused,[Line]}}],
2037+
St1 = shadow_vars(Nvt0, Vt, 'named fun', St0),
2038+
Nvt1 = vtupdate(vtsubtract(Vt, Nvt0), Nvt0),
2039+
{Csvt,St2} = fun_clauses(Cs, Nvt1, St1),
2040+
{_,St3} = check_unused_vars(vtupdate(Csvt, Nvt0), [], St2),
2041+
{vtold(Csvt, Vt),St3};
20332042
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
20342043
{Rvt,St1} = expr(E, Vt, St0),
20352044
{Rvt,exist_record(Ln, Name, St1)};
@@ -2182,6 +2191,7 @@ is_valid_record(Rec) ->
21822191
{lc, _, _, _} -> false;
21832192
{record_index, _, _, _} -> false;
21842193
{'fun', _, _} -> false;
2194+
{named_fun, _, _, _} -> false;
21852195
_ -> true
21862196
end.
21872197

‎lib/stdlib/src/erl_parse.yrl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,9 @@ fun_clause -> argument_list clause_guard clause_body :
406406
{Args,Pos} = '$1',
407407
{clause,Pos,'fun',Args,'$2','$3'}.
408408

409+
fun_clause -> var argument_list clause_guard clause_body :
410+
{clause,element(2, '$1'),element(3, '$1'),element(1, '$2'),'$3','$4'}.
411+
409412
try_expr -> 'try' exprs 'of' cr_clauses try_catch :
410413
build_try(?line('$1'),'$2','$4','$5').
411414
try_expr -> 'try' exprs try_catch :
@@ -799,8 +802,15 @@ build_rule(Cs) ->
799802
%% build_fun(Line, [Clause]) -> {'fun',Line,{clauses,[Clause]}}.
800803

801804
build_fun(Line, Cs) ->
805+
Name = element(3, hd(Cs)),
802806
Arity = length(element(4, hd(Cs))),
803-
{'fun',Line,{clauses,check_clauses(Cs, 'fun', Arity)}}.
807+
CheckedCs = check_clauses(Cs, Name, Arity),
808+
case Name of
809+
'fun' ->
810+
{'fun',Line,{clauses,CheckedCs}};
811+
Name ->
812+
{named_fun,Line,Name,CheckedCs}
813+
end.
804814

805815
check_clauses(Cs, Name, Arity) ->
806816
mapl(fun ({clause,L,N,As,G,B}) when N =:= Name, length(As) =:= Arity ->

‎lib/stdlib/src/erl_pp.erl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,17 @@ lexpr({'fun',_,{function,M,F,A}}, _Prec, Opts) ->
511511
ArityItem = lexpr(A, Opts),
512512
["fun ",NameItem,$:,CallItem,$/,ArityItem];
513513
lexpr({'fun',_,{clauses,Cs}}, _Prec, Opts) ->
514-
{list,[{first,'fun',fun_clauses(Cs, Opts)},'end']};
514+
{list,[{first,'fun',fun_clauses(Cs, Opts, unnamed)},'end']};
515+
lexpr({named_fun,_,Name,Cs}, _Prec, Opts) ->
516+
{list,[{first,['fun', " "],fun_clauses(Cs, Opts, {named, Name})},'end']};
515517
lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Opts) ->
516518
{force_nl,fun_info(Extra),
517-
{list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}};
519+
{list,[{first,'fun',fun_clauses(Cs, Opts, unnamed)},'end']}};
520+
lexpr({named_fun,_,Name,Cs,Extra}, _Prec, Opts) ->
521+
{force_nl,fun_info(Extra),
522+
{list,[{first,['fun', " "],fun_clauses(Cs, Opts, {named, Name})},'end']}};
523+
lexpr({'query',_,Lc}, _Prec, Opts) ->
524+
{list,[{step,leaf("query"),lexpr(Lc, 0, Opts)},'end']};
518525
lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) ->
519526
case erl_internal:bif(M, F, length(Args)) of
520527
true ->
@@ -729,8 +736,13 @@ stack_backtrace(S, El, Opts) ->
729736
%% fun_clauses(Clauses, Opts) -> [Char].
730737
%% Print 'fun' clauses.
731738

732-
fun_clauses(Cs, Opts) ->
733-
nl_clauses(fun fun_clause/2, [$;], Opts, Cs).
739+
fun_clauses(Cs, Opts, unnamed) ->
740+
nl_clauses(fun fun_clause/2, [$;], Opts, Cs);
741+
fun_clauses(Cs, Opts, {named, Name}) ->
742+
nl_clauses(fun (C, H) ->
743+
{step,Gl,Bl} = fun_clause(C, H),
744+
{step,[atom_to_list(Name),Gl],Bl}
745+
end, [$;], Opts, Cs).
734746

735747
fun_clause({clause,_,A,G,B}, Opts) ->
736748
El = args(A, Opts),

‎lib/stdlib/src/ms_transform.erl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,13 @@ copy({var,_Line,Name} = VarDef,Bound) ->
369369
copy({'fun',Line,{clauses,Clauses}},Bound) -> % Dont export bindings from funs
370370
{NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound),
371371
{{'fun',Line,{clauses,NewClauses}},Bound};
372+
copy({named_fun,Line,Name,Clauses},Bound) -> % Dont export bindings from funs
373+
Bound1 = case Name of
374+
'_' -> Bound;
375+
Name -> gb_sets:add(Name,Bound)
376+
end,
377+
{NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound1),
378+
{{named_fun,Line,Name,NewClauses},Bound};
372379
copy({'case',Line,Of,ClausesList},Bound) -> % Dont export bindings from funs
373380
{NewOf,NewBind0} = copy(Of,Bound),
374381
{NewClausesList,NewBindings} = copy_case_clauses(ClausesList,NewBind0,[]),

‎lib/stdlib/src/qlc_pt.erl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,19 @@ nos({'fun',L,{clauses,Cs}}, S) ->
25402540
{clause,Ln,H,G,B}
25412541
end || {clause,Ln,H0,G0,B0} <- Cs],
25422542
{{'fun',L,{clauses,NCs}}, S};
2543+
nos({named_fun,Loc,Name,Cs}, S) ->
2544+
{{var,NLoc,NName}, S1} = case Name of
2545+
'_' ->
2546+
S;
2547+
Name ->
2548+
nos_pattern({var,Loc,Name}, S)
2549+
end,
2550+
NCs = [begin
2551+
{H, S2} = nos_pattern(H0, S1),
2552+
{[G, B], _} = nos([G0, B0], S2),
2553+
{clause,CLoc,H,G,B}
2554+
end || {clause,CLoc,H0,G0,B0} <- Cs],
2555+
{{named_fun,NLoc,NName,NCs}, S};
25432556
nos({lc,L,E0,Qs0}, S) ->
25442557
%% QLCs as well as LCs. It is OK to modify LCs as long as they
25452558
%% occur within QLCs--the warning messages have already been found
@@ -2713,6 +2726,9 @@ var2const(E) ->
27132726

27142727
var_map(F, {var, _, _}=V) ->
27152728
F(V);
2729+
var_map(F, {named_fun,NLoc,NName,Cs}) ->
2730+
{var,Loc,Name} = F({var,NLoc,NName}),
2731+
{named_fun,Loc,Name,var_map(F, Cs)};
27162732
var_map(F, T) when is_tuple(T) ->
27172733
list_to_tuple(var_map(F, tuple_to_list(T)));
27182734
var_map(F, [E | Es]) ->

0 commit comments

Comments
 (0)