Having fun with funs :)

Brujo Benavides
Erlang Battleground
5 min readJun 10, 2016

--

To continue with my Erlang battle-story saga, let’s talk a bit about anonymous functions (a.k.a. funs). I think I already shared this story somewhere but I googled it and I couldn’t find it anywhere. It was probably on IRC. Anyway, let’s have some fun

Adam Sandler & Seth Rogen - Funny People (2009)

In the Shell

This is how I discovered this thing: I popped up an Erlang shell and I did something like this:

1> F = fun() -> “Hello, world!” end.
#Fun<erl_eval.20.50752066>
2> F = fun() -> “Hello, world!” end.
#Fun<erl_eval.20.50752066>
3>

That looks totally reasonable at first sight, but then you remember that = in Erlang is not assignment, it’s pattern-matching. So, in line #2, is Erlang actually detecting that the given function is the same as the one in line #1? Last time I checked that was a hard problem. It surely can’t be checking that both functions produce the same outputs for the same inputs, right? Maybe it’s just checking the function text… let’s see…

3> F = fun() -> “Hello, “ “world!” end.
#Fun<erl_eval.20.50752066>

Well… that’s not really fair, is it? “Hello, “ “world!” is in the end the same string as “Hello, world!”. Let’s try with something syntactically different…

4> F = fun() -> “Hello, “ ++ “world!” end.
** exception error: no match of right hand side value #Fun<erl_eval.20.50752066>

Ok. That makes sense. Erlang’s pattern-matching algorithm is checking the fun’s syntax not its semantics. But, wait a second… look at that exception error again… #Fun<erl_eval.20.50752066>. Wasn’t that our original function?

5> G = fun() -> goodbye end.
#Fun<erl_eval.20.50752066>

Ohhhh, look at that… all of our funs have the same name! Well, that’s odd, but it’s ok as long as we can tell that they’re internally different.

In your Modules

And at this point, everything gets weird super-fast. Because all that pattern-matching checking fun syntax thing I described above only works in the shell. If you try to do the same in a module, like this one:

you’ll get this result…

1> c(hello).
{ok,hello}
2> hello:world().
** exception error: no match of right hand side value #Fun<hello.1.65732938>
in function hello:world/0 (hello.erl, line 7)
3>

and the same happens in the shell if you use a single expression, like this:

1> F = fun() -> “Hello, world!” end,
1> F = fun() -> “Hello, world!” end.
** exception error: no match of right hand side value #Fun<erl_eval.20.50752066>

What’s going on here?

As usual, I’ll pause here and let you go check if you can figure this out on your own.

Now, let me tell you what I’ve found…

It all boils down to what an anonymous function actually represents in the different contexts. To understand that we have a very useful function: erlang:fun_info/1. Given an anonymous function, fun_info will tell you all you need to know to evaluate it. I added an exported function to our hello module so I can retrieve an anonymous function from it. Look…

So now I can compare the fun_info from all of our funs. Let’s start with this new one:

7> erlang:fun_info(hello:f()).
[{pid,<0.74.0>},
{module,hello},
{new_index,0},
{new_uniq,<<29,221,195,226,62,18,74,8,213,112,251,233,
174,128,161,226>>},
{index,0},
{uniq,15658527},
{name,’-f/0-fun-0-’},
{arity,0},
{env,[]},
{type,local}]
8> hello:f().
#Fun<hello.0.15658527>

I highlighted the important pieces. As you can see, the module in charge of this fun’s code is hello, it’s index is 0 and it’s uniq is 15658527, therefore the fun is called #Fun<hello.0.15658527>. But that’s not all, it also has an atom as name: ‘-f/0-fun-0-’. And you can totally use that name, for instance to trace the function with redbug:

2> X = hello:f().
#Fun<hello.0.15658527>
3> redbug:start(“hello:’-f/0-fun-0-’”).
{33,1}
4> X().
“Hello, world!”
% 14:02:16 <0.41.0>({erlang,apply,2})
% hello:’-f/0-fun-0-’()
5>

It looks pretty much like any other non exported function, right? Hold on to that thought, we’ll come back to it in a minute…

Now, let’s see how our shell-defined funs look like:

5> F = fun() -> “Hello, world!” end.
#Fun<erl_eval.20.50752066>
6> erlang:fun_info(F).
[{pid,<0.41.0>},
{module,erl_eval},
{new_index,20},
{new_uniq,<<96,205,72,68,75,104,221,132,114,190,222,211,
56,153,90,202>>},
{index,20},
{uniq,50752066},
{name,’-expr/5-fun-3-’},
{arity,0},
{env,[{[],
{eval,#Fun<shell.21.83096281>},
{value,#Fun<shell.5.83096281>},
[{clause,1,[],[],[{string,1,”Hello, world!”}]}]}]
},
{type,local}]

As you can see, in this case the fun lives in erl_eval. That’s odd, right? We never edited that module, did we? The index and uniq matches the name again, which what we would’ve expected. But the key part is the env. There you have the AST for our fun. That is telling us the fun which we have in F is not exactly the fun we wrote. It’s an anonymous function contained in the erl_eval module that evaluates the AST that we defined above. Do you want to see that anonymous function? I did! And given its name (-expr/5-fun-3-) I could infer that we’re talking about this one. And bonus track if you follow that link: you can find how many args can a fun have if you define it in the shell ;)

Ok, but then why can I assign a variable twice to the same fun in the shell in different expressions, but not in the same expression? fun_info to the rescue, again!!

7> F = fun() -> “Hello, world!” end,
7> G = fun() -> “Hello, world!” end.
#Fun<erl_eval.20.50752066>
8> erlang:fun_info(G).
[{pid,<0.41.0>},
{module,erl_eval},
{new_index,20},
{new_uniq,<<96,205,72,68,75,104,221,132,114,190,222,211,
56,153,90,202>>},
{index,20},
{uniq,50752066},
{name,’-expr/5-fun-3-’},
{arity,0},
{env,[{[],
{eval,#Fun<shell.21.83096281>},
{value,#Fun<shell.5.83096281>},
[{clause,2,[],[],[{string,2,”Hello, world!”}]}]}]},
{type,local}]

As you can see, the AST is different this time, since our function is in the second clause of the expression. Therefore, the fun is different, because the env is obviously part of what defines an anonymous function.

Blowing things Up

As a bonus track, I wanted to show you something that you should not do in your production servers, kids.

Remember that function name that made our fun look a lot like a not exported function… ‘-f/0-fun-0-’. Well, I saw that and my inner-devil immediately thought

What if I do have a not-exported function with that name? After all, it is just an atom, isn’t it?

Let’s try to compile that module…

$ erlc hello.erl
$ erl
1> hello:f().
** exception error: undefined function hello:f/0
2>
=ERROR REPORT==== 10-Jun-2016::14:55:29 ===
Loading of /Users/elbrujohalcon/hello.beam failed: badfile
=ERROR REPORT==== 10-Jun-2016::14:55:29 ===
beam/beam_load.c(4356): Error loading module hello:
missing or short chunk ‘FunT’

The module compiles… into a bad file. I’m not sure how to use this thing yet… but I’m pretty sure someone will find a way to take advantage of this eventually.

--

--