Functions in data structures

Fredrik Linder fredrik.linder@REDACTED
Tue Jun 17 13:06:26 CEST 2003


Thank you!

This was very informative, and as you said, this IS groovy. :-)

/Fredrik

> -----Original Message-----
> From: owner-erlang-questions@REDACTED
> [mailto:owner-erlang-questions@REDACTED]On Behalf Of Luke Gorrie
> Sent: den 17 juni 2003 12:30
> To: Fredrik Linder
> Cc: Erlang Questions
> Subject: Re: Functions in data structures
> 
> 
> "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