17

Is there a straightforward way to convert an Erlang fun to a string? A call to io_lib:format only prints the function reference, e.g. something like "#Fun<erl_eval.20.67289768>". For example, I'd like to be able to do this:

1> Fun = fun() -> atom_to_list('hello world') end.
2> FunStr = fun_to_str(Fun).
"fun() -> atom_to_list('hello world') end."

I'm looking for how to implement fun_to_str. In javascript, some interpreters have a .toSource() function that can be called on any object, including functions, that print their string representation. Any info is appreciated, thanks.

Travis Webb
  • 14,688
  • 7
  • 55
  • 109

2 Answers2

18

First, get the environment variables for the fun (which includes the abstract code):

1> {env, [{_, _, _, Abs}]} = erlang:fun_info(Fun, env).                     
{env,[{[],
       {eval,#Fun<shell.21.83096281>},
       {value,#Fun<shell.5.83096281>},
       [{clause,1,[],[],
                [{call,1,{atom,1,atom_to_list},[{atom,1,hello_world}]}]}]}]}

Pretty print the abstract code using erl_pp:

3> Str = erl_pp:expr({'fun', 1, {clauses, Abs}}).           
[[[["fun",
    [[[[["()"]," ->"],
       ["\n       ",
        [["atom_to_list",[[40,["'hello world'",41]]]]]]]]]]],
  [10,["end"]]]]
4> io:format([Str|"\n"]).
fun() ->
       atom_to_list('hello world')
end
ok

(You have to add {'fun', 1, {clauses, ...}} around it to make it a complete Erlang expression)

Adam Lindberg
  • 16,447
  • 6
  • 65
  • 85
  • In practice, this works really well, but sometimes I'll write a non-trivial `fun` and using this method to convert it to a string produces the following: `fun()-> end` with no body. Any ideas on what could cause this? – Travis Webb Mar 16 '11 at 14:52
  • What do you mean with a non-trivial fun? Can you give an example? – Adam Lindberg Mar 17 '11 at 09:19
  • 1
    any fun that does something inside; I can run your commands in the shell and it works, but doing the same thing inside my program doesn't. The `fun_info` step returns no tokens other than `fun` and `end`, even though the function is syntactically correct and compiles and all that – Travis Webb Mar 17 '11 at 12:37
  • Funs in compiled code are just a reference to a hidden function compiled into the module they're defined in. Have you tried to compile your modules with the option `debug_info`? – Adam Lindberg Mar 17 '11 at 13:04
  • On 17RC2 `+debug_info` doesn't seem to change anything. – aronisstav Apr 09 '14 at 08:58
3

You might be able to use erlang:fun_info/2 for that, atleast i get some information from the shell when doing

1> erlang:fun_info(fun() -> test,ok end, env).
{env,[[],
     {value,#Fun<shell.7.37281544>},
     {eval,#Fun<shell.24.85590193>},
     [{clause,1,[],[],[{atom,1,test},{atom,1,ok}]}]]}
2>

You want the last list with the clause atom and then pretty print it using for instance erl_pp

Lukas
  • 5,182
  • 26
  • 17