tags:

views:

195

answers:

4

I'm trying to get around a problem with file:consult/1 not allowing tuples with fun in them like in this example:

{add_one, fun(X) -> X+1 end}.

To get around this I'm considering writing the fun inside a string and evaluating it

{add_one, "fun(X) -> X+1 end"}.

The question is. How do I convert the string into a fun?

A: 

Maybe by using the erl_eval module?

jldupont
Yeah I was lead to that. However not sure on how to use it. It takes expressions generated by erl_parse but using erl_parse I usually end up with an Expression of a string.
Jon Gretar
+9  A: 
parse_fun_expr(S) ->
  {ok, Ts, _} = erl_scan:string(S),
  {ok, Exprs} = erl_parse:parse_exprs(Ts),
  {value, Fun, _} = erl_eval:exprs(Exprs, []),
  Fun.

Note that you need a period at the end of your fun expression, e.g. S = "fun(X) -> X + 1 end.".

Zed
Absolutely brilliant. Thank you.
Jon Gretar
+1  A: 

I'd like to point out that Zed's answer creates an interpreted fun. When the fun is called it enters the evaluator which starts to evaluates the abstract syntax tree returned by erl_parse:parse_exprs/1 that it has captured. Looking at the fun created:

11> erlang:fun_info(Fun, env).
{env,[[],none,none,
      [{clause,1,
               [{var,1,'X'}],
               [],
               [{op,1,'+',{var,1,'X'},{integer,1,1}}]}]]}
12> erlang:fun_info(Fun, module).
{module,erl_eval}

One can see that it has closed over the parsed abstract syntax tree as seen in the env info, and it is a fun created inside erlang_eval as seen in the module info.

It is possible to use the erlang compiler to create a compiled module at runtime, and a pointer toward that is compile:forms/2 and code:load_binary/3. But the details of that should probably go into another stackoverflow question.

Christian
Thanks for pointing that out. Here is an example of compiling forms: http://stackoverflow.com/questions/1974236/string-to-abstract-syntax-tree.
Zed
+5  A: 

file:script/1 almost does what you want - it evaluates a series of erlang expressions from a file and returns the last result. You could use it in place of file:consult/1 but you'd need to change the format of the file from "term. term. term." giving [term, term ,term] to "[term, term , term]." giving [term, term, term] - place a single expression in the file instead of a sequence.

archaelus
I think this is a better solution. If there is a library function which does what you want then you should use it. Also parsing strings when you don't need to seems a bit off.
rvirding
Ahh. I may consider doing that instead. I had my head so stuck in file:consult that I forgot to consider alternatives on that part.
Jon Gretar
One extra question though. How can I use records in file:script?
Jon Gretar
I don't think you can use records in file:script -- you can pass in a set of bindings, but I think it expects records to be replaced at this point. To use records you'd need to parse it yourself, expand out the records ala erl_expand_records and then run it through erl_eval. Someone should wrap all those stages up into a better file:consult/eval really.
archaelus
Yeah I figured. But thanks. file:script/1 was a much better way than the direction I was heading.
Jon Gretar