Jon Gretar
Jon Gretar

Reputation: 5412

Convert a string into a fun

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?

Upvotes: 8

Views: 1441

Answers (5)

ligaoren
ligaoren

Reputation: 1063

2> F =fun(Str,Binding) ->
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case lists:reverse(Ts) of
          [{dot,_}|_] -> Ts;
          TsR -> lists:reverse([{dot,1} | TsR])
      end,
{ok,Expr} = erl_parse:parse_exprs(Ts1),
erl_eval:exprs(Expr, Binding) end.
#Fun<erl_eval.12.111823515>
3> F("A=23.",[]).
{value,23,[{'A',23}]}

5> F("12+B.",[{'B',23}]).
{value,35,[{'B',23}]}

Upvotes: 0

archaelus
archaelus

Reputation: 7129

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.

Upvotes: 6

Christian
Christian

Reputation: 9486

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.

Upvotes: 2

Zed
Zed

Reputation: 57648

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.".

Upvotes: 11

jldupont
jldupont

Reputation: 96716

Maybe by using the erl_eval module?

Upvotes: 0

Related Questions