Funs references - clarification needed

If we write in a shell:

1> fun() ->  "hello" end.
#Fun<erl_eval.43.3316493>
2> fun() ->  "world" end.
#Fun<erl_eval.43.3316493>
3>

we can see that both funs have the same printed “reference”.

Can someone explain what these numbers after erl_eval mean and if we could have different numbers for different funs?
I think it would be easier to debug and/or understand what is happening if there was some kind of differentiation there.

4 Likes

Interesting. I had never noticed that the reference is always the same.

1 Like

This is an artifact of you creating code in the shell, which is an interpreter. If you want different printed representation for different same-arity functions then put the code in a module and compile it.

The shell uses erl_eval to interpret a representation of the code. It doesn’t create new actual (BEAM) code, instead each function expression you enter in the shell gets mapped to a closure combining a pre-existing actual (BEAM) function (that’s the #Fun<erl_eval.43.3316493>) with free variables that tell it how to interpret calls to it. There is one such BEAM function per arity, which is why those two same-arity funs look the same in your shell.

Try looking at those fun values with erlang:fun_info/1 or /2.

Look in lib/stdlib/src/erl_eval.erl line 339 (OTP-25.1) for details.

7 Likes

The first field in the fun reference is the name of the module in which the fun is created. In this case they are created by the Erlang evaluator module erl_eval which is what you see in the fun reference. The second number is, or at least used to be anyway :smile:, the index of the fun in that module, so in this case it was/would have been the 43rd fun. As I said that might not be so now.

6 Likes

From my testing (at least for now) for #Fun<X.Y.Z>

  • X is the module or creation (as others stated before)
  • Y seems to be index in X (as stated by @rvirding) and it is an artifact of the shell that is the same in the original example
  • Z seems to have something to do with the module version? erlang:fun_info/1 shows that Z is marked as uniq but I still don’t understand what is it exactly

in any case printout of fun doesn’t show much information about an instance of function (how to differentiate between multiple instances?).For module:

-module(test).
-export([rf/0]).

rf() -> 
	R = rand:uniform(), 
	fun() -> R end.

when we run

10> test:rf().
#Fun<test.0.108715254>
11> test:rf().
#Fun<test.0.108715254>

both references look the same. IMHO it would be helpful if they were not.

2 Likes

Well, in one way it is the same fun function, the difference is the environment in each of them which in this case contains the value of the variable R, amongst other things.

NOTE: what we see here is “just” how the fun is printed. It is not possible to create a fun from its printed representation and of course this printed format could change in the future. The only defined and safe way to get information about a fun is to use the functions erlang:fun_info/1/2.

5 Likes

I once wrote a blog post exactly about this: Having fun with funs :). To continue with my Erlang battle-story… | by Brujo Benavides | Erlang Battleground | Medium

8 Likes

Thank you, for sharing the blog post it is really good. For me, it is not surprising at all that there is some “magic” with erl_eval when in the shell, rather uniq number is mysterious. Furthermore, it is not very intuitive that all instances of anonymous function have the same uniq.

I get that fun references have markers that firmly identify their “position” in defining the module but I wonder if there is a lost opportunity to have more information there.

If my assumption about uniq is correct (that it is linked to the version of the module) then we can read reference #Fun<X.Y.Z> as: an anonymous function that is defined (Y+1)nth in version Z of module X.

Maybe just for clarity reasons, there can be a fourth marker T that would be something as an instantiation number. T doesn’t have to be ordered just guaranteed to be unique. So that we would read #Fun<X.Y.Z.T> as: a T instance of an anonymous function that is defined (Y+1)nth in version Z of module X.

3 Likes