[erlang-questions] Defining static functions in abstract modules

Matthew Dempsky matthew@REDACTED
Wed Apr 9 11:53:35 CEST 2008


I have an abstract module zap.  I have a client module quux.

quux should not know about zap's internals, in particular the abstract
module parameters.  I can hide the abstract module parameters by
writing a custom zap:new function to handle instantiating the zap
module.

However, because zap:new is not entirely trivial, I would like to
define helper functions.  These helper functions don't make sense
outside of implementing zap, so I'd prefer to define them within the
same file as zap:new.

zap:new cannot call other functions in zap except for zap:new,
zap:instance, and zap:module_info/{0,1}.

If I want to define a static zap:foo/1 function to help in
implementing zap:new/1, I can work around this by defining a zap:new/2
function with a form like "new(foo, X) -> ....".  But that's gross.

I could define a zap_static module for the static helper functions,
but then I have to do extra work when changing the parameters to the
helper functions.


On Wed, Apr 9, 2008 at 2:37 AM, Hynek Vychodil <vychodil.hynek@REDACTED> wrote:
> Still don't understand. Internal helper functions should be internally in
> which? From where you want call "foo(X)"? Where you would like to define
> "foo(X)"?
>
>
>
> On Wed, Apr 9, 2008 at 11:22 AM, Matthew Dempsky <matthew@REDACTED>
> wrote:
>
> > Because I want to be able to abstract away how to instantiate a
> > module, and I want to be able to define internal helper functions to
> > do it, and I want to be able to call them "foo(X)" instead of
> > "new(foo, X)".
> >
> >
> >
> >
> > On Wed, Apr 9, 2008 at 2:20 AM, Hynek Vychodil <vychodil.hynek@REDACTED>
> wrote:
> > > UGH, static functions? Where is benefit to do it? What matter? I don't
> > > understand.
> > >
> > >
> > >
> > > On Wed, Apr 9, 2008 at 11:04 AM, Matthew Dempsky <matthew@REDACTED>
> > > wrote:
> > >
> > > >
> > > >
> > > >
> > > > 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),
> > > > _______________________________________________
> > > > erlang-questions mailing list
> > > > erlang-questions@REDACTED
> > > > http://www.erlang.org/mailman/listinfo/erlang-questions
> > > >
> > >
> > >
> > >
> > > --
> > > --Hynek (Pichi) Vychodil
> >
>
>
>
> --
> --Hynek (Pichi) Vychodil



More information about the erlang-questions mailing list