[erlang-questions] Defining static functions in abstract modules
Matthew Dempsky
matthew@REDACTED
Wed Apr 9 11:04:54 CEST 2008
It would be nice to be able to define arbitrary static functions
within an abstract module. Right now you can kludge around this by
exploiting that local functions named 'new' are always static and
write something like
new(incr, X) -> X + 1.
but it would be nicer to just write
-static([incr/1]).
incr(X) -> X + 1.
Below is a very rudimentary patch that adds support for this syntax.
It's basically only enough to compile and run very simple test cases
and to demonstrate that implementing this does not seem too difficult.
(One problem I'm already aware of is that a static function foo/1 and
an instance function foo/0 silently collide without any warning... so
don't do that. ;-))
Another option would be to make the attribute statement just
"-static([incr])." and then all incr functions are defined static, but
that seems unnecessarily limiting.
If the long-term goal is to have separate namespaces for static and
instance functions (e.g., so (Mod:new(...)):module_info() works and
returns something meaningful for the module instance), then maybe a
syntax that actually decorates individual function definitions would
be better?
Are there any plans on adding static functions in a future release?
--- stdlib/src/erl_parse.yrl.orig 2007-11-27 07:57:32.000000000 -0800
+++ stdlib/src/erl_parse.yrl 2008-04-09 01:25:31.000000000 -0700
@@ -627,6 +627,12 @@
{attribute,La,export,farity_list(ExpList)};
_Other -> error_bad_decl(La, export)
end;
+build_attribute({atom,La,static}, Val) ->
+ case Val of
+ [ExpList] ->
+ {attribute,La,static,farity_list(ExpList)};
+ _Other -> error_bad_decl(La, static)
+ end;
build_attribute({atom,La,import}, Val) ->
case Val of
[Name] ->
--- compiler/src/sys_pre_expand.erl.orig 2008-02-05 05:37:09.000000000 -0800
+++ compiler/src/sys_pre_expand.erl 2008-04-09 01:26:06.000000000 -0700
@@ -38,6 +38,7 @@
parameters=undefined, %Module parameters
package="", %Module package
exports=[], %Exports
+ statics=[], %Statics
imports=[], %Imports
mod_imports, %Module Imports
compile=[], %Compile flags
@@ -109,6 +110,7 @@
end,
{Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps,
St0#expand.exports,
+ St0#expand.statics,
St0#expand.defined),
St1 = St0#expand{exports=Xs, defined=Ds},
{Fs2,St2} = add_instance(Ps, Fs1, St1),
@@ -220,6 +222,8 @@
package = packages:strip_last(M)};
attribute(export, Es, St) ->
St#expand{exports=union(from_list(Es), St#expand.exports)};
+attribute(static, Ss, St) ->
+ St#expand{statics=union(from_list(Ss), St#expand.statics)};
attribute(import, Is, St) ->
import(Is, St);
attribute(compile, C, St) when is_list(C) ->
--- compiler/src/sys_expand_pmod.erl.orig 2008-02-05 05:38:12.000000000 -0800
+++ compiler/src/sys_expand_pmod.erl 2008-04-09 01:32:36.000000000 -0700
@@ -25,18 +25,19 @@
%% and 'exports'. The automatic 'new/N' function is neither added to the
%% definitions nor to the 'exports'/'defines' lists yet.
--export([forms/4]).
+-export([forms/5]).
--record(pmod, {parameters, exports, defined, predef}).
+-record(pmod, {parameters, exports, statics, defined, predef}).
%% TODO: more abstract handling of predefined/static functions.
-forms(Fs0, Ps, Es0, Ds0) ->
+forms(Fs0, Ps, Es0, Ss0, Ds0) ->
PreDef = [{module_info,0},{module_info,1}],
- forms(Fs0, Ps, Es0, Ds0, PreDef).
+ forms(Fs0, Ps, Es0, Ss0, Ds0, PreDef).
-forms(Fs0, Ps, Es0, Ds0, PreDef) ->
- St0 = #pmod{parameters=Ps,exports=Es0,defined=Ds0, predef=PreDef},
+forms(Fs0, Ps, Es0, Ss0, Ds0, PreDef) ->
+ St0 = #pmod{parameters=Ps,exports=Es0,statics=Ss0,defined=Ds0,
+ predef=PreDef},
{Fs1, St1} = forms(Fs0, St0),
Es1 = update_function_names(Es0, St1),
Ds1 = update_function_names(Ds0, St1),
@@ -50,15 +51,22 @@
[update_function_name(E, St) || E <- Es].
update_function_name(E={F,A}, St) when F =/= new ->
- case ordsets:is_element(E, St#pmod.predef) of
+ case ordsets:is_element(E, St#pmod.predef) orelse
+ ordsets:is_element(E, St#pmod.statics) of
true -> E;
false -> {F, A + 1}
end;
update_function_name(E, _St) ->
E.
-update_forms([{function,L,N,A,Cs}|Fs],St) when N =/= new ->
- [{function,L,N,A+1,Cs}|update_forms(Fs,St)];
+update_forms([F0={function,L,N,A,Cs}|Fs],St) when N =/= new ->
+ F = case ordsets:is_element({N,A}, St#pmod.statics) of
+ true ->
+ F0;
+ false ->
+ {function,L,N,A+1,Cs}
+ end,
+ [F|update_forms(Fs,St)];
update_forms([F|Fs],St) ->
[F|update_forms(Fs,St)];
update_forms([],_St) ->
@@ -74,9 +82,14 @@
{[], St0}.
%% Only function definitions are of interest here. State is not updated.
-form({function,Line,Name0,Arity0,Clauses0},St) when Name0 =/= new ->
- {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
- {{function,Line,Name,Arity,Clauses},St};
+form(F = {function,Line,Name0,Arity0,Clauses0},St) when Name0 =/= new ->
+ case ordsets:is_element({Name0, Arity0}, St#pmod.statics) of
+ true ->
+ {F,St};
+ false ->
+ {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
+ {{function,Line,Name,Arity,Clauses},St}
+ end;
%% Pass anything else through
form(F,St) -> {F,St}.
@@ -362,7 +375,13 @@
expr({call,Lc,{atom,Lf,F},As0},St) ->
%% Local function call - needs THIS parameter.
As1 = expr_list(As0,St),
- {call,Lc,{atom,Lf,F},As1 ++ [{var,0,'THIS'}]};
+ As2 = case ordsets:is_element({F, length(As1)}, St#pmod.statics) of
+ true ->
+ As1;
+ false ->
+ As1 ++ [{var,0,'THIS'}]
+ end,
+ {call,Lc,{atom,Lf,F},As2};
expr({call,Line,F0,As0},St) ->
%% Other function call
F1 = expr(F0,St),
More information about the erlang-questions
mailing list