Functions in data structures

Luke Gorrie <>
Tue Jun 17 12:29:51 CEST 2003


"Fredrik Linder" <> writes:

> -- ZIP --
> > There is another option, seldomly exercized: you can send an
> > abstract form and evaluate it on the other side. This is
> > more stable in time and space than the other option, but
> > at the cost of slightly worse performance. The performance
> > issue can be partly overcome by on-the-fly compilation.
> >
> > /Uffe
> 
> Interesting, is that implemented within Erlang? And if so, where can I read
> about it/how to use it?

Consider this shell fragment:

  ()1> Node = node().
  
  ()2> F = fun() -> {fun_created_on, Node} end.
  #Fun<erl_eval.19.280769>

The last return value, #Fun<erl_eval.19.280769>, is not a fun created
directly from the expression {fun_created_on, Node}. Instead, the fun
was created in erl_eval, and it captures the current variable bindings
and the syntax-tree of the code it's actually supposed to run
(i.e. {fun_created_on, Node}).

Here is the bit of erl_eval that creates the fun:

 1. expr({'fun',Line,{clauses,Cs}}, Bs, Lf) ->
 2.     %% This is a really ugly hack!
 3.     case length(element(3,hd(Cs))) of
 4.         0 -> {value,fun () -> eval_fun(Cs, [], Bs, Lf) end,Bs};

If you actually call the fun:

  ()3> F().  
  {}

The code it executes is actually the eval_fun(...) from line (4)
above. This then *interprets* the {fun_created_on, Node} expression,
which was captured in the erl_eval fun along with the set of variable
bindings.

Very groovy!

The nice part is that, so long as two nodes have compatible copies of
erl_eval, they will always be able to run each others' code in this
way.

BTW, here is a code snippet out of Distel's backend which uses
erl_eval directly:

  eval_expression(S) ->
      case parse_expr(S) of
          {ok, Parse} ->
              try_evaluation(Parse);
          {error, {_, erl_parse, Err}} ->
              {error, Err}
      end.

  try_evaluation(Parse) ->
      case catch erl_eval:exprs(Parse, []) of
          {value, V, _} ->
              {ok, flatten(io_lib:format("~p", [V]))};
          {'EXIT', Reason} ->
              {error, Reason}
      end.

  parse_expr(S) ->
      {ok, Scan, _} = erl_scan:string(S),
      erl_parse:parse_exprs(Scan).

Using that, you can create "portable" funs in your programs, like
this:

  1> distel:eval_expression("fun() -> hello() end.").
  {ok,"#Fun<erl_eval.19.280769>"}

Although if you want the funs to include variable bindings, you'll
need to pass them as a key-value list to that call to erl_eval:exprs,
where I just use the empty list.

Cheers,
Luke




More information about the erlang-questions mailing list