Functions in data structures
Luke Gorrie
luke@REDACTED
Tue Jun 17 12:29:51 CEST 2003
"Fredrik Linder" <fredrik.linder@REDACTED> 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:
(x@REDACTED)1> Node = node().
x@REDACTED
(x@REDACTED)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:
(x@REDACTED)3> F().
{fun_created_on,x@REDACTED}
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