[erlang-questions] Variable bindings

Richard O'Keefe <>
Mon Jan 28 00:40:17 CET 2013


On 28/01/2013, at 12:11 AM, Tyron Zerafa wrote:

> I want to build a call which accepts a fun, extracts all of its code and dependencies, pass everything over to a remote node and execute it there.

The problem is that funs don't normally _have_ code available
unless you compile the containing module with a special option.

Have you considered the alternative of accepting the *source code*
of a fun and parsing that?

> The only problem I am having is extracting the value of the free vars of such fun.

Er, the free variables of a fun are _not_ the variables that are
in scope at its point of creation, they are that subset of those
variables that are *used* inside the fun.

What a fun _is_, conceptually, is a tuple containing a code reference
and a set of variable bindings.  If someone gives you a fun, you know
exactly what the values for those variables are because they are right
there.  A fun does not point back to the stack frame of its creator.

Let's take a look at a tiny example.

foo(X, Y) ->
    M = (X+Y)/2,
    fun (Z) -> {Z,M} end.

Now let's look at the BEAM code.

{function, foo, 2, 2}.
  {label,1}.
    {func_info,{atom,foo},{atom,foo},2}.
  {label,2}.
    % add Y to X; put the result back in X
    {gc_bif,'+',{f,0},2,[{x,0},{x,1}],{x,0}}.
    % check that there is room for two floats
    {test_heap,{alloc,[{words,0},{floats,1}]},2}.
    % convert X to floating point (in FR 0)
    {fconv,{x,0},{fr,0}}.
    % convert 2 to floating point (in FR 1)
    %?? Why is this not done at compile time?
    {fconv,{integer,2},{fr,1}}.
    fclearerror.
    % Divide FR 0 by FR 1 and put the result in FR 0.
    {bif,fdiv,{f,0},[{fr,0},{fr,1}],{fr,0}}.
    {fcheckerror,{f,0}}.
    % Now that we know all went well, box the result
    % and put it back in X.
    {fmove,{fr,0},{x,0}}.
    {'%live',1}.
    % Create a fun, copying X (but not what X points to)
    % into it.
    {make_fun2,{f,8},0,98308069,1}.
    return.

{function, '-foo/2-fun-0-', 2, 8}.
  {label,7}.
    {func_info,{atom,foo},{atom,'-foo/2-fun-0-'},2}.
    % {f,8} in the make_fun2 instruction points here.
  {label,8}.
    % Check that there is room for a 2-tuple
    {test_heap,3,2}.
    % start to create it, and let X2 point to it.
    {put_tuple,2,{x,2}}.
    % put in Z
    {put,{x,0}}.
    % put in M
    {put,{x,1}}.
    % move X2 down to X0
    {move,{x,2},{x,0}}.
    % return X0
    return.

> I was thinking about adding these to the parameter list of the fun,

As the code shows, the (selected) free variables _are_ hidden
parameters of the fun.

> i.e shadowing the respective vars.

?

Finding the dependencies of a fun, at least those not already
compiled inline, can be done from its BEAM code.  You do not
need the source code.  What you cannot get, either way, is
which _versions_ of the modules used the code depends on.

And that creates a big problem.

On local node L, send F1 to remote node R.
F1 needs module M.
Ensure that R has M loaded.
But M on R is a completely different module from M on L.
So send L to R as well and have it install that.
Congratulations, you just broke everything else on R
that used M.

If you can manage version consistency to the point at
which every node you try to send stuff to has the same
versions of all the relevant modules you need, then you
don't need to extract dependencies at run time.
If you can't do that, it's not going to work anyway.

This whole sending-code-to-a-remote-node business is to be done
with *extreme* vigilant trepidation, if at all.

Do you *really* want me to be able to call
   your_module:your_handy_function(fun () -> halt() end)

Consider whether it is possible to devise a *sublanguage* of
trustworthy code.




More information about the erlang-questions mailing list